]> granicus.if.org Git - imagemagick/blob - MagickCore/display.c
(no commit message)
[imagemagick] / MagickCore / display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7 %               D   D    I    SS     P   P  L      A   A   Y Y                %
8 %               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9 %               D   D    I       SS  P      L      A   A    Y                 %
10 %               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11 %                                                                             %
12 %                                                                             %
13 %        MagickCore Methods to Interactively Display and Edit an Image        %
14 %                                                                             %
15 %                             Software Design                                 %
16 %                               John Cristy                                   %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/client.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/colorspace.h"
49 #include "MagickCore/composite.h"
50 #include "MagickCore/constitute.h"
51 #include "MagickCore/decorate.h"
52 #include "MagickCore/delegate.h"
53 #include "MagickCore/display.h"
54 #include "MagickCore/display-private.h"
55 #include "MagickCore/draw.h"
56 #include "MagickCore/effect.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/fx.h"
61 #include "MagickCore/geometry.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/magick.h"
67 #include "MagickCore/memory_.h"
68 #include "MagickCore/monitor.h"
69 #include "MagickCore/monitor-private.h"
70 #include "MagickCore/montage.h"
71 #include "MagickCore/option.h"
72 #include "MagickCore/paint.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/PreRvIcccm.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum.h"
78 #include "MagickCore/quantum-private.h"
79 #include "MagickCore/resize.h"
80 #include "MagickCore/resource_.h"
81 #include "MagickCore/shear.h"
82 #include "MagickCore/segment.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/threshold.h"
87 #include "MagickCore/utility.h"
88 #include "MagickCore/version.h"
89 #include "MagickCore/widget.h"
90 #include "MagickCore/xwindow-private.h"
91 \f
92 #if defined(MAGICKCORE_X11_DELEGATE)
93 /*
94   Define declarations.
95 */
96 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
97 \f
98 /*
99   Constant declarations.
100 */
101 static const unsigned char
102   HighlightBitmap[8] =
103   {
104     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
105   },
106   OpaqueBitmap[8] =
107   {
108     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
109   },
110   ShadowBitmap[8] =
111   {
112     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
113   };
114
115 static const char
116   *PageSizes[] =
117   {
118     "Letter",
119     "Tabloid",
120     "Ledger",
121     "Legal",
122     "Statement",
123     "Executive",
124     "A3",
125     "A4",
126     "A5",
127     "B4",
128     "B5",
129     "Folio",
130     "Quarto",
131     "10x14",
132     (char *) NULL
133   };
134 \f
135 /*
136   Help widget declarations.
137 */
138 static const char
139   *ImageAnnotateHelp[] =
140   {
141     "In annotate mode, the Command widget has these options:",
142     "",
143     "    Font Name",
144     "      fixed",
145     "      variable",
146     "      5x8",
147     "      6x10",
148     "      7x13bold",
149     "      8x13bold",
150     "      9x15bold",
151     "      10x20",
152     "      12x24",
153     "      Browser...",
154     "    Font Color",
155     "      black",
156     "      blue",
157     "      cyan",
158     "      green",
159     "      gray",
160     "      red",
161     "      magenta",
162     "      yellow",
163     "      white",
164     "      transparent",
165     "      Browser...",
166     "    Font Color",
167     "      black",
168     "      blue",
169     "      cyan",
170     "      green",
171     "      gray",
172     "      red",
173     "      magenta",
174     "      yellow",
175     "      white",
176     "      transparent",
177     "      Browser...",
178     "    Rotate Text",
179     "      -90",
180     "      -45",
181     "      -30",
182     "      0",
183     "      30",
184     "      45",
185     "      90",
186     "      180",
187     "      Dialog...",
188     "    Help",
189     "    Dismiss",
190     "",
191     "Choose a font name from the Font Name sub-menu.  Additional",
192     "font names can be specified with the font browser.  You can",
193     "change the menu names by setting the X resources font1",
194     "through font9.",
195     "",
196     "Choose a font color from the Font Color sub-menu.",
197     "Additional font colors can be specified with the color",
198     "browser.  You can change the menu colors by setting the X",
199     "resources pen1 through pen9.",
200     "",
201     "If you select the color browser and press Grab, you can",
202     "choose the font color by moving the pointer to the desired",
203     "color on the screen and press any button.",
204     "",
205     "If you choose to rotate the text, choose Rotate Text from the",
206     "menu and select an angle.  Typically you will only want to",
207     "rotate one line of text at a time.  Depending on the angle you",
208     "choose, subsequent lines may end up overwriting each other.",
209     "",
210     "Choosing a font and its color is optional.  The default font",
211     "is fixed and the default color is black.  However, you must",
212     "choose a location to begin entering text and press button 1.",
213     "An underscore character will appear at the location of the",
214     "pointer.  The cursor changes to a pencil to indicate you are",
215     "in text mode.  To exit immediately, press Dismiss.",
216     "",
217     "In text mode, any key presses will display the character at",
218     "the location of the underscore and advance the underscore",
219     "cursor.  Enter your text and once completed press Apply to",
220     "finish your image annotation.  To correct errors press BACK",
221     "SPACE.  To delete an entire line of text, press DELETE.  Any",
222     "text that exceeds the boundaries of the image window is",
223     "automagically continued onto the next line.",
224     "",
225     "The actual color you request for the font is saved in the",
226     "image.  However, the color that appears in your image window",
227     "may be different.  For example, on a monochrome screen the",
228     "text will appear black or white even if you choose the color",
229     "red as the font color.  However, the image saved to a file",
230     "with -write is written with red lettering.  To assure the",
231     "correct color text in the final image, any PseudoClass image",
232     "is promoted to DirectClass (see miff(5)).  To force a",
233     "PseudoClass image to remain PseudoClass, use -colors.",
234     (char *) NULL,
235   },
236   *ImageChopHelp[] =
237   {
238     "In chop mode, the Command widget has these options:",
239     "",
240     "    Direction",
241     "      horizontal",
242     "      vertical",
243     "    Help",
244     "    Dismiss",
245     "",
246     "If the you choose the horizontal direction (this the",
247     "default), the area of the image between the two horizontal",
248     "endpoints of the chop line is removed.  Otherwise, the area",
249     "of the image between the two vertical endpoints of the chop",
250     "line is removed.",
251     "",
252     "Select a location within the image window to begin your chop,",
253     "press and hold any button.  Next, move the pointer to",
254     "another location in the image.  As you move a line will",
255     "connect the initial location and the pointer.  When you",
256     "release the button, the area within the image to chop is",
257     "determined by which direction you choose from the Command",
258     "widget.",
259     "",
260     "To cancel the image chopping, move the pointer back to the",
261     "starting point of the line and release the button.",
262     (char *) NULL,
263   },
264   *ImageColorEditHelp[] =
265   {
266     "In color edit mode, the Command widget has these options:",
267     "",
268     "    Method",
269     "      point",
270     "      replace",
271     "      floodfill",
272     "      filltoborder",
273     "      reset",
274     "    Pixel Color",
275     "      black",
276     "      blue",
277     "      cyan",
278     "      green",
279     "      gray",
280     "      red",
281     "      magenta",
282     "      yellow",
283     "      white",
284     "      Browser...",
285     "    Border Color",
286     "      black",
287     "      blue",
288     "      cyan",
289     "      green",
290     "      gray",
291     "      red",
292     "      magenta",
293     "      yellow",
294     "      white",
295     "      Browser...",
296     "    Fuzz",
297     "      0%",
298     "      2%",
299     "      5%",
300     "      10%",
301     "      15%",
302     "      Dialog...",
303     "    Undo",
304     "    Help",
305     "    Dismiss",
306     "",
307     "Choose a color editing method from the Method sub-menu",
308     "of the Command widget.  The point method recolors any pixel",
309     "selected with the pointer until the button is released.  The",
310     "replace method recolors any pixel that matches the color of",
311     "the pixel you select with a button press.  Floodfill recolors",
312     "any pixel that matches the color of the pixel you select with",
313     "a button press and is a neighbor.  Whereas filltoborder recolors",
314     "any neighbor pixel that is not the border color.  Finally reset",
315     "changes the entire image to the designated color.",
316     "",
317     "Next, choose a pixel color from the Pixel Color sub-menu.",
318     "Additional pixel colors can be specified with the color",
319     "browser.  You can change the menu colors by setting the X",
320     "resources pen1 through pen9.",
321     "",
322     "Now press button 1 to select a pixel within the image window",
323     "to change its color.  Additional pixels may be recolored as",
324     "prescribed by the method you choose.",
325     "",
326     "If the Magnify widget is mapped, it can be helpful in positioning",
327     "your pointer within the image (refer to button 2).",
328     "",
329     "The actual color you request for the pixels is saved in the",
330     "image.  However, the color that appears in your image window",
331     "may be different.  For example, on a monochrome screen the",
332     "pixel will appear black or white even if you choose the",
333     "color red as the pixel color.  However, the image saved to a",
334     "file with -write is written with red pixels.  To assure the",
335     "correct color text in the final image, any PseudoClass image",
336     "is promoted to DirectClass (see miff(5)).  To force a",
337     "PseudoClass image to remain PseudoClass, use -colors.",
338     (char *) NULL,
339   },
340   *ImageCompositeHelp[] =
341   {
342     "First a widget window is displayed requesting you to enter an",
343     "image name. Press Composite, Grab or type a file name.",
344     "Press Cancel if you choose not to create a composite image.",
345     "When you choose Grab, move the pointer to the desired window",
346     "and press any button.",
347     "",
348     "If the Composite image does not have any matte information,",
349     "you are informed and the file browser is displayed again.",
350     "Enter the name of a mask image.  The image is typically",
351     "grayscale and the same size as the composite image.  If the",
352     "image is not grayscale, it is converted to grayscale and the",
353     "resulting intensities are used as matte information.",
354     "",
355     "A small window appears showing the location of the cursor in",
356     "the image window. You are now in composite mode.  To exit",
357     "immediately, press Dismiss.  In composite mode, the Command",
358     "widget has these options:",
359     "",
360     "    Operators",
361     "      Over",
362     "      In",
363     "      Out",
364     "      Atop",
365     "      Xor",
366     "      Plus",
367     "      Minus",
368     "      Add",
369     "      Subtract",
370     "      Difference",
371     "      Multiply",
372     "      Bumpmap",
373     "      Copy",
374     "      CopyRed",
375     "      CopyGreen",
376     "      CopyBlue",
377     "      CopyOpacity",
378     "      Clear",
379     "    Dissolve",
380     "    Displace",
381     "    Help",
382     "    Dismiss",
383     "",
384     "Choose a composite operation from the Operators sub-menu of",
385     "the Command widget.  How each operator behaves is described",
386     "below.  Image window is the image currently displayed on",
387     "your X server and image is the image obtained with the File",
388     "Browser widget.",
389     "",
390     "Over     The result is the union of the two image shapes,",
391     "         with image obscuring image window in the region of",
392     "         overlap.",
393     "",
394     "In       The result is simply image cut by the shape of",
395     "         image window.  None of the image data of image",
396     "         window is in the result.",
397     "",
398     "Out      The resulting image is image with the shape of",
399     "         image window cut out.",
400     "",
401     "Atop     The result is the same shape as image image window,",
402     "         with image obscuring image window where the image",
403     "         shapes overlap.  Note this differs from over",
404     "         because the portion of image outside image window's",
405     "         shape does not appear in the result.",
406     "",
407     "Xor      The result is the image data from both image and",
408     "         image window that is outside the overlap region.",
409     "         The overlap region is blank.",
410     "",
411     "Plus     The result is just the sum of the image data.",
412     "         Output values are cropped to QuantumRange (no overflow).",
413     "",
414     "Minus    The result of image - image window, with underflow",
415     "         cropped to zero.",
416     "",
417     "Add      The result of image + image window, with overflow",
418     "         wrapping around (mod 256).",
419     "",
420     "Subtract The result of image - image window, with underflow",
421     "         wrapping around (mod 256).  The add and subtract",
422     "         operators can be used to perform reversible",
423     "         transformations.",
424     "",
425     "Difference",
426     "         The result of abs(image - image window).  This",
427     "         useful for comparing two very similar images.",
428     "",
429     "Multiply",
430     "         The result of image * image window.  This",
431     "         useful for the creation of drop-shadows.",
432     "",
433     "Bumpmap  The result of surface normals from image * image",
434     "         window.",
435     "",
436     "Copy     The resulting image is image window replaced with",
437     "         image.  Here the matte information is ignored.",
438     "",
439     "CopyRed  The red layer of the image window is replace with",
440     "         the red layer of the image.  The other layers are",
441     "         untouched.",
442     "",
443     "CopyGreen",
444     "         The green layer of the image window is replace with",
445     "         the green layer of the image.  The other layers are",
446     "         untouched.",
447     "",
448     "CopyBlue The blue layer of the image window is replace with",
449     "         the blue layer of the image.  The other layers are",
450     "         untouched.",
451     "",
452     "CopyOpacity",
453     "         The matte layer of the image window is replace with",
454     "         the matte layer of the image.  The other layers are",
455     "         untouched.",
456     "",
457     "The image compositor requires a matte, or alpha channel in",
458     "the image for some operations.  This extra channel usually",
459     "defines a mask which represents a sort of a cookie-cutter",
460     "for the image.  This the case when matte is opaque (full",
461     "coverage) for pixels inside the shape, zero outside, and",
462     "between 0 and QuantumRange on the boundary.  If image does not",
463     "have a matte channel, it is initialized with 0 for any pixel",
464     "matching in color to pixel location (0,0), otherwise QuantumRange.",
465     "",
466     "If you choose Dissolve, the composite operator becomes Over.  The",
467     "image matte channel percent transparency is initialized to factor.",
468     "The image window is initialized to (100-factor). Where factor is the",
469     "value you specify in the Dialog widget.",
470     "",
471     "Displace shifts the image pixels as defined by a displacement",
472     "map.  With this option, image is used as a displacement map.",
473     "Black, within the displacement map, is a maximum positive",
474     "displacement.  White is a maximum negative displacement and",
475     "middle gray is neutral.  The displacement is scaled to determine",
476     "the pixel shift.  By default, the displacement applies in both the",
477     "horizontal and vertical directions.  However, if you specify a mask,",
478     "image is the horizontal X displacement and mask the vertical Y",
479     "displacement.",
480     "",
481     "Note that matte information for image window is not retained",
482     "for colormapped X server visuals (e.g. StaticColor,",
483     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
484     "behavior may require a TrueColor or DirectColor visual or a",
485     "Standard Colormap.",
486     "",
487     "Choosing a composite operator is optional.  The default",
488     "operator is replace.  However, you must choose a location to",
489     "composite your image and press button 1.  Press and hold the",
490     "button before releasing and an outline of the image will",
491     "appear to help you identify your location.",
492     "",
493     "The actual colors of the composite image is saved.  However,",
494     "the color that appears in image window may be different.",
495     "For example, on a monochrome screen image window will appear",
496     "black or white even though your composited image may have",
497     "many colors.  If the image is saved to a file it is written",
498     "with the correct colors.  To assure the correct colors are",
499     "saved in the final image, any PseudoClass image is promoted",
500     "to DirectClass (see miff(5)).  To force a PseudoClass image",
501     "to remain PseudoClass, use -colors.",
502     (char *) NULL,
503   },
504   *ImageCutHelp[] =
505   {
506     "In cut mode, the Command widget has these options:",
507     "",
508     "    Help",
509     "    Dismiss",
510     "",
511     "To define a cut region, press button 1 and drag.  The",
512     "cut region is defined by a highlighted rectangle that",
513     "expands or contracts as it follows the pointer.  Once you",
514     "are satisfied with the cut region, release the button.",
515     "You are now in rectify mode.  In rectify mode, the Command",
516     "widget has these options:",
517     "",
518     "    Cut",
519     "    Help",
520     "    Dismiss",
521     "",
522     "You can make adjustments by moving the pointer to one of the",
523     "cut rectangle corners, pressing a button, and dragging.",
524     "Finally, press Cut to commit your copy region.  To",
525     "exit without cutting the image, press Dismiss.",
526     (char *) NULL,
527   },
528   *ImageCopyHelp[] =
529   {
530     "In copy mode, the Command widget has these options:",
531     "",
532     "    Help",
533     "    Dismiss",
534     "",
535     "To define a copy region, press button 1 and drag.  The",
536     "copy region is defined by a highlighted rectangle that",
537     "expands or contracts as it follows the pointer.  Once you",
538     "are satisfied with the copy region, release the button.",
539     "You are now in rectify mode.  In rectify mode, the Command",
540     "widget has these options:",
541     "",
542     "    Copy",
543     "    Help",
544     "    Dismiss",
545     "",
546     "You can make adjustments by moving the pointer to one of the",
547     "copy rectangle corners, pressing a button, and dragging.",
548     "Finally, press Copy to commit your copy region.  To",
549     "exit without copying the image, press Dismiss.",
550     (char *) NULL,
551   },
552   *ImageCropHelp[] =
553   {
554     "In crop mode, the Command widget has these options:",
555     "",
556     "    Help",
557     "    Dismiss",
558     "",
559     "To define a cropping region, press button 1 and drag.  The",
560     "cropping region is defined by a highlighted rectangle that",
561     "expands or contracts as it follows the pointer.  Once you",
562     "are satisfied with the cropping region, release the button.",
563     "You are now in rectify mode.  In rectify mode, the Command",
564     "widget has these options:",
565     "",
566     "    Crop",
567     "    Help",
568     "    Dismiss",
569     "",
570     "You can make adjustments by moving the pointer to one of the",
571     "cropping rectangle corners, pressing a button, and dragging.",
572     "Finally, press Crop to commit your cropping region.  To",
573     "exit without cropping the image, press Dismiss.",
574     (char *) NULL,
575   },
576   *ImageDrawHelp[] =
577   {
578     "The cursor changes to a crosshair to indicate you are in",
579     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
580     "the Command widget has these options:",
581     "",
582     "    Element",
583     "      point",
584     "      line",
585     "      rectangle",
586     "      fill rectangle",
587     "      circle",
588     "      fill circle",
589     "      ellipse",
590     "      fill ellipse",
591     "      polygon",
592     "      fill polygon",
593     "    Color",
594     "      black",
595     "      blue",
596     "      cyan",
597     "      green",
598     "      gray",
599     "      red",
600     "      magenta",
601     "      yellow",
602     "      white",
603     "      transparent",
604     "      Browser...",
605     "    Stipple",
606     "      Brick",
607     "      Diagonal",
608     "      Scales",
609     "      Vertical",
610     "      Wavy",
611     "      Translucent",
612     "      Opaque",
613     "      Open...",
614     "    Width",
615     "      1",
616     "      2",
617     "      4",
618     "      8",
619     "      16",
620     "      Dialog...",
621     "    Undo",
622     "    Help",
623     "    Dismiss",
624     "",
625     "Choose a drawing primitive from the Element sub-menu.",
626     "",
627     "Choose a color from the Color sub-menu.  Additional",
628     "colors can be specified with the color browser.",
629     "",
630     "If you choose the color browser and press Grab, you can",
631     "select the color by moving the pointer to the desired",
632     "color on the screen and press any button.  The transparent",
633     "color updates the image matte channel and is useful for",
634     "image compositing.",
635     "",
636     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
637     "Additional stipples can be specified with the file browser.",
638     "Stipples obtained from the file browser must be on disk in the",
639     "X11 bitmap format.",
640     "",
641     "Choose a width, if appropriate, from the Width sub-menu.  To",
642     "choose a specific width select the Dialog widget.",
643     "",
644     "Choose a point in the Image window and press button 1 and",
645     "hold.  Next, move the pointer to another location in the",
646     "image.  As you move, a line connects the initial location and",
647     "the pointer.  When you release the button, the image is",
648     "updated with the primitive you just drew.  For polygons, the",
649     "image is updated when you press and release the button without",
650     "moving the pointer.",
651     "",
652     "To cancel image drawing, move the pointer back to the",
653     "starting point of the line and release the button.",
654     (char *) NULL,
655   },
656   *DisplayHelp[] =
657   {
658     "BUTTONS",
659     "  The effects of each button press is described below.  Three",
660     "  buttons are required.  If you have a two button mouse,",
661     "  button 1 and 3 are returned.  Press ALT and button 3 to",
662     "  simulate button 2.",
663     "",
664     "  1    Press this button to map or unmap the Command widget.",
665     "",
666     "  2    Press and drag to define a region of the image to",
667     "       magnify.",
668     "",
669     "  3    Press and drag to choose from a select set of commands.",
670     "       This button behaves differently if the image being",
671     "       displayed is a visual image directory.  Here, choose a",
672     "       particular tile of the directory and press this button and",
673     "       drag to select a command from a pop-up menu.  Choose from",
674     "       these menu items:",
675     "",
676     "           Open",
677     "           Next",
678     "           Former",
679     "           Delete",
680     "           Update",
681     "",
682     "       If you choose Open, the image represented by the tile is",
683     "       displayed.  To return to the visual image directory, choose",
684     "       Next from the Command widget.  Next and Former moves to the",
685     "       next or former image respectively.  Choose Delete to delete",
686     "       a particular image tile.  Finally, choose Update to",
687     "       synchronize all the image tiles with their respective",
688     "       images.",
689     "",
690     "COMMAND WIDGET",
691     "  The Command widget lists a number of sub-menus and commands.",
692     "  They are",
693     "",
694     "      File",
695     "        Open...",
696     "        Next",
697     "        Former",
698     "        Select...",
699     "        Save...",
700     "        Print...",
701     "        Delete...",
702     "        New...",
703     "        Visual Directory...",
704     "        Quit",
705     "      Edit",
706     "        Undo",
707     "        Redo",
708     "        Cut",
709     "        Copy",
710     "        Paste",
711     "      View",
712     "        Half Size",
713     "        Original Size",
714     "        Double Size",
715     "        Resize...",
716     "        Apply",
717     "        Refresh",
718     "        Restore",
719     "      Transform",
720     "        Crop",
721     "        Chop",
722     "        Flop",
723     "        Flip",
724     "        Rotate Right",
725     "        Rotate Left",
726     "        Rotate...",
727     "        Shear...",
728     "        Roll...",
729     "        Trim Edges",
730     "      Enhance",
731     "        Brightness...",
732     "        Saturation...",
733     "        Hue...",
734     "        Gamma...",
735     "        Sharpen...",
736     "        Dull",
737     "        Contrast Stretch...",
738     "        Sigmoidal Contrast...",
739     "        Normalize",
740     "        Equalize",
741     "        Negate",
742     "        Grayscale",
743     "        Map...",
744     "        Quantize...",
745     "      Effects",
746     "        Despeckle",
747     "        Emboss",
748     "        Reduce Noise",
749     "        Add Noise",
750     "        Sharpen...",
751     "        Blur...",
752     "        Threshold...",
753     "        Edge Detect...",
754     "        Spread...",
755     "        Shade...",
756     "        Painting...",
757     "        Segment...",
758     "      F/X",
759     "        Solarize...",
760     "        Sepia Tone...",
761     "        Swirl...",
762     "        Implode...",
763     "        Vignette...",
764     "        Wave...",
765     "        Oil Painting...",
766     "        Charcoal Drawing...",
767     "      Image Edit",
768     "        Annotate...",
769     "        Draw...",
770     "        Color...",
771     "        Matte...",
772     "        Composite...",
773     "        Add Border...",
774     "        Add Frame...",
775     "        Comment...",
776     "        Launch...",
777     "        Region of Interest...",
778     "      Miscellany",
779     "        Image Info",
780     "        Zoom Image",
781     "        Show Preview...",
782     "        Show Histogram",
783     "        Show Matte",
784     "        Background...",
785     "        Slide Show",
786     "        Preferences...",
787     "      Help",
788     "        Overview",
789     "        Browse Documentation",
790     "        About Display",
791     "",
792     "  Menu items with a indented triangle have a sub-menu.  They",
793     "  are represented above as the indented items.  To access a",
794     "  sub-menu item, move the pointer to the appropriate menu and",
795     "  press a button and drag.  When you find the desired sub-menu",
796     "  item, release the button and the command is executed.  Move",
797     "  the pointer away from the sub-menu if you decide not to",
798     "  execute a particular command.",
799     "",
800     "KEYBOARD ACCELERATORS",
801     "  Accelerators are one or two key presses that effect a",
802     "  particular command.  The keyboard accelerators that",
803     "  display(1) understands is:",
804     "",
805     "  Ctl+O     Press to open an image from a file.",
806     "",
807     "  space     Press to display the next image.",
808     "",
809     "            If the image is a multi-paged document such as a Postscript",
810     "            document, you can skip ahead several pages by preceding",
811     "            this command with a number.  For example to display the",
812     "            third page beyond the current page, press 3<space>.",
813     "",
814     "  backspace Press to display the former image.",
815     "",
816     "            If the image is a multi-paged document such as a Postscript",
817     "            document, you can skip behind several pages by preceding",
818     "            this command with a number.  For example to display the",
819     "            third page preceding the current page, press 3<backspace>.",
820     "",
821     "  Ctl+S     Press to write the image to a file.",
822     "",
823     "  Ctl+P     Press to print the image to a Postscript printer.",
824     "",
825     "  Ctl+D     Press to delete an image file.",
826     "",
827     "  Ctl+N     Press to create a blank canvas.",
828     "",
829     "  Ctl+Q     Press to discard all images and exit program.",
830     "",
831     "  Ctl+Z     Press to undo last image transformation.",
832     "",
833     "  Ctl+R     Press to redo last image transformation.",
834     "",
835     "  Ctl+X     Press to cut a region of the image.",
836     "",
837     "  Ctl+C     Press to copy a region of the image.",
838     "",
839     "  Ctl+V     Press to paste a region to the image.",
840     "",
841     "  <         Press to half the image size.",
842     "",
843     "  -         Press to return to the original image size.",
844     "",
845     "  >         Press to double the image size.",
846     "",
847     "  %         Press to resize the image to a width and height you",
848     "            specify.",
849     "",
850     "Cmd-A       Press to make any image transformations permanent."
851     "",
852     "            By default, any image size transformations are applied",
853     "            to the original image to create the image displayed on",
854     "            the X server.  However, the transformations are not",
855     "            permanent (i.e. the original image does not change",
856     "            size only the X image does).  For example, if you",
857     "            press > the X image will appear to double in size,",
858     "            but the original image will in fact remain the same size.",
859     "            To force the original image to double in size, press >",
860     "            followed by Cmd-A.",
861     "",
862     "  @         Press to refresh the image window.",
863     "",
864     "  C         Press to cut out a rectangular region of the image.",
865     "",
866     "  [         Press to chop the image.",
867     "",
868     "  H         Press to flop image in the horizontal direction.",
869     "",
870     "  V         Press to flip image in the vertical direction.",
871     "",
872     "  /         Press to rotate the image 90 degrees clockwise.",
873     "",
874     " \\         Press to rotate the image 90 degrees counter-clockwise.",
875     "",
876     "  *         Press to rotate the image the number of degrees you",
877     "            specify.",
878     "",
879     "  S         Press to shear the image the number of degrees you",
880     "            specify.",
881     "",
882     "  R         Press to roll the image.",
883     "",
884     "  T         Press to trim the image edges.",
885     "",
886     "  Shft-H    Press to vary the image hue.",
887     "",
888     "  Shft-S    Press to vary the color saturation.",
889     "",
890     "  Shft-L    Press to vary the color brightness.",
891     "",
892     "  Shft-G    Press to gamma correct the image.",
893     "",
894     "  Shft-C    Press to sharpen the image contrast.",
895     "",
896     "  Shft-Z    Press to dull the image contrast.",
897     "",
898     "  =         Press to perform histogram equalization on the image.",
899     "",
900     "  Shft-N    Press to perform histogram normalization on the image.",
901     "",
902     "  Shft-~    Press to negate the colors of the image.",
903     "",
904     "  .         Press to convert the image colors to gray.",
905     "",
906     "  Shft-#    Press to set the maximum number of unique colors in the",
907     "            image.",
908     "",
909     "  F2        Press to reduce the speckles in an image.",
910     "",
911     "  F3        Press to eliminate peak noise from an image.",
912     "",
913     "  F4        Press to add noise to an image.",
914     "",
915     "  F5        Press to sharpen an image.",
916     "",
917     "  F6        Press to delete an image file.",
918     "",
919     "  F7        Press to threshold the image.",
920     "",
921     "  F8        Press to detect edges within an image.",
922     "",
923     "  F9        Press to emboss an image.",
924     "",
925     "  F10       Press to displace pixels by a random amount.",
926     "",
927     "  F11       Press to negate all pixels above the threshold level.",
928     "",
929     "  F12       Press to shade the image using a distant light source.",
930     "",
931     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
932     "",
933     "  F14       Press to segment the image by color.",
934     "",
935     "  Meta-S    Press to swirl image pixels about the center.",
936     "",
937     "  Meta-I    Press to implode image pixels about the center.",
938     "",
939     "  Meta-W    Press to alter an image along a sine wave.",
940     "",
941     "  Meta-P    Press to simulate an oil painting.",
942     "",
943     "  Meta-C    Press to simulate a charcoal drawing.",
944     "",
945     "  Alt-A     Press to annotate the image with text.",
946     "",
947     "  Alt-D     Press to draw on an image.",
948     "",
949     "  Alt-P     Press to edit an image pixel color.",
950     "",
951     "  Alt-M     Press to edit the image matte information.",
952     "",
953     "  Alt-V     Press to composite the image with another.",
954     "",
955     "  Alt-B     Press to add a border to the image.",
956     "",
957     "  Alt-F     Press to add an ornamental border to the image.",
958     "",
959     "  Alt-Shft-!",
960     "            Press to add an image comment.",
961     "",
962     "  Ctl-A     Press to apply image processing techniques to a region",
963     "            of interest.",
964     "",
965     "  Shft-?    Press to display information about the image.",
966     "",
967     "  Shft-+    Press to map the zoom image window.",
968     "",
969     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
970     "",
971     "  F1        Press to display helpful information about display(1).",
972     "",
973     "  Find      Press to browse documentation about ImageMagick.",
974     "",
975     "  1-9       Press to change the level of magnification.",
976     "",
977     "  Use the arrow keys to move the image one pixel up, down,",
978     "  left, or right within the magnify window.  Be sure to first",
979     "  map the magnify window by pressing button 2.",
980     "",
981     "  Press ALT and one of the arrow keys to trim off one pixel",
982     "  from any side of the image.",
983     (char *) NULL,
984   },
985   *ImageMatteEditHelp[] =
986   {
987     "Matte information within an image is useful for some",
988     "operations such as image compositing (See IMAGE",
989     "COMPOSITING).  This extra channel usually defines a mask",
990     "which represents a sort of a cookie-cutter for the image.",
991     "This the case when matte is opaque (full coverage) for",
992     "pixels inside the shape, zero outside, and between 0 and",
993     "QuantumRange on the boundary.",
994     "",
995     "A small window appears showing the location of the cursor in",
996     "the image window. You are now in matte edit mode.  To exit",
997     "immediately, press Dismiss.  In matte edit mode, the Command",
998     "widget has these options:",
999     "",
1000     "    Method",
1001     "      point",
1002     "      replace",
1003     "      floodfill",
1004     "      filltoborder",
1005     "      reset",
1006     "    Border Color",
1007     "      black",
1008     "      blue",
1009     "      cyan",
1010     "      green",
1011     "      gray",
1012     "      red",
1013     "      magenta",
1014     "      yellow",
1015     "      white",
1016     "      Browser...",
1017     "    Fuzz",
1018     "      0%",
1019     "      2%",
1020     "      5%",
1021     "      10%",
1022     "      15%",
1023     "      Dialog...",
1024     "    Matte",
1025     "      Opaque",
1026     "      Transparent",
1027     "      Dialog...",
1028     "    Undo",
1029     "    Help",
1030     "    Dismiss",
1031     "",
1032     "Choose a matte editing method from the Method sub-menu of",
1033     "the Command widget.  The point method changes the matte value",
1034     "of any pixel selected with the pointer until the button is",
1035     "is released.  The replace method changes the matte value of",
1036     "any pixel that matches the color of the pixel you select with",
1037     "a button press.  Floodfill changes the matte value of any pixel",
1038     "that matches the color of the pixel you select with a button",
1039     "press and is a neighbor.  Whereas filltoborder changes the matte",
1040     "value any neighbor pixel that is not the border color.  Finally",
1041     "reset changes the entire image to the designated matte value.",
1042     "",
1043     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1044     "select the Dialog entry.  Here a dialog appears requesting a matte",
1045     "value.  The value you select is assigned as the opacity value of the",
1046     "selected pixel or pixels.",
1047     "",
1048     "Now, press any button to select a pixel within the image",
1049     "window to change its matte value.",
1050     "",
1051     "If the Magnify widget is mapped, it can be helpful in positioning",
1052     "your pointer within the image (refer to button 2).",
1053     "",
1054     "Matte information is only valid in a DirectClass image.",
1055     "Therefore, any PseudoClass image is promoted to DirectClass",
1056     "(see miff(5)).  Note that matte information for PseudoClass",
1057     "is not retained for colormapped X server visuals (e.g.",
1058     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1059     "immediately save your image to a file (refer to Write).",
1060     "Correct matte editing behavior may require a TrueColor or",
1061     "DirectColor visual or a Standard Colormap.",
1062     (char *) NULL,
1063   },
1064   *ImagePanHelp[] =
1065   {
1066     "When an image exceeds the width or height of the X server",
1067     "screen, display maps a small panning icon.  The rectangle",
1068     "within the panning icon shows the area that is currently",
1069     "displayed in the image window.  To pan about the image,",
1070     "press any button and drag the pointer within the panning",
1071     "icon.  The pan rectangle moves with the pointer and the",
1072     "image window is updated to reflect the location of the",
1073     "rectangle within the panning icon.  When you have selected",
1074     "the area of the image you wish to view, release the button.",
1075     "",
1076     "Use the arrow keys to pan the image one pixel up, down,",
1077     "left, or right within the image window.",
1078     "",
1079     "The panning icon is withdrawn if the image becomes smaller",
1080     "than the dimensions of the X server screen.",
1081     (char *) NULL,
1082   },
1083   *ImagePasteHelp[] =
1084   {
1085     "A small window appears showing the location of the cursor in",
1086     "the image window. You are now in paste mode.  To exit",
1087     "immediately, press Dismiss.  In paste mode, the Command",
1088     "widget has these options:",
1089     "",
1090     "    Operators",
1091     "      over",
1092     "      in",
1093     "      out",
1094     "      atop",
1095     "      xor",
1096     "      plus",
1097     "      minus",
1098     "      add",
1099     "      subtract",
1100     "      difference",
1101     "      replace",
1102     "    Help",
1103     "    Dismiss",
1104     "",
1105     "Choose a composite operation from the Operators sub-menu of",
1106     "the Command widget.  How each operator behaves is described",
1107     "below.  Image window is the image currently displayed on",
1108     "your X server and image is the image obtained with the File",
1109     "Browser widget.",
1110     "",
1111     "Over     The result is the union of the two image shapes,",
1112     "         with image obscuring image window in the region of",
1113     "         overlap.",
1114     "",
1115     "In       The result is simply image cut by the shape of",
1116     "         image window.  None of the image data of image",
1117     "         window is in the result.",
1118     "",
1119     "Out      The resulting image is image with the shape of",
1120     "         image window cut out.",
1121     "",
1122     "Atop     The result is the same shape as image image window,",
1123     "         with image obscuring image window where the image",
1124     "         shapes overlap.  Note this differs from over",
1125     "         because the portion of image outside image window's",
1126     "         shape does not appear in the result.",
1127     "",
1128     "Xor      The result is the image data from both image and",
1129     "         image window that is outside the overlap region.",
1130     "         The overlap region is blank.",
1131     "",
1132     "Plus     The result is just the sum of the image data.",
1133     "         Output values are cropped to QuantumRange (no overflow).",
1134     "         This operation is independent of the matte",
1135     "         channels.",
1136     "",
1137     "Minus    The result of image - image window, with underflow",
1138     "         cropped to zero.",
1139     "",
1140     "Add      The result of image + image window, with overflow",
1141     "         wrapping around (mod 256).",
1142     "",
1143     "Subtract The result of image - image window, with underflow",
1144     "         wrapping around (mod 256).  The add and subtract",
1145     "         operators can be used to perform reversible",
1146     "         transformations.",
1147     "",
1148     "Difference",
1149     "         The result of abs(image - image window).  This",
1150     "         useful for comparing two very similar images.",
1151     "",
1152     "Copy     The resulting image is image window replaced with",
1153     "         image.  Here the matte information is ignored.",
1154     "",
1155     "CopyRed  The red layer of the image window is replace with",
1156     "         the red layer of the image.  The other layers are",
1157     "         untouched.",
1158     "",
1159     "CopyGreen",
1160     "         The green layer of the image window is replace with",
1161     "         the green layer of the image.  The other layers are",
1162     "         untouched.",
1163     "",
1164     "CopyBlue The blue layer of the image window is replace with",
1165     "         the blue layer of the image.  The other layers are",
1166     "         untouched.",
1167     "",
1168     "CopyOpacity",
1169     "         The matte layer of the image window is replace with",
1170     "         the matte layer of the image.  The other layers are",
1171     "         untouched.",
1172     "",
1173     "The image compositor requires a matte, or alpha channel in",
1174     "the image for some operations.  This extra channel usually",
1175     "defines a mask which represents a sort of a cookie-cutter",
1176     "for the image.  This the case when matte is opaque (full",
1177     "coverage) for pixels inside the shape, zero outside, and",
1178     "between 0 and QuantumRange on the boundary.  If image does not",
1179     "have a matte channel, it is initialized with 0 for any pixel",
1180     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1181     "",
1182     "Note that matte information for image window is not retained",
1183     "for colormapped X server visuals (e.g. StaticColor,",
1184     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1185     "behavior may require a TrueColor or DirectColor visual or a",
1186     "Standard Colormap.",
1187     "",
1188     "Choosing a composite operator is optional.  The default",
1189     "operator is replace.  However, you must choose a location to",
1190     "paste your image and press button 1.  Press and hold the",
1191     "button before releasing and an outline of the image will",
1192     "appear to help you identify your location.",
1193     "",
1194     "The actual colors of the pasted image is saved.  However,",
1195     "the color that appears in image window may be different.",
1196     "For example, on a monochrome screen image window will appear",
1197     "black or white even though your pasted image may have",
1198     "many colors.  If the image is saved to a file it is written",
1199     "with the correct colors.  To assure the correct colors are",
1200     "saved in the final image, any PseudoClass image is promoted",
1201     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1202     "to remain PseudoClass, use -colors.",
1203     (char *) NULL,
1204   },
1205   *ImageROIHelp[] =
1206   {
1207     "In region of interest mode, the Command widget has these",
1208     "options:",
1209     "",
1210     "    Help",
1211     "    Dismiss",
1212     "",
1213     "To define a region of interest, press button 1 and drag.",
1214     "The region of interest is defined by a highlighted rectangle",
1215     "that expands or contracts as it follows the pointer.  Once",
1216     "you are satisfied with the region of interest, release the",
1217     "button.  You are now in apply mode.  In apply mode the",
1218     "Command widget has these options:",
1219     "",
1220     "      File",
1221     "        Save...",
1222     "        Print...",
1223     "      Edit",
1224     "        Undo",
1225     "        Redo",
1226     "      Transform",
1227     "        Flop",
1228     "        Flip",
1229     "        Rotate Right",
1230     "        Rotate Left",
1231     "      Enhance",
1232     "        Hue...",
1233     "        Saturation...",
1234     "        Brightness...",
1235     "        Gamma...",
1236     "        Spiff",
1237     "        Dull",
1238     "        Contrast Stretch",
1239     "        Sigmoidal Contrast...",
1240     "        Normalize",
1241     "        Equalize",
1242     "        Negate",
1243     "        Grayscale",
1244     "        Map...",
1245     "        Quantize...",
1246     "      Effects",
1247     "        Despeckle",
1248     "        Emboss",
1249     "        Reduce Noise",
1250     "        Sharpen...",
1251     "        Blur...",
1252     "        Threshold...",
1253     "        Edge Detect...",
1254     "        Spread...",
1255     "        Shade...",
1256     "        Raise...",
1257     "        Segment...",
1258     "      F/X",
1259     "        Solarize...",
1260     "        Sepia Tone...",
1261     "        Swirl...",
1262     "        Implode...",
1263     "        Vignette...",
1264     "        Wave...",
1265     "        Oil Painting...",
1266     "        Charcoal Drawing...",
1267     "      Miscellany",
1268     "        Image Info",
1269     "        Zoom Image",
1270     "        Show Preview...",
1271     "        Show Histogram",
1272     "        Show Matte",
1273     "      Help",
1274     "      Dismiss",
1275     "",
1276     "You can make adjustments to the region of interest by moving",
1277     "the pointer to one of the rectangle corners, pressing a",
1278     "button, and dragging.  Finally, choose an image processing",
1279     "technique from the Command widget.  You can choose more than",
1280     "one image processing technique to apply to an area.",
1281     "Alternatively, you can move the region of interest before",
1282     "applying another image processing technique.  To exit, press",
1283     "Dismiss.",
1284     (char *) NULL,
1285   },
1286   *ImageRotateHelp[] =
1287   {
1288     "In rotate mode, the Command widget has these options:",
1289     "",
1290     "    Pixel Color",
1291     "      black",
1292     "      blue",
1293     "      cyan",
1294     "      green",
1295     "      gray",
1296     "      red",
1297     "      magenta",
1298     "      yellow",
1299     "      white",
1300     "      Browser...",
1301     "    Direction",
1302     "      horizontal",
1303     "      vertical",
1304     "    Help",
1305     "    Dismiss",
1306     "",
1307     "Choose a background color from the Pixel Color sub-menu.",
1308     "Additional background colors can be specified with the color",
1309     "browser.  You can change the menu colors by setting the X",
1310     "resources pen1 through pen9.",
1311     "",
1312     "If you choose the color browser and press Grab, you can",
1313     "select the background color by moving the pointer to the",
1314     "desired color on the screen and press any button.",
1315     "",
1316     "Choose a point in the image window and press this button and",
1317     "hold.  Next, move the pointer to another location in the",
1318     "image.  As you move a line connects the initial location and",
1319     "the pointer.  When you release the button, the degree of",
1320     "image rotation is determined by the slope of the line you",
1321     "just drew.  The slope is relative to the direction you",
1322     "choose from the Direction sub-menu of the Command widget.",
1323     "",
1324     "To cancel the image rotation, move the pointer back to the",
1325     "starting point of the line and release the button.",
1326     (char *) NULL,
1327   };
1328 \f
1329 /*
1330   Enumeration declarations.
1331 */
1332 typedef enum
1333 {
1334   CopyMode,
1335   CropMode,
1336   CutMode
1337 } ClipboardMode;
1338
1339 typedef enum
1340 {
1341   OpenCommand,
1342   NextCommand,
1343   FormerCommand,
1344   SelectCommand,
1345   SaveCommand,
1346   PrintCommand,
1347   DeleteCommand,
1348   NewCommand,
1349   VisualDirectoryCommand,
1350   QuitCommand,
1351   UndoCommand,
1352   RedoCommand,
1353   CutCommand,
1354   CopyCommand,
1355   PasteCommand,
1356   HalfSizeCommand,
1357   OriginalSizeCommand,
1358   DoubleSizeCommand,
1359   ResizeCommand,
1360   ApplyCommand,
1361   RefreshCommand,
1362   RestoreCommand,
1363   CropCommand,
1364   ChopCommand,
1365   FlopCommand,
1366   FlipCommand,
1367   RotateRightCommand,
1368   RotateLeftCommand,
1369   RotateCommand,
1370   ShearCommand,
1371   RollCommand,
1372   TrimCommand,
1373   HueCommand,
1374   SaturationCommand,
1375   BrightnessCommand,
1376   GammaCommand,
1377   SpiffCommand,
1378   DullCommand,
1379   ContrastStretchCommand,
1380   SigmoidalContrastCommand,
1381   NormalizeCommand,
1382   EqualizeCommand,
1383   NegateCommand,
1384   GrayscaleCommand,
1385   MapCommand,
1386   QuantizeCommand,
1387   DespeckleCommand,
1388   EmbossCommand,
1389   ReduceNoiseCommand,
1390   AddNoiseCommand,
1391   SharpenCommand,
1392   BlurCommand,
1393   ThresholdCommand,
1394   EdgeDetectCommand,
1395   SpreadCommand,
1396   ShadeCommand,
1397   RaiseCommand,
1398   SegmentCommand,
1399   SolarizeCommand,
1400   SepiaToneCommand,
1401   SwirlCommand,
1402   ImplodeCommand,
1403   VignetteCommand,
1404   WaveCommand,
1405   OilPaintCommand,
1406   CharcoalDrawCommand,
1407   AnnotateCommand,
1408   DrawCommand,
1409   ColorCommand,
1410   MatteCommand,
1411   CompositeCommand,
1412   AddBorderCommand,
1413   AddFrameCommand,
1414   CommentCommand,
1415   LaunchCommand,
1416   RegionofInterestCommand,
1417   ROIHelpCommand,
1418   ROIDismissCommand,
1419   InfoCommand,
1420   ZoomCommand,
1421   ShowPreviewCommand,
1422   ShowHistogramCommand,
1423   ShowMatteCommand,
1424   BackgroundCommand,
1425   SlideShowCommand,
1426   PreferencesCommand,
1427   HelpCommand,
1428   BrowseDocumentationCommand,
1429   VersionCommand,
1430   SaveToUndoBufferCommand,
1431   FreeBuffersCommand,
1432   NullCommand
1433 } CommandType;
1434
1435 typedef enum
1436 {
1437   AnnotateNameCommand,
1438   AnnotateFontColorCommand,
1439   AnnotateBackgroundColorCommand,
1440   AnnotateRotateCommand,
1441   AnnotateHelpCommand,
1442   AnnotateDismissCommand,
1443   TextHelpCommand,
1444   TextApplyCommand,
1445   ChopDirectionCommand,
1446   ChopHelpCommand,
1447   ChopDismissCommand,
1448   HorizontalChopCommand,
1449   VerticalChopCommand,
1450   ColorEditMethodCommand,
1451   ColorEditColorCommand,
1452   ColorEditBorderCommand,
1453   ColorEditFuzzCommand,
1454   ColorEditUndoCommand,
1455   ColorEditHelpCommand,
1456   ColorEditDismissCommand,
1457   CompositeOperatorsCommand,
1458   CompositeDissolveCommand,
1459   CompositeDisplaceCommand,
1460   CompositeHelpCommand,
1461   CompositeDismissCommand,
1462   CropHelpCommand,
1463   CropDismissCommand,
1464   RectifyCopyCommand,
1465   RectifyHelpCommand,
1466   RectifyDismissCommand,
1467   DrawElementCommand,
1468   DrawColorCommand,
1469   DrawStippleCommand,
1470   DrawWidthCommand,
1471   DrawUndoCommand,
1472   DrawHelpCommand,
1473   DrawDismissCommand,
1474   MatteEditMethod,
1475   MatteEditBorderCommand,
1476   MatteEditFuzzCommand,
1477   MatteEditValueCommand,
1478   MatteEditUndoCommand,
1479   MatteEditHelpCommand,
1480   MatteEditDismissCommand,
1481   PasteOperatorsCommand,
1482   PasteHelpCommand,
1483   PasteDismissCommand,
1484   RotateColorCommand,
1485   RotateDirectionCommand,
1486   RotateCropCommand,
1487   RotateSharpenCommand,
1488   RotateHelpCommand,
1489   RotateDismissCommand,
1490   HorizontalRotateCommand,
1491   VerticalRotateCommand,
1492   TileLoadCommand,
1493   TileNextCommand,
1494   TileFormerCommand,
1495   TileDeleteCommand,
1496   TileUpdateCommand
1497 } ModeType;
1498 \f
1499 /*
1500   Stipples.
1501 */
1502 #define BricksWidth  20
1503 #define BricksHeight  20
1504 #define DiagonalWidth  16
1505 #define DiagonalHeight  16
1506 #define HighlightWidth  8
1507 #define HighlightHeight  8
1508 #define OpaqueWidth  8
1509 #define OpaqueHeight  8
1510 #define ScalesWidth  16
1511 #define ScalesHeight  16
1512 #define ShadowWidth  8
1513 #define ShadowHeight  8
1514 #define VerticalWidth  16
1515 #define VerticalHeight  16
1516 #define WavyWidth  16
1517 #define WavyHeight  16
1518 \f
1519 /*
1520   Constant declaration.
1521 */
1522 static const int
1523   RoiDelta = 8;
1524
1525 static const unsigned char
1526   BricksBitmap[] =
1527   {
1528     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1529     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1530     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1531     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1532     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1533   },
1534   DiagonalBitmap[] =
1535   {
1536     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1537     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1538     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1539   },
1540   ScalesBitmap[] =
1541   {
1542     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1543     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1544     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1545   },
1546   VerticalBitmap[] =
1547   {
1548     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1549     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1550     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1551   },
1552   WavyBitmap[] =
1553   {
1554     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1555     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1556     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1557   };
1558 \f
1559 /*
1560   Function prototypes.
1561 */
1562 static CommandType
1563   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1564     const MagickStatusType,KeySym,Image **);
1565
1566 static Image
1567   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1568     Image **),
1569   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1570   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
1571   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
1572
1573 static MagickBooleanType
1574   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
1575   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1576   XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
1577   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
1578   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
1579   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1580   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
1581   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
1582   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1583   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
1584   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
1585   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
1586   XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
1587   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
1588   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
1589
1590 static void
1591   XDrawPanRectangle(Display *,XWindows *),
1592   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **),
1593   XMagnifyImage(Display *,XWindows *,XEvent *),
1594   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
1595   XPanImage(Display *,XWindows *,XEvent *),
1596   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1597     const KeySym),
1598   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1599   XScreenEvent(Display *,XWindows *,XEvent *),
1600   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1601 \f
1602 /*
1603 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1604 %                                                                             %
1605 %                                                                             %
1606 %                                                                             %
1607 %   D i s p l a y I m a g e s                                                 %
1608 %                                                                             %
1609 %                                                                             %
1610 %                                                                             %
1611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1612 %
1613 %  DisplayImages() displays an image sequence to any X window screen.  It
1614 %  returns a value other than 0 if successful.  Check the exception member
1615 %  of image to determine the reason for any failure.
1616 %
1617 %  The format of the DisplayImages method is:
1618 %
1619 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1620 %        Image *images)
1621 %
1622 %  A description of each parameter follows:
1623 %
1624 %    o image_info: the image info.
1625 %
1626 %    o image: the image.
1627 %
1628 */
1629 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1630   Image *images)
1631 {
1632   char
1633     *argv[1];
1634
1635   Display
1636     *display;
1637
1638   Image
1639     *image;
1640
1641   register ssize_t
1642     i;
1643
1644   size_t
1645     state;
1646
1647   XrmDatabase
1648     resource_database;
1649
1650   XResourceInfo
1651     resource_info;
1652
1653   assert(image_info != (const ImageInfo *) NULL);
1654   assert(image_info->signature == MagickSignature);
1655   assert(images != (Image *) NULL);
1656   assert(images->signature == MagickSignature);
1657   if (images->debug != MagickFalse)
1658     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1659   display=XOpenDisplay(image_info->server_name);
1660   if (display == (Display *) NULL)
1661     {
1662       (void) ThrowMagickException(&images->exception,GetMagickModule(),
1663         XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
1664         image_info->server_name));
1665       return(MagickFalse);
1666     }
1667   if (images->exception.severity != UndefinedException)
1668     CatchException(&images->exception);
1669   (void) XSetErrorHandler(XError);
1670   resource_database=XGetResourceDatabase(display,GetClientName());
1671   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1672   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1673   if (image_info->page != (char *) NULL)
1674     resource_info.image_geometry=AcquireString(image_info->page);
1675   resource_info.immutable=MagickTrue;
1676   argv[0]=AcquireString(GetClientName());
1677   state=DefaultState;
1678   for (i=0; (state & ExitState) == 0; i++)
1679   {
1680     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1681       break;
1682     image=GetImageFromList(images,i % GetImageListLength(images));
1683     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
1684   }
1685   argv[0]=DestroyString(argv[0]);
1686   (void) XCloseDisplay(display);
1687   XDestroyResourceInfo(&resource_info);
1688   if (images->exception.severity != UndefinedException)
1689     return(MagickFalse);
1690   return(MagickTrue);
1691 }
1692 \f
1693 /*
1694 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1695 %                                                                             %
1696 %                                                                             %
1697 %                                                                             %
1698 %   R e m o t e D i s p l a y C o m m a n d                                   %
1699 %                                                                             %
1700 %                                                                             %
1701 %                                                                             %
1702 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1703 %
1704 %  RemoteDisplayCommand() encourages a remote display program to display the
1705 %  specified image filename.
1706 %
1707 %  The format of the RemoteDisplayCommand method is:
1708 %
1709 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1710 %        const char *window,const char *filename,ExceptionInfo *exception)
1711 %
1712 %  A description of each parameter follows:
1713 %
1714 %    o image_info: the image info.
1715 %
1716 %    o window: Specifies the name or id of an X window.
1717 %
1718 %    o filename: the name of the image filename to display.
1719 %
1720 %    o exception: return any errors or warnings in this structure.
1721 %
1722 */
1723 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1724   const char *window,const char *filename,ExceptionInfo *exception)
1725 {
1726   Display
1727     *display;
1728
1729   MagickStatusType
1730     status;
1731
1732   assert(image_info != (const ImageInfo *) NULL);
1733   assert(image_info->signature == MagickSignature);
1734   assert(filename != (char *) NULL);
1735   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1736   display=XOpenDisplay(image_info->server_name);
1737   if (display == (Display *) NULL)
1738     {
1739       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1740         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1741       return(MagickFalse);
1742     }
1743   (void) XSetErrorHandler(XError);
1744   status=XRemoteCommand(display,window,filename);
1745   (void) XCloseDisplay(display);
1746   return(status != 0 ? MagickTrue : MagickFalse);
1747 }
1748 \f
1749 /*
1750 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1751 %                                                                             %
1752 %                                                                             %
1753 %                                                                             %
1754 +   X A n n o t a t e E d i t I m a g e                                       %
1755 %                                                                             %
1756 %                                                                             %
1757 %                                                                             %
1758 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1759 %
1760 %  XAnnotateEditImage() annotates the image with text.
1761 %
1762 %  The format of the XAnnotateEditImage method is:
1763 %
1764 %      MagickBooleanType XAnnotateEditImage(Display *display,
1765 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
1766 %
1767 %  A description of each parameter follows:
1768 %
1769 %    o display: Specifies a connection to an X server;  returned from
1770 %      XOpenDisplay.
1771 %
1772 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1773 %
1774 %    o windows: Specifies a pointer to a XWindows structure.
1775 %
1776 %    o image: the image; returned from ReadImage.
1777 %
1778 */
1779
1780 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1781 {
1782   if (x > y)
1783     return(x);
1784   return(y);
1785 }
1786
1787 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1788 {
1789   if (x < y)
1790     return(x);
1791   return(y);
1792 }
1793
1794 static MagickBooleanType XAnnotateEditImage(Display *display,
1795   XResourceInfo *resource_info,XWindows *windows,Image *image)
1796 {
1797   static const char
1798     *AnnotateMenu[] =
1799     {
1800       "Font Name",
1801       "Font Color",
1802       "Box Color",
1803       "Rotate Text",
1804       "Help",
1805       "Dismiss",
1806       (char *) NULL
1807     },
1808     *TextMenu[] =
1809     {
1810       "Help",
1811       "Apply",
1812       (char *) NULL
1813     };
1814
1815   static const ModeType
1816     AnnotateCommands[] =
1817     {
1818       AnnotateNameCommand,
1819       AnnotateFontColorCommand,
1820       AnnotateBackgroundColorCommand,
1821       AnnotateRotateCommand,
1822       AnnotateHelpCommand,
1823       AnnotateDismissCommand
1824     },
1825     TextCommands[] =
1826     {
1827       TextHelpCommand,
1828       TextApplyCommand
1829     };
1830
1831   static MagickBooleanType
1832     transparent_box = MagickTrue,
1833     transparent_pen = MagickFalse;
1834
1835   static MagickRealType
1836     degrees = 0.0;
1837
1838   static unsigned int
1839     box_id = MaxNumberPens-2,
1840     font_id = 0,
1841     pen_id = 0;
1842
1843   char
1844     command[MaxTextExtent],
1845     text[MaxTextExtent];
1846
1847   const char
1848     *ColorMenu[MaxNumberPens+1];
1849
1850   Cursor
1851     cursor;
1852
1853   GC
1854     annotate_context;
1855
1856   int
1857     id,
1858     pen_number,
1859     status,
1860     x,
1861     y;
1862
1863   KeySym
1864     key_symbol;
1865
1866   register char
1867     *p;
1868
1869   register ssize_t
1870     i;
1871
1872   unsigned int
1873     height,
1874     width;
1875
1876   size_t
1877     state;
1878
1879   XAnnotateInfo
1880     *annotate_info,
1881     *previous_info;
1882
1883   XColor
1884     color;
1885
1886   XFontStruct
1887     *font_info;
1888
1889   XEvent
1890     event,
1891     text_event;
1892
1893   /*
1894     Map Command widget.
1895   */
1896   (void) CloneString(&windows->command.name,"Annotate");
1897   windows->command.data=4;
1898   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1899   (void) XMapRaised(display,windows->command.id);
1900   XClientMessage(display,windows->image.id,windows->im_protocols,
1901     windows->im_update_widget,CurrentTime);
1902   /*
1903     Track pointer until button 1 is pressed.
1904   */
1905   XQueryPosition(display,windows->image.id,&x,&y);
1906   (void) XSelectInput(display,windows->image.id,
1907     windows->image.attributes.event_mask | PointerMotionMask);
1908   cursor=XCreateFontCursor(display,XC_left_side);
1909   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1910   state=DefaultState;
1911   do
1912   {
1913     if (windows->info.mapped != MagickFalse)
1914       {
1915         /*
1916           Display pointer position.
1917         */
1918         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1919           x+windows->image.x,y+windows->image.y);
1920         XInfoWidget(display,windows,text);
1921       }
1922     /*
1923       Wait for next event.
1924     */
1925     XScreenEvent(display,windows,&event);
1926     if (event.xany.window == windows->command.id)
1927       {
1928         /*
1929           Select a command from the Command widget.
1930         */
1931         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1932         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1933         if (id < 0)
1934           continue;
1935         switch (AnnotateCommands[id])
1936         {
1937           case AnnotateNameCommand:
1938           {
1939             const char
1940               *FontMenu[MaxNumberFonts];
1941
1942             int
1943               font_number;
1944
1945             /*
1946               Initialize menu selections.
1947             */
1948             for (i=0; i < MaxNumberFonts; i++)
1949               FontMenu[i]=resource_info->font_name[i];
1950             FontMenu[MaxNumberFonts-2]="Browser...";
1951             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1952             /*
1953               Select a font name from the pop-up menu.
1954             */
1955             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1956               (const char **) FontMenu,command);
1957             if (font_number < 0)
1958               break;
1959             if (font_number == (MaxNumberFonts-2))
1960               {
1961                 static char
1962                   font_name[MaxTextExtent] = "fixed";
1963
1964                 /*
1965                   Select a font name from a browser.
1966                 */
1967                 resource_info->font_name[font_number]=font_name;
1968                 XFontBrowserWidget(display,windows,"Select",font_name);
1969                 if (*font_name == '\0')
1970                   break;
1971               }
1972             /*
1973               Initialize font info.
1974             */
1975             font_info=XLoadQueryFont(display,resource_info->font_name[
1976               font_number]);
1977             if (font_info == (XFontStruct *) NULL)
1978               {
1979                 XNoticeWidget(display,windows,"Unable to load font:",
1980                   resource_info->font_name[font_number]);
1981                 break;
1982               }
1983             font_id=(unsigned int) font_number;
1984             (void) XFreeFont(display,font_info);
1985             break;
1986           }
1987           case AnnotateFontColorCommand:
1988           {
1989             /*
1990               Initialize menu selections.
1991             */
1992             for (i=0; i < (int) (MaxNumberPens-2); i++)
1993               ColorMenu[i]=resource_info->pen_colors[i];
1994             ColorMenu[MaxNumberPens-2]="transparent";
1995             ColorMenu[MaxNumberPens-1]="Browser...";
1996             ColorMenu[MaxNumberPens]=(const char *) NULL;
1997             /*
1998               Select a pen color from the pop-up menu.
1999             */
2000             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2001               (const char **) ColorMenu,command);
2002             if (pen_number < 0)
2003               break;
2004             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2005               MagickFalse;
2006             if (transparent_pen != MagickFalse)
2007               break;
2008             if (pen_number == (MaxNumberPens-1))
2009               {
2010                 static char
2011                   color_name[MaxTextExtent] = "gray";
2012
2013                 /*
2014                   Select a pen color from a dialog.
2015                 */
2016                 resource_info->pen_colors[pen_number]=color_name;
2017                 XColorBrowserWidget(display,windows,"Select",color_name);
2018                 if (*color_name == '\0')
2019                   break;
2020               }
2021             /*
2022               Set pen color.
2023             */
2024             (void) XParseColor(display,windows->map_info->colormap,
2025               resource_info->pen_colors[pen_number],&color);
2026             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2027               (unsigned int) MaxColors,&color);
2028             windows->pixel_info->pen_colors[pen_number]=color;
2029             pen_id=(unsigned int) pen_number;
2030             break;
2031           }
2032           case AnnotateBackgroundColorCommand:
2033           {
2034             /*
2035               Initialize menu selections.
2036             */
2037             for (i=0; i < (int) (MaxNumberPens-2); i++)
2038               ColorMenu[i]=resource_info->pen_colors[i];
2039             ColorMenu[MaxNumberPens-2]="transparent";
2040             ColorMenu[MaxNumberPens-1]="Browser...";
2041             ColorMenu[MaxNumberPens]=(const char *) NULL;
2042             /*
2043               Select a pen color from the pop-up menu.
2044             */
2045             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2046               (const char **) ColorMenu,command);
2047             if (pen_number < 0)
2048               break;
2049             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2050               MagickFalse;
2051             if (transparent_box != MagickFalse)
2052               break;
2053             if (pen_number == (MaxNumberPens-1))
2054               {
2055                 static char
2056                   color_name[MaxTextExtent] = "gray";
2057
2058                 /*
2059                   Select a pen color from a dialog.
2060                 */
2061                 resource_info->pen_colors[pen_number]=color_name;
2062                 XColorBrowserWidget(display,windows,"Select",color_name);
2063                 if (*color_name == '\0')
2064                   break;
2065               }
2066             /*
2067               Set pen color.
2068             */
2069             (void) XParseColor(display,windows->map_info->colormap,
2070               resource_info->pen_colors[pen_number],&color);
2071             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2072               (unsigned int) MaxColors,&color);
2073             windows->pixel_info->pen_colors[pen_number]=color;
2074             box_id=(unsigned int) pen_number;
2075             break;
2076           }
2077           case AnnotateRotateCommand:
2078           {
2079             int
2080               entry;
2081
2082             static char
2083               angle[MaxTextExtent] = "30.0";
2084
2085             static const char
2086               *RotateMenu[] =
2087               {
2088                 "-90",
2089                 "-45",
2090                 "-30",
2091                 "0",
2092                 "30",
2093                 "45",
2094                 "90",
2095                 "180",
2096                 "Dialog...",
2097                 (char *) NULL,
2098               };
2099
2100             /*
2101               Select a command from the pop-up menu.
2102             */
2103             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2104               command);
2105             if (entry < 0)
2106               break;
2107             if (entry != 8)
2108               {
2109                 degrees=InterpretLocaleValue(RotateMenu[entry],(char **) NULL);
2110                 break;
2111               }
2112             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2113               angle);
2114             if (*angle == '\0')
2115               break;
2116             degrees=InterpretLocaleValue(angle,(char **) NULL);
2117             break;
2118           }
2119           case AnnotateHelpCommand:
2120           {
2121             XTextViewWidget(display,resource_info,windows,MagickFalse,
2122               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2123             break;
2124           }
2125           case AnnotateDismissCommand:
2126           {
2127             /*
2128               Prematurely exit.
2129             */
2130             state|=EscapeState;
2131             state|=ExitState;
2132             break;
2133           }
2134           default:
2135             break;
2136         }
2137         continue;
2138       }
2139     switch (event.type)
2140     {
2141       case ButtonPress:
2142       {
2143         if (event.xbutton.button != Button1)
2144           break;
2145         if (event.xbutton.window != windows->image.id)
2146           break;
2147         /*
2148           Change to text entering mode.
2149         */
2150         x=event.xbutton.x;
2151         y=event.xbutton.y;
2152         state|=ExitState;
2153         break;
2154       }
2155       case ButtonRelease:
2156         break;
2157       case Expose:
2158         break;
2159       case KeyPress:
2160       {
2161         if (event.xkey.window != windows->image.id)
2162           break;
2163         /*
2164           Respond to a user key press.
2165         */
2166         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2167           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2168         switch ((int) key_symbol)
2169         {
2170           case XK_Escape:
2171           case XK_F20:
2172           {
2173             /*
2174               Prematurely exit.
2175             */
2176             state|=EscapeState;
2177             state|=ExitState;
2178             break;
2179           }
2180           case XK_F1:
2181           case XK_Help:
2182           {
2183             XTextViewWidget(display,resource_info,windows,MagickFalse,
2184               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2185             break;
2186           }
2187           default:
2188           {
2189             (void) XBell(display,0);
2190             break;
2191           }
2192         }
2193         break;
2194       }
2195       case MotionNotify:
2196       {
2197         /*
2198           Map and unmap Info widget as cursor crosses its boundaries.
2199         */
2200         x=event.xmotion.x;
2201         y=event.xmotion.y;
2202         if (windows->info.mapped != MagickFalse)
2203           {
2204             if ((x < (int) (windows->info.x+windows->info.width)) &&
2205                 (y < (int) (windows->info.y+windows->info.height)))
2206               (void) XWithdrawWindow(display,windows->info.id,
2207                 windows->info.screen);
2208           }
2209         else
2210           if ((x > (int) (windows->info.x+windows->info.width)) ||
2211               (y > (int) (windows->info.y+windows->info.height)))
2212             (void) XMapWindow(display,windows->info.id);
2213         break;
2214       }
2215       default:
2216         break;
2217     }
2218   } while ((state & ExitState) == 0);
2219   (void) XSelectInput(display,windows->image.id,
2220     windows->image.attributes.event_mask);
2221   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2222   if ((state & EscapeState) != 0)
2223     return(MagickTrue);
2224   /*
2225     Set font info and check boundary conditions.
2226   */
2227   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2228   if (font_info == (XFontStruct *) NULL)
2229     {
2230       XNoticeWidget(display,windows,"Unable to load font:",
2231         resource_info->font_name[font_id]);
2232       font_info=windows->font_info;
2233     }
2234   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2235     x=(int) windows->image.width-font_info->max_bounds.width;
2236   if (y < (int) (font_info->ascent+font_info->descent))
2237     y=(int) font_info->ascent+font_info->descent;
2238   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2239       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2240     return(MagickFalse);
2241   /*
2242     Initialize annotate structure.
2243   */
2244   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2245   if (annotate_info == (XAnnotateInfo *) NULL)
2246     return(MagickFalse);
2247   XGetAnnotateInfo(annotate_info);
2248   annotate_info->x=x;
2249   annotate_info->y=y;
2250   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2251     annotate_info->stencil=OpaqueStencil;
2252   else
2253     if (transparent_box == MagickFalse)
2254       annotate_info->stencil=BackgroundStencil;
2255     else
2256       annotate_info->stencil=ForegroundStencil;
2257   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2258   annotate_info->degrees=degrees;
2259   annotate_info->font_info=font_info;
2260   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2261     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2262     sizeof(*annotate_info->text));
2263   if (annotate_info->text == (char *) NULL)
2264     return(MagickFalse);
2265   /*
2266     Create cursor and set graphic context.
2267   */
2268   cursor=XCreateFontCursor(display,XC_pencil);
2269   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2270   annotate_context=windows->image.annotate_context;
2271   (void) XSetFont(display,annotate_context,font_info->fid);
2272   (void) XSetBackground(display,annotate_context,
2273     windows->pixel_info->pen_colors[box_id].pixel);
2274   (void) XSetForeground(display,annotate_context,
2275     windows->pixel_info->pen_colors[pen_id].pixel);
2276   /*
2277     Begin annotating the image with text.
2278   */
2279   (void) CloneString(&windows->command.name,"Text");
2280   windows->command.data=0;
2281   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2282   state=DefaultState;
2283   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2284   text_event.xexpose.width=(int) font_info->max_bounds.width;
2285   text_event.xexpose.height=font_info->max_bounds.ascent+
2286     font_info->max_bounds.descent;
2287   p=annotate_info->text;
2288   do
2289   {
2290     /*
2291       Display text cursor.
2292     */
2293     *p='\0';
2294     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2295     /*
2296       Wait for next event.
2297     */
2298     XScreenEvent(display,windows,&event);
2299     if (event.xany.window == windows->command.id)
2300       {
2301         /*
2302           Select a command from the Command widget.
2303         */
2304         (void) XSetBackground(display,annotate_context,
2305           windows->pixel_info->background_color.pixel);
2306         (void) XSetForeground(display,annotate_context,
2307           windows->pixel_info->foreground_color.pixel);
2308         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2309         (void) XSetBackground(display,annotate_context,
2310           windows->pixel_info->pen_colors[box_id].pixel);
2311         (void) XSetForeground(display,annotate_context,
2312           windows->pixel_info->pen_colors[pen_id].pixel);
2313         if (id < 0)
2314           continue;
2315         switch (TextCommands[id])
2316         {
2317           case TextHelpCommand:
2318           {
2319             XTextViewWidget(display,resource_info,windows,MagickFalse,
2320               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2321             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2322             break;
2323           }
2324           case TextApplyCommand:
2325           {
2326             /*
2327               Finished annotating.
2328             */
2329             annotate_info->width=(unsigned int) XTextWidth(font_info,
2330               annotate_info->text,(int) strlen(annotate_info->text));
2331             XRefreshWindow(display,&windows->image,&text_event);
2332             state|=ExitState;
2333             break;
2334           }
2335           default:
2336             break;
2337         }
2338         continue;
2339       }
2340     /*
2341       Erase text cursor.
2342     */
2343     text_event.xexpose.x=x;
2344     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2345     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2346       (unsigned int) text_event.xexpose.width,(unsigned int)
2347       text_event.xexpose.height,MagickFalse);
2348     XRefreshWindow(display,&windows->image,&text_event);
2349     switch (event.type)
2350     {
2351       case ButtonPress:
2352       {
2353         if (event.xbutton.window != windows->image.id)
2354           break;
2355         if (event.xbutton.button == Button2)
2356           {
2357             /*
2358               Request primary selection.
2359             */
2360             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2361               windows->image.id,CurrentTime);
2362             break;
2363           }
2364         break;
2365       }
2366       case Expose:
2367       {
2368         if (event.xexpose.count == 0)
2369           {
2370             XAnnotateInfo
2371               *text_info;
2372
2373             /*
2374               Refresh Image window.
2375             */
2376             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2377             text_info=annotate_info;
2378             while (text_info != (XAnnotateInfo *) NULL)
2379             {
2380               if (annotate_info->stencil == ForegroundStencil)
2381                 (void) XDrawString(display,windows->image.id,annotate_context,
2382                   text_info->x,text_info->y,text_info->text,
2383                   (int) strlen(text_info->text));
2384               else
2385                 (void) XDrawImageString(display,windows->image.id,
2386                   annotate_context,text_info->x,text_info->y,text_info->text,
2387                   (int) strlen(text_info->text));
2388               text_info=text_info->previous;
2389             }
2390             (void) XDrawString(display,windows->image.id,annotate_context,
2391               x,y,"_",1);
2392           }
2393         break;
2394       }
2395       case KeyPress:
2396       {
2397         int
2398           length;
2399
2400         if (event.xkey.window != windows->image.id)
2401           break;
2402         /*
2403           Respond to a user key press.
2404         */
2405         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2406           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2407         *(command+length)='\0';
2408         if (((event.xkey.state & ControlMask) != 0) ||
2409             ((event.xkey.state & Mod1Mask) != 0))
2410           state|=ModifierState;
2411         if ((state & ModifierState) != 0)
2412           switch ((int) key_symbol)
2413           {
2414             case XK_u:
2415             case XK_U:
2416             {
2417               key_symbol=DeleteCommand;
2418               break;
2419             }
2420             default:
2421               break;
2422           }
2423         switch ((int) key_symbol)
2424         {
2425           case XK_BackSpace:
2426           {
2427             /*
2428               Erase one character.
2429             */
2430             if (p == annotate_info->text)
2431               {
2432                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2433                   break;
2434                 else
2435                   {
2436                     /*
2437                       Go to end of the previous line of text.
2438                     */
2439                     annotate_info=annotate_info->previous;
2440                     p=annotate_info->text;
2441                     x=annotate_info->x+annotate_info->width;
2442                     y=annotate_info->y;
2443                     if (annotate_info->width != 0)
2444                       p+=strlen(annotate_info->text);
2445                     break;
2446                   }
2447               }
2448             p--;
2449             x-=XTextWidth(font_info,p,1);
2450             text_event.xexpose.x=x;
2451             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2452             XRefreshWindow(display,&windows->image,&text_event);
2453             break;
2454           }
2455           case XK_bracketleft:
2456           {
2457             key_symbol=XK_Escape;
2458             break;
2459           }
2460           case DeleteCommand:
2461           {
2462             /*
2463               Erase the entire line of text.
2464             */
2465             while (p != annotate_info->text)
2466             {
2467               p--;
2468               x-=XTextWidth(font_info,p,1);
2469               text_event.xexpose.x=x;
2470               XRefreshWindow(display,&windows->image,&text_event);
2471             }
2472             break;
2473           }
2474           case XK_Escape:
2475           case XK_F20:
2476           {
2477             /*
2478               Finished annotating.
2479             */
2480             annotate_info->width=(unsigned int) XTextWidth(font_info,
2481               annotate_info->text,(int) strlen(annotate_info->text));
2482             XRefreshWindow(display,&windows->image,&text_event);
2483             state|=ExitState;
2484             break;
2485           }
2486           default:
2487           {
2488             /*
2489               Draw a single character on the Image window.
2490             */
2491             if ((state & ModifierState) != 0)
2492               break;
2493             if (*command == '\0')
2494               break;
2495             *p=(*command);
2496             if (annotate_info->stencil == ForegroundStencil)
2497               (void) XDrawString(display,windows->image.id,annotate_context,
2498                 x,y,p,1);
2499             else
2500               (void) XDrawImageString(display,windows->image.id,
2501                 annotate_context,x,y,p,1);
2502             x+=XTextWidth(font_info,p,1);
2503             p++;
2504             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2505               break;
2506           }
2507           case XK_Return:
2508           case XK_KP_Enter:
2509           {
2510             /*
2511               Advance to the next line of text.
2512             */
2513             *p='\0';
2514             annotate_info->width=(unsigned int) XTextWidth(font_info,
2515               annotate_info->text,(int) strlen(annotate_info->text));
2516             if (annotate_info->next != (XAnnotateInfo *) NULL)
2517               {
2518                 /*
2519                   Line of text already exists.
2520                 */
2521                 annotate_info=annotate_info->next;
2522                 x=annotate_info->x;
2523                 y=annotate_info->y;
2524                 p=annotate_info->text;
2525                 break;
2526               }
2527             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2528               sizeof(*annotate_info->next));
2529             if (annotate_info->next == (XAnnotateInfo *) NULL)
2530               return(MagickFalse);
2531             *annotate_info->next=(*annotate_info);
2532             annotate_info->next->previous=annotate_info;
2533             annotate_info=annotate_info->next;
2534             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2535               windows->image.width/MagickMax((ssize_t)
2536               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2537             if (annotate_info->text == (char *) NULL)
2538               return(MagickFalse);
2539             annotate_info->y+=annotate_info->height;
2540             if (annotate_info->y > (int) windows->image.height)
2541               annotate_info->y=(int) annotate_info->height;
2542             annotate_info->next=(XAnnotateInfo *) NULL;
2543             x=annotate_info->x;
2544             y=annotate_info->y;
2545             p=annotate_info->text;
2546             break;
2547           }
2548         }
2549         break;
2550       }
2551       case KeyRelease:
2552       {
2553         /*
2554           Respond to a user key release.
2555         */
2556         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2557           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2558         state&=(~ModifierState);
2559         break;
2560       }
2561       case SelectionNotify:
2562       {
2563         Atom
2564           type;
2565
2566         int
2567           format;
2568
2569         unsigned char
2570           *data;
2571
2572         unsigned long
2573           after,
2574           length;
2575
2576         /*
2577           Obtain response from primary selection.
2578         */
2579         if (event.xselection.property == (Atom) None)
2580           break;
2581         status=XGetWindowProperty(display,event.xselection.requestor,
2582           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2583           &type,&format,&length,&after,&data);
2584         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2585             (length == 0))
2586           break;
2587         /*
2588           Annotate Image window with primary selection.
2589         */
2590         for (i=0; i < (ssize_t) length; i++)
2591         {
2592           if ((char) data[i] != '\n')
2593             {
2594               /*
2595                 Draw a single character on the Image window.
2596               */
2597               *p=(char) data[i];
2598               (void) XDrawString(display,windows->image.id,annotate_context,
2599                 x,y,p,1);
2600               x+=XTextWidth(font_info,p,1);
2601               p++;
2602               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2603                 continue;
2604             }
2605           /*
2606             Advance to the next line of text.
2607           */
2608           *p='\0';
2609           annotate_info->width=(unsigned int) XTextWidth(font_info,
2610             annotate_info->text,(int) strlen(annotate_info->text));
2611           if (annotate_info->next != (XAnnotateInfo *) NULL)
2612             {
2613               /*
2614                 Line of text already exists.
2615               */
2616               annotate_info=annotate_info->next;
2617               x=annotate_info->x;
2618               y=annotate_info->y;
2619               p=annotate_info->text;
2620               continue;
2621             }
2622           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2623             sizeof(*annotate_info->next));
2624           if (annotate_info->next == (XAnnotateInfo *) NULL)
2625             return(MagickFalse);
2626           *annotate_info->next=(*annotate_info);
2627           annotate_info->next->previous=annotate_info;
2628           annotate_info=annotate_info->next;
2629           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2630             windows->image.width/MagickMax((ssize_t)
2631             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2632           if (annotate_info->text == (char *) NULL)
2633             return(MagickFalse);
2634           annotate_info->y+=annotate_info->height;
2635           if (annotate_info->y > (int) windows->image.height)
2636             annotate_info->y=(int) annotate_info->height;
2637           annotate_info->next=(XAnnotateInfo *) NULL;
2638           x=annotate_info->x;
2639           y=annotate_info->y;
2640           p=annotate_info->text;
2641         }
2642         (void) XFree((void *) data);
2643         break;
2644       }
2645       default:
2646         break;
2647     }
2648   } while ((state & ExitState) == 0);
2649   (void) XFreeCursor(display,cursor);
2650   /*
2651     Annotation is relative to image configuration.
2652   */
2653   width=(unsigned int) image->columns;
2654   height=(unsigned int) image->rows;
2655   x=0;
2656   y=0;
2657   if (windows->image.crop_geometry != (char *) NULL)
2658     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2659   /*
2660     Initialize annotated image.
2661   */
2662   XSetCursorState(display,windows,MagickTrue);
2663   XCheckRefreshWindows(display,windows);
2664   while (annotate_info != (XAnnotateInfo *) NULL)
2665   {
2666     if (annotate_info->width == 0)
2667       {
2668         /*
2669           No text on this line--  go to the next line of text.
2670         */
2671         previous_info=annotate_info->previous;
2672         annotate_info->text=(char *)
2673           RelinquishMagickMemory(annotate_info->text);
2674         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2675         annotate_info=previous_info;
2676         continue;
2677       }
2678     /*
2679       Determine pixel index for box and pen color.
2680     */
2681     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2682     if (windows->pixel_info->colors != 0)
2683       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2684         if (windows->pixel_info->pixels[i] ==
2685             windows->pixel_info->pen_colors[box_id].pixel)
2686           {
2687             windows->pixel_info->box_index=(unsigned short) i;
2688             break;
2689           }
2690     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2691     if (windows->pixel_info->colors != 0)
2692       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2693         if (windows->pixel_info->pixels[i] ==
2694             windows->pixel_info->pen_colors[pen_id].pixel)
2695           {
2696             windows->pixel_info->pen_index=(unsigned short) i;
2697             break;
2698           }
2699     /*
2700       Define the annotate geometry string.
2701     */
2702     annotate_info->x=(int)
2703       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2704     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2705       windows->image.y)/windows->image.ximage->height;
2706     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2707       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2708       height*annotate_info->height/windows->image.ximage->height,
2709       annotate_info->x+x,annotate_info->y+y);
2710     /*
2711       Annotate image with text.
2712     */
2713     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2714     if (status == 0)
2715       return(MagickFalse);
2716     /*
2717       Free up memory.
2718     */
2719     previous_info=annotate_info->previous;
2720     annotate_info->text=DestroyString(annotate_info->text);
2721     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2722     annotate_info=previous_info;
2723   }
2724   (void) XSetForeground(display,annotate_context,
2725     windows->pixel_info->foreground_color.pixel);
2726   (void) XSetBackground(display,annotate_context,
2727     windows->pixel_info->background_color.pixel);
2728   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2729   XSetCursorState(display,windows,MagickFalse);
2730   (void) XFreeFont(display,font_info);
2731   /*
2732     Update image configuration.
2733   */
2734   XConfigureImageColormap(display,resource_info,windows,image);
2735   (void) XConfigureImage(display,resource_info,windows,image);
2736   return(MagickTrue);
2737 }
2738 \f
2739 /*
2740 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2741 %                                                                             %
2742 %                                                                             %
2743 %                                                                             %
2744 +   X B a c k g r o u n d I m a g e                                           %
2745 %                                                                             %
2746 %                                                                             %
2747 %                                                                             %
2748 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2749 %
2750 %  XBackgroundImage() displays the image in the background of a window.
2751 %
2752 %  The format of the XBackgroundImage method is:
2753 %
2754 %      MagickBooleanType XBackgroundImage(Display *display,
2755 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
2756 %
2757 %  A description of each parameter follows:
2758 %
2759 %    o display: Specifies a connection to an X server; returned from
2760 %      XOpenDisplay.
2761 %
2762 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2763 %
2764 %    o windows: Specifies a pointer to a XWindows structure.
2765 %
2766 %    o image: the image.
2767 %
2768 */
2769 static MagickBooleanType XBackgroundImage(Display *display,
2770   XResourceInfo *resource_info,XWindows *windows,Image **image)
2771 {
2772 #define BackgroundImageTag  "Background/Image"
2773
2774   int
2775     status;
2776
2777   static char
2778     window_id[MaxTextExtent] = "root";
2779
2780   XResourceInfo
2781     background_resources;
2782
2783   /*
2784     Put image in background.
2785   */
2786   status=XDialogWidget(display,windows,"Background",
2787     "Enter window id (id 0x00 selects window with pointer):",window_id);
2788   if (*window_id == '\0')
2789     return(MagickFalse);
2790   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
2791   XInfoWidget(display,windows,BackgroundImageTag);
2792   XSetCursorState(display,windows,MagickTrue);
2793   XCheckRefreshWindows(display,windows);
2794   background_resources=(*resource_info);
2795   background_resources.window_id=window_id;
2796   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2797   status=XDisplayBackgroundImage(display,&background_resources,*image);
2798   if (status != MagickFalse)
2799     XClientMessage(display,windows->image.id,windows->im_protocols,
2800       windows->im_retain_colors,CurrentTime);
2801   XSetCursorState(display,windows,MagickFalse);
2802   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
2803   return(MagickTrue);
2804 }
2805 \f
2806 /*
2807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2808 %                                                                             %
2809 %                                                                             %
2810 %                                                                             %
2811 +   X C h o p I m a g e                                                       %
2812 %                                                                             %
2813 %                                                                             %
2814 %                                                                             %
2815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2816 %
2817 %  XChopImage() chops the X image.
2818 %
2819 %  The format of the XChopImage method is:
2820 %
2821 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2822 %      XWindows *windows,Image **image)
2823 %
2824 %  A description of each parameter follows:
2825 %
2826 %    o display: Specifies a connection to an X server; returned from
2827 %      XOpenDisplay.
2828 %
2829 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2830 %
2831 %    o windows: Specifies a pointer to a XWindows structure.
2832 %
2833 %    o image: the image.
2834 %
2835 */
2836 static MagickBooleanType XChopImage(Display *display,
2837   XResourceInfo *resource_info,XWindows *windows,Image **image)
2838 {
2839   static const char
2840     *ChopMenu[] =
2841     {
2842       "Direction",
2843       "Help",
2844       "Dismiss",
2845       (char *) NULL
2846     };
2847
2848   static ModeType
2849     direction = HorizontalChopCommand;
2850
2851   static const ModeType
2852     ChopCommands[] =
2853     {
2854       ChopDirectionCommand,
2855       ChopHelpCommand,
2856       ChopDismissCommand
2857     },
2858     DirectionCommands[] =
2859     {
2860       HorizontalChopCommand,
2861       VerticalChopCommand
2862     };
2863
2864   char
2865     text[MaxTextExtent];
2866
2867   Image
2868     *chop_image;
2869
2870   int
2871     id,
2872     x,
2873     y;
2874
2875   MagickRealType
2876     scale_factor;
2877
2878   RectangleInfo
2879     chop_info;
2880
2881   unsigned int
2882     distance,
2883     height,
2884     width;
2885
2886   size_t
2887     state;
2888
2889   XEvent
2890     event;
2891
2892   XSegment
2893     segment_info;
2894
2895   /*
2896     Map Command widget.
2897   */
2898   (void) CloneString(&windows->command.name,"Chop");
2899   windows->command.data=1;
2900   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2901   (void) XMapRaised(display,windows->command.id);
2902   XClientMessage(display,windows->image.id,windows->im_protocols,
2903     windows->im_update_widget,CurrentTime);
2904   /*
2905     Track pointer until button 1 is pressed.
2906   */
2907   XQueryPosition(display,windows->image.id,&x,&y);
2908   (void) XSelectInput(display,windows->image.id,
2909     windows->image.attributes.event_mask | PointerMotionMask);
2910   state=DefaultState;
2911   do
2912   {
2913     if (windows->info.mapped != MagickFalse)
2914       {
2915         /*
2916           Display pointer position.
2917         */
2918         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2919           x+windows->image.x,y+windows->image.y);
2920         XInfoWidget(display,windows,text);
2921       }
2922     /*
2923       Wait for next event.
2924     */
2925     XScreenEvent(display,windows,&event);
2926     if (event.xany.window == windows->command.id)
2927       {
2928         /*
2929           Select a command from the Command widget.
2930         */
2931         id=XCommandWidget(display,windows,ChopMenu,&event);
2932         if (id < 0)
2933           continue;
2934         switch (ChopCommands[id])
2935         {
2936           case ChopDirectionCommand:
2937           {
2938             char
2939               command[MaxTextExtent];
2940
2941             static const char
2942               *Directions[] =
2943               {
2944                 "horizontal",
2945                 "vertical",
2946                 (char *) NULL,
2947               };
2948
2949             /*
2950               Select a command from the pop-up menu.
2951             */
2952             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2953             if (id >= 0)
2954               direction=DirectionCommands[id];
2955             break;
2956           }
2957           case ChopHelpCommand:
2958           {
2959             XTextViewWidget(display,resource_info,windows,MagickFalse,
2960               "Help Viewer - Image Chop",ImageChopHelp);
2961             break;
2962           }
2963           case ChopDismissCommand:
2964           {
2965             /*
2966               Prematurely exit.
2967             */
2968             state|=EscapeState;
2969             state|=ExitState;
2970             break;
2971           }
2972           default:
2973             break;
2974         }
2975         continue;
2976       }
2977     switch (event.type)
2978     {
2979       case ButtonPress:
2980       {
2981         if (event.xbutton.button != Button1)
2982           break;
2983         if (event.xbutton.window != windows->image.id)
2984           break;
2985         /*
2986           User has committed to start point of chopping line.
2987         */
2988         segment_info.x1=(short int) event.xbutton.x;
2989         segment_info.x2=(short int) event.xbutton.x;
2990         segment_info.y1=(short int) event.xbutton.y;
2991         segment_info.y2=(short int) event.xbutton.y;
2992         state|=ExitState;
2993         break;
2994       }
2995       case ButtonRelease:
2996         break;
2997       case Expose:
2998         break;
2999       case KeyPress:
3000       {
3001         char
3002           command[MaxTextExtent];
3003
3004         KeySym
3005           key_symbol;
3006
3007         if (event.xkey.window != windows->image.id)
3008           break;
3009         /*
3010           Respond to a user key press.
3011         */
3012         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3013           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3014         switch ((int) key_symbol)
3015         {
3016           case XK_Escape:
3017           case XK_F20:
3018           {
3019             /*
3020               Prematurely exit.
3021             */
3022             state|=EscapeState;
3023             state|=ExitState;
3024             break;
3025           }
3026           case XK_F1:
3027           case XK_Help:
3028           {
3029             (void) XSetFunction(display,windows->image.highlight_context,
3030               GXcopy);
3031             XTextViewWidget(display,resource_info,windows,MagickFalse,
3032               "Help Viewer - Image Chop",ImageChopHelp);
3033             (void) XSetFunction(display,windows->image.highlight_context,
3034               GXinvert);
3035             break;
3036           }
3037           default:
3038           {
3039             (void) XBell(display,0);
3040             break;
3041           }
3042         }
3043         break;
3044       }
3045       case MotionNotify:
3046       {
3047         /*
3048           Map and unmap Info widget as text cursor crosses its boundaries.
3049         */
3050         x=event.xmotion.x;
3051         y=event.xmotion.y;
3052         if (windows->info.mapped != MagickFalse)
3053           {
3054             if ((x < (int) (windows->info.x+windows->info.width)) &&
3055                 (y < (int) (windows->info.y+windows->info.height)))
3056               (void) XWithdrawWindow(display,windows->info.id,
3057                 windows->info.screen);
3058           }
3059         else
3060           if ((x > (int) (windows->info.x+windows->info.width)) ||
3061               (y > (int) (windows->info.y+windows->info.height)))
3062             (void) XMapWindow(display,windows->info.id);
3063       }
3064     }
3065   } while ((state & ExitState) == 0);
3066   (void) XSelectInput(display,windows->image.id,
3067     windows->image.attributes.event_mask);
3068   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3069   if ((state & EscapeState) != 0)
3070     return(MagickTrue);
3071   /*
3072     Draw line as pointer moves until the mouse button is released.
3073   */
3074   chop_info.width=0;
3075   chop_info.height=0;
3076   chop_info.x=0;
3077   chop_info.y=0;
3078   distance=0;
3079   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3080   state=DefaultState;
3081   do
3082   {
3083     if (distance > 9)
3084       {
3085         /*
3086           Display info and draw chopping line.
3087         */
3088         if (windows->info.mapped == MagickFalse)
3089           (void) XMapWindow(display,windows->info.id);
3090         (void) FormatLocaleString(text,MaxTextExtent,
3091           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3092           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3093         XInfoWidget(display,windows,text);
3094         XHighlightLine(display,windows->image.id,
3095           windows->image.highlight_context,&segment_info);
3096       }
3097     else
3098       if (windows->info.mapped != MagickFalse)
3099         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3100     /*
3101       Wait for next event.
3102     */
3103     XScreenEvent(display,windows,&event);
3104     if (distance > 9)
3105       XHighlightLine(display,windows->image.id,
3106         windows->image.highlight_context,&segment_info);
3107     switch (event.type)
3108     {
3109       case ButtonPress:
3110       {
3111         segment_info.x2=(short int) event.xmotion.x;
3112         segment_info.y2=(short int) event.xmotion.y;
3113         break;
3114       }
3115       case ButtonRelease:
3116       {
3117         /*
3118           User has committed to chopping line.
3119         */
3120         segment_info.x2=(short int) event.xbutton.x;
3121         segment_info.y2=(short int) event.xbutton.y;
3122         state|=ExitState;
3123         break;
3124       }
3125       case Expose:
3126         break;
3127       case MotionNotify:
3128       {
3129         segment_info.x2=(short int) event.xmotion.x;
3130         segment_info.y2=(short int) event.xmotion.y;
3131       }
3132       default:
3133         break;
3134     }
3135     /*
3136       Check boundary conditions.
3137     */
3138     if (segment_info.x2 < 0)
3139       segment_info.x2=0;
3140     else
3141       if (segment_info.x2 > windows->image.ximage->width)
3142         segment_info.x2=windows->image.ximage->width;
3143     if (segment_info.y2 < 0)
3144       segment_info.y2=0;
3145     else
3146       if (segment_info.y2 > windows->image.ximage->height)
3147         segment_info.y2=windows->image.ximage->height;
3148     distance=(unsigned int)
3149       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3150        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3151     /*
3152       Compute chopping geometry.
3153     */
3154     if (direction == HorizontalChopCommand)
3155       {
3156         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3157         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3158         chop_info.height=0;
3159         chop_info.y=0;
3160         if (segment_info.x1 > (int) segment_info.x2)
3161           {
3162             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3163             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3164           }
3165       }
3166     else
3167       {
3168         chop_info.width=0;
3169         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3170         chop_info.x=0;
3171         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3172         if (segment_info.y1 > segment_info.y2)
3173           {
3174             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3175             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3176           }
3177       }
3178   } while ((state & ExitState) == 0);
3179   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3180   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3181   if (distance <= 9)
3182     return(MagickTrue);
3183   /*
3184     Image chopping is relative to image configuration.
3185   */
3186   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
3187   XSetCursorState(display,windows,MagickTrue);
3188   XCheckRefreshWindows(display,windows);
3189   windows->image.window_changes.width=windows->image.ximage->width-
3190     (unsigned int) chop_info.width;
3191   windows->image.window_changes.height=windows->image.ximage->height-
3192     (unsigned int) chop_info.height;
3193   width=(unsigned int) (*image)->columns;
3194   height=(unsigned int) (*image)->rows;
3195   x=0;
3196   y=0;
3197   if (windows->image.crop_geometry != (char *) NULL)
3198     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3199   scale_factor=(MagickRealType) width/windows->image.ximage->width;
3200   chop_info.x+=x;
3201   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3202   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3203   scale_factor=(MagickRealType) height/windows->image.ximage->height;
3204   chop_info.y+=y;
3205   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3206   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3207   /*
3208     Chop image.
3209   */
3210   chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
3211   XSetCursorState(display,windows,MagickFalse);
3212   if (chop_image == (Image *) NULL)
3213     return(MagickFalse);
3214   *image=DestroyImage(*image);
3215   *image=chop_image;
3216   /*
3217     Update image configuration.
3218   */
3219   XConfigureImageColormap(display,resource_info,windows,*image);
3220   (void) XConfigureImage(display,resource_info,windows,*image);
3221   return(MagickTrue);
3222 }
3223 \f
3224 /*
3225 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3226 %                                                                             %
3227 %                                                                             %
3228 %                                                                             %
3229 +   X C o l o r E d i t I m a g e                                             %
3230 %                                                                             %
3231 %                                                                             %
3232 %                                                                             %
3233 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3234 %
3235 %  XColorEditImage() allows the user to interactively change the color of one
3236 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3237 %
3238 %  The format of the XColorEditImage method is:
3239 %
3240 %      MagickBooleanType XColorEditImage(Display *display,
3241 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
3242 %
3243 %  A description of each parameter follows:
3244 %
3245 %    o display: Specifies a connection to an X server;  returned from
3246 %      XOpenDisplay.
3247 %
3248 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3249 %
3250 %    o windows: Specifies a pointer to a XWindows structure.
3251 %
3252 %    o image: the image; returned from ReadImage.
3253 %
3254 */
3255
3256
3257 static MagickBooleanType XColorEditImage(Display *display,
3258   XResourceInfo *resource_info,XWindows *windows,Image **image)
3259 {
3260   static const char
3261     *ColorEditMenu[] =
3262     {
3263       "Method",
3264       "Pixel Color",
3265       "Border Color",
3266       "Fuzz",
3267       "Undo",
3268       "Help",
3269       "Dismiss",
3270       (char *) NULL
3271     };
3272
3273   static const ModeType
3274     ColorEditCommands[] =
3275     {
3276       ColorEditMethodCommand,
3277       ColorEditColorCommand,
3278       ColorEditBorderCommand,
3279       ColorEditFuzzCommand,
3280       ColorEditUndoCommand,
3281       ColorEditHelpCommand,
3282       ColorEditDismissCommand
3283     };
3284
3285   static PaintMethod
3286     method = PointMethod;
3287
3288   static unsigned int
3289     pen_id = 0;
3290
3291   static XColor
3292     border_color = { 0, 0, 0, 0, 0, 0 };
3293
3294   char
3295     command[MaxTextExtent],
3296     text[MaxTextExtent];
3297
3298   Cursor
3299     cursor;
3300
3301   ExceptionInfo
3302     *exception;
3303
3304   int
3305     entry,
3306     id,
3307     x,
3308     x_offset,
3309     y,
3310     y_offset;
3311
3312   register Quantum
3313     *q;
3314
3315   register ssize_t
3316     i;
3317
3318   unsigned int
3319     height,
3320     width;
3321
3322   size_t
3323     state;
3324
3325   XColor
3326     color;
3327
3328   XEvent
3329     event;
3330
3331   /*
3332     Map Command widget.
3333   */
3334   (void) CloneString(&windows->command.name,"Color Edit");
3335   windows->command.data=4;
3336   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3337   (void) XMapRaised(display,windows->command.id);
3338   XClientMessage(display,windows->image.id,windows->im_protocols,
3339     windows->im_update_widget,CurrentTime);
3340   /*
3341     Make cursor.
3342   */
3343   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3344     resource_info->background_color,resource_info->foreground_color);
3345   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3346   /*
3347     Track pointer until button 1 is pressed.
3348   */
3349   XQueryPosition(display,windows->image.id,&x,&y);
3350   (void) XSelectInput(display,windows->image.id,
3351     windows->image.attributes.event_mask | PointerMotionMask);
3352   state=DefaultState;
3353   do
3354   {
3355     if (windows->info.mapped != MagickFalse)
3356       {
3357         /*
3358           Display pointer position.
3359         */
3360         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3361           x+windows->image.x,y+windows->image.y);
3362         XInfoWidget(display,windows,text);
3363       }
3364     /*
3365       Wait for next event.
3366     */
3367     XScreenEvent(display,windows,&event);
3368     if (event.xany.window == windows->command.id)
3369       {
3370         /*
3371           Select a command from the Command widget.
3372         */
3373         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3374         if (id < 0)
3375           {
3376             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3377             continue;
3378           }
3379         switch (ColorEditCommands[id])
3380         {
3381           case ColorEditMethodCommand:
3382           {
3383             char
3384               **methods;
3385
3386             /*
3387               Select a method from the pop-up menu.
3388             */
3389             methods=(char **) GetCommandOptions(MagickMethodOptions);
3390             if (methods == (char **) NULL)
3391               break;
3392             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3393               (const char **) methods,command);
3394             if (entry >= 0)
3395               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3396                 MagickFalse,methods[entry]);
3397             methods=DestroyStringList(methods);
3398             break;
3399           }
3400           case ColorEditColorCommand:
3401           {
3402             const char
3403               *ColorMenu[MaxNumberPens];
3404
3405             int
3406               pen_number;
3407
3408             /*
3409               Initialize menu selections.
3410             */
3411             for (i=0; i < (int) (MaxNumberPens-2); i++)
3412               ColorMenu[i]=resource_info->pen_colors[i];
3413             ColorMenu[MaxNumberPens-2]="Browser...";
3414             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3415             /*
3416               Select a pen color from the pop-up menu.
3417             */
3418             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3419               (const char **) ColorMenu,command);
3420             if (pen_number < 0)
3421               break;
3422             if (pen_number == (MaxNumberPens-2))
3423               {
3424                 static char
3425                   color_name[MaxTextExtent] = "gray";
3426
3427                 /*
3428                   Select a pen color from a dialog.
3429                 */
3430                 resource_info->pen_colors[pen_number]=color_name;
3431                 XColorBrowserWidget(display,windows,"Select",color_name);
3432                 if (*color_name == '\0')
3433                   break;
3434               }
3435             /*
3436               Set pen color.
3437             */
3438             (void) XParseColor(display,windows->map_info->colormap,
3439               resource_info->pen_colors[pen_number],&color);
3440             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3441               (unsigned int) MaxColors,&color);
3442             windows->pixel_info->pen_colors[pen_number]=color;
3443             pen_id=(unsigned int) pen_number;
3444             break;
3445           }
3446           case ColorEditBorderCommand:
3447           {
3448             const char
3449               *ColorMenu[MaxNumberPens];
3450
3451             int
3452               pen_number;
3453
3454             /*
3455               Initialize menu selections.
3456             */
3457             for (i=0; i < (int) (MaxNumberPens-2); i++)
3458               ColorMenu[i]=resource_info->pen_colors[i];
3459             ColorMenu[MaxNumberPens-2]="Browser...";
3460             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3461             /*
3462               Select a pen color from the pop-up menu.
3463             */
3464             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3465               (const char **) ColorMenu,command);
3466             if (pen_number < 0)
3467               break;
3468             if (pen_number == (MaxNumberPens-2))
3469               {
3470                 static char
3471                   color_name[MaxTextExtent] = "gray";
3472
3473                 /*
3474                   Select a pen color from a dialog.
3475                 */
3476                 resource_info->pen_colors[pen_number]=color_name;
3477                 XColorBrowserWidget(display,windows,"Select",color_name);
3478                 if (*color_name == '\0')
3479                   break;
3480               }
3481             /*
3482               Set border color.
3483             */
3484             (void) XParseColor(display,windows->map_info->colormap,
3485               resource_info->pen_colors[pen_number],&border_color);
3486             break;
3487           }
3488           case ColorEditFuzzCommand:
3489           {
3490             static char
3491               fuzz[MaxTextExtent];
3492
3493             static const char
3494               *FuzzMenu[] =
3495               {
3496                 "0%",
3497                 "2%",
3498                 "5%",
3499                 "10%",
3500                 "15%",
3501                 "Dialog...",
3502                 (char *) NULL,
3503               };
3504
3505             /*
3506               Select a command from the pop-up menu.
3507             */
3508             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3509               command);
3510             if (entry < 0)
3511               break;
3512             if (entry != 5)
3513               {
3514                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
3515                   QuantumRange+1.0);
3516                 break;
3517               }
3518             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3519             (void) XDialogWidget(display,windows,"Ok",
3520               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3521             if (*fuzz == '\0')
3522               break;
3523             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3524             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
3525             break;
3526           }
3527           case ColorEditUndoCommand:
3528           {
3529             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3530               image);
3531             break;
3532           }
3533           case ColorEditHelpCommand:
3534           default:
3535           {
3536             XTextViewWidget(display,resource_info,windows,MagickFalse,
3537               "Help Viewer - Image Annotation",ImageColorEditHelp);
3538             break;
3539           }
3540           case ColorEditDismissCommand:
3541           {
3542             /*
3543               Prematurely exit.
3544             */
3545             state|=EscapeState;
3546             state|=ExitState;
3547             break;
3548           }
3549         }
3550         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3551         continue;
3552       }
3553     switch (event.type)
3554     {
3555       case ButtonPress:
3556       {
3557         if (event.xbutton.button != Button1)
3558           break;
3559         if ((event.xbutton.window != windows->image.id) &&
3560             (event.xbutton.window != windows->magnify.id))
3561           break;
3562         /*
3563           exit loop.
3564         */
3565         x=event.xbutton.x;
3566         y=event.xbutton.y;
3567         (void) XMagickCommand(display,resource_info,windows,
3568           SaveToUndoBufferCommand,image);
3569         state|=UpdateConfigurationState;
3570         break;
3571       }
3572       case ButtonRelease:
3573       {
3574         if (event.xbutton.button != Button1)
3575           break;
3576         if ((event.xbutton.window != windows->image.id) &&
3577             (event.xbutton.window != windows->magnify.id))
3578           break;
3579         /*
3580           Update colormap information.
3581         */
3582         x=event.xbutton.x;
3583         y=event.xbutton.y;
3584         XConfigureImageColormap(display,resource_info,windows,*image);
3585         (void) XConfigureImage(display,resource_info,windows,*image);
3586         XInfoWidget(display,windows,text);
3587         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3588         state&=(~UpdateConfigurationState);
3589         break;
3590       }
3591       case Expose:
3592         break;
3593       case KeyPress:
3594       {
3595         KeySym
3596           key_symbol;
3597
3598         if (event.xkey.window == windows->magnify.id)
3599           {
3600             Window
3601               window;
3602
3603             window=windows->magnify.id;
3604             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3605           }
3606         if (event.xkey.window != windows->image.id)
3607           break;
3608         /*
3609           Respond to a user key press.
3610         */
3611         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3612           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3613         switch ((int) key_symbol)
3614         {
3615           case XK_Escape:
3616           case XK_F20:
3617           {
3618             /*
3619               Prematurely exit.
3620             */
3621             state|=ExitState;
3622             break;
3623           }
3624           case XK_F1:
3625           case XK_Help:
3626           {
3627             XTextViewWidget(display,resource_info,windows,MagickFalse,
3628               "Help Viewer - Image Annotation",ImageColorEditHelp);
3629             break;
3630           }
3631           default:
3632           {
3633             (void) XBell(display,0);
3634             break;
3635           }
3636         }
3637         break;
3638       }
3639       case MotionNotify:
3640       {
3641         /*
3642           Map and unmap Info widget as cursor crosses its boundaries.
3643         */
3644         x=event.xmotion.x;
3645         y=event.xmotion.y;
3646         if (windows->info.mapped != MagickFalse)
3647           {
3648             if ((x < (int) (windows->info.x+windows->info.width)) &&
3649                 (y < (int) (windows->info.y+windows->info.height)))
3650               (void) XWithdrawWindow(display,windows->info.id,
3651                 windows->info.screen);
3652           }
3653         else
3654           if ((x > (int) (windows->info.x+windows->info.width)) ||
3655               (y > (int) (windows->info.y+windows->info.height)))
3656             (void) XMapWindow(display,windows->info.id);
3657         break;
3658       }
3659       default:
3660         break;
3661     }
3662     if (event.xany.window == windows->magnify.id)
3663       {
3664         x=windows->magnify.x-windows->image.x;
3665         y=windows->magnify.y-windows->image.y;
3666       }
3667     x_offset=x;
3668     y_offset=y;
3669     if ((state & UpdateConfigurationState) != 0)
3670       {
3671         CacheView
3672           *image_view;
3673
3674         int
3675           x,
3676           y;
3677
3678         /*
3679           Pixel edit is relative to image configuration.
3680         */
3681         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3682           MagickTrue);
3683         color=windows->pixel_info->pen_colors[pen_id];
3684         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3685         width=(unsigned int) (*image)->columns;
3686         height=(unsigned int) (*image)->rows;
3687         x=0;
3688         y=0;
3689         if (windows->image.crop_geometry != (char *) NULL)
3690           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3691             &width,&height);
3692         x_offset=(int)
3693           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3694         y_offset=(int)
3695           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3696         if ((x_offset < 0) || (y_offset < 0))
3697           continue;
3698         if ((x_offset >= (int) (*image)->columns) ||
3699             (y_offset >= (int) (*image)->rows))
3700           continue;
3701         exception=(&(*image)->exception);
3702         image_view=AcquireCacheView(*image);
3703         switch (method)
3704         {
3705           case PointMethod:
3706           default:
3707           {
3708             /*
3709               Update color information using point algorithm.
3710             */
3711             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3712               return(MagickFalse);
3713             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3714               (ssize_t) y_offset,1,1,exception);
3715             if (q == (Quantum *) NULL)
3716               break;
3717             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3718             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3719             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3720             (void) SyncCacheViewAuthenticPixels(image_view,
3721               &(*image)->exception);
3722             break;
3723           }
3724           case ReplaceMethod:
3725           {
3726             PixelPacket
3727               pixel,
3728               target;
3729
3730             /*
3731               Update color information using replace algorithm.
3732             */
3733             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3734               (ssize_t) y_offset,&target,&(*image)->exception);
3735             if ((*image)->storage_class == DirectClass)
3736               {
3737                 for (y=0; y < (int) (*image)->rows; y++)
3738                 {
3739                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3740                     (*image)->columns,1,exception);
3741                   if (q == (Quantum *) NULL)
3742                     break;
3743                   for (x=0; x < (int) (*image)->columns; x++)
3744                   {
3745                     GetPixelPacket(*image,q,&pixel);
3746                     if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
3747                       {
3748                         SetPixelRed(*image,ScaleShortToQuantum(
3749                           color.red),q);
3750                         SetPixelGreen(*image,ScaleShortToQuantum(
3751                           color.green),q);
3752                         SetPixelBlue(*image,ScaleShortToQuantum(
3753                           color.blue),q);
3754                       }
3755                     q+=GetPixelChannels(*image);
3756                   }
3757                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3758                     break;
3759                 }
3760               }
3761             else
3762               {
3763                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3764                   if (IsFuzzyEquivalencePixelPacket(*image,(*image)->colormap+i,&target))
3765                     {
3766                       (*image)->colormap[i].red=ScaleShortToQuantum(
3767                         color.red);
3768                       (*image)->colormap[i].green=ScaleShortToQuantum(
3769                         color.green);
3770                       (*image)->colormap[i].blue=ScaleShortToQuantum(
3771                         color.blue);
3772                     }
3773                 (void) SyncImage(*image);
3774               }
3775             break;
3776           }
3777           case FloodfillMethod:
3778           case FillToBorderMethod:
3779           {
3780             DrawInfo
3781               *draw_info;
3782
3783             PixelInfo
3784               target;
3785
3786             /*
3787               Update color information using floodfill algorithm.
3788             */
3789             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3790               (ssize_t) y_offset,&target,exception);
3791             if (method == FillToBorderMethod)
3792               {
3793                 target.red=(MagickRealType)
3794                   ScaleShortToQuantum(border_color.red);
3795                 target.green=(MagickRealType)
3796                   ScaleShortToQuantum(border_color.green);
3797                 target.blue=(MagickRealType)
3798                   ScaleShortToQuantum(border_color.blue);
3799               }
3800             draw_info=CloneDrawInfo(resource_info->image_info,
3801               (DrawInfo *) NULL);
3802             (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3803               &draw_info->fill,exception);
3804             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3805               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3806               MagickFalse : MagickTrue,exception);
3807             draw_info=DestroyDrawInfo(draw_info);
3808             break;
3809           }
3810           case ResetMethod:
3811           {
3812             /*
3813               Update color information using reset algorithm.
3814             */
3815             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3816               return(MagickFalse);
3817             for (y=0; y < (int) (*image)->rows; y++)
3818             {
3819               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3820                 (*image)->columns,1,exception);
3821               if (q == (Quantum *) NULL)
3822                 break;
3823               for (x=0; x < (int) (*image)->columns; x++)
3824               {
3825                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3826                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3827                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3828                 q+=GetPixelChannels(*image);
3829               }
3830               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3831                 break;
3832             }
3833             break;
3834           }
3835         }
3836         image_view=DestroyCacheView(image_view);
3837         state&=(~UpdateConfigurationState);
3838       }
3839   } while ((state & ExitState) == 0);
3840   (void) XSelectInput(display,windows->image.id,
3841     windows->image.attributes.event_mask);
3842   XSetCursorState(display,windows,MagickFalse);
3843   (void) XFreeCursor(display,cursor);
3844   return(MagickTrue);
3845 }
3846 \f
3847 /*
3848 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3849 %                                                                             %
3850 %                                                                             %
3851 %                                                                             %
3852 +   X C o m p o s i t e I m a g e                                             %
3853 %                                                                             %
3854 %                                                                             %
3855 %                                                                             %
3856 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3857 %
3858 %  XCompositeImage() requests an image name from the user, reads the image and
3859 %  composites it with the X window image at a location the user chooses with
3860 %  the pointer.
3861 %
3862 %  The format of the XCompositeImage method is:
3863 %
3864 %      MagickBooleanType XCompositeImage(Display *display,
3865 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
3866 %
3867 %  A description of each parameter follows:
3868 %
3869 %    o display: Specifies a connection to an X server;  returned from
3870 %      XOpenDisplay.
3871 %
3872 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3873 %
3874 %    o windows: Specifies a pointer to a XWindows structure.
3875 %
3876 %    o image: the image; returned from ReadImage.
3877 %
3878 */
3879 static MagickBooleanType XCompositeImage(Display *display,
3880   XResourceInfo *resource_info,XWindows *windows,Image *image)
3881 {
3882   static char
3883     displacement_geometry[MaxTextExtent] = "30x30",
3884     filename[MaxTextExtent] = "\0";
3885
3886   static const char
3887     *CompositeMenu[] =
3888     {
3889       "Operators",
3890       "Dissolve",
3891       "Displace",
3892       "Help",
3893       "Dismiss",
3894       (char *) NULL
3895     };
3896
3897   static CompositeOperator
3898     compose = CopyCompositeOp;
3899
3900   static const ModeType
3901     CompositeCommands[] =
3902     {
3903       CompositeOperatorsCommand,
3904       CompositeDissolveCommand,
3905       CompositeDisplaceCommand,
3906       CompositeHelpCommand,
3907       CompositeDismissCommand
3908     };
3909
3910   char
3911     text[MaxTextExtent];
3912
3913   Cursor
3914     cursor;
3915
3916   Image
3917     *composite_image;
3918
3919   int
3920     entry,
3921     id,
3922     x,
3923     y;
3924
3925   MagickRealType
3926     blend,
3927     scale_factor;
3928
3929   RectangleInfo
3930     highlight_info,
3931     composite_info;
3932
3933   unsigned int
3934     height,
3935     width;
3936
3937   size_t
3938     state;
3939
3940   XEvent
3941     event;
3942
3943   /*
3944     Request image file name from user.
3945   */
3946   XFileBrowserWidget(display,windows,"Composite",filename);
3947   if (*filename == '\0')
3948     return(MagickTrue);
3949   /*
3950     Read image.
3951   */
3952   XSetCursorState(display,windows,MagickTrue);
3953   XCheckRefreshWindows(display,windows);
3954   (void) CopyMagickString(resource_info->image_info->filename,filename,
3955     MaxTextExtent);
3956   composite_image=ReadImage(resource_info->image_info,&image->exception);
3957   CatchException(&image->exception);
3958   XSetCursorState(display,windows,MagickFalse);
3959   if (composite_image == (Image *) NULL)
3960     return(MagickFalse);
3961   /*
3962     Map Command widget.
3963   */
3964   (void) CloneString(&windows->command.name,"Composite");
3965   windows->command.data=1;
3966   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3967   (void) XMapRaised(display,windows->command.id);
3968   XClientMessage(display,windows->image.id,windows->im_protocols,
3969     windows->im_update_widget,CurrentTime);
3970   /*
3971     Track pointer until button 1 is pressed.
3972   */
3973   XQueryPosition(display,windows->image.id,&x,&y);
3974   (void) XSelectInput(display,windows->image.id,
3975     windows->image.attributes.event_mask | PointerMotionMask);
3976   composite_info.x=(ssize_t) windows->image.x+x;
3977   composite_info.y=(ssize_t) windows->image.y+y;
3978   composite_info.width=0;
3979   composite_info.height=0;
3980   cursor=XCreateFontCursor(display,XC_ul_angle);
3981   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3982   blend=0.0;
3983   state=DefaultState;
3984   do
3985   {
3986     if (windows->info.mapped != MagickFalse)
3987       {
3988         /*
3989           Display pointer position.
3990         */
3991         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
3992           (long) composite_info.x,(long) composite_info.y);
3993         XInfoWidget(display,windows,text);
3994       }
3995     highlight_info=composite_info;
3996     highlight_info.x=composite_info.x-windows->image.x;
3997     highlight_info.y=composite_info.y-windows->image.y;
3998     XHighlightRectangle(display,windows->image.id,
3999       windows->image.highlight_context,&highlight_info);
4000     /*
4001       Wait for next event.
4002     */
4003     XScreenEvent(display,windows,&event);
4004     XHighlightRectangle(display,windows->image.id,
4005       windows->image.highlight_context,&highlight_info);
4006     if (event.xany.window == windows->command.id)
4007       {
4008         /*
4009           Select a command from the Command widget.
4010         */
4011         id=XCommandWidget(display,windows,CompositeMenu,&event);
4012         if (id < 0)
4013           continue;
4014         switch (CompositeCommands[id])
4015         {
4016           case CompositeOperatorsCommand:
4017           {
4018             char
4019               command[MaxTextExtent],
4020               **operators;
4021
4022             /*
4023               Select a command from the pop-up menu.
4024             */
4025             operators=GetCommandOptions(MagickComposeOptions);
4026             if (operators == (char **) NULL)
4027               break;
4028             entry=XMenuWidget(display,windows,CompositeMenu[id],
4029               (const char **) operators,command);
4030             if (entry >= 0)
4031               compose=(CompositeOperator) ParseCommandOption(
4032                 MagickComposeOptions,MagickFalse,operators[entry]);
4033             operators=DestroyStringList(operators);
4034             break;
4035           }
4036           case CompositeDissolveCommand:
4037           {
4038             static char
4039               factor[MaxTextExtent] = "20.0";
4040
4041             /*
4042               Dissolve the two images a given percent.
4043             */
4044             (void) XSetFunction(display,windows->image.highlight_context,
4045               GXcopy);
4046             (void) XDialogWidget(display,windows,"Dissolve",
4047               "Enter the blend factor (0.0 - 99.9%):",factor);
4048             (void) XSetFunction(display,windows->image.highlight_context,
4049               GXinvert);
4050             if (*factor == '\0')
4051               break;
4052             blend=InterpretLocaleValue(factor,(char **) NULL);
4053             compose=DissolveCompositeOp;
4054             break;
4055           }
4056           case CompositeDisplaceCommand:
4057           {
4058             /*
4059               Get horizontal and vertical scale displacement geometry.
4060             */
4061             (void) XSetFunction(display,windows->image.highlight_context,
4062               GXcopy);
4063             (void) XDialogWidget(display,windows,"Displace",
4064               "Enter the horizontal and vertical scale:",displacement_geometry);
4065             (void) XSetFunction(display,windows->image.highlight_context,
4066               GXinvert);
4067             if (*displacement_geometry == '\0')
4068               break;
4069             compose=DisplaceCompositeOp;
4070             break;
4071           }
4072           case CompositeHelpCommand:
4073           {
4074             (void) XSetFunction(display,windows->image.highlight_context,
4075               GXcopy);
4076             XTextViewWidget(display,resource_info,windows,MagickFalse,
4077               "Help Viewer - Image Composite",ImageCompositeHelp);
4078             (void) XSetFunction(display,windows->image.highlight_context,
4079               GXinvert);
4080             break;
4081           }
4082           case CompositeDismissCommand:
4083           {
4084             /*
4085               Prematurely exit.
4086             */
4087             state|=EscapeState;
4088             state|=ExitState;
4089             break;
4090           }
4091           default:
4092             break;
4093         }
4094         continue;
4095       }
4096     switch (event.type)
4097     {
4098       case ButtonPress:
4099       {
4100         if (image->debug != MagickFalse)
4101           (void) LogMagickEvent(X11Event,GetMagickModule(),
4102             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4103             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4104         if (event.xbutton.button != Button1)
4105           break;
4106         if (event.xbutton.window != windows->image.id)
4107           break;
4108         /*
4109           Change cursor.
4110         */
4111         composite_info.width=composite_image->columns;
4112         composite_info.height=composite_image->rows;
4113         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4114         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4115         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4116         break;
4117       }
4118       case ButtonRelease:
4119       {
4120         if (image->debug != MagickFalse)
4121           (void) LogMagickEvent(X11Event,GetMagickModule(),
4122             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4123             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4124         if (event.xbutton.button != Button1)
4125           break;
4126         if (event.xbutton.window != windows->image.id)
4127           break;
4128         if ((composite_info.width != 0) && (composite_info.height != 0))
4129           {
4130             /*
4131               User has selected the location of the composite image.
4132             */
4133             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4134             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4135             state|=ExitState;
4136           }
4137         break;
4138       }
4139       case Expose:
4140         break;
4141       case KeyPress:
4142       {
4143         char
4144           command[MaxTextExtent];
4145
4146         KeySym
4147           key_symbol;
4148
4149         int
4150           length;
4151
4152         if (event.xkey.window != windows->image.id)
4153           break;
4154         /*
4155           Respond to a user key press.
4156         */
4157         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4158           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4159         *(command+length)='\0';
4160         if (image->debug != MagickFalse)
4161           (void) LogMagickEvent(X11Event,GetMagickModule(),
4162             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4163         switch ((int) key_symbol)
4164         {
4165           case XK_Escape:
4166           case XK_F20:
4167           {
4168             /*
4169               Prematurely exit.
4170             */
4171             composite_image=DestroyImage(composite_image);
4172             state|=EscapeState;
4173             state|=ExitState;
4174             break;
4175           }
4176           case XK_F1:
4177           case XK_Help:
4178           {
4179             (void) XSetFunction(display,windows->image.highlight_context,
4180               GXcopy);
4181             XTextViewWidget(display,resource_info,windows,MagickFalse,
4182               "Help Viewer - Image Composite",ImageCompositeHelp);
4183             (void) XSetFunction(display,windows->image.highlight_context,
4184               GXinvert);
4185             break;
4186           }
4187           default:
4188           {
4189             (void) XBell(display,0);
4190             break;
4191           }
4192         }
4193         break;
4194       }
4195       case MotionNotify:
4196       {
4197         /*
4198           Map and unmap Info widget as text cursor crosses its boundaries.
4199         */
4200         x=event.xmotion.x;
4201         y=event.xmotion.y;
4202         if (windows->info.mapped != MagickFalse)
4203           {
4204             if ((x < (int) (windows->info.x+windows->info.width)) &&
4205                 (y < (int) (windows->info.y+windows->info.height)))
4206               (void) XWithdrawWindow(display,windows->info.id,
4207                 windows->info.screen);
4208           }
4209         else
4210           if ((x > (int) (windows->info.x+windows->info.width)) ||
4211               (y > (int) (windows->info.y+windows->info.height)))
4212             (void) XMapWindow(display,windows->info.id);
4213         composite_info.x=(ssize_t) windows->image.x+x;
4214         composite_info.y=(ssize_t) windows->image.y+y;
4215         break;
4216       }
4217       default:
4218       {
4219         if (image->debug != MagickFalse)
4220           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4221             event.type);
4222         break;
4223       }
4224     }
4225   } while ((state & ExitState) == 0);
4226   (void) XSelectInput(display,windows->image.id,
4227     windows->image.attributes.event_mask);
4228   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4229   XSetCursorState(display,windows,MagickFalse);
4230   (void) XFreeCursor(display,cursor);
4231   if ((state & EscapeState) != 0)
4232     return(MagickTrue);
4233   /*
4234     Image compositing is relative to image configuration.
4235   */
4236   XSetCursorState(display,windows,MagickTrue);
4237   XCheckRefreshWindows(display,windows);
4238   width=(unsigned int) image->columns;
4239   height=(unsigned int) image->rows;
4240   x=0;
4241   y=0;
4242   if (windows->image.crop_geometry != (char *) NULL)
4243     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4244   scale_factor=(MagickRealType) width/windows->image.ximage->width;
4245   composite_info.x+=x;
4246   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4247   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4248   scale_factor=(MagickRealType) height/windows->image.ximage->height;
4249   composite_info.y+=y;
4250   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4251   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4252   if ((composite_info.width != composite_image->columns) ||
4253       (composite_info.height != composite_image->rows))
4254     {
4255       Image
4256         *resize_image;
4257
4258       /*
4259         Scale composite image.
4260       */
4261       resize_image=ResizeImage(composite_image,composite_info.width,
4262         composite_info.height,composite_image->filter,composite_image->blur,
4263         &image->exception);
4264       composite_image=DestroyImage(composite_image);
4265       if (resize_image == (Image *) NULL)
4266         {
4267           XSetCursorState(display,windows,MagickFalse);
4268           return(MagickFalse);
4269         }
4270       composite_image=resize_image;
4271     }
4272   if (compose == DisplaceCompositeOp)
4273     (void) SetImageArtifact(composite_image,"compose:args",
4274       displacement_geometry);
4275   if (blend != 0.0)
4276     {
4277       CacheView
4278         *image_view;
4279
4280       ExceptionInfo
4281         *exception;
4282
4283       int
4284         y;
4285
4286       Quantum
4287         opacity;
4288
4289       register int
4290         x;
4291
4292       register Quantum
4293         *q;
4294
4295       /*
4296         Create mattes for blending.
4297       */
4298       exception=(&image->exception);
4299       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4300       opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4301         ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4302       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4303         return(MagickFalse);
4304       image->matte=MagickTrue;
4305       image_view=AcquireCacheView(image);
4306       for (y=0; y < (int) image->rows; y++)
4307       {
4308         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4309           exception);
4310         if (q == (Quantum *) NULL)
4311           break;
4312         for (x=0; x < (int) image->columns; x++)
4313         {
4314           SetPixelAlpha(image,opacity,q);
4315           q+=GetPixelChannels(image);
4316         }
4317         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4318           break;
4319       }
4320       image_view=DestroyCacheView(image_view);
4321     }
4322   /*
4323     Composite image with X Image window.
4324   */
4325   (void) CompositeImage(image,compose,composite_image,composite_info.x,
4326     composite_info.y);
4327   composite_image=DestroyImage(composite_image);
4328   XSetCursorState(display,windows,MagickFalse);
4329   /*
4330     Update image configuration.
4331   */
4332   XConfigureImageColormap(display,resource_info,windows,image);
4333   (void) XConfigureImage(display,resource_info,windows,image);
4334   return(MagickTrue);
4335 }
4336 \f
4337 /*
4338 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4339 %                                                                             %
4340 %                                                                             %
4341 %                                                                             %
4342 +   X C o n f i g u r e I m a g e                                             %
4343 %                                                                             %
4344 %                                                                             %
4345 %                                                                             %
4346 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4347 %
4348 %  XConfigureImage() creates a new X image.  It also notifies the window
4349 %  manager of the new image size and configures the transient widows.
4350 %
4351 %  The format of the XConfigureImage method is:
4352 %
4353 %      MagickBooleanType XConfigureImage(Display *display,
4354 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
4355 %
4356 %  A description of each parameter follows:
4357 %
4358 %    o display: Specifies a connection to an X server; returned from
4359 %      XOpenDisplay.
4360 %
4361 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4362 %
4363 %    o windows: Specifies a pointer to a XWindows structure.
4364 %
4365 %    o image: the image.
4366 %
4367 %
4368 */
4369 static MagickBooleanType XConfigureImage(Display *display,
4370   XResourceInfo *resource_info,XWindows *windows,Image *image)
4371 {
4372   char
4373     geometry[MaxTextExtent];
4374
4375   MagickStatusType
4376     status;
4377
4378   size_t
4379     mask,
4380     height,
4381     width;
4382
4383   ssize_t
4384     x,
4385     y;
4386
4387   XSizeHints
4388     *size_hints;
4389
4390   XWindowChanges
4391     window_changes;
4392
4393   /*
4394     Dismiss if window dimensions are zero.
4395   */
4396   width=(unsigned int) windows->image.window_changes.width;
4397   height=(unsigned int) windows->image.window_changes.height;
4398   if (image->debug != MagickFalse)
4399     (void) LogMagickEvent(X11Event,GetMagickModule(),
4400       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4401       windows->image.ximage->height,(double) width,(double) height);
4402   if ((width*height) == 0)
4403     return(MagickTrue);
4404   x=0;
4405   y=0;
4406   /*
4407     Resize image to fit Image window dimensions.
4408   */
4409   XSetCursorState(display,windows,MagickTrue);
4410   (void) XFlush(display);
4411   if (((int) width != windows->image.ximage->width) ||
4412       ((int) height != windows->image.ximage->height))
4413     image->taint=MagickTrue;
4414   windows->magnify.x=(int)
4415     width*windows->magnify.x/windows->image.ximage->width;
4416   windows->magnify.y=(int)
4417     height*windows->magnify.y/windows->image.ximage->height;
4418   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4419   windows->image.y=(int)
4420     (height*windows->image.y/windows->image.ximage->height);
4421   status=XMakeImage(display,resource_info,&windows->image,image,
4422     (unsigned int) width,(unsigned int) height);
4423   if (status == MagickFalse)
4424     XNoticeWidget(display,windows,"Unable to configure X image:",
4425       windows->image.name);
4426   /*
4427     Notify window manager of the new configuration.
4428   */
4429   if (resource_info->image_geometry != (char *) NULL)
4430     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4431       resource_info->image_geometry);
4432   else
4433     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4434       XDisplayWidth(display,windows->image.screen),
4435       XDisplayHeight(display,windows->image.screen));
4436   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4437   window_changes.width=(int) width;
4438   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4439     window_changes.width=XDisplayWidth(display,windows->image.screen);
4440   window_changes.height=(int) height;
4441   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4442     window_changes.height=XDisplayHeight(display,windows->image.screen);
4443   mask=(size_t) (CWWidth | CWHeight);
4444   if (resource_info->backdrop)
4445     {
4446       mask|=CWX | CWY;
4447       window_changes.x=(int)
4448         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4449       window_changes.y=(int)
4450         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4451     }
4452   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4453     (unsigned int) mask,&window_changes);
4454   (void) XClearWindow(display,windows->image.id);
4455   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4456   /*
4457     Update Magnify window configuration.
4458   */
4459   if (windows->magnify.mapped != MagickFalse)
4460     XMakeMagnifyImage(display,windows);
4461   windows->pan.crop_geometry=windows->image.crop_geometry;
4462   XBestIconSize(display,&windows->pan,image);
4463   while (((windows->pan.width << 1) < MaxIconSize) &&
4464          ((windows->pan.height << 1) < MaxIconSize))
4465   {
4466     windows->pan.width<<=1;
4467     windows->pan.height<<=1;
4468   }
4469   if (windows->pan.geometry != (char *) NULL)
4470     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4471       &windows->pan.width,&windows->pan.height);
4472   window_changes.width=(int) windows->pan.width;
4473   window_changes.height=(int) windows->pan.height;
4474   size_hints=XAllocSizeHints();
4475   if (size_hints != (XSizeHints *) NULL)
4476     {
4477       /*
4478         Set new size hints.
4479       */
4480       size_hints->flags=PSize | PMinSize | PMaxSize;
4481       size_hints->width=window_changes.width;
4482       size_hints->height=window_changes.height;
4483       size_hints->min_width=size_hints->width;
4484       size_hints->min_height=size_hints->height;
4485       size_hints->max_width=size_hints->width;
4486       size_hints->max_height=size_hints->height;
4487       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4488       (void) XFree((void *) size_hints);
4489     }
4490   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4491     (unsigned int) (CWWidth | CWHeight),&window_changes);
4492   /*
4493     Update icon window configuration.
4494   */
4495   windows->icon.crop_geometry=windows->image.crop_geometry;
4496   XBestIconSize(display,&windows->icon,image);
4497   window_changes.width=(int) windows->icon.width;
4498   window_changes.height=(int) windows->icon.height;
4499   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4500     (unsigned int) (CWWidth | CWHeight),&window_changes);
4501   XSetCursorState(display,windows,MagickFalse);
4502   return(status != 0 ? MagickTrue : MagickFalse);
4503 }
4504 \f
4505 /*
4506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4507 %                                                                             %
4508 %                                                                             %
4509 %                                                                             %
4510 +   X C r o p I m a g e                                                       %
4511 %                                                                             %
4512 %                                                                             %
4513 %                                                                             %
4514 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4515 %
4516 %  XCropImage() allows the user to select a region of the image and crop, copy,
4517 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4518 %  the image with XPasteImage.
4519 %
4520 %  The format of the XCropImage method is:
4521 %
4522 %      MagickBooleanType XCropImage(Display *display,
4523 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4524 %        const ClipboardMode mode)
4525 %
4526 %  A description of each parameter follows:
4527 %
4528 %    o display: Specifies a connection to an X server; returned from
4529 %      XOpenDisplay.
4530 %
4531 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4532 %
4533 %    o windows: Specifies a pointer to a XWindows structure.
4534 %
4535 %    o image: the image; returned from ReadImage.
4536 %
4537 %    o mode: This unsigned value specified whether the image should be
4538 %      cropped, copied, or cut.
4539 %
4540 */
4541 static MagickBooleanType XCropImage(Display *display,
4542   XResourceInfo *resource_info,XWindows *windows,Image *image,
4543   const ClipboardMode mode)
4544 {
4545   static const char
4546     *CropModeMenu[] =
4547     {
4548       "Help",
4549       "Dismiss",
4550       (char *) NULL
4551     },
4552     *RectifyModeMenu[] =
4553     {
4554       "Crop",
4555       "Help",
4556       "Dismiss",
4557       (char *) NULL
4558     };
4559
4560   static const ModeType
4561     CropCommands[] =
4562     {
4563       CropHelpCommand,
4564       CropDismissCommand
4565     },
4566     RectifyCommands[] =
4567     {
4568       RectifyCopyCommand,
4569       RectifyHelpCommand,
4570       RectifyDismissCommand
4571     };
4572
4573   CacheView
4574     *image_view;
4575
4576   char
4577     command[MaxTextExtent],
4578     text[MaxTextExtent];
4579
4580   Cursor
4581     cursor;
4582
4583   ExceptionInfo
4584     *exception;
4585
4586   int
4587     id,
4588     x,
4589     y;
4590
4591   KeySym
4592     key_symbol;
4593
4594   Image
4595     *crop_image;
4596
4597   MagickRealType
4598     scale_factor;
4599
4600   RectangleInfo
4601     crop_info,
4602     highlight_info;
4603
4604   register Quantum
4605     *q;
4606
4607   unsigned int
4608     height,
4609     width;
4610
4611   size_t
4612     state;
4613
4614   XEvent
4615     event;
4616
4617   /*
4618     Map Command widget.
4619   */
4620   switch (mode)
4621   {
4622     case CopyMode:
4623     {
4624       (void) CloneString(&windows->command.name,"Copy");
4625       break;
4626     }
4627     case CropMode:
4628     {
4629       (void) CloneString(&windows->command.name,"Crop");
4630       break;
4631     }
4632     case CutMode:
4633     {
4634       (void) CloneString(&windows->command.name,"Cut");
4635       break;
4636     }
4637   }
4638   RectifyModeMenu[0]=windows->command.name;
4639   windows->command.data=0;
4640   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4641   (void) XMapRaised(display,windows->command.id);
4642   XClientMessage(display,windows->image.id,windows->im_protocols,
4643     windows->im_update_widget,CurrentTime);
4644   /*
4645     Track pointer until button 1 is pressed.
4646   */
4647   XQueryPosition(display,windows->image.id,&x,&y);
4648   (void) XSelectInput(display,windows->image.id,
4649     windows->image.attributes.event_mask | PointerMotionMask);
4650   crop_info.x=(ssize_t) windows->image.x+x;
4651   crop_info.y=(ssize_t) windows->image.y+y;
4652   crop_info.width=0;
4653   crop_info.height=0;
4654   cursor=XCreateFontCursor(display,XC_fleur);
4655   state=DefaultState;
4656   do
4657   {
4658     if (windows->info.mapped != MagickFalse)
4659       {
4660         /*
4661           Display pointer position.
4662         */
4663         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4664           (long) crop_info.x,(long) crop_info.y);
4665         XInfoWidget(display,windows,text);
4666       }
4667     /*
4668       Wait for next event.
4669     */
4670     XScreenEvent(display,windows,&event);
4671     if (event.xany.window == windows->command.id)
4672       {
4673         /*
4674           Select a command from the Command widget.
4675         */
4676         id=XCommandWidget(display,windows,CropModeMenu,&event);
4677         if (id < 0)
4678           continue;
4679         switch (CropCommands[id])
4680         {
4681           case CropHelpCommand:
4682           {
4683             switch (mode)
4684             {
4685               case CopyMode:
4686               {
4687                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4688                   "Help Viewer - Image Copy",ImageCopyHelp);
4689                 break;
4690               }
4691               case CropMode:
4692               {
4693                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4694                   "Help Viewer - Image Crop",ImageCropHelp);
4695                 break;
4696               }
4697               case CutMode:
4698               {
4699                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4700                   "Help Viewer - Image Cut",ImageCutHelp);
4701                 break;
4702               }
4703             }
4704             break;
4705           }
4706           case CropDismissCommand:
4707           {
4708             /*
4709               Prematurely exit.
4710             */
4711             state|=EscapeState;
4712             state|=ExitState;
4713             break;
4714           }
4715           default:
4716             break;
4717         }
4718         continue;
4719       }
4720     switch (event.type)
4721     {
4722       case ButtonPress:
4723       {
4724         if (event.xbutton.button != Button1)
4725           break;
4726         if (event.xbutton.window != windows->image.id)
4727           break;
4728         /*
4729           Note first corner of cropping rectangle-- exit loop.
4730         */
4731         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4732         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4733         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4734         state|=ExitState;
4735         break;
4736       }
4737       case ButtonRelease:
4738         break;
4739       case Expose:
4740         break;
4741       case KeyPress:
4742       {
4743         if (event.xkey.window != windows->image.id)
4744           break;
4745         /*
4746           Respond to a user key press.
4747         */
4748         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4749           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4750         switch ((int) key_symbol)
4751         {
4752           case XK_Escape:
4753           case XK_F20:
4754           {
4755             /*
4756               Prematurely exit.
4757             */
4758             state|=EscapeState;
4759             state|=ExitState;
4760             break;
4761           }
4762           case XK_F1:
4763           case XK_Help:
4764           {
4765             switch (mode)
4766             {
4767               case CopyMode:
4768               {
4769                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4770                   "Help Viewer - Image Copy",ImageCopyHelp);
4771                 break;
4772               }
4773               case CropMode:
4774               {
4775                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4776                   "Help Viewer - Image Crop",ImageCropHelp);
4777                 break;
4778               }
4779               case CutMode:
4780               {
4781                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4782                   "Help Viewer - Image Cut",ImageCutHelp);
4783                 break;
4784               }
4785             }
4786             break;
4787           }
4788           default:
4789           {
4790             (void) XBell(display,0);
4791             break;
4792           }
4793         }
4794         break;
4795       }
4796       case MotionNotify:
4797       {
4798         if (event.xmotion.window != windows->image.id)
4799           break;
4800         /*
4801           Map and unmap Info widget as text cursor crosses its boundaries.
4802         */
4803         x=event.xmotion.x;
4804         y=event.xmotion.y;
4805         if (windows->info.mapped != MagickFalse)
4806           {
4807             if ((x < (int) (windows->info.x+windows->info.width)) &&
4808                 (y < (int) (windows->info.y+windows->info.height)))
4809               (void) XWithdrawWindow(display,windows->info.id,
4810                 windows->info.screen);
4811           }
4812         else
4813           if ((x > (int) (windows->info.x+windows->info.width)) ||
4814               (y > (int) (windows->info.y+windows->info.height)))
4815             (void) XMapWindow(display,windows->info.id);
4816         crop_info.x=(ssize_t) windows->image.x+x;
4817         crop_info.y=(ssize_t) windows->image.y+y;
4818         break;
4819       }
4820       default:
4821         break;
4822     }
4823   } while ((state & ExitState) == 0);
4824   (void) XSelectInput(display,windows->image.id,
4825     windows->image.attributes.event_mask);
4826   if ((state & EscapeState) != 0)
4827     {
4828       /*
4829         User want to exit without cropping.
4830       */
4831       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4832       (void) XFreeCursor(display,cursor);
4833       return(MagickTrue);
4834     }
4835   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4836   do
4837   {
4838     /*
4839       Size rectangle as pointer moves until the mouse button is released.
4840     */
4841     x=(int) crop_info.x;
4842     y=(int) crop_info.y;
4843     crop_info.width=0;
4844     crop_info.height=0;
4845     state=DefaultState;
4846     do
4847     {
4848       highlight_info=crop_info;
4849       highlight_info.x=crop_info.x-windows->image.x;
4850       highlight_info.y=crop_info.y-windows->image.y;
4851       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4852         {
4853           /*
4854             Display info and draw cropping rectangle.
4855           */
4856           if (windows->info.mapped == MagickFalse)
4857             (void) XMapWindow(display,windows->info.id);
4858           (void) FormatLocaleString(text,MaxTextExtent,
4859             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4860             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4861           XInfoWidget(display,windows,text);
4862           XHighlightRectangle(display,windows->image.id,
4863             windows->image.highlight_context,&highlight_info);
4864         }
4865       else
4866         if (windows->info.mapped != MagickFalse)
4867           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4868       /*
4869         Wait for next event.
4870       */
4871       XScreenEvent(display,windows,&event);
4872       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4873         XHighlightRectangle(display,windows->image.id,
4874           windows->image.highlight_context,&highlight_info);
4875       switch (event.type)
4876       {
4877         case ButtonPress:
4878         {
4879           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4880           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4881           break;
4882         }
4883         case ButtonRelease:
4884         {
4885           /*
4886             User has committed to cropping rectangle.
4887           */
4888           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4889           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4890           XSetCursorState(display,windows,MagickFalse);
4891           state|=ExitState;
4892           windows->command.data=0;
4893           (void) XCommandWidget(display,windows,RectifyModeMenu,
4894             (XEvent *) NULL);
4895           break;
4896         }
4897         case Expose:
4898           break;
4899         case MotionNotify:
4900         {
4901           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4902           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4903         }
4904         default:
4905           break;
4906       }
4907       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4908           ((state & ExitState) != 0))
4909         {
4910           /*
4911             Check boundary conditions.
4912           */
4913           if (crop_info.x < 0)
4914             crop_info.x=0;
4915           else
4916             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4917               crop_info.x=(ssize_t) windows->image.ximage->width;
4918           if ((int) crop_info.x < x)
4919             crop_info.width=(unsigned int) (x-crop_info.x);
4920           else
4921             {
4922               crop_info.width=(unsigned int) (crop_info.x-x);
4923               crop_info.x=(ssize_t) x;
4924             }
4925           if (crop_info.y < 0)
4926             crop_info.y=0;
4927           else
4928             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4929               crop_info.y=(ssize_t) windows->image.ximage->height;
4930           if ((int) crop_info.y < y)
4931             crop_info.height=(unsigned int) (y-crop_info.y);
4932           else
4933             {
4934               crop_info.height=(unsigned int) (crop_info.y-y);
4935               crop_info.y=(ssize_t) y;
4936             }
4937         }
4938     } while ((state & ExitState) == 0);
4939     /*
4940       Wait for user to grab a corner of the rectangle or press return.
4941     */
4942     state=DefaultState;
4943     (void) XMapWindow(display,windows->info.id);
4944     do
4945     {
4946       if (windows->info.mapped != MagickFalse)
4947         {
4948           /*
4949             Display pointer position.
4950           */
4951           (void) FormatLocaleString(text,MaxTextExtent,
4952             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4953             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4954           XInfoWidget(display,windows,text);
4955         }
4956       highlight_info=crop_info;
4957       highlight_info.x=crop_info.x-windows->image.x;
4958       highlight_info.y=crop_info.y-windows->image.y;
4959       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4960         {
4961           state|=EscapeState;
4962           state|=ExitState;
4963           break;
4964         }
4965       XHighlightRectangle(display,windows->image.id,
4966         windows->image.highlight_context,&highlight_info);
4967       XScreenEvent(display,windows,&event);
4968       if (event.xany.window == windows->command.id)
4969         {
4970           /*
4971             Select a command from the Command widget.
4972           */
4973           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4974           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4975           (void) XSetFunction(display,windows->image.highlight_context,
4976             GXinvert);
4977           XHighlightRectangle(display,windows->image.id,
4978             windows->image.highlight_context,&highlight_info);
4979           if (id >= 0)
4980             switch (RectifyCommands[id])
4981             {
4982               case RectifyCopyCommand:
4983               {
4984                 state|=ExitState;
4985                 break;
4986               }
4987               case RectifyHelpCommand:
4988               {
4989                 (void) XSetFunction(display,windows->image.highlight_context,
4990                   GXcopy);
4991                 switch (mode)
4992                 {
4993                   case CopyMode:
4994                   {
4995                     XTextViewWidget(display,resource_info,windows,MagickFalse,
4996                       "Help Viewer - Image Copy",ImageCopyHelp);
4997                     break;
4998                   }
4999                   case CropMode:
5000                   {
5001                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5002                       "Help Viewer - Image Crop",ImageCropHelp);
5003                     break;
5004                   }
5005                   case CutMode:
5006                   {
5007                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5008                       "Help Viewer - Image Cut",ImageCutHelp);
5009                     break;
5010                   }
5011                 }
5012                 (void) XSetFunction(display,windows->image.highlight_context,
5013                   GXinvert);
5014                 break;
5015               }
5016               case RectifyDismissCommand:
5017               {
5018                 /*
5019                   Prematurely exit.
5020                 */
5021                 state|=EscapeState;
5022                 state|=ExitState;
5023                 break;
5024               }
5025               default:
5026                 break;
5027             }
5028           continue;
5029         }
5030       XHighlightRectangle(display,windows->image.id,
5031         windows->image.highlight_context,&highlight_info);
5032       switch (event.type)
5033       {
5034         case ButtonPress:
5035         {
5036           if (event.xbutton.button != Button1)
5037             break;
5038           if (event.xbutton.window != windows->image.id)
5039             break;
5040           x=windows->image.x+event.xbutton.x;
5041           y=windows->image.y+event.xbutton.y;
5042           if ((x < (int) (crop_info.x+RoiDelta)) &&
5043               (x > (int) (crop_info.x-RoiDelta)) &&
5044               (y < (int) (crop_info.y+RoiDelta)) &&
5045               (y > (int) (crop_info.y-RoiDelta)))
5046             {
5047               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5048               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5049               state|=UpdateConfigurationState;
5050               break;
5051             }
5052           if ((x < (int) (crop_info.x+RoiDelta)) &&
5053               (x > (int) (crop_info.x-RoiDelta)) &&
5054               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5055               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5056             {
5057               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5058               state|=UpdateConfigurationState;
5059               break;
5060             }
5061           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5062               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5063               (y < (int) (crop_info.y+RoiDelta)) &&
5064               (y > (int) (crop_info.y-RoiDelta)))
5065             {
5066               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5067               state|=UpdateConfigurationState;
5068               break;
5069             }
5070           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5071               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5072               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5073               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5074             {
5075               state|=UpdateConfigurationState;
5076               break;
5077             }
5078         }
5079         case ButtonRelease:
5080         {
5081           if (event.xbutton.window == windows->pan.id)
5082             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5083                 (highlight_info.y != crop_info.y-windows->image.y))
5084               XHighlightRectangle(display,windows->image.id,
5085                 windows->image.highlight_context,&highlight_info);
5086           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5087             event.xbutton.time);
5088           break;
5089         }
5090         case Expose:
5091         {
5092           if (event.xexpose.window == windows->image.id)
5093             if (event.xexpose.count == 0)
5094               {
5095                 event.xexpose.x=(int) highlight_info.x;
5096                 event.xexpose.y=(int) highlight_info.y;
5097                 event.xexpose.width=(int) highlight_info.width;
5098                 event.xexpose.height=(int) highlight_info.height;
5099                 XRefreshWindow(display,&windows->image,&event);
5100               }
5101           if (event.xexpose.window == windows->info.id)
5102             if (event.xexpose.count == 0)
5103               XInfoWidget(display,windows,text);
5104           break;
5105         }
5106         case KeyPress:
5107         {
5108           if (event.xkey.window != windows->image.id)
5109             break;
5110           /*
5111             Respond to a user key press.
5112           */
5113           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5114             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5115           switch ((int) key_symbol)
5116           {
5117             case XK_Escape:
5118             case XK_F20:
5119               state|=EscapeState;
5120             case XK_Return:
5121             {
5122               state|=ExitState;
5123               break;
5124             }
5125             case XK_Home:
5126             case XK_KP_Home:
5127             {
5128               crop_info.x=(ssize_t) (windows->image.width/2L-
5129                 crop_info.width/2L);
5130               crop_info.y=(ssize_t) (windows->image.height/2L-
5131                 crop_info.height/2L);
5132               break;
5133             }
5134             case XK_Left:
5135             case XK_KP_Left:
5136             {
5137               crop_info.x--;
5138               break;
5139             }
5140             case XK_Up:
5141             case XK_KP_Up:
5142             case XK_Next:
5143             {
5144               crop_info.y--;
5145               break;
5146             }
5147             case XK_Right:
5148             case XK_KP_Right:
5149             {
5150               crop_info.x++;
5151               break;
5152             }
5153             case XK_Prior:
5154             case XK_Down:
5155             case XK_KP_Down:
5156             {
5157               crop_info.y++;
5158               break;
5159             }
5160             case XK_F1:
5161             case XK_Help:
5162             {
5163               (void) XSetFunction(display,windows->image.highlight_context,
5164                 GXcopy);
5165               switch (mode)
5166               {
5167                 case CopyMode:
5168                 {
5169                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5170                     "Help Viewer - Image Copy",ImageCopyHelp);
5171                   break;
5172                 }
5173                 case CropMode:
5174                 {
5175                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5176                     "Help Viewer - Image Cropg",ImageCropHelp);
5177                   break;
5178                 }
5179                 case CutMode:
5180                 {
5181                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5182                     "Help Viewer - Image Cutg",ImageCutHelp);
5183                   break;
5184                 }
5185               }
5186               (void) XSetFunction(display,windows->image.highlight_context,
5187                 GXinvert);
5188               break;
5189             }
5190             default:
5191             {
5192               (void) XBell(display,0);
5193               break;
5194             }
5195           }
5196           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5197             event.xkey.time);
5198           break;
5199         }
5200         case KeyRelease:
5201           break;
5202         case MotionNotify:
5203         {
5204           if (event.xmotion.window != windows->image.id)
5205             break;
5206           /*
5207             Map and unmap Info widget as text cursor crosses its boundaries.
5208           */
5209           x=event.xmotion.x;
5210           y=event.xmotion.y;
5211           if (windows->info.mapped != MagickFalse)
5212             {
5213               if ((x < (int) (windows->info.x+windows->info.width)) &&
5214                   (y < (int) (windows->info.y+windows->info.height)))
5215                 (void) XWithdrawWindow(display,windows->info.id,
5216                   windows->info.screen);
5217             }
5218           else
5219             if ((x > (int) (windows->info.x+windows->info.width)) ||
5220                 (y > (int) (windows->info.y+windows->info.height)))
5221               (void) XMapWindow(display,windows->info.id);
5222           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5223           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5224           break;
5225         }
5226         case SelectionRequest:
5227         {
5228           XSelectionEvent
5229             notify;
5230
5231           XSelectionRequestEvent
5232             *request;
5233
5234           /*
5235             Set primary selection.
5236           */
5237           (void) FormatLocaleString(text,MaxTextExtent,
5238             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5239             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5240           request=(&(event.xselectionrequest));
5241           (void) XChangeProperty(request->display,request->requestor,
5242             request->property,request->target,8,PropModeReplace,
5243             (unsigned char *) text,(int) strlen(text));
5244           notify.type=SelectionNotify;
5245           notify.display=request->display;
5246           notify.requestor=request->requestor;
5247           notify.selection=request->selection;
5248           notify.target=request->target;
5249           notify.time=request->time;
5250           if (request->property == None)
5251             notify.property=request->target;
5252           else
5253             notify.property=request->property;
5254           (void) XSendEvent(request->display,request->requestor,False,0,
5255             (XEvent *) &notify);
5256         }
5257         default:
5258           break;
5259       }
5260       if ((state & UpdateConfigurationState) != 0)
5261         {
5262           (void) XPutBackEvent(display,&event);
5263           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5264           break;
5265         }
5266     } while ((state & ExitState) == 0);
5267   } while ((state & ExitState) == 0);
5268   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5269   XSetCursorState(display,windows,MagickFalse);
5270   if ((state & EscapeState) != 0)
5271     return(MagickTrue);
5272   if (mode == CropMode)
5273     if (((int) crop_info.width != windows->image.ximage->width) ||
5274         ((int) crop_info.height != windows->image.ximage->height))
5275       {
5276         /*
5277           Reconfigure Image window as defined by cropping rectangle.
5278         */
5279         XSetCropGeometry(display,windows,&crop_info,image);
5280         windows->image.window_changes.width=(int) crop_info.width;
5281         windows->image.window_changes.height=(int) crop_info.height;
5282         (void) XConfigureImage(display,resource_info,windows,image);
5283         return(MagickTrue);
5284       }
5285   /*
5286     Copy image before applying image transforms.
5287   */
5288   XSetCursorState(display,windows,MagickTrue);
5289   XCheckRefreshWindows(display,windows);
5290   width=(unsigned int) image->columns;
5291   height=(unsigned int) image->rows;
5292   x=0;
5293   y=0;
5294   if (windows->image.crop_geometry != (char *) NULL)
5295     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5296   scale_factor=(MagickRealType) width/windows->image.ximage->width;
5297   crop_info.x+=x;
5298   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5299   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5300   scale_factor=(MagickRealType) height/windows->image.ximage->height;
5301   crop_info.y+=y;
5302   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5303   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5304   crop_image=CropImage(image,&crop_info,&image->exception);
5305   XSetCursorState(display,windows,MagickFalse);
5306   if (crop_image == (Image *) NULL)
5307     return(MagickFalse);
5308   if (resource_info->copy_image != (Image *) NULL)
5309     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5310   resource_info->copy_image=crop_image;
5311   if (mode == CopyMode)
5312     {
5313       (void) XConfigureImage(display,resource_info,windows,image);
5314       return(MagickTrue);
5315     }
5316   /*
5317     Cut image.
5318   */
5319   exception=(&image->exception);
5320   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5321     return(MagickFalse);
5322   image->matte=MagickTrue;
5323   image_view=AcquireCacheView(image);
5324   for (y=0; y < (int) crop_info.height; y++)
5325   {
5326     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5327       crop_info.width,1,exception);
5328     if (q == (Quantum *) NULL)
5329       break;
5330     for (x=0; x < (int) crop_info.width; x++)
5331     {
5332       SetPixelAlpha(image,TransparentAlpha,q);
5333       q+=GetPixelChannels(image);
5334     }
5335     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5336       break;
5337   }
5338   image_view=DestroyCacheView(image_view);
5339   /*
5340     Update image configuration.
5341   */
5342   XConfigureImageColormap(display,resource_info,windows,image);
5343   (void) XConfigureImage(display,resource_info,windows,image);
5344   return(MagickTrue);
5345 }
5346 \f
5347 /*
5348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5349 %                                                                             %
5350 %                                                                             %
5351 %                                                                             %
5352 +   X D r a w I m a g e                                                       %
5353 %                                                                             %
5354 %                                                                             %
5355 %                                                                             %
5356 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5357 %
5358 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5359 %  the image.
5360 %
5361 %  The format of the XDrawEditImage method is:
5362 %
5363 %      MagickBooleanType XDrawEditImage(Display *display,
5364 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
5365 %
5366 %  A description of each parameter follows:
5367 %
5368 %    o display: Specifies a connection to an X server; returned from
5369 %      XOpenDisplay.
5370 %
5371 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5372 %
5373 %    o windows: Specifies a pointer to a XWindows structure.
5374 %
5375 %    o image: the image.
5376 %
5377 */
5378 static MagickBooleanType XDrawEditImage(Display *display,
5379   XResourceInfo *resource_info,XWindows *windows,Image **image)
5380 {
5381   static const char
5382     *DrawMenu[] =
5383     {
5384       "Element",
5385       "Color",
5386       "Stipple",
5387       "Width",
5388       "Undo",
5389       "Help",
5390       "Dismiss",
5391       (char *) NULL
5392     };
5393
5394   static ElementType
5395     element = PointElement;
5396
5397   static const ModeType
5398     DrawCommands[] =
5399     {
5400       DrawElementCommand,
5401       DrawColorCommand,
5402       DrawStippleCommand,
5403       DrawWidthCommand,
5404       DrawUndoCommand,
5405       DrawHelpCommand,
5406       DrawDismissCommand
5407     };
5408
5409   static Pixmap
5410     stipple = (Pixmap) NULL;
5411
5412   static unsigned int
5413     pen_id = 0,
5414     line_width = 1;
5415
5416   char
5417     command[MaxTextExtent],
5418     text[MaxTextExtent];
5419
5420   Cursor
5421     cursor;
5422
5423   int
5424     entry,
5425     id,
5426     number_coordinates,
5427     x,
5428     y;
5429
5430   MagickRealType
5431     degrees;
5432
5433   MagickStatusType
5434     status;
5435
5436   RectangleInfo
5437     rectangle_info;
5438
5439   register int
5440     i;
5441
5442   unsigned int
5443     distance,
5444     height,
5445     max_coordinates,
5446     width;
5447
5448   size_t
5449     state;
5450
5451   Window
5452     root_window;
5453
5454   XDrawInfo
5455     draw_info;
5456
5457   XEvent
5458     event;
5459
5460   XPoint
5461     *coordinate_info;
5462
5463   XSegment
5464     line_info;
5465
5466   /*
5467     Allocate polygon info.
5468   */
5469   max_coordinates=2048;
5470   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5471     sizeof(*coordinate_info));
5472   if (coordinate_info == (XPoint *) NULL)
5473     {
5474       (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
5475         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5476       return(MagickFalse);
5477     }
5478   /*
5479     Map Command widget.
5480   */
5481   (void) CloneString(&windows->command.name,"Draw");
5482   windows->command.data=4;
5483   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5484   (void) XMapRaised(display,windows->command.id);
5485   XClientMessage(display,windows->image.id,windows->im_protocols,
5486     windows->im_update_widget,CurrentTime);
5487   /*
5488     Wait for first button press.
5489   */
5490   root_window=XRootWindow(display,XDefaultScreen(display));
5491   draw_info.stencil=OpaqueStencil;
5492   status=MagickTrue;
5493   cursor=XCreateFontCursor(display,XC_tcross);
5494   for ( ; ; )
5495   {
5496     XQueryPosition(display,windows->image.id,&x,&y);
5497     (void) XSelectInput(display,windows->image.id,
5498       windows->image.attributes.event_mask | PointerMotionMask);
5499     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5500     state=DefaultState;
5501     do
5502     {
5503       if (windows->info.mapped != MagickFalse)
5504         {
5505           /*
5506             Display pointer position.
5507           */
5508           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5509             x+windows->image.x,y+windows->image.y);
5510           XInfoWidget(display,windows,text);
5511         }
5512       /*
5513         Wait for next event.
5514       */
5515       XScreenEvent(display,windows,&event);
5516       if (event.xany.window == windows->command.id)
5517         {
5518           /*
5519             Select a command from the Command widget.
5520           */
5521           id=XCommandWidget(display,windows,DrawMenu,&event);
5522           if (id < 0)
5523             continue;
5524           switch (DrawCommands[id])
5525           {
5526             case DrawElementCommand:
5527             {
5528               static const char
5529                 *Elements[] =
5530                 {
5531                   "point",
5532                   "line",
5533                   "rectangle",
5534                   "fill rectangle",
5535                   "circle",
5536                   "fill circle",
5537                   "ellipse",
5538                   "fill ellipse",
5539                   "polygon",
5540                   "fill polygon",
5541                   (char *) NULL,
5542                 };
5543
5544               /*
5545                 Select a command from the pop-up menu.
5546               */
5547               element=(ElementType) (XMenuWidget(display,windows,
5548                 DrawMenu[id],Elements,command)+1);
5549               break;
5550             }
5551             case DrawColorCommand:
5552             {
5553               const char
5554                 *ColorMenu[MaxNumberPens+1];
5555
5556               int
5557                 pen_number;
5558
5559               MagickBooleanType
5560                 transparent;
5561
5562               XColor
5563                 color;
5564
5565               /*
5566                 Initialize menu selections.
5567               */
5568               for (i=0; i < (int) (MaxNumberPens-2); i++)
5569                 ColorMenu[i]=resource_info->pen_colors[i];
5570               ColorMenu[MaxNumberPens-2]="transparent";
5571               ColorMenu[MaxNumberPens-1]="Browser...";
5572               ColorMenu[MaxNumberPens]=(char *) NULL;
5573               /*
5574                 Select a pen color from the pop-up menu.
5575               */
5576               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5577                 (const char **) ColorMenu,command);
5578               if (pen_number < 0)
5579                 break;
5580               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5581                 MagickFalse;
5582               if (transparent != MagickFalse)
5583                 {
5584                   draw_info.stencil=TransparentStencil;
5585                   break;
5586                 }
5587               if (pen_number == (MaxNumberPens-1))
5588                 {
5589                   static char
5590                     color_name[MaxTextExtent] = "gray";
5591
5592                   /*
5593                     Select a pen color from a dialog.
5594                   */
5595                   resource_info->pen_colors[pen_number]=color_name;
5596                   XColorBrowserWidget(display,windows,"Select",color_name);
5597                   if (*color_name == '\0')
5598                     break;
5599                 }
5600               /*
5601                 Set pen color.
5602               */
5603               (void) XParseColor(display,windows->map_info->colormap,
5604                 resource_info->pen_colors[pen_number],&color);
5605               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5606                 (unsigned int) MaxColors,&color);
5607               windows->pixel_info->pen_colors[pen_number]=color;
5608               pen_id=(unsigned int) pen_number;
5609               draw_info.stencil=OpaqueStencil;
5610               break;
5611             }
5612             case DrawStippleCommand:
5613             {
5614               Image
5615                 *stipple_image;
5616
5617               ImageInfo
5618                 *image_info;
5619
5620               int
5621                 status;
5622
5623               static char
5624                 filename[MaxTextExtent] = "\0";
5625
5626               static const char
5627                 *StipplesMenu[] =
5628                 {
5629                   "Brick",
5630                   "Diagonal",
5631                   "Scales",
5632                   "Vertical",
5633                   "Wavy",
5634                   "Translucent",
5635                   "Opaque",
5636                   (char *) NULL,
5637                   (char *) NULL,
5638                 };
5639
5640               /*
5641                 Select a command from the pop-up menu.
5642               */
5643               StipplesMenu[7]="Open...";
5644               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5645                 command);
5646               if (entry < 0)
5647                 break;
5648               if (stipple != (Pixmap) NULL)
5649                 (void) XFreePixmap(display,stipple);
5650               stipple=(Pixmap) NULL;
5651               if (entry != 7)
5652                 {
5653                   switch (entry)
5654                   {
5655                     case 0:
5656                     {
5657                       stipple=XCreateBitmapFromData(display,root_window,
5658                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5659                       break;
5660                     }
5661                     case 1:
5662                     {
5663                       stipple=XCreateBitmapFromData(display,root_window,
5664                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5665                       break;
5666                     }
5667                     case 2:
5668                     {
5669                       stipple=XCreateBitmapFromData(display,root_window,
5670                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5671                       break;
5672                     }
5673                     case 3:
5674                     {
5675                       stipple=XCreateBitmapFromData(display,root_window,
5676                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5677                       break;
5678                     }
5679                     case 4:
5680                     {
5681                       stipple=XCreateBitmapFromData(display,root_window,
5682                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5683                       break;
5684                     }
5685                     case 5:
5686                     {
5687                       stipple=XCreateBitmapFromData(display,root_window,
5688                         (char *) HighlightBitmap,HighlightWidth,
5689                         HighlightHeight);
5690                       break;
5691                     }
5692                     case 6:
5693                     default:
5694                     {
5695                       stipple=XCreateBitmapFromData(display,root_window,
5696                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5697                       break;
5698                     }
5699                   }
5700                   break;
5701                 }
5702               XFileBrowserWidget(display,windows,"Stipple",filename);
5703               if (*filename == '\0')
5704                 break;
5705               /*
5706                 Read image.
5707               */
5708               XSetCursorState(display,windows,MagickTrue);
5709               XCheckRefreshWindows(display,windows);
5710               image_info=AcquireImageInfo();
5711               (void) CopyMagickString(image_info->filename,filename,
5712                 MaxTextExtent);
5713               stipple_image=ReadImage(image_info,&(*image)->exception);
5714               CatchException(&(*image)->exception);
5715               XSetCursorState(display,windows,MagickFalse);
5716               if (stipple_image == (Image *) NULL)
5717                 break;
5718               (void) AcquireUniqueFileResource(filename);
5719               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5720                 "xbm:%s",filename);
5721               (void) WriteImage(image_info,stipple_image,&(*image)->exception);
5722               stipple_image=DestroyImage(stipple_image);
5723               image_info=DestroyImageInfo(image_info);
5724               status=XReadBitmapFile(display,root_window,filename,&width,
5725                 &height,&stipple,&x,&y);
5726               (void) RelinquishUniqueFileResource(filename);
5727               if ((status != BitmapSuccess) != 0)
5728                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5729                   filename);
5730               break;
5731             }
5732             case DrawWidthCommand:
5733             {
5734               static char
5735                 width[MaxTextExtent] = "0";
5736
5737               static const char
5738                 *WidthsMenu[] =
5739                 {
5740                   "1",
5741                   "2",
5742                   "4",
5743                   "8",
5744                   "16",
5745                   "Dialog...",
5746                   (char *) NULL,
5747                 };
5748
5749               /*
5750                 Select a command from the pop-up menu.
5751               */
5752               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5753                 command);
5754               if (entry < 0)
5755                 break;
5756               if (entry != 5)
5757                 {
5758                   line_width=(unsigned int) StringToUnsignedLong(
5759                     WidthsMenu[entry]);
5760                   break;
5761                 }
5762               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5763                 width);
5764               if (*width == '\0')
5765                 break;
5766               line_width=(unsigned int) StringToUnsignedLong(width);
5767               break;
5768             }
5769             case DrawUndoCommand:
5770             {
5771               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5772                 image);
5773               break;
5774             }
5775             case DrawHelpCommand:
5776             {
5777               XTextViewWidget(display,resource_info,windows,MagickFalse,
5778                 "Help Viewer - Image Rotation",ImageDrawHelp);
5779               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5780               break;
5781             }
5782             case DrawDismissCommand:
5783             {
5784               /*
5785                 Prematurely exit.
5786               */
5787               state|=EscapeState;
5788               state|=ExitState;
5789               break;
5790             }
5791             default:
5792               break;
5793           }
5794           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5795           continue;
5796         }
5797       switch (event.type)
5798       {
5799         case ButtonPress:
5800         {
5801           if (event.xbutton.button != Button1)
5802             break;
5803           if (event.xbutton.window != windows->image.id)
5804             break;
5805           /*
5806             exit loop.
5807           */
5808           x=event.xbutton.x;
5809           y=event.xbutton.y;
5810           state|=ExitState;
5811           break;
5812         }
5813         case ButtonRelease:
5814           break;
5815         case Expose:
5816           break;
5817         case KeyPress:
5818         {
5819           KeySym
5820             key_symbol;
5821
5822           if (event.xkey.window != windows->image.id)
5823             break;
5824           /*
5825             Respond to a user key press.
5826           */
5827           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5828             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5829           switch ((int) key_symbol)
5830           {
5831             case XK_Escape:
5832             case XK_F20:
5833             {
5834               /*
5835                 Prematurely exit.
5836               */
5837               state|=EscapeState;
5838               state|=ExitState;
5839               break;
5840             }
5841             case XK_F1:
5842             case XK_Help:
5843             {
5844               XTextViewWidget(display,resource_info,windows,MagickFalse,
5845                 "Help Viewer - Image Rotation",ImageDrawHelp);
5846               break;
5847             }
5848             default:
5849             {
5850               (void) XBell(display,0);
5851               break;
5852             }
5853           }
5854           break;
5855         }
5856         case MotionNotify:
5857         {
5858           /*
5859             Map and unmap Info widget as text cursor crosses its boundaries.
5860           */
5861           x=event.xmotion.x;
5862           y=event.xmotion.y;
5863           if (windows->info.mapped != MagickFalse)
5864             {
5865               if ((x < (int) (windows->info.x+windows->info.width)) &&
5866                   (y < (int) (windows->info.y+windows->info.height)))
5867                 (void) XWithdrawWindow(display,windows->info.id,
5868                   windows->info.screen);
5869             }
5870           else
5871             if ((x > (int) (windows->info.x+windows->info.width)) ||
5872                 (y > (int) (windows->info.y+windows->info.height)))
5873               (void) XMapWindow(display,windows->info.id);
5874           break;
5875         }
5876       }
5877     } while ((state & ExitState) == 0);
5878     (void) XSelectInput(display,windows->image.id,
5879       windows->image.attributes.event_mask);
5880     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5881     if ((state & EscapeState) != 0)
5882       break;
5883     /*
5884       Draw element as pointer moves until the button is released.
5885     */
5886     distance=0;
5887     degrees=0.0;
5888     line_info.x1=x;
5889     line_info.y1=y;
5890     line_info.x2=x;
5891     line_info.y2=y;
5892     rectangle_info.x=(ssize_t) x;
5893     rectangle_info.y=(ssize_t) y;
5894     rectangle_info.width=0;
5895     rectangle_info.height=0;
5896     number_coordinates=1;
5897     coordinate_info->x=x;
5898     coordinate_info->y=y;
5899     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5900     state=DefaultState;
5901     do
5902     {
5903       switch (element)
5904       {
5905         case PointElement:
5906         default:
5907         {
5908           if (number_coordinates > 1)
5909             {
5910               (void) XDrawLines(display,windows->image.id,
5911                 windows->image.highlight_context,coordinate_info,
5912                 number_coordinates,CoordModeOrigin);
5913               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5914                 coordinate_info[number_coordinates-1].x,
5915                 coordinate_info[number_coordinates-1].y);
5916               XInfoWidget(display,windows,text);
5917             }
5918           break;
5919         }
5920         case LineElement:
5921         {
5922           if (distance > 9)
5923             {
5924               /*
5925                 Display angle of the line.
5926               */
5927               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5928                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5929               (void) FormatLocaleString(text,MaxTextExtent," %g",
5930                 (double) degrees);
5931               XInfoWidget(display,windows,text);
5932               XHighlightLine(display,windows->image.id,
5933                 windows->image.highlight_context,&line_info);
5934             }
5935           else
5936             if (windows->info.mapped != MagickFalse)
5937               (void) XWithdrawWindow(display,windows->info.id,
5938                 windows->info.screen);
5939           break;
5940         }
5941         case RectangleElement:
5942         case FillRectangleElement:
5943         {
5944           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5945             {
5946               /*
5947                 Display info and draw drawing rectangle.
5948               */
5949               (void) FormatLocaleString(text,MaxTextExtent,
5950                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5951                 (double) rectangle_info.height,(double) rectangle_info.x,
5952                 (double) rectangle_info.y);
5953               XInfoWidget(display,windows,text);
5954               XHighlightRectangle(display,windows->image.id,
5955                 windows->image.highlight_context,&rectangle_info);
5956             }
5957           else
5958             if (windows->info.mapped != MagickFalse)
5959               (void) XWithdrawWindow(display,windows->info.id,
5960                 windows->info.screen);
5961           break;
5962         }
5963         case CircleElement:
5964         case FillCircleElement:
5965         case EllipseElement:
5966         case FillEllipseElement:
5967         {
5968           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5969             {
5970               /*
5971                 Display info and draw drawing rectangle.
5972               */
5973               (void) FormatLocaleString(text,MaxTextExtent,
5974                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5975                 (double) rectangle_info.height,(double) rectangle_info.x,
5976                 (double) rectangle_info.y);
5977               XInfoWidget(display,windows,text);
5978               XHighlightEllipse(display,windows->image.id,
5979                 windows->image.highlight_context,&rectangle_info);
5980             }
5981           else
5982             if (windows->info.mapped != MagickFalse)
5983               (void) XWithdrawWindow(display,windows->info.id,
5984                 windows->info.screen);
5985           break;
5986         }
5987         case PolygonElement:
5988         case FillPolygonElement:
5989         {
5990           if (number_coordinates > 1)
5991             (void) XDrawLines(display,windows->image.id,
5992               windows->image.highlight_context,coordinate_info,
5993               number_coordinates,CoordModeOrigin);
5994           if (distance > 9)
5995             {
5996               /*
5997                 Display angle of the line.
5998               */
5999               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6000                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6001               (void) FormatLocaleString(text,MaxTextExtent," %g",
6002                 (double) degrees);
6003               XInfoWidget(display,windows,text);
6004               XHighlightLine(display,windows->image.id,
6005                 windows->image.highlight_context,&line_info);
6006             }
6007           else
6008             if (windows->info.mapped != MagickFalse)
6009               (void) XWithdrawWindow(display,windows->info.id,
6010                 windows->info.screen);
6011           break;
6012         }
6013       }
6014       /*
6015         Wait for next event.
6016       */
6017       XScreenEvent(display,windows,&event);
6018       switch (element)
6019       {
6020         case PointElement:
6021         default:
6022         {
6023           if (number_coordinates > 1)
6024             (void) XDrawLines(display,windows->image.id,
6025               windows->image.highlight_context,coordinate_info,
6026               number_coordinates,CoordModeOrigin);
6027           break;
6028         }
6029         case LineElement:
6030         {
6031           if (distance > 9)
6032             XHighlightLine(display,windows->image.id,
6033               windows->image.highlight_context,&line_info);
6034           break;
6035         }
6036         case RectangleElement:
6037         case FillRectangleElement:
6038         {
6039           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6040             XHighlightRectangle(display,windows->image.id,
6041               windows->image.highlight_context,&rectangle_info);
6042           break;
6043         }
6044         case CircleElement:
6045         case FillCircleElement:
6046         case EllipseElement:
6047         case FillEllipseElement:
6048         {
6049           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6050             XHighlightEllipse(display,windows->image.id,
6051               windows->image.highlight_context,&rectangle_info);
6052           break;
6053         }
6054         case PolygonElement:
6055         case FillPolygonElement:
6056         {
6057           if (number_coordinates > 1)
6058             (void) XDrawLines(display,windows->image.id,
6059               windows->image.highlight_context,coordinate_info,
6060               number_coordinates,CoordModeOrigin);
6061           if (distance > 9)
6062             XHighlightLine(display,windows->image.id,
6063               windows->image.highlight_context,&line_info);
6064           break;
6065         }
6066       }
6067       switch (event.type)
6068       {
6069         case ButtonPress:
6070           break;
6071         case ButtonRelease:
6072         {
6073           /*
6074             User has committed to element.
6075           */
6076           line_info.x2=event.xbutton.x;
6077           line_info.y2=event.xbutton.y;
6078           rectangle_info.x=(ssize_t) event.xbutton.x;
6079           rectangle_info.y=(ssize_t) event.xbutton.y;
6080           coordinate_info[number_coordinates].x=event.xbutton.x;
6081           coordinate_info[number_coordinates].y=event.xbutton.y;
6082           if (((element != PolygonElement) &&
6083                (element != FillPolygonElement)) || (distance <= 9))
6084             {
6085               state|=ExitState;
6086               break;
6087             }
6088           number_coordinates++;
6089           if (number_coordinates < (int) max_coordinates)
6090             {
6091               line_info.x1=event.xbutton.x;
6092               line_info.y1=event.xbutton.y;
6093               break;
6094             }
6095           max_coordinates<<=1;
6096           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6097             max_coordinates,sizeof(*coordinate_info));
6098           if (coordinate_info == (XPoint *) NULL)
6099             (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6100               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6101           break;
6102         }
6103         case Expose:
6104           break;
6105         case MotionNotify:
6106         {
6107           if (event.xmotion.window != windows->image.id)
6108             break;
6109           if (element != PointElement)
6110             {
6111               line_info.x2=event.xmotion.x;
6112               line_info.y2=event.xmotion.y;
6113               rectangle_info.x=(ssize_t) event.xmotion.x;
6114               rectangle_info.y=(ssize_t) event.xmotion.y;
6115               break;
6116             }
6117           coordinate_info[number_coordinates].x=event.xbutton.x;
6118           coordinate_info[number_coordinates].y=event.xbutton.y;
6119           number_coordinates++;
6120           if (number_coordinates < (int) max_coordinates)
6121             break;
6122           max_coordinates<<=1;
6123           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6124             max_coordinates,sizeof(*coordinate_info));
6125           if (coordinate_info == (XPoint *) NULL)
6126             (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6127               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6128           break;
6129         }
6130         default:
6131           break;
6132       }
6133       /*
6134         Check boundary conditions.
6135       */
6136       if (line_info.x2 < 0)
6137         line_info.x2=0;
6138       else
6139         if (line_info.x2 > (int) windows->image.width)
6140           line_info.x2=(short) windows->image.width;
6141       if (line_info.y2 < 0)
6142         line_info.y2=0;
6143       else
6144         if (line_info.y2 > (int) windows->image.height)
6145           line_info.y2=(short) windows->image.height;
6146       distance=(unsigned int)
6147         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6148          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6149       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6150           ((state & ExitState) != 0))
6151         {
6152           if (rectangle_info.x < 0)
6153             rectangle_info.x=0;
6154           else
6155             if (rectangle_info.x > (ssize_t) windows->image.width)
6156               rectangle_info.x=(ssize_t) windows->image.width;
6157           if ((int) rectangle_info.x < x)
6158             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6159           else
6160             {
6161               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6162               rectangle_info.x=(ssize_t) x;
6163             }
6164           if (rectangle_info.y < 0)
6165             rectangle_info.y=0;
6166           else
6167             if (rectangle_info.y > (ssize_t) windows->image.height)
6168               rectangle_info.y=(ssize_t) windows->image.height;
6169           if ((int) rectangle_info.y < y)
6170             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6171           else
6172             {
6173               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6174               rectangle_info.y=(ssize_t) y;
6175             }
6176         }
6177     } while ((state & ExitState) == 0);
6178     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6179     if ((element == PointElement) || (element == PolygonElement) ||
6180         (element == FillPolygonElement))
6181       {
6182         /*
6183           Determine polygon bounding box.
6184         */
6185         rectangle_info.x=(ssize_t) coordinate_info->x;
6186         rectangle_info.y=(ssize_t) coordinate_info->y;
6187         x=coordinate_info->x;
6188         y=coordinate_info->y;
6189         for (i=1; i < number_coordinates; i++)
6190         {
6191           if (coordinate_info[i].x > x)
6192             x=coordinate_info[i].x;
6193           if (coordinate_info[i].y > y)
6194             y=coordinate_info[i].y;
6195           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6196             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6197           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6198             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6199         }
6200         rectangle_info.width=(size_t) (x-rectangle_info.x);
6201         rectangle_info.height=(size_t) (y-rectangle_info.y);
6202         for (i=0; i < number_coordinates; i++)
6203         {
6204           coordinate_info[i].x-=rectangle_info.x;
6205           coordinate_info[i].y-=rectangle_info.y;
6206         }
6207       }
6208     else
6209       if (distance <= 9)
6210         continue;
6211       else
6212         if ((element == RectangleElement) ||
6213             (element == CircleElement) || (element == EllipseElement))
6214           {
6215             rectangle_info.width--;
6216             rectangle_info.height--;
6217           }
6218     /*
6219       Drawing is relative to image configuration.
6220     */
6221     draw_info.x=(int) rectangle_info.x;
6222     draw_info.y=(int) rectangle_info.y;
6223     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6224       image);
6225     width=(unsigned int) (*image)->columns;
6226     height=(unsigned int) (*image)->rows;
6227     x=0;
6228     y=0;
6229     if (windows->image.crop_geometry != (char *) NULL)
6230       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6231     draw_info.x+=windows->image.x-(line_width/2);
6232     if (draw_info.x < 0)
6233       draw_info.x=0;
6234     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6235     draw_info.y+=windows->image.y-(line_width/2);
6236     if (draw_info.y < 0)
6237       draw_info.y=0;
6238     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6239     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6240     if (draw_info.width > (unsigned int) (*image)->columns)
6241       draw_info.width=(unsigned int) (*image)->columns;
6242     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6243     if (draw_info.height > (unsigned int) (*image)->rows)
6244       draw_info.height=(unsigned int) (*image)->rows;
6245     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6246       width*draw_info.width/windows->image.ximage->width,
6247       height*draw_info.height/windows->image.ximage->height,
6248       draw_info.x+x,draw_info.y+y);
6249     /*
6250       Initialize drawing attributes.
6251     */
6252     draw_info.degrees=0.0;
6253     draw_info.element=element;
6254     draw_info.stipple=stipple;
6255     draw_info.line_width=line_width;
6256     draw_info.line_info=line_info;
6257     if (line_info.x1 > (int) (line_width/2))
6258       draw_info.line_info.x1=(short) line_width/2;
6259     if (line_info.y1 > (int) (line_width/2))
6260       draw_info.line_info.y1=(short) line_width/2;
6261     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6262     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6263     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6264       {
6265         draw_info.line_info.x2=(-draw_info.line_info.x2);
6266         draw_info.line_info.y2=(-draw_info.line_info.y2);
6267       }
6268     if (draw_info.line_info.x2 < 0)
6269       {
6270         draw_info.line_info.x2=(-draw_info.line_info.x2);
6271         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6272       }
6273     if (draw_info.line_info.y2 < 0)
6274       {
6275         draw_info.line_info.y2=(-draw_info.line_info.y2);
6276         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6277       }
6278     draw_info.rectangle_info=rectangle_info;
6279     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6280       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6281     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6282       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6283     draw_info.number_coordinates=(unsigned int) number_coordinates;
6284     draw_info.coordinate_info=coordinate_info;
6285     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6286     /*
6287       Draw element on image.
6288     */
6289     XSetCursorState(display,windows,MagickTrue);
6290     XCheckRefreshWindows(display,windows);
6291     status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6292     XSetCursorState(display,windows,MagickFalse);
6293     /*
6294       Update image colormap and return to image drawing.
6295     */
6296     XConfigureImageColormap(display,resource_info,windows,*image);
6297     (void) XConfigureImage(display,resource_info,windows,*image);
6298   }
6299   XSetCursorState(display,windows,MagickFalse);
6300   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6301   return(status != 0 ? MagickTrue : MagickFalse);
6302 }
6303 \f
6304 /*
6305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6306 %                                                                             %
6307 %                                                                             %
6308 %                                                                             %
6309 +   X D r a w P a n R e c t a n g l e                                         %
6310 %                                                                             %
6311 %                                                                             %
6312 %                                                                             %
6313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6314 %
6315 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6316 %  displays a zoom image and the rectangle shows which portion of the image is
6317 %  displayed in the Image window.
6318 %
6319 %  The format of the XDrawPanRectangle method is:
6320 %
6321 %      XDrawPanRectangle(Display *display,XWindows *windows)
6322 %
6323 %  A description of each parameter follows:
6324 %
6325 %    o display: Specifies a connection to an X server;  returned from
6326 %      XOpenDisplay.
6327 %
6328 %    o windows: Specifies a pointer to a XWindows structure.
6329 %
6330 */
6331 static void XDrawPanRectangle(Display *display,XWindows *windows)
6332 {
6333   MagickRealType
6334     scale_factor;
6335
6336   RectangleInfo
6337     highlight_info;
6338
6339   /*
6340     Determine dimensions of the panning rectangle.
6341   */
6342   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6343   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6344   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6345   scale_factor=(MagickRealType)
6346     windows->pan.height/windows->image.ximage->height;
6347   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6348   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6349   /*
6350     Display the panning rectangle.
6351   */
6352   (void) XClearWindow(display,windows->pan.id);
6353   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6354     &highlight_info);
6355 }
6356 \f
6357 /*
6358 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6359 %                                                                             %
6360 %                                                                             %
6361 %                                                                             %
6362 +   X I m a g e C a c h e                                                     %
6363 %                                                                             %
6364 %                                                                             %
6365 %                                                                             %
6366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6367 %
6368 %  XImageCache() handles the creation, manipulation, and destruction of the
6369 %  image cache (undo and redo buffers).
6370 %
6371 %  The format of the XImageCache method is:
6372 %
6373 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6374 %        XWindows *windows,const CommandType command,Image **image)
6375 %
6376 %  A description of each parameter follows:
6377 %
6378 %    o display: Specifies a connection to an X server; returned from
6379 %      XOpenDisplay.
6380 %
6381 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6382 %
6383 %    o windows: Specifies a pointer to a XWindows structure.
6384 %
6385 %    o command: Specifies a command to perform.
6386 %
6387 %    o image: the image;  XImageCache may transform the image and return a new
6388 %      image pointer.
6389 %
6390 */
6391 static void XImageCache(Display *display,XResourceInfo *resource_info,
6392   XWindows *windows,const CommandType command,Image **image)
6393 {
6394   Image
6395     *cache_image;
6396
6397   static Image
6398     *redo_image = (Image *) NULL,
6399     *undo_image = (Image *) NULL;
6400
6401   switch (command)
6402   {
6403     case FreeBuffersCommand:
6404     {
6405       /*
6406         Free memory from the undo and redo cache.
6407       */
6408       while (undo_image != (Image *) NULL)
6409       {
6410         cache_image=undo_image;
6411         undo_image=GetPreviousImageInList(undo_image);
6412         cache_image->list=DestroyImage(cache_image->list);
6413         cache_image=DestroyImage(cache_image);
6414       }
6415       undo_image=NewImageList();
6416       if (redo_image != (Image *) NULL)
6417         redo_image=DestroyImage(redo_image);
6418       redo_image=NewImageList();
6419       return;
6420     }
6421     case UndoCommand:
6422     {
6423       char
6424         image_geometry[MaxTextExtent];
6425
6426       /*
6427         Undo the last image transformation.
6428       */
6429       if (undo_image == (Image *) NULL)
6430         {
6431           (void) XBell(display,0);
6432           return;
6433         }
6434       cache_image=undo_image;
6435       undo_image=GetPreviousImageInList(undo_image);
6436       windows->image.window_changes.width=(int) cache_image->columns;
6437       windows->image.window_changes.height=(int) cache_image->rows;
6438       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6439         windows->image.ximage->width,windows->image.ximage->height);
6440       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6441       if (windows->image.crop_geometry != (char *) NULL)
6442         windows->image.crop_geometry=(char *)
6443           RelinquishMagickMemory(windows->image.crop_geometry);
6444       windows->image.crop_geometry=cache_image->geometry;
6445       if (redo_image != (Image *) NULL)
6446         redo_image=DestroyImage(redo_image);
6447       redo_image=(*image);
6448       *image=cache_image->list;
6449       cache_image=DestroyImage(cache_image);
6450       if (windows->image.orphan != MagickFalse)
6451         return;
6452       XConfigureImageColormap(display,resource_info,windows,*image);
6453       (void) XConfigureImage(display,resource_info,windows,*image);
6454       return;
6455     }
6456     case CutCommand:
6457     case PasteCommand:
6458     case ApplyCommand:
6459     case HalfSizeCommand:
6460     case OriginalSizeCommand:
6461     case DoubleSizeCommand:
6462     case ResizeCommand:
6463     case TrimCommand:
6464     case CropCommand:
6465     case ChopCommand:
6466     case FlipCommand:
6467     case FlopCommand:
6468     case RotateRightCommand:
6469     case RotateLeftCommand:
6470     case RotateCommand:
6471     case ShearCommand:
6472     case RollCommand:
6473     case NegateCommand:
6474     case ContrastStretchCommand:
6475     case SigmoidalContrastCommand:
6476     case NormalizeCommand:
6477     case EqualizeCommand:
6478     case HueCommand:
6479     case SaturationCommand:
6480     case BrightnessCommand:
6481     case GammaCommand:
6482     case SpiffCommand:
6483     case DullCommand:
6484     case GrayscaleCommand:
6485     case MapCommand:
6486     case QuantizeCommand:
6487     case DespeckleCommand:
6488     case EmbossCommand:
6489     case ReduceNoiseCommand:
6490     case AddNoiseCommand:
6491     case SharpenCommand:
6492     case BlurCommand:
6493     case ThresholdCommand:
6494     case EdgeDetectCommand:
6495     case SpreadCommand:
6496     case ShadeCommand:
6497     case RaiseCommand:
6498     case SegmentCommand:
6499     case SolarizeCommand:
6500     case SepiaToneCommand:
6501     case SwirlCommand:
6502     case ImplodeCommand:
6503     case VignetteCommand:
6504     case WaveCommand:
6505     case OilPaintCommand:
6506     case CharcoalDrawCommand:
6507     case AnnotateCommand:
6508     case AddBorderCommand:
6509     case AddFrameCommand:
6510     case CompositeCommand:
6511     case CommentCommand:
6512     case LaunchCommand:
6513     case RegionofInterestCommand:
6514     case SaveToUndoBufferCommand:
6515     case RedoCommand:
6516     {
6517       Image
6518         *previous_image;
6519
6520       ssize_t
6521         bytes;
6522
6523       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6524       if (undo_image != (Image *) NULL)
6525         {
6526           /*
6527             Ensure the undo cache has enough memory available.
6528           */
6529           previous_image=undo_image;
6530           while (previous_image != (Image *) NULL)
6531           {
6532             bytes+=previous_image->list->columns*previous_image->list->rows*
6533               sizeof(PixelPacket);
6534             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6535               {
6536                 previous_image=GetPreviousImageInList(previous_image);
6537                 continue;
6538               }
6539             bytes-=previous_image->list->columns*previous_image->list->rows*
6540               sizeof(PixelPacket);
6541             if (previous_image == undo_image)
6542               undo_image=NewImageList();
6543             else
6544               previous_image->next->previous=NewImageList();
6545             break;
6546           }
6547           while (previous_image != (Image *) NULL)
6548           {
6549             /*
6550               Delete any excess memory from undo cache.
6551             */
6552             cache_image=previous_image;
6553             previous_image=GetPreviousImageInList(previous_image);
6554             cache_image->list=DestroyImage(cache_image->list);
6555             cache_image=DestroyImage(cache_image);
6556           }
6557         }
6558       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6559         break;
6560       /*
6561         Save image before transformations are applied.
6562       */
6563       cache_image=AcquireImage((ImageInfo *) NULL);
6564       if (cache_image == (Image *) NULL)
6565         break;
6566       XSetCursorState(display,windows,MagickTrue);
6567       XCheckRefreshWindows(display,windows);
6568       cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6569       XSetCursorState(display,windows,MagickFalse);
6570       if (cache_image->list == (Image *) NULL)
6571         {
6572           cache_image=DestroyImage(cache_image);
6573           break;
6574         }
6575       cache_image->columns=(size_t) windows->image.ximage->width;
6576       cache_image->rows=(size_t) windows->image.ximage->height;
6577       cache_image->geometry=windows->image.crop_geometry;
6578       if (windows->image.crop_geometry != (char *) NULL)
6579         {
6580           cache_image->geometry=AcquireString((char *) NULL);
6581           (void) CopyMagickString(cache_image->geometry,
6582             windows->image.crop_geometry,MaxTextExtent);
6583         }
6584       if (undo_image == (Image *) NULL)
6585         {
6586           undo_image=cache_image;
6587           break;
6588         }
6589       undo_image->next=cache_image;
6590       undo_image->next->previous=undo_image;
6591       undo_image=undo_image->next;
6592       break;
6593     }
6594     default:
6595       break;
6596   }
6597   if (command == RedoCommand)
6598     {
6599       /*
6600         Redo the last image transformation.
6601       */
6602       if (redo_image == (Image *) NULL)
6603         {
6604           (void) XBell(display,0);
6605           return;
6606         }
6607       windows->image.window_changes.width=(int) redo_image->columns;
6608       windows->image.window_changes.height=(int) redo_image->rows;
6609       if (windows->image.crop_geometry != (char *) NULL)
6610         windows->image.crop_geometry=(char *)
6611           RelinquishMagickMemory(windows->image.crop_geometry);
6612       windows->image.crop_geometry=redo_image->geometry;
6613       *image=DestroyImage(*image);
6614       *image=redo_image;
6615       redo_image=NewImageList();
6616       if (windows->image.orphan != MagickFalse)
6617         return;
6618       XConfigureImageColormap(display,resource_info,windows,*image);
6619       (void) XConfigureImage(display,resource_info,windows,*image);
6620       return;
6621     }
6622   if (command != InfoCommand)
6623     return;
6624   /*
6625     Display image info.
6626   */
6627   XSetCursorState(display,windows,MagickTrue);
6628   XCheckRefreshWindows(display,windows);
6629   XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6630   XSetCursorState(display,windows,MagickFalse);
6631 }
6632 \f
6633 /*
6634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6635 %                                                                             %
6636 %                                                                             %
6637 %                                                                             %
6638 +   X I m a g e W i n d o w C o m m a n d                                     %
6639 %                                                                             %
6640 %                                                                             %
6641 %                                                                             %
6642 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6643 %
6644 %  XImageWindowCommand() makes a transform to the image or Image window as
6645 %  specified by a user menu button or keyboard command.
6646 %
6647 %  The format of the XMagickCommand method is:
6648 %
6649 %      CommandType XImageWindowCommand(Display *display,
6650 %        XResourceInfo *resource_info,XWindows *windows,
6651 %        const MagickStatusType state,KeySym key_symbol,Image **image)
6652 %
6653 %  A description of each parameter follows:
6654 %
6655 %    o nexus:  Method XImageWindowCommand returns an image when the
6656 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6657 %      image is returned.
6658 %
6659 %    o display: Specifies a connection to an X server; returned from
6660 %      XOpenDisplay.
6661 %
6662 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6663 %
6664 %    o windows: Specifies a pointer to a XWindows structure.
6665 %
6666 %    o state: key mask.
6667 %
6668 %    o key_symbol: Specifies a command to perform.
6669 %
6670 %    o image: the image;  XImageWIndowCommand
6671 %      may transform the image and return a new image pointer.
6672 %
6673 */
6674 static CommandType XImageWindowCommand(Display *display,
6675   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6676   KeySym key_symbol,Image **image)
6677 {
6678   static char
6679     delta[MaxTextExtent] = "";
6680
6681   static const char
6682     Digits[] = "01234567890";
6683
6684   static KeySym
6685     last_symbol = XK_0;
6686
6687   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6688     {
6689       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6690         {
6691           *delta='\0';
6692           resource_info->quantum=1;
6693         }
6694       last_symbol=key_symbol;
6695       delta[strlen(delta)+1]='\0';
6696       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6697       resource_info->quantum=StringToLong(delta);
6698       return(NullCommand);
6699     }
6700   last_symbol=key_symbol;
6701   if (resource_info->immutable)
6702     {
6703       /*
6704         Virtual image window has a restricted command set.
6705       */
6706       switch (key_symbol)
6707       {
6708         case XK_question:
6709           return(InfoCommand);
6710         case XK_p:
6711         case XK_Print:
6712           return(PrintCommand);
6713         case XK_space:
6714           return(NextCommand);
6715         case XK_q:
6716         case XK_Escape:
6717           return(QuitCommand);
6718         default:
6719           break;
6720       }
6721       return(NullCommand);
6722     }
6723   switch ((int) key_symbol)
6724   {
6725     case XK_o:
6726     {
6727       if ((state & ControlMask) == 0)
6728         break;
6729       return(OpenCommand);
6730     }
6731     case XK_space:
6732       return(NextCommand);
6733     case XK_BackSpace:
6734       return(FormerCommand);
6735     case XK_s:
6736     {
6737       if ((state & Mod1Mask) != 0)
6738         return(SwirlCommand);
6739       if ((state & ControlMask) == 0)
6740         return(ShearCommand);
6741       return(SaveCommand);
6742     }
6743     case XK_p:
6744     case XK_Print:
6745     {
6746       if ((state & Mod1Mask) != 0)
6747         return(OilPaintCommand);
6748       if ((state & Mod4Mask) != 0)
6749         return(ColorCommand);
6750       if ((state & ControlMask) == 0)
6751         return(NullCommand);
6752       return(PrintCommand);
6753     }
6754     case XK_d:
6755     {
6756       if ((state & Mod4Mask) != 0)
6757         return(DrawCommand);
6758       if ((state & ControlMask) == 0)
6759         return(NullCommand);
6760       return(DeleteCommand);
6761     }
6762     case XK_Select:
6763     {
6764       if ((state & ControlMask) == 0)
6765         return(NullCommand);
6766       return(SelectCommand);
6767     }
6768     case XK_n:
6769     {
6770       if ((state & ControlMask) == 0)
6771         return(NullCommand);
6772       return(NewCommand);
6773     }
6774     case XK_q:
6775     case XK_Escape:
6776       return(QuitCommand);
6777     case XK_z:
6778     case XK_Undo:
6779     {
6780       if ((state & ControlMask) == 0)
6781         return(NullCommand);
6782       return(UndoCommand);
6783     }
6784     case XK_r:
6785     case XK_Redo:
6786     {
6787       if ((state & ControlMask) == 0)
6788         return(RollCommand);
6789       return(RedoCommand);
6790     }
6791     case XK_x:
6792     {
6793       if ((state & ControlMask) == 0)
6794         return(NullCommand);
6795       return(CutCommand);
6796     }
6797     case XK_c:
6798     {
6799       if ((state & Mod1Mask) != 0)
6800         return(CharcoalDrawCommand);
6801       if ((state & ControlMask) == 0)
6802         return(CropCommand);
6803       return(CopyCommand);
6804     }
6805     case XK_v:
6806     case XK_Insert:
6807     {
6808       if ((state & Mod4Mask) != 0)
6809         return(CompositeCommand);
6810       if ((state & ControlMask) == 0)
6811         return(FlipCommand);
6812       return(PasteCommand);
6813     }
6814     case XK_less:
6815       return(HalfSizeCommand);
6816     case XK_minus:
6817       return(OriginalSizeCommand);
6818     case XK_greater:
6819       return(DoubleSizeCommand);
6820     case XK_percent:
6821       return(ResizeCommand);
6822     case XK_at:
6823       return(RefreshCommand);
6824     case XK_bracketleft:
6825       return(ChopCommand);
6826     case XK_h:
6827       return(FlopCommand);
6828     case XK_slash:
6829       return(RotateRightCommand);
6830     case XK_backslash:
6831       return(RotateLeftCommand);
6832     case XK_asterisk:
6833       return(RotateCommand);
6834     case XK_t:
6835       return(TrimCommand);
6836     case XK_H:
6837       return(HueCommand);
6838     case XK_S:
6839       return(SaturationCommand);
6840     case XK_L:
6841       return(BrightnessCommand);
6842     case XK_G:
6843       return(GammaCommand);
6844     case XK_C:
6845       return(SpiffCommand);
6846     case XK_Z:
6847       return(DullCommand);
6848     case XK_N:
6849       return(NormalizeCommand);
6850     case XK_equal:
6851       return(EqualizeCommand);
6852     case XK_asciitilde:
6853       return(NegateCommand);
6854     case XK_period:
6855       return(GrayscaleCommand);
6856     case XK_numbersign:
6857       return(QuantizeCommand);
6858     case XK_F2:
6859       return(DespeckleCommand);
6860     case XK_F3:
6861       return(EmbossCommand);
6862     case XK_F4:
6863       return(ReduceNoiseCommand);
6864     case XK_F5:
6865       return(AddNoiseCommand);
6866     case XK_F6:
6867       return(SharpenCommand);
6868     case XK_F7:
6869       return(BlurCommand);
6870     case XK_F8:
6871       return(ThresholdCommand);
6872     case XK_F9:
6873       return(EdgeDetectCommand);
6874     case XK_F10:
6875       return(SpreadCommand);
6876     case XK_F11:
6877       return(ShadeCommand);
6878     case XK_F12:
6879       return(RaiseCommand);
6880     case XK_F13:
6881       return(SegmentCommand);
6882     case XK_i:
6883     {
6884       if ((state & Mod1Mask) == 0)
6885         return(NullCommand);
6886       return(ImplodeCommand);
6887     }
6888     case XK_w:
6889     {
6890       if ((state & Mod1Mask) == 0)
6891         return(NullCommand);
6892       return(WaveCommand);
6893     }
6894     case XK_m:
6895     {
6896       if ((state & Mod4Mask) == 0)
6897         return(NullCommand);
6898       return(MatteCommand);
6899     }
6900     case XK_b:
6901     {
6902       if ((state & Mod4Mask) == 0)
6903         return(NullCommand);
6904       return(AddBorderCommand);
6905     }
6906     case XK_f:
6907     {
6908       if ((state & Mod4Mask) == 0)
6909         return(NullCommand);
6910       return(AddFrameCommand);
6911     }
6912     case XK_exclam:
6913     {
6914       if ((state & Mod4Mask) == 0)
6915         return(NullCommand);
6916       return(CommentCommand);
6917     }
6918     case XK_a:
6919     {
6920       if ((state & Mod1Mask) != 0)
6921         return(ApplyCommand);
6922       if ((state & Mod4Mask) != 0)
6923         return(AnnotateCommand);
6924       if ((state & ControlMask) == 0)
6925         return(NullCommand);
6926       return(RegionofInterestCommand);
6927     }
6928     case XK_question:
6929       return(InfoCommand);
6930     case XK_plus:
6931       return(ZoomCommand);
6932     case XK_P:
6933     {
6934       if ((state & ShiftMask) == 0)
6935         return(NullCommand);
6936       return(ShowPreviewCommand);
6937     }
6938     case XK_Execute:
6939       return(LaunchCommand);
6940     case XK_F1:
6941       return(HelpCommand);
6942     case XK_Find:
6943       return(BrowseDocumentationCommand);
6944     case XK_Menu:
6945     {
6946       (void) XMapRaised(display,windows->command.id);
6947       return(NullCommand);
6948     }
6949     case XK_Next:
6950     case XK_Prior:
6951     case XK_Home:
6952     case XK_KP_Home:
6953     {
6954       XTranslateImage(display,windows,*image,key_symbol);
6955       return(NullCommand);
6956     }
6957     case XK_Up:
6958     case XK_KP_Up:
6959     case XK_Down:
6960     case XK_KP_Down:
6961     case XK_Left:
6962     case XK_KP_Left:
6963     case XK_Right:
6964     case XK_KP_Right:
6965     {
6966       if ((state & Mod1Mask) != 0)
6967         {
6968           RectangleInfo
6969             crop_info;
6970
6971           /*
6972             Trim one pixel from edge of image.
6973           */
6974           crop_info.x=0;
6975           crop_info.y=0;
6976           crop_info.width=(size_t) windows->image.ximage->width;
6977           crop_info.height=(size_t) windows->image.ximage->height;
6978           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6979             {
6980               if (resource_info->quantum >= (int) crop_info.height)
6981                 resource_info->quantum=(int) crop_info.height-1;
6982               crop_info.height-=resource_info->quantum;
6983             }
6984           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6985             {
6986               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6987                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6988               crop_info.y+=resource_info->quantum;
6989               crop_info.height-=resource_info->quantum;
6990             }
6991           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6992             {
6993               if (resource_info->quantum >= (int) crop_info.width)
6994                 resource_info->quantum=(int) crop_info.width-1;
6995               crop_info.width-=resource_info->quantum;
6996             }
6997           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6998             {
6999               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7000                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7001               crop_info.x+=resource_info->quantum;
7002               crop_info.width-=resource_info->quantum;
7003             }
7004           if ((int) (windows->image.x+windows->image.width) >
7005               (int) crop_info.width)
7006             windows->image.x=(int) (crop_info.width-windows->image.width);
7007           if ((int) (windows->image.y+windows->image.height) >
7008               (int) crop_info.height)
7009             windows->image.y=(int) (crop_info.height-windows->image.height);
7010           XSetCropGeometry(display,windows,&crop_info,*image);
7011           windows->image.window_changes.width=(int) crop_info.width;
7012           windows->image.window_changes.height=(int) crop_info.height;
7013           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7014           (void) XConfigureImage(display,resource_info,windows,*image);
7015           return(NullCommand);
7016         }
7017       XTranslateImage(display,windows,*image,key_symbol);
7018       return(NullCommand);
7019     }
7020     default:
7021       return(NullCommand);
7022   }
7023   return(NullCommand);
7024 }
7025 \f
7026 /*
7027 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7028 %                                                                             %
7029 %                                                                             %
7030 %                                                                             %
7031 +   X M a g i c k C o m m a n d                                               %
7032 %                                                                             %
7033 %                                                                             %
7034 %                                                                             %
7035 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7036 %
7037 %  XMagickCommand() makes a transform to the image or Image window as
7038 %  specified by a user menu button or keyboard command.
7039 %
7040 %  The format of the XMagickCommand method is:
7041 %
7042 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7043 %        XWindows *windows,const CommandType command,Image **image)
7044 %
7045 %  A description of each parameter follows:
7046 %
7047 %    o nexus:  Method XMagickCommand returns an image when the
7048 %      user chooses 'Load Image' from the command menu.  Otherwise a null
7049 %      image is returned.
7050 %
7051 %    o display: Specifies a connection to an X server; returned from
7052 %      XOpenDisplay.
7053 %
7054 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7055 %
7056 %    o windows: Specifies a pointer to a XWindows structure.
7057 %
7058 %    o command: Specifies a command to perform.
7059 %
7060 %    o image: the image;  XMagickCommand
7061 %      may transform the image and return a new image pointer.
7062 %
7063 */
7064 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7065   XWindows *windows,const CommandType command,Image **image)
7066 {
7067   char
7068     filename[MaxTextExtent],
7069     geometry[MaxTextExtent],
7070     modulate_factors[MaxTextExtent];
7071
7072   GeometryInfo
7073     geometry_info;
7074
7075   Image
7076     *nexus;
7077
7078   ImageInfo
7079     *image_info;
7080
7081   int
7082     x,
7083     y;
7084
7085   MagickStatusType
7086     flags,
7087     status;
7088
7089   QuantizeInfo
7090     quantize_info;
7091
7092   RectangleInfo
7093     page_geometry;
7094
7095   register int
7096     i;
7097
7098   static char
7099     color[MaxTextExtent] = "gray";
7100
7101   unsigned int
7102     height,
7103     width;
7104
7105   /*
7106     Process user command.
7107   */
7108   XCheckRefreshWindows(display,windows);
7109   XImageCache(display,resource_info,windows,command,image);
7110   nexus=NewImageList();
7111   windows->image.window_changes.width=windows->image.ximage->width;
7112   windows->image.window_changes.height=windows->image.ximage->height;
7113   image_info=CloneImageInfo(resource_info->image_info);
7114   SetGeometryInfo(&geometry_info);
7115   GetQuantizeInfo(&quantize_info);
7116   switch (command)
7117   {
7118     case OpenCommand:
7119     {
7120       /*
7121         Load image.
7122       */
7123       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7124       break;
7125     }
7126     case NextCommand:
7127     {
7128       /*
7129         Display next image.
7130       */
7131       for (i=0; i < resource_info->quantum; i++)
7132         XClientMessage(display,windows->image.id,windows->im_protocols,
7133           windows->im_next_image,CurrentTime);
7134       break;
7135     }
7136     case FormerCommand:
7137     {
7138       /*
7139         Display former image.
7140       */
7141       for (i=0; i < resource_info->quantum; i++)
7142         XClientMessage(display,windows->image.id,windows->im_protocols,
7143           windows->im_former_image,CurrentTime);
7144       break;
7145     }
7146     case SelectCommand:
7147     {
7148       int
7149         status;
7150
7151       /*
7152         Select image.
7153       */
7154       status=chdir(resource_info->home_directory);
7155       if (status == -1)
7156         (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7157           FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7158       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7159       break;
7160     }
7161     case SaveCommand:
7162     {
7163       /*
7164         Save image.
7165       */
7166       status=XSaveImage(display,resource_info,windows,*image);
7167       if (status == MagickFalse)
7168         {
7169           XNoticeWidget(display,windows,"Unable to write X image:",
7170             (*image)->filename);
7171           break;
7172         }
7173       break;
7174     }
7175     case PrintCommand:
7176     {
7177       /*
7178         Print image.
7179       */
7180       status=XPrintImage(display,resource_info,windows,*image);
7181       if (status == MagickFalse)
7182         {
7183           XNoticeWidget(display,windows,"Unable to print X image:",
7184             (*image)->filename);
7185           break;
7186         }
7187       break;
7188     }
7189     case DeleteCommand:
7190     {
7191       static char
7192         filename[MaxTextExtent] = "\0";
7193
7194       /*
7195         Delete image file.
7196       */
7197       XFileBrowserWidget(display,windows,"Delete",filename);
7198       if (*filename == '\0')
7199         break;
7200       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
7201       if (status != MagickFalse)
7202         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7203       break;
7204     }
7205     case NewCommand:
7206     {
7207       int
7208         status;
7209
7210       static char
7211         color[MaxTextExtent] = "gray",
7212         geometry[MaxTextExtent] = "640x480";
7213
7214       static const char
7215         *format = "gradient";
7216
7217       /*
7218         Query user for canvas geometry.
7219       */
7220       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7221         geometry);
7222       if (*geometry == '\0')
7223         break;
7224       if (status == 0)
7225         format="xc";
7226       XColorBrowserWidget(display,windows,"Select",color);
7227       if (*color == '\0')
7228         break;
7229       /*
7230         Create canvas.
7231       */
7232       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7233         "%s:%s",format,color);
7234       (void) CloneString(&image_info->size,geometry);
7235       nexus=ReadImage(image_info,&(*image)->exception);
7236       CatchException(&(*image)->exception);
7237       XClientMessage(display,windows->image.id,windows->im_protocols,
7238         windows->im_next_image,CurrentTime);
7239       break;
7240     }
7241     case VisualDirectoryCommand:
7242     {
7243       /*
7244         Visual Image directory.
7245       */
7246       nexus=XVisualDirectoryImage(display,resource_info,windows);
7247       break;
7248     }
7249     case QuitCommand:
7250     {
7251       /*
7252         exit program.
7253       */
7254       if (resource_info->confirm_exit == MagickFalse)
7255         XClientMessage(display,windows->image.id,windows->im_protocols,
7256           windows->im_exit,CurrentTime);
7257       else
7258         {
7259           int
7260             status;
7261
7262           /*
7263             Confirm program exit.
7264           */
7265           status=XConfirmWidget(display,windows,"Do you really want to exit",
7266             resource_info->client_name);
7267           if (status > 0)
7268             XClientMessage(display,windows->image.id,windows->im_protocols,
7269               windows->im_exit,CurrentTime);
7270         }
7271       break;
7272     }
7273     case CutCommand:
7274     {
7275       /*
7276         Cut image.
7277       */
7278       (void) XCropImage(display,resource_info,windows,*image,CutMode);
7279       break;
7280     }
7281     case CopyCommand:
7282     {
7283       /*
7284         Copy image.
7285       */
7286       (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7287       break;
7288     }
7289     case PasteCommand:
7290     {
7291       /*
7292         Paste image.
7293       */
7294       status=XPasteImage(display,resource_info,windows,*image);
7295       if (status == MagickFalse)
7296         {
7297           XNoticeWidget(display,windows,"Unable to paste X image",
7298             (*image)->filename);
7299           break;
7300         }
7301       break;
7302     }
7303     case HalfSizeCommand:
7304     {
7305       /*
7306         Half image size.
7307       */
7308       windows->image.window_changes.width=windows->image.ximage->width/2;
7309       windows->image.window_changes.height=windows->image.ximage->height/2;
7310       (void) XConfigureImage(display,resource_info,windows,*image);
7311       break;
7312     }
7313     case OriginalSizeCommand:
7314     {
7315       /*
7316         Original image size.
7317       */
7318       windows->image.window_changes.width=(int) (*image)->columns;
7319       windows->image.window_changes.height=(int) (*image)->rows;
7320       (void) XConfigureImage(display,resource_info,windows,*image);
7321       break;
7322     }
7323     case DoubleSizeCommand:
7324     {
7325       /*
7326         Double the image size.
7327       */
7328       windows->image.window_changes.width=windows->image.ximage->width << 1;
7329       windows->image.window_changes.height=windows->image.ximage->height << 1;
7330       (void) XConfigureImage(display,resource_info,windows,*image);
7331       break;
7332     }
7333     case ResizeCommand:
7334     {
7335       int
7336         status;
7337
7338       size_t
7339         height,
7340         width;
7341
7342       ssize_t
7343         x,
7344         y;
7345
7346       /*
7347         Resize image.
7348       */
7349       width=(size_t) windows->image.ximage->width;
7350       height=(size_t) windows->image.ximage->height;
7351       x=0;
7352       y=0;
7353       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7354         (double) width,(double) height);
7355       status=XDialogWidget(display,windows,"Resize",
7356         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7357       if (*geometry == '\0')
7358         break;
7359       if (status == 0)
7360         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7361       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7362       windows->image.window_changes.width=(int) width;
7363       windows->image.window_changes.height=(int) height;
7364       (void) XConfigureImage(display,resource_info,windows,*image);
7365       break;
7366     }
7367     case ApplyCommand:
7368     {
7369       char
7370         image_geometry[MaxTextExtent];
7371
7372       if ((windows->image.crop_geometry == (char *) NULL) &&
7373           ((int) (*image)->columns == windows->image.ximage->width) &&
7374           ((int) (*image)->rows == windows->image.ximage->height))
7375         break;
7376       /*
7377         Apply size transforms to image.
7378       */
7379       XSetCursorState(display,windows,MagickTrue);
7380       XCheckRefreshWindows(display,windows);
7381       /*
7382         Crop and/or scale displayed image.
7383       */
7384       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7385         windows->image.ximage->width,windows->image.ximage->height);
7386       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7387       if (windows->image.crop_geometry != (char *) NULL)
7388         windows->image.crop_geometry=(char *)
7389           RelinquishMagickMemory(windows->image.crop_geometry);
7390       windows->image.x=0;
7391       windows->image.y=0;
7392       XConfigureImageColormap(display,resource_info,windows,*image);
7393       (void) XConfigureImage(display,resource_info,windows,*image);
7394       break;
7395     }
7396     case RefreshCommand:
7397     {
7398       (void) XConfigureImage(display,resource_info,windows,*image);
7399       break;
7400     }
7401     case RestoreCommand:
7402     {
7403       /*
7404         Restore Image window to its original size.
7405       */
7406       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7407           (windows->image.height == (unsigned int) (*image)->rows) &&
7408           (windows->image.crop_geometry == (char *) NULL))
7409         {
7410           (void) XBell(display,0);
7411           break;
7412         }
7413       windows->image.window_changes.width=(int) (*image)->columns;
7414       windows->image.window_changes.height=(int) (*image)->rows;
7415       if (windows->image.crop_geometry != (char *) NULL)
7416         {
7417           windows->image.crop_geometry=(char *)
7418             RelinquishMagickMemory(windows->image.crop_geometry);
7419           windows->image.crop_geometry=(char *) NULL;
7420           windows->image.x=0;
7421           windows->image.y=0;
7422         }
7423       XConfigureImageColormap(display,resource_info,windows,*image);
7424       (void) XConfigureImage(display,resource_info,windows,*image);
7425       break;
7426     }
7427     case CropCommand:
7428     {
7429       /*
7430         Crop image.
7431       */
7432       (void) XCropImage(display,resource_info,windows,*image,CropMode);
7433       break;
7434     }
7435     case ChopCommand:
7436     {
7437       /*
7438         Chop image.
7439       */
7440       status=XChopImage(display,resource_info,windows,image);
7441       if (status == MagickFalse)
7442         {
7443           XNoticeWidget(display,windows,"Unable to cut X image",
7444             (*image)->filename);
7445           break;
7446         }
7447       break;
7448     }
7449     case FlopCommand:
7450     {
7451       Image
7452         *flop_image;
7453
7454       /*
7455         Flop image scanlines.
7456       */
7457       XSetCursorState(display,windows,MagickTrue);
7458       XCheckRefreshWindows(display,windows);
7459       flop_image=FlopImage(*image,&(*image)->exception);
7460       if (flop_image != (Image *) NULL)
7461         {
7462           *image=DestroyImage(*image);
7463           *image=flop_image;
7464         }
7465       CatchException(&(*image)->exception);
7466       XSetCursorState(display,windows,MagickFalse);
7467       if (windows->image.crop_geometry != (char *) NULL)
7468         {
7469           /*
7470             Flop crop geometry.
7471           */
7472           width=(unsigned int) (*image)->columns;
7473           height=(unsigned int) (*image)->rows;
7474           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7475             &width,&height);
7476           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7477             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7478         }
7479       if (windows->image.orphan != MagickFalse)
7480         break;
7481       (void) XConfigureImage(display,resource_info,windows,*image);
7482       break;
7483     }
7484     case FlipCommand:
7485     {
7486       Image
7487         *flip_image;
7488
7489       /*
7490         Flip image scanlines.
7491       */
7492       XSetCursorState(display,windows,MagickTrue);
7493       XCheckRefreshWindows(display,windows);
7494       flip_image=FlipImage(*image,&(*image)->exception);
7495       if (flip_image != (Image *) NULL)
7496         {
7497           *image=DestroyImage(*image);
7498           *image=flip_image;
7499         }
7500       CatchException(&(*image)->exception);
7501       XSetCursorState(display,windows,MagickFalse);
7502       if (windows->image.crop_geometry != (char *) NULL)
7503         {
7504           /*
7505             Flip crop geometry.
7506           */
7507           width=(unsigned int) (*image)->columns;
7508           height=(unsigned int) (*image)->rows;
7509           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7510             &width,&height);
7511           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7512             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7513         }
7514       if (windows->image.orphan != MagickFalse)
7515         break;
7516       (void) XConfigureImage(display,resource_info,windows,*image);
7517       break;
7518     }
7519     case RotateRightCommand:
7520     {
7521       /*
7522         Rotate image 90 degrees clockwise.
7523       */
7524       status=XRotateImage(display,resource_info,windows,90.0,image);
7525       if (status == MagickFalse)
7526         {
7527           XNoticeWidget(display,windows,"Unable to rotate X image",
7528             (*image)->filename);
7529           break;
7530         }
7531       break;
7532     }
7533     case RotateLeftCommand:
7534     {
7535       /*
7536         Rotate image 90 degrees counter-clockwise.
7537       */
7538       status=XRotateImage(display,resource_info,windows,-90.0,image);
7539       if (status == MagickFalse)
7540         {
7541           XNoticeWidget(display,windows,"Unable to rotate X image",
7542             (*image)->filename);
7543           break;
7544         }
7545       break;
7546     }
7547     case RotateCommand:
7548     {
7549       /*
7550         Rotate image.
7551       */
7552       status=XRotateImage(display,resource_info,windows,0.0,image);
7553       if (status == MagickFalse)
7554         {
7555           XNoticeWidget(display,windows,"Unable to rotate X image",
7556             (*image)->filename);
7557           break;
7558         }
7559       break;
7560     }
7561     case ShearCommand:
7562     {
7563       Image
7564         *shear_image;
7565
7566       static char
7567         geometry[MaxTextExtent] = "45.0x45.0";
7568
7569       /*
7570         Query user for shear color and geometry.
7571       */
7572       XColorBrowserWidget(display,windows,"Select",color);
7573       if (*color == '\0')
7574         break;
7575       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7576         geometry);
7577       if (*geometry == '\0')
7578         break;
7579       /*
7580         Shear image.
7581       */
7582       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7583       XSetCursorState(display,windows,MagickTrue);
7584       XCheckRefreshWindows(display,windows);
7585       (void) QueryColorDatabase(color,&(*image)->background_color,
7586         &(*image)->exception);
7587       flags=ParseGeometry(geometry,&geometry_info);
7588       if ((flags & SigmaValue) == 0)
7589         geometry_info.sigma=geometry_info.rho;
7590       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7591         &(*image)->exception);
7592       if (shear_image != (Image *) NULL)
7593         {
7594           *image=DestroyImage(*image);
7595           *image=shear_image;
7596         }
7597       CatchException(&(*image)->exception);
7598       XSetCursorState(display,windows,MagickFalse);
7599       if (windows->image.orphan != MagickFalse)
7600         break;
7601       windows->image.window_changes.width=(int) (*image)->columns;
7602       windows->image.window_changes.height=(int) (*image)->rows;
7603       XConfigureImageColormap(display,resource_info,windows,*image);
7604       (void) XConfigureImage(display,resource_info,windows,*image);
7605       break;
7606     }
7607     case RollCommand:
7608     {
7609       Image
7610         *roll_image;
7611
7612       static char
7613         geometry[MaxTextExtent] = "+2+2";
7614
7615       /*
7616         Query user for the roll geometry.
7617       */
7618       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7619         geometry);
7620       if (*geometry == '\0')
7621         break;
7622       /*
7623         Roll image.
7624       */
7625       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7626       XSetCursorState(display,windows,MagickTrue);
7627       XCheckRefreshWindows(display,windows);
7628       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7629         &(*image)->exception);
7630       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7631         &(*image)->exception);
7632       if (roll_image != (Image *) NULL)
7633         {
7634           *image=DestroyImage(*image);
7635           *image=roll_image;
7636         }
7637       CatchException(&(*image)->exception);
7638       XSetCursorState(display,windows,MagickFalse);
7639       if (windows->image.orphan != MagickFalse)
7640         break;
7641       windows->image.window_changes.width=(int) (*image)->columns;
7642       windows->image.window_changes.height=(int) (*image)->rows;
7643       XConfigureImageColormap(display,resource_info,windows,*image);
7644       (void) XConfigureImage(display,resource_info,windows,*image);
7645       break;
7646     }
7647     case TrimCommand:
7648     {
7649       static char
7650         fuzz[MaxTextExtent];
7651
7652       /*
7653         Query user for the fuzz factor.
7654       */
7655       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7656         (*image)->fuzz/(QuantumRange+1.0));
7657       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7658       if (*fuzz == '\0')
7659         break;
7660       (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7661       /*
7662         Trim image.
7663       */
7664       status=XTrimImage(display,resource_info,windows,*image);
7665       if (status == MagickFalse)
7666         {
7667           XNoticeWidget(display,windows,"Unable to trim X image",
7668             (*image)->filename);
7669           break;
7670         }
7671       break;
7672     }
7673     case HueCommand:
7674     {
7675       static char
7676         hue_percent[MaxTextExtent] = "110";
7677
7678       /*
7679         Query user for percent hue change.
7680       */
7681       (void) XDialogWidget(display,windows,"Apply",
7682         "Enter percent change in image hue (0-200):",hue_percent);
7683       if (*hue_percent == '\0')
7684         break;
7685       /*
7686         Vary the image hue.
7687       */
7688       XSetCursorState(display,windows,MagickTrue);
7689       XCheckRefreshWindows(display,windows);
7690       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7691       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7692         MaxTextExtent);
7693       (void) ModulateImage(*image,modulate_factors,&(*image)->exception);
7694       XSetCursorState(display,windows,MagickFalse);
7695       if (windows->image.orphan != MagickFalse)
7696         break;
7697       XConfigureImageColormap(display,resource_info,windows,*image);
7698       (void) XConfigureImage(display,resource_info,windows,*image);
7699       break;
7700     }
7701     case SaturationCommand:
7702     {
7703       static char
7704         saturation_percent[MaxTextExtent] = "110";
7705
7706       /*
7707         Query user for percent saturation change.
7708       */
7709       (void) XDialogWidget(display,windows,"Apply",
7710         "Enter percent change in color saturation (0-200):",saturation_percent);
7711       if (*saturation_percent == '\0')
7712         break;
7713       /*
7714         Vary color saturation.
7715       */
7716       XSetCursorState(display,windows,MagickTrue);
7717       XCheckRefreshWindows(display,windows);
7718       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7719       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7720         MaxTextExtent);
7721       (void) ModulateImage(*image,modulate_factors,&(*image)->exception);
7722       XSetCursorState(display,windows,MagickFalse);
7723       if (windows->image.orphan != MagickFalse)
7724         break;
7725       XConfigureImageColormap(display,resource_info,windows,*image);
7726       (void) XConfigureImage(display,resource_info,windows,*image);
7727       break;
7728     }
7729     case BrightnessCommand:
7730     {
7731       static char
7732         brightness_percent[MaxTextExtent] = "110";
7733
7734       /*
7735         Query user for percent brightness change.
7736       */
7737       (void) XDialogWidget(display,windows,"Apply",
7738         "Enter percent change in color brightness (0-200):",brightness_percent);
7739       if (*brightness_percent == '\0')
7740         break;
7741       /*
7742         Vary the color brightness.
7743       */
7744       XSetCursorState(display,windows,MagickTrue);
7745       XCheckRefreshWindows(display,windows);
7746       (void) CopyMagickString(modulate_factors,brightness_percent,
7747         MaxTextExtent);
7748       (void) ModulateImage(*image,modulate_factors,&(*image)->exception);
7749       XSetCursorState(display,windows,MagickFalse);
7750       if (windows->image.orphan != MagickFalse)
7751         break;
7752       XConfigureImageColormap(display,resource_info,windows,*image);
7753       (void) XConfigureImage(display,resource_info,windows,*image);
7754       break;
7755     }
7756     case GammaCommand:
7757     {
7758       static char
7759         factor[MaxTextExtent] = "1.6";
7760
7761       /*
7762         Query user for gamma value.
7763       */
7764       (void) XDialogWidget(display,windows,"Gamma",
7765         "Enter gamma value (e.g. 1.2):",factor);
7766       if (*factor == '\0')
7767         break;
7768       /*
7769         Gamma correct image.
7770       */
7771       XSetCursorState(display,windows,MagickTrue);
7772       XCheckRefreshWindows(display,windows);
7773       (void) GammaImage(*image,atof(factor),&(*image)->exception);
7774       XSetCursorState(display,windows,MagickFalse);
7775       if (windows->image.orphan != MagickFalse)
7776         break;
7777       XConfigureImageColormap(display,resource_info,windows,*image);
7778       (void) XConfigureImage(display,resource_info,windows,*image);
7779       break;
7780     }
7781     case SpiffCommand:
7782     {
7783       /*
7784         Sharpen the image contrast.
7785       */
7786       XSetCursorState(display,windows,MagickTrue);
7787       XCheckRefreshWindows(display,windows);
7788       (void) ContrastImage(*image,MagickTrue,&(*image)->exception);
7789       XSetCursorState(display,windows,MagickFalse);
7790       if (windows->image.orphan != MagickFalse)
7791         break;
7792       XConfigureImageColormap(display,resource_info,windows,*image);
7793       (void) XConfigureImage(display,resource_info,windows,*image);
7794       break;
7795     }
7796     case DullCommand:
7797     {
7798       /*
7799         Dull the image contrast.
7800       */
7801       XSetCursorState(display,windows,MagickTrue);
7802       XCheckRefreshWindows(display,windows);
7803       (void) ContrastImage(*image,MagickFalse,&(*image)->exception);
7804       XSetCursorState(display,windows,MagickFalse);
7805       if (windows->image.orphan != MagickFalse)
7806         break;
7807       XConfigureImageColormap(display,resource_info,windows,*image);
7808       (void) XConfigureImage(display,resource_info,windows,*image);
7809       break;
7810     }
7811     case ContrastStretchCommand:
7812     {
7813       double
7814         black_point,
7815         white_point;
7816
7817       static char
7818         levels[MaxTextExtent] = "1%";
7819
7820       /*
7821         Query user for gamma value.
7822       */
7823       (void) XDialogWidget(display,windows,"Contrast Stretch",
7824         "Enter black and white points:",levels);
7825       if (*levels == '\0')
7826         break;
7827       /*
7828         Contrast stretch image.
7829       */
7830       XSetCursorState(display,windows,MagickTrue);
7831       XCheckRefreshWindows(display,windows);
7832       flags=ParseGeometry(levels,&geometry_info);
7833       black_point=geometry_info.rho;
7834       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7835       if ((flags & PercentValue) != 0)
7836         {
7837           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7838           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7839         }
7840       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7841       (void) ContrastStretchImage(*image,black_point,white_point,
7842         &(*image)->exception);
7843       XSetCursorState(display,windows,MagickFalse);
7844       if (windows->image.orphan != MagickFalse)
7845         break;
7846       XConfigureImageColormap(display,resource_info,windows,*image);
7847       (void) XConfigureImage(display,resource_info,windows,*image);
7848       break;
7849     }
7850     case SigmoidalContrastCommand:
7851     {
7852       GeometryInfo
7853         geometry_info;
7854
7855       MagickStatusType
7856         flags;
7857
7858       static char
7859         levels[MaxTextExtent] = "3x50%";
7860
7861       /*
7862         Query user for gamma value.
7863       */
7864       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7865         "Enter contrast and midpoint:",levels);
7866       if (*levels == '\0')
7867         break;
7868       /*
7869         Contrast stretch image.
7870       */
7871       XSetCursorState(display,windows,MagickTrue);
7872       XCheckRefreshWindows(display,windows);
7873       flags=ParseGeometry(levels,&geometry_info);
7874       if ((flags & SigmaValue) == 0)
7875         geometry_info.sigma=1.0*QuantumRange/2.0;
7876       if ((flags & PercentValue) != 0)
7877         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7878       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7879         geometry_info.sigma,&(*image)->exception);
7880       XSetCursorState(display,windows,MagickFalse);
7881       if (windows->image.orphan != MagickFalse)
7882         break;
7883       XConfigureImageColormap(display,resource_info,windows,*image);
7884       (void) XConfigureImage(display,resource_info,windows,*image);
7885       break;
7886     }
7887     case NormalizeCommand:
7888     {
7889       /*
7890         Perform histogram normalization on the image.
7891       */
7892       XSetCursorState(display,windows,MagickTrue);
7893       XCheckRefreshWindows(display,windows);
7894       (void) NormalizeImage(*image,&(*image)->exception);
7895       XSetCursorState(display,windows,MagickFalse);
7896       if (windows->image.orphan != MagickFalse)
7897         break;
7898       XConfigureImageColormap(display,resource_info,windows,*image);
7899       (void) XConfigureImage(display,resource_info,windows,*image);
7900       break;
7901     }
7902     case EqualizeCommand:
7903     {
7904       /*
7905         Perform histogram equalization on the image.
7906       */
7907       XSetCursorState(display,windows,MagickTrue);
7908       XCheckRefreshWindows(display,windows);
7909       (void) EqualizeImage(*image,&(*image)->exception);
7910       XSetCursorState(display,windows,MagickFalse);
7911       if (windows->image.orphan != MagickFalse)
7912         break;
7913       XConfigureImageColormap(display,resource_info,windows,*image);
7914       (void) XConfigureImage(display,resource_info,windows,*image);
7915       break;
7916     }
7917     case NegateCommand:
7918     {
7919       /*
7920         Negate colors in image.
7921       */
7922       XSetCursorState(display,windows,MagickTrue);
7923       XCheckRefreshWindows(display,windows);
7924       (void) NegateImage(*image,MagickFalse,&(*image)->exception);
7925       XSetCursorState(display,windows,MagickFalse);
7926       if (windows->image.orphan != MagickFalse)
7927         break;
7928       XConfigureImageColormap(display,resource_info,windows,*image);
7929       (void) XConfigureImage(display,resource_info,windows,*image);
7930       break;
7931     }
7932     case GrayscaleCommand:
7933     {
7934       /*
7935         Convert image to grayscale.
7936       */
7937       XSetCursorState(display,windows,MagickTrue);
7938       XCheckRefreshWindows(display,windows);
7939       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7940         GrayscaleType : GrayscaleMatteType);
7941       XSetCursorState(display,windows,MagickFalse);
7942       if (windows->image.orphan != MagickFalse)
7943         break;
7944       XConfigureImageColormap(display,resource_info,windows,*image);
7945       (void) XConfigureImage(display,resource_info,windows,*image);
7946       break;
7947     }
7948     case MapCommand:
7949     {
7950       Image
7951         *affinity_image;
7952
7953       static char
7954         filename[MaxTextExtent] = "\0";
7955
7956       /*
7957         Request image file name from user.
7958       */
7959       XFileBrowserWidget(display,windows,"Map",filename);
7960       if (*filename == '\0')
7961         break;
7962       /*
7963         Map image.
7964       */
7965       XSetCursorState(display,windows,MagickTrue);
7966       XCheckRefreshWindows(display,windows);
7967       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7968       affinity_image=ReadImage(image_info,&(*image)->exception);
7969       if (affinity_image != (Image *) NULL)
7970         {
7971           (void) RemapImage(&quantize_info,*image,affinity_image);
7972           affinity_image=DestroyImage(affinity_image);
7973         }
7974       CatchException(&(*image)->exception);
7975       XSetCursorState(display,windows,MagickFalse);
7976       if (windows->image.orphan != MagickFalse)
7977         break;
7978       XConfigureImageColormap(display,resource_info,windows,*image);
7979       (void) XConfigureImage(display,resource_info,windows,*image);
7980       break;
7981     }
7982     case QuantizeCommand:
7983     {
7984       int
7985         status;
7986
7987       static char
7988         colors[MaxTextExtent] = "256";
7989
7990       /*
7991         Query user for maximum number of colors.
7992       */
7993       status=XDialogWidget(display,windows,"Quantize",
7994         "Maximum number of colors:",colors);
7995       if (*colors == '\0')
7996         break;
7997       /*
7998         Color reduce the image.
7999       */
8000       XSetCursorState(display,windows,MagickTrue);
8001       XCheckRefreshWindows(display,windows);
8002       quantize_info.number_colors=StringToUnsignedLong(colors);
8003       quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8004       (void) QuantizeImage(&quantize_info,*image);
8005       XSetCursorState(display,windows,MagickFalse);
8006       if (windows->image.orphan != MagickFalse)
8007         break;
8008       XConfigureImageColormap(display,resource_info,windows,*image);
8009       (void) XConfigureImage(display,resource_info,windows,*image);
8010       break;
8011     }
8012     case DespeckleCommand:
8013     {
8014       Image
8015         *despeckle_image;
8016
8017       /*
8018         Despeckle image.
8019       */
8020       XSetCursorState(display,windows,MagickTrue);
8021       XCheckRefreshWindows(display,windows);
8022       despeckle_image=DespeckleImage(*image,&(*image)->exception);
8023       if (despeckle_image != (Image *) NULL)
8024         {
8025           *image=DestroyImage(*image);
8026           *image=despeckle_image;
8027         }
8028       CatchException(&(*image)->exception);
8029       XSetCursorState(display,windows,MagickFalse);
8030       if (windows->image.orphan != MagickFalse)
8031         break;
8032       XConfigureImageColormap(display,resource_info,windows,*image);
8033       (void) XConfigureImage(display,resource_info,windows,*image);
8034       break;
8035     }
8036     case EmbossCommand:
8037     {
8038       Image
8039         *emboss_image;
8040
8041       static char
8042         radius[MaxTextExtent] = "0.0x1.0";
8043
8044       /*
8045         Query user for emboss radius.
8046       */
8047       (void) XDialogWidget(display,windows,"Emboss",
8048         "Enter the emboss radius and standard deviation:",radius);
8049       if (*radius == '\0')
8050         break;
8051       /*
8052         Reduce noise in the image.
8053       */
8054       XSetCursorState(display,windows,MagickTrue);
8055       XCheckRefreshWindows(display,windows);
8056       flags=ParseGeometry(radius,&geometry_info);
8057       if ((flags & SigmaValue) == 0)
8058         geometry_info.sigma=1.0;
8059       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8060         &(*image)->exception);
8061       if (emboss_image != (Image *) NULL)
8062         {
8063           *image=DestroyImage(*image);
8064           *image=emboss_image;
8065         }
8066       CatchException(&(*image)->exception);
8067       XSetCursorState(display,windows,MagickFalse);
8068       if (windows->image.orphan != MagickFalse)
8069         break;
8070       XConfigureImageColormap(display,resource_info,windows,*image);
8071       (void) XConfigureImage(display,resource_info,windows,*image);
8072       break;
8073     }
8074     case ReduceNoiseCommand:
8075     {
8076       Image
8077         *noise_image;
8078
8079       static char
8080         radius[MaxTextExtent] = "0";
8081
8082       /*
8083         Query user for noise radius.
8084       */
8085       (void) XDialogWidget(display,windows,"Reduce Noise",
8086         "Enter the noise radius:",radius);
8087       if (*radius == '\0')
8088         break;
8089       /*
8090         Reduce noise in the image.
8091       */
8092       XSetCursorState(display,windows,MagickTrue);
8093       XCheckRefreshWindows(display,windows);
8094       flags=ParseGeometry(radius,&geometry_info);
8095       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8096         geometry_info.rho,(size_t) geometry_info.rho,&(*image)->exception);
8097       if (noise_image != (Image *) NULL)
8098         {
8099           *image=DestroyImage(*image);
8100           *image=noise_image;
8101         }
8102       CatchException(&(*image)->exception);
8103       XSetCursorState(display,windows,MagickFalse);
8104       if (windows->image.orphan != MagickFalse)
8105         break;
8106       XConfigureImageColormap(display,resource_info,windows,*image);
8107       (void) XConfigureImage(display,resource_info,windows,*image);
8108       break;
8109     }
8110     case AddNoiseCommand:
8111     {
8112       char
8113         **noises;
8114
8115       Image
8116         *noise_image;
8117
8118       static char
8119         noise_type[MaxTextExtent] = "Gaussian";
8120
8121       /*
8122         Add noise to the image.
8123       */
8124       noises=GetCommandOptions(MagickNoiseOptions);
8125       if (noises == (char **) NULL)
8126         break;
8127       XListBrowserWidget(display,windows,&windows->widget,
8128         (const char **) noises,"Add Noise",
8129         "Select a type of noise to add to your image:",noise_type);
8130       noises=DestroyStringList(noises);
8131       if (*noise_type == '\0')
8132         break;
8133       XSetCursorState(display,windows,MagickTrue);
8134       XCheckRefreshWindows(display,windows);
8135       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8136         MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8137       if (noise_image != (Image *) NULL)
8138         {
8139           *image=DestroyImage(*image);
8140           *image=noise_image;
8141         }
8142       CatchException(&(*image)->exception);
8143       XSetCursorState(display,windows,MagickFalse);
8144       if (windows->image.orphan != MagickFalse)
8145         break;
8146       XConfigureImageColormap(display,resource_info,windows,*image);
8147       (void) XConfigureImage(display,resource_info,windows,*image);
8148       break;
8149     }
8150     case SharpenCommand:
8151     {
8152       Image
8153         *sharp_image;
8154
8155       static char
8156         radius[MaxTextExtent] = "0.0x1.0";
8157
8158       /*
8159         Query user for sharpen radius.
8160       */
8161       (void) XDialogWidget(display,windows,"Sharpen",
8162         "Enter the sharpen radius and standard deviation:",radius);
8163       if (*radius == '\0')
8164         break;
8165       /*
8166         Sharpen image scanlines.
8167       */
8168       XSetCursorState(display,windows,MagickTrue);
8169       XCheckRefreshWindows(display,windows);
8170       flags=ParseGeometry(radius,&geometry_info);
8171       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8172         &(*image)->exception);
8173       if (sharp_image != (Image *) NULL)
8174         {
8175           *image=DestroyImage(*image);
8176           *image=sharp_image;
8177         }
8178       CatchException(&(*image)->exception);
8179       XSetCursorState(display,windows,MagickFalse);
8180       if (windows->image.orphan != MagickFalse)
8181         break;
8182       XConfigureImageColormap(display,resource_info,windows,*image);
8183       (void) XConfigureImage(display,resource_info,windows,*image);
8184       break;
8185     }
8186     case BlurCommand:
8187     {
8188       Image
8189         *blur_image;
8190
8191       static char
8192         radius[MaxTextExtent] = "0.0x1.0";
8193
8194       /*
8195         Query user for blur radius.
8196       */
8197       (void) XDialogWidget(display,windows,"Blur",
8198         "Enter the blur radius and standard deviation:",radius);
8199       if (*radius == '\0')
8200         break;
8201       /*
8202         Blur an image.
8203       */
8204       XSetCursorState(display,windows,MagickTrue);
8205       XCheckRefreshWindows(display,windows);
8206       flags=ParseGeometry(radius,&geometry_info);
8207       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8208         &(*image)->exception);
8209       if (blur_image != (Image *) NULL)
8210         {
8211           *image=DestroyImage(*image);
8212           *image=blur_image;
8213         }
8214       CatchException(&(*image)->exception);
8215       XSetCursorState(display,windows,MagickFalse);
8216       if (windows->image.orphan != MagickFalse)
8217         break;
8218       XConfigureImageColormap(display,resource_info,windows,*image);
8219       (void) XConfigureImage(display,resource_info,windows,*image);
8220       break;
8221     }
8222     case ThresholdCommand:
8223     {
8224       double
8225         threshold;
8226
8227       static char
8228         factor[MaxTextExtent] = "128";
8229
8230       /*
8231         Query user for threshold value.
8232       */
8233       (void) XDialogWidget(display,windows,"Threshold",
8234         "Enter threshold value:",factor);
8235       if (*factor == '\0')
8236         break;
8237       /*
8238         Gamma correct image.
8239       */
8240       XSetCursorState(display,windows,MagickTrue);
8241       XCheckRefreshWindows(display,windows);
8242       threshold=SiPrefixToDouble(factor,QuantumRange);
8243       (void) BilevelImage(*image,threshold);
8244       XSetCursorState(display,windows,MagickFalse);
8245       if (windows->image.orphan != MagickFalse)
8246         break;
8247       XConfigureImageColormap(display,resource_info,windows,*image);
8248       (void) XConfigureImage(display,resource_info,windows,*image);
8249       break;
8250     }
8251     case EdgeDetectCommand:
8252     {
8253       Image
8254         *edge_image;
8255
8256       static char
8257         radius[MaxTextExtent] = "0";
8258
8259       /*
8260         Query user for edge factor.
8261       */
8262       (void) XDialogWidget(display,windows,"Detect Edges",
8263         "Enter the edge detect radius:",radius);
8264       if (*radius == '\0')
8265         break;
8266       /*
8267         Detect edge in image.
8268       */
8269       XSetCursorState(display,windows,MagickTrue);
8270       XCheckRefreshWindows(display,windows);
8271       flags=ParseGeometry(radius,&geometry_info);
8272       edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8273       if (edge_image != (Image *) NULL)
8274         {
8275           *image=DestroyImage(*image);
8276           *image=edge_image;
8277         }
8278       CatchException(&(*image)->exception);
8279       XSetCursorState(display,windows,MagickFalse);
8280       if (windows->image.orphan != MagickFalse)
8281         break;
8282       XConfigureImageColormap(display,resource_info,windows,*image);
8283       (void) XConfigureImage(display,resource_info,windows,*image);
8284       break;
8285     }
8286     case SpreadCommand:
8287     {
8288       Image
8289         *spread_image;
8290
8291       static char
8292         amount[MaxTextExtent] = "2";
8293
8294       /*
8295         Query user for spread amount.
8296       */
8297       (void) XDialogWidget(display,windows,"Spread",
8298         "Enter the displacement amount:",amount);
8299       if (*amount == '\0')
8300         break;
8301       /*
8302         Displace image pixels by a random amount.
8303       */
8304       XSetCursorState(display,windows,MagickTrue);
8305       XCheckRefreshWindows(display,windows);
8306       flags=ParseGeometry(amount,&geometry_info);
8307       spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8308       if (spread_image != (Image *) NULL)
8309         {
8310           *image=DestroyImage(*image);
8311           *image=spread_image;
8312         }
8313       CatchException(&(*image)->exception);
8314       XSetCursorState(display,windows,MagickFalse);
8315       if (windows->image.orphan != MagickFalse)
8316         break;
8317       XConfigureImageColormap(display,resource_info,windows,*image);
8318       (void) XConfigureImage(display,resource_info,windows,*image);
8319       break;
8320     }
8321     case ShadeCommand:
8322     {
8323       Image
8324         *shade_image;
8325
8326       int
8327         status;
8328
8329       static char
8330         geometry[MaxTextExtent] = "30x30";
8331
8332       /*
8333         Query user for the shade geometry.
8334       */
8335       status=XDialogWidget(display,windows,"Shade",
8336         "Enter the azimuth and elevation of the light source:",geometry);
8337       if (*geometry == '\0')
8338         break;
8339       /*
8340         Shade image pixels.
8341       */
8342       XSetCursorState(display,windows,MagickTrue);
8343       XCheckRefreshWindows(display,windows);
8344       flags=ParseGeometry(geometry,&geometry_info);
8345       if ((flags & SigmaValue) == 0)
8346         geometry_info.sigma=1.0;
8347       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8348         geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8349       if (shade_image != (Image *) NULL)
8350         {
8351           *image=DestroyImage(*image);
8352           *image=shade_image;
8353         }
8354       CatchException(&(*image)->exception);
8355       XSetCursorState(display,windows,MagickFalse);
8356       if (windows->image.orphan != MagickFalse)
8357         break;
8358       XConfigureImageColormap(display,resource_info,windows,*image);
8359       (void) XConfigureImage(display,resource_info,windows,*image);
8360       break;
8361     }
8362     case RaiseCommand:
8363     {
8364       static char
8365         bevel_width[MaxTextExtent] = "10";
8366
8367       /*
8368         Query user for bevel width.
8369       */
8370       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8371       if (*bevel_width == '\0')
8372         break;
8373       /*
8374         Raise an image.
8375       */
8376       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8377       XSetCursorState(display,windows,MagickTrue);
8378       XCheckRefreshWindows(display,windows);
8379       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8380         &(*image)->exception);
8381       (void) RaiseImage(*image,&page_geometry,MagickTrue,&(*image)->exception);
8382       XSetCursorState(display,windows,MagickFalse);
8383       if (windows->image.orphan != MagickFalse)
8384         break;
8385       XConfigureImageColormap(display,resource_info,windows,*image);
8386       (void) XConfigureImage(display,resource_info,windows,*image);
8387       break;
8388     }
8389     case SegmentCommand:
8390     {
8391       static char
8392         threshold[MaxTextExtent] = "1.0x1.5";
8393
8394       /*
8395         Query user for smoothing threshold.
8396       */
8397       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8398         threshold);
8399       if (*threshold == '\0')
8400         break;
8401       /*
8402         Segment an image.
8403       */
8404       XSetCursorState(display,windows,MagickTrue);
8405       XCheckRefreshWindows(display,windows);
8406       flags=ParseGeometry(threshold,&geometry_info);
8407       if ((flags & SigmaValue) == 0)
8408         geometry_info.sigma=1.0;
8409       (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8410         geometry_info.sigma);
8411       XSetCursorState(display,windows,MagickFalse);
8412       if (windows->image.orphan != MagickFalse)
8413         break;
8414       XConfigureImageColormap(display,resource_info,windows,*image);
8415       (void) XConfigureImage(display,resource_info,windows,*image);
8416       break;
8417     }
8418     case SepiaToneCommand:
8419     {
8420       double
8421         threshold;
8422
8423       Image
8424         *sepia_image;
8425
8426       static char
8427         factor[MaxTextExtent] = "80%";
8428
8429       /*
8430         Query user for sepia-tone factor.
8431       */
8432       (void) XDialogWidget(display,windows,"Sepia Tone",
8433         "Enter the sepia tone factor (0 - 99.9%):",factor);
8434       if (*factor == '\0')
8435         break;
8436       /*
8437         Sepia tone image pixels.
8438       */
8439       XSetCursorState(display,windows,MagickTrue);
8440       XCheckRefreshWindows(display,windows);
8441       threshold=SiPrefixToDouble(factor,QuantumRange);
8442       sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8443       if (sepia_image != (Image *) NULL)
8444         {
8445           *image=DestroyImage(*image);
8446           *image=sepia_image;
8447         }
8448       CatchException(&(*image)->exception);
8449       XSetCursorState(display,windows,MagickFalse);
8450       if (windows->image.orphan != MagickFalse)
8451         break;
8452       XConfigureImageColormap(display,resource_info,windows,*image);
8453       (void) XConfigureImage(display,resource_info,windows,*image);
8454       break;
8455     }
8456     case SolarizeCommand:
8457     {
8458       double
8459         threshold;
8460
8461       static char
8462         factor[MaxTextExtent] = "60%";
8463
8464       /*
8465         Query user for solarize factor.
8466       */
8467       (void) XDialogWidget(display,windows,"Solarize",
8468         "Enter the solarize factor (0 - 99.9%):",factor);
8469       if (*factor == '\0')
8470         break;
8471       /*
8472         Solarize image pixels.
8473       */
8474       XSetCursorState(display,windows,MagickTrue);
8475       XCheckRefreshWindows(display,windows);
8476       threshold=SiPrefixToDouble(factor,QuantumRange);
8477       (void) SolarizeImage(*image,threshold);
8478       XSetCursorState(display,windows,MagickFalse);
8479       if (windows->image.orphan != MagickFalse)
8480         break;
8481       XConfigureImageColormap(display,resource_info,windows,*image);
8482       (void) XConfigureImage(display,resource_info,windows,*image);
8483       break;
8484     }
8485     case SwirlCommand:
8486     {
8487       Image
8488         *swirl_image;
8489
8490       static char
8491         degrees[MaxTextExtent] = "60";
8492
8493       /*
8494         Query user for swirl angle.
8495       */
8496       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8497         degrees);
8498       if (*degrees == '\0')
8499         break;
8500       /*
8501         Swirl image pixels about the center.
8502       */
8503       XSetCursorState(display,windows,MagickTrue);
8504       XCheckRefreshWindows(display,windows);
8505       flags=ParseGeometry(degrees,&geometry_info);
8506       swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8507       if (swirl_image != (Image *) NULL)
8508         {
8509           *image=DestroyImage(*image);
8510           *image=swirl_image;
8511         }
8512       CatchException(&(*image)->exception);
8513       XSetCursorState(display,windows,MagickFalse);
8514       if (windows->image.orphan != MagickFalse)
8515         break;
8516       XConfigureImageColormap(display,resource_info,windows,*image);
8517       (void) XConfigureImage(display,resource_info,windows,*image);
8518       break;
8519     }
8520     case ImplodeCommand:
8521     {
8522       Image
8523         *implode_image;
8524
8525       static char
8526         factor[MaxTextExtent] = "0.3";
8527
8528       /*
8529         Query user for implode factor.
8530       */
8531       (void) XDialogWidget(display,windows,"Implode",
8532         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8533       if (*factor == '\0')
8534         break;
8535       /*
8536         Implode image pixels about the center.
8537       */
8538       XSetCursorState(display,windows,MagickTrue);
8539       XCheckRefreshWindows(display,windows);
8540       flags=ParseGeometry(factor,&geometry_info);
8541       implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8542       if (implode_image != (Image *) NULL)
8543         {
8544           *image=DestroyImage(*image);
8545           *image=implode_image;
8546         }
8547       CatchException(&(*image)->exception);
8548       XSetCursorState(display,windows,MagickFalse);
8549       if (windows->image.orphan != MagickFalse)
8550         break;
8551       XConfigureImageColormap(display,resource_info,windows,*image);
8552       (void) XConfigureImage(display,resource_info,windows,*image);
8553       break;
8554     }
8555     case VignetteCommand:
8556     {
8557       Image
8558         *vignette_image;
8559
8560       static char
8561         geometry[MaxTextExtent] = "0x20";
8562
8563       /*
8564         Query user for the vignette geometry.
8565       */
8566       (void) XDialogWidget(display,windows,"Vignette",
8567         "Enter the radius, sigma, and x and y offsets:",geometry);
8568       if (*geometry == '\0')
8569         break;
8570       /*
8571         Soften the edges of the image in vignette style
8572       */
8573       XSetCursorState(display,windows,MagickTrue);
8574       XCheckRefreshWindows(display,windows);
8575       flags=ParseGeometry(geometry,&geometry_info);
8576       if ((flags & SigmaValue) == 0)
8577         geometry_info.sigma=1.0;
8578       if ((flags & XiValue) == 0)
8579         geometry_info.xi=0.1*(*image)->columns;
8580       if ((flags & PsiValue) == 0)
8581         geometry_info.psi=0.1*(*image)->rows;
8582       vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8583         (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8584         0.5),&(*image)->exception);
8585       if (vignette_image != (Image *) NULL)
8586         {
8587           *image=DestroyImage(*image);
8588           *image=vignette_image;
8589         }
8590       CatchException(&(*image)->exception);
8591       XSetCursorState(display,windows,MagickFalse);
8592       if (windows->image.orphan != MagickFalse)
8593         break;
8594       XConfigureImageColormap(display,resource_info,windows,*image);
8595       (void) XConfigureImage(display,resource_info,windows,*image);
8596       break;
8597     }
8598     case WaveCommand:
8599     {
8600       Image
8601         *wave_image;
8602
8603       static char
8604         geometry[MaxTextExtent] = "25x150";
8605
8606       /*
8607         Query user for the wave geometry.
8608       */
8609       (void) XDialogWidget(display,windows,"Wave",
8610         "Enter the amplitude and length of the wave:",geometry);
8611       if (*geometry == '\0')
8612         break;
8613       /*
8614         Alter an image along a sine wave.
8615       */
8616       XSetCursorState(display,windows,MagickTrue);
8617       XCheckRefreshWindows(display,windows);
8618       flags=ParseGeometry(geometry,&geometry_info);
8619       if ((flags & SigmaValue) == 0)
8620         geometry_info.sigma=1.0;
8621       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8622         &(*image)->exception);
8623       if (wave_image != (Image *) NULL)
8624         {
8625           *image=DestroyImage(*image);
8626           *image=wave_image;
8627         }
8628       CatchException(&(*image)->exception);
8629       XSetCursorState(display,windows,MagickFalse);
8630       if (windows->image.orphan != MagickFalse)
8631         break;
8632       XConfigureImageColormap(display,resource_info,windows,*image);
8633       (void) XConfigureImage(display,resource_info,windows,*image);
8634       break;
8635     }
8636     case OilPaintCommand:
8637     {
8638       Image
8639         *paint_image;
8640
8641       static char
8642         radius[MaxTextExtent] = "0";
8643
8644       /*
8645         Query user for circular neighborhood radius.
8646       */
8647       (void) XDialogWidget(display,windows,"Oil Paint",
8648         "Enter the mask radius:",radius);
8649       if (*radius == '\0')
8650         break;
8651       /*
8652         OilPaint image scanlines.
8653       */
8654       XSetCursorState(display,windows,MagickTrue);
8655       XCheckRefreshWindows(display,windows);
8656       flags=ParseGeometry(radius,&geometry_info);
8657       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8658         &(*image)->exception);
8659       if (paint_image != (Image *) NULL)
8660         {
8661           *image=DestroyImage(*image);
8662           *image=paint_image;
8663         }
8664       CatchException(&(*image)->exception);
8665       XSetCursorState(display,windows,MagickFalse);
8666       if (windows->image.orphan != MagickFalse)
8667         break;
8668       XConfigureImageColormap(display,resource_info,windows,*image);
8669       (void) XConfigureImage(display,resource_info,windows,*image);
8670       break;
8671     }
8672     case CharcoalDrawCommand:
8673     {
8674       Image
8675         *charcoal_image;
8676
8677       static char
8678         radius[MaxTextExtent] = "0x1";
8679
8680       /*
8681         Query user for charcoal radius.
8682       */
8683       (void) XDialogWidget(display,windows,"Charcoal Draw",
8684         "Enter the charcoal radius and sigma:",radius);
8685       if (*radius == '\0')
8686         break;
8687       /*
8688         Charcoal the image.
8689       */
8690       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8691       XSetCursorState(display,windows,MagickTrue);
8692       XCheckRefreshWindows(display,windows);
8693       flags=ParseGeometry(radius,&geometry_info);
8694       if ((flags & SigmaValue) == 0)
8695         geometry_info.sigma=geometry_info.rho;
8696       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8697         &(*image)->exception);
8698       if (charcoal_image != (Image *) NULL)
8699         {
8700           *image=DestroyImage(*image);
8701           *image=charcoal_image;
8702         }
8703       CatchException(&(*image)->exception);
8704       XSetCursorState(display,windows,MagickFalse);
8705       if (windows->image.orphan != MagickFalse)
8706         break;
8707       XConfigureImageColormap(display,resource_info,windows,*image);
8708       (void) XConfigureImage(display,resource_info,windows,*image);
8709       break;
8710     }
8711     case AnnotateCommand:
8712     {
8713       /*
8714         Annotate the image with text.
8715       */
8716       status=XAnnotateEditImage(display,resource_info,windows,*image);
8717       if (status == MagickFalse)
8718         {
8719           XNoticeWidget(display,windows,"Unable to annotate X image",
8720             (*image)->filename);
8721           break;
8722         }
8723       break;
8724     }
8725     case DrawCommand:
8726     {
8727       /*
8728         Draw image.
8729       */
8730       status=XDrawEditImage(display,resource_info,windows,image);
8731       if (status == MagickFalse)
8732         {
8733           XNoticeWidget(display,windows,"Unable to draw on the X image",
8734             (*image)->filename);
8735           break;
8736         }
8737       break;
8738     }
8739     case ColorCommand:
8740     {
8741       /*
8742         Color edit.
8743       */
8744       status=XColorEditImage(display,resource_info,windows,image);
8745       if (status == MagickFalse)
8746         {
8747           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8748             (*image)->filename);
8749           break;
8750         }
8751       break;
8752     }
8753     case MatteCommand:
8754     {
8755       /*
8756         Matte edit.
8757       */
8758       status=XMatteEditImage(display,resource_info,windows,image);
8759       if (status == MagickFalse)
8760         {
8761           XNoticeWidget(display,windows,"Unable to matte edit X image",
8762             (*image)->filename);
8763           break;
8764         }
8765       break;
8766     }
8767     case CompositeCommand:
8768     {
8769       /*
8770         Composite image.
8771       */
8772       status=XCompositeImage(display,resource_info,windows,*image);
8773       if (status == MagickFalse)
8774         {
8775           XNoticeWidget(display,windows,"Unable to composite X image",
8776             (*image)->filename);
8777           break;
8778         }
8779       break;
8780     }
8781     case AddBorderCommand:
8782     {
8783       Image
8784         *border_image;
8785
8786       static char
8787         geometry[MaxTextExtent] = "6x6";
8788
8789       /*
8790         Query user for border color and geometry.
8791       */
8792       XColorBrowserWidget(display,windows,"Select",color);
8793       if (*color == '\0')
8794         break;
8795       (void) XDialogWidget(display,windows,"Add Border",
8796         "Enter border geometry:",geometry);
8797       if (*geometry == '\0')
8798         break;
8799       /*
8800         Add a border to the image.
8801       */
8802       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8803       XSetCursorState(display,windows,MagickTrue);
8804       XCheckRefreshWindows(display,windows);
8805       (void) QueryColorDatabase(color,&(*image)->border_color,
8806         &(*image)->exception);
8807       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8808         &(*image)->exception);
8809       border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8810       if (border_image != (Image *) NULL)
8811         {
8812           *image=DestroyImage(*image);
8813           *image=border_image;
8814         }
8815       CatchException(&(*image)->exception);
8816       XSetCursorState(display,windows,MagickFalse);
8817       if (windows->image.orphan != MagickFalse)
8818         break;
8819       windows->image.window_changes.width=(int) (*image)->columns;
8820       windows->image.window_changes.height=(int) (*image)->rows;
8821       XConfigureImageColormap(display,resource_info,windows,*image);
8822       (void) XConfigureImage(display,resource_info,windows,*image);
8823       break;
8824     }
8825     case AddFrameCommand:
8826     {
8827       FrameInfo
8828         frame_info;
8829
8830       Image
8831         *frame_image;
8832
8833       static char
8834         geometry[MaxTextExtent] = "6x6";
8835
8836       /*
8837         Query user for frame color and geometry.
8838       */
8839       XColorBrowserWidget(display,windows,"Select",color);
8840       if (*color == '\0')
8841         break;
8842       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8843         geometry);
8844       if (*geometry == '\0')
8845         break;
8846       /*
8847         Surround image with an ornamental border.
8848       */
8849       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8850       XSetCursorState(display,windows,MagickTrue);
8851       XCheckRefreshWindows(display,windows);
8852       (void) QueryColorDatabase(color,&(*image)->matte_color,
8853         &(*image)->exception);
8854       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8855         &(*image)->exception);
8856       frame_info.width=page_geometry.width;
8857       frame_info.height=page_geometry.height;
8858       frame_info.outer_bevel=page_geometry.x;
8859       frame_info.inner_bevel=page_geometry.y;
8860       frame_info.x=(ssize_t) frame_info.width;
8861       frame_info.y=(ssize_t) frame_info.height;
8862       frame_info.width=(*image)->columns+2*frame_info.width;
8863       frame_info.height=(*image)->rows+2*frame_info.height;
8864       frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8865       if (frame_image != (Image *) NULL)
8866         {
8867           *image=DestroyImage(*image);
8868           *image=frame_image;
8869         }
8870       CatchException(&(*image)->exception);
8871       XSetCursorState(display,windows,MagickFalse);
8872       if (windows->image.orphan != MagickFalse)
8873         break;
8874       windows->image.window_changes.width=(int) (*image)->columns;
8875       windows->image.window_changes.height=(int) (*image)->rows;
8876       XConfigureImageColormap(display,resource_info,windows,*image);
8877       (void) XConfigureImage(display,resource_info,windows,*image);
8878       break;
8879     }
8880     case CommentCommand:
8881     {
8882       const char
8883         *value;
8884
8885       FILE
8886         *file;
8887
8888       int
8889         unique_file;
8890
8891       /*
8892         Edit image comment.
8893       */
8894       unique_file=AcquireUniqueFileResource(image_info->filename);
8895       if (unique_file == -1)
8896         XNoticeWidget(display,windows,"Unable to edit image comment",
8897           image_info->filename);
8898       value=GetImageProperty(*image,"comment");
8899       if (value == (char *) NULL)
8900         unique_file=close(unique_file)-1;
8901       else
8902         {
8903           register const char
8904             *p;
8905
8906           file=fdopen(unique_file,"w");
8907           if (file == (FILE *) NULL)
8908             {
8909               XNoticeWidget(display,windows,"Unable to edit image comment",
8910                 image_info->filename);
8911               break;
8912             }
8913           for (p=value; *p != '\0'; p++)
8914             (void) fputc((int) *p,file);
8915           (void) fputc('\n',file);
8916           (void) fclose(file);
8917         }
8918       XSetCursorState(display,windows,MagickTrue);
8919       XCheckRefreshWindows(display,windows);
8920       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8921         &(*image)->exception);
8922       if (status == MagickFalse)
8923         XNoticeWidget(display,windows,"Unable to edit image comment",
8924           (char *) NULL);
8925       else
8926         {
8927           char
8928             *comment;
8929
8930           comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8931           if (comment != (char *) NULL)
8932             {
8933               (void) SetImageProperty(*image,"comment",comment);
8934               (*image)->taint=MagickTrue;
8935             }
8936         }
8937       (void) RelinquishUniqueFileResource(image_info->filename);
8938       XSetCursorState(display,windows,MagickFalse);
8939       break;
8940     }
8941     case LaunchCommand:
8942     {
8943       /*
8944         Launch program.
8945       */
8946       XSetCursorState(display,windows,MagickTrue);
8947       XCheckRefreshWindows(display,windows);
8948       (void) AcquireUniqueFilename(filename);
8949       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
8950         filename);
8951       status=WriteImage(image_info,*image,&(*image)->exception);
8952       if (status == MagickFalse)
8953         XNoticeWidget(display,windows,"Unable to launch image editor",
8954           (char *) NULL);
8955       else
8956         {
8957           nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8958           CatchException(&(*image)->exception);
8959           XClientMessage(display,windows->image.id,windows->im_protocols,
8960             windows->im_next_image,CurrentTime);
8961         }
8962       (void) RelinquishUniqueFileResource(filename);
8963       XSetCursorState(display,windows,MagickFalse);
8964       break;
8965     }
8966     case RegionofInterestCommand:
8967     {
8968       /*
8969         Apply an image processing technique to a region of interest.
8970       */
8971       (void) XROIImage(display,resource_info,windows,image);
8972       break;
8973     }
8974     case InfoCommand:
8975       break;
8976     case ZoomCommand:
8977     {
8978       /*
8979         Zoom image.
8980       */
8981       if (windows->magnify.mapped != MagickFalse)
8982         (void) XRaiseWindow(display,windows->magnify.id);
8983       else
8984         {
8985           /*
8986             Make magnify image.
8987           */
8988           XSetCursorState(display,windows,MagickTrue);
8989           (void) XMapRaised(display,windows->magnify.id);
8990           XSetCursorState(display,windows,MagickFalse);
8991         }
8992       break;
8993     }
8994     case ShowPreviewCommand:
8995     {
8996       char
8997         **previews;
8998
8999       Image
9000         *preview_image;
9001
9002       static char
9003         preview_type[MaxTextExtent] = "Gamma";
9004
9005       /*
9006         Select preview type from menu.
9007       */
9008       previews=GetCommandOptions(MagickPreviewOptions);
9009       if (previews == (char **) NULL)
9010         break;
9011       XListBrowserWidget(display,windows,&windows->widget,
9012         (const char **) previews,"Preview",
9013         "Select an enhancement, effect, or F/X:",preview_type);
9014       previews=DestroyStringList(previews);
9015       if (*preview_type == '\0')
9016         break;
9017       /*
9018         Show image preview.
9019       */
9020       XSetCursorState(display,windows,MagickTrue);
9021       XCheckRefreshWindows(display,windows);
9022       image_info->preview_type=(PreviewType)
9023         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9024       image_info->group=(ssize_t) windows->image.id;
9025       (void) DeleteImageProperty(*image,"label");
9026       (void) SetImageProperty(*image,"label","Preview");
9027       (void) AcquireUniqueFilename(filename);
9028       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9029         filename);
9030       status=WriteImage(image_info,*image,&(*image)->exception);
9031       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9032       preview_image=ReadImage(image_info,&(*image)->exception);
9033       (void) RelinquishUniqueFileResource(filename);
9034       if (preview_image == (Image *) NULL)
9035         break;
9036       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9037         filename);
9038       status=WriteImage(image_info,preview_image,&(*image)->exception);
9039       preview_image=DestroyImage(preview_image);
9040       if (status == MagickFalse)
9041         XNoticeWidget(display,windows,"Unable to show image preview",
9042           (*image)->filename);
9043       XDelay(display,1500);
9044       XSetCursorState(display,windows,MagickFalse);
9045       break;
9046     }
9047     case ShowHistogramCommand:
9048     {
9049       Image
9050         *histogram_image;
9051
9052       /*
9053         Show image histogram.
9054       */
9055       XSetCursorState(display,windows,MagickTrue);
9056       XCheckRefreshWindows(display,windows);
9057       image_info->group=(ssize_t) windows->image.id;
9058       (void) DeleteImageProperty(*image,"label");
9059       (void) SetImageProperty(*image,"label","Histogram");
9060       (void) AcquireUniqueFilename(filename);
9061       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9062         filename);
9063       status=WriteImage(image_info,*image,&(*image)->exception);
9064       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9065       histogram_image=ReadImage(image_info,&(*image)->exception);
9066       (void) RelinquishUniqueFileResource(filename);
9067       if (histogram_image == (Image *) NULL)
9068         break;
9069       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9070         "show:%s",filename);
9071       status=WriteImage(image_info,histogram_image,&(*image)->exception);
9072       histogram_image=DestroyImage(histogram_image);
9073       if (status == MagickFalse)
9074         XNoticeWidget(display,windows,"Unable to show histogram",
9075           (*image)->filename);
9076       XDelay(display,1500);
9077       XSetCursorState(display,windows,MagickFalse);
9078       break;
9079     }
9080     case ShowMatteCommand:
9081     {
9082       Image
9083         *matte_image;
9084
9085       if ((*image)->matte == MagickFalse)
9086         {
9087           XNoticeWidget(display,windows,
9088             "Image does not have any matte information",(*image)->filename);
9089           break;
9090         }
9091       /*
9092         Show image matte.
9093       */
9094       XSetCursorState(display,windows,MagickTrue);
9095       XCheckRefreshWindows(display,windows);
9096       image_info->group=(ssize_t) windows->image.id;
9097       (void) DeleteImageProperty(*image,"label");
9098       (void) SetImageProperty(*image,"label","Matte");
9099       (void) AcquireUniqueFilename(filename);
9100       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9101         filename);
9102       status=WriteImage(image_info,*image,&(*image)->exception);
9103       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9104       matte_image=ReadImage(image_info,&(*image)->exception);
9105       (void) RelinquishUniqueFileResource(filename);
9106       if (matte_image == (Image *) NULL)
9107         break;
9108       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9109         filename);
9110       status=WriteImage(image_info,matte_image,&(*image)->exception);
9111       matte_image=DestroyImage(matte_image);
9112       if (status == MagickFalse)
9113         XNoticeWidget(display,windows,"Unable to show matte",
9114           (*image)->filename);
9115       XDelay(display,1500);
9116       XSetCursorState(display,windows,MagickFalse);
9117       break;
9118     }
9119     case BackgroundCommand:
9120     {
9121       /*
9122         Background image.
9123       */
9124       status=XBackgroundImage(display,resource_info,windows,image);
9125       if (status == MagickFalse)
9126         break;
9127       nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9128       if (nexus != (Image *) NULL)
9129         XClientMessage(display,windows->image.id,windows->im_protocols,
9130           windows->im_next_image,CurrentTime);
9131       break;
9132     }
9133     case SlideShowCommand:
9134     {
9135       static char
9136         delay[MaxTextExtent] = "5";
9137
9138       /*
9139         Display next image after pausing.
9140       */
9141       (void) XDialogWidget(display,windows,"Slide Show",
9142         "Pause how many 1/100ths of a second between images:",delay);
9143       if (*delay == '\0')
9144         break;
9145       resource_info->delay=StringToUnsignedLong(delay);
9146       XClientMessage(display,windows->image.id,windows->im_protocols,
9147         windows->im_next_image,CurrentTime);
9148       break;
9149     }
9150     case PreferencesCommand:
9151     {
9152       /*
9153         Set user preferences.
9154       */
9155       status=XPreferencesWidget(display,resource_info,windows);
9156       if (status == MagickFalse)
9157         break;
9158       nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9159       if (nexus != (Image *) NULL)
9160         XClientMessage(display,windows->image.id,windows->im_protocols,
9161           windows->im_next_image,CurrentTime);
9162       break;
9163     }
9164     case HelpCommand:
9165     {
9166       /*
9167         User requested help.
9168       */
9169       XTextViewWidget(display,resource_info,windows,MagickFalse,
9170         "Help Viewer - Display",DisplayHelp);
9171       break;
9172     }
9173     case BrowseDocumentationCommand:
9174     {
9175       Atom
9176         mozilla_atom;
9177
9178       Window
9179         mozilla_window,
9180         root_window;
9181
9182       /*
9183         Browse the ImageMagick documentation.
9184       */
9185       root_window=XRootWindow(display,XDefaultScreen(display));
9186       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9187       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9188       if (mozilla_window != (Window) NULL)
9189         {
9190           char
9191             command[MaxTextExtent],
9192             *url;
9193
9194           /*
9195             Display documentation using Netscape remote control.
9196           */
9197           url=GetMagickHomeURL();
9198           (void) FormatLocaleString(command,MaxTextExtent,
9199             "openurl(%s,new-tab)",url);
9200           url=DestroyString(url);
9201           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9202           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9203             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9204           XSetCursorState(display,windows,MagickFalse);
9205           break;
9206         }
9207       XSetCursorState(display,windows,MagickTrue);
9208       XCheckRefreshWindows(display,windows);
9209       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9210         &(*image)->exception);
9211       if (status == MagickFalse)
9212         XNoticeWidget(display,windows,"Unable to browse documentation",
9213           (char *) NULL);
9214       XDelay(display,1500);
9215       XSetCursorState(display,windows,MagickFalse);
9216       break;
9217     }
9218     case VersionCommand:
9219     {
9220       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9221         GetMagickCopyright());
9222       break;
9223     }
9224     case SaveToUndoBufferCommand:
9225       break;
9226     default:
9227     {
9228       (void) XBell(display,0);
9229       break;
9230     }
9231   }
9232   image_info=DestroyImageInfo(image_info);
9233   return(nexus);
9234 }
9235 \f
9236 /*
9237 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9238 %                                                                             %
9239 %                                                                             %
9240 %                                                                             %
9241 +   X M a g n i f y I m a g e                                                 %
9242 %                                                                             %
9243 %                                                                             %
9244 %                                                                             %
9245 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9246 %
9247 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9248 %  The magnified portion is displayed in a separate window.
9249 %
9250 %  The format of the XMagnifyImage method is:
9251 %
9252 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9253 %
9254 %  A description of each parameter follows:
9255 %
9256 %    o display: Specifies a connection to an X server;  returned from
9257 %      XOpenDisplay.
9258 %
9259 %    o windows: Specifies a pointer to a XWindows structure.
9260 %
9261 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9262 %      the entire image is refreshed.
9263 %
9264 */
9265 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9266 {
9267   char
9268     text[MaxTextExtent];
9269
9270   register int
9271     x,
9272     y;
9273
9274   size_t
9275     state;
9276
9277   /*
9278     Update magnified image until the mouse button is released.
9279   */
9280   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9281   state=DefaultState;
9282   x=event->xbutton.x;
9283   y=event->xbutton.y;
9284   windows->magnify.x=(int) windows->image.x+x;
9285   windows->magnify.y=(int) windows->image.y+y;
9286   do
9287   {
9288     /*
9289       Map and unmap Info widget as text cursor crosses its boundaries.
9290     */
9291     if (windows->info.mapped != MagickFalse)
9292       {
9293         if ((x < (int) (windows->info.x+windows->info.width)) &&
9294             (y < (int) (windows->info.y+windows->info.height)))
9295           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9296       }
9297     else
9298       if ((x > (int) (windows->info.x+windows->info.width)) ||
9299           (y > (int) (windows->info.y+windows->info.height)))
9300         (void) XMapWindow(display,windows->info.id);
9301     if (windows->info.mapped != MagickFalse)
9302       {
9303         /*
9304           Display pointer position.
9305         */
9306         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9307           windows->magnify.x,windows->magnify.y);
9308         XInfoWidget(display,windows,text);
9309       }
9310     /*
9311       Wait for next event.
9312     */
9313     XScreenEvent(display,windows,event);
9314     switch (event->type)
9315     {
9316       case ButtonPress:
9317         break;
9318       case ButtonRelease:
9319       {
9320         /*
9321           User has finished magnifying image.
9322         */
9323         x=event->xbutton.x;
9324         y=event->xbutton.y;
9325         state|=ExitState;
9326         break;
9327       }
9328       case Expose:
9329         break;
9330       case MotionNotify:
9331       {
9332         x=event->xmotion.x;
9333         y=event->xmotion.y;
9334         break;
9335       }
9336       default:
9337         break;
9338     }
9339     /*
9340       Check boundary conditions.
9341     */
9342     if (x < 0)
9343       x=0;
9344     else
9345       if (x >= (int) windows->image.width)
9346         x=(int) windows->image.width-1;
9347     if (y < 0)
9348       y=0;
9349     else
9350      if (y >= (int) windows->image.height)
9351        y=(int) windows->image.height-1;
9352   } while ((state & ExitState) == 0);
9353   /*
9354     Display magnified image.
9355   */
9356   XSetCursorState(display,windows,MagickFalse);
9357 }
9358 \f
9359 /*
9360 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9361 %                                                                             %
9362 %                                                                             %
9363 %                                                                             %
9364 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9365 %                                                                             %
9366 %                                                                             %
9367 %                                                                             %
9368 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9369 %
9370 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9371 %  pixel as specified by the key symbol.
9372 %
9373 %  The format of the XMagnifyWindowCommand method is:
9374 %
9375 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9376 %        const MagickStatusType state,const KeySym key_symbol)
9377 %
9378 %  A description of each parameter follows:
9379 %
9380 %    o display: Specifies a connection to an X server; returned from
9381 %      XOpenDisplay.
9382 %
9383 %    o windows: Specifies a pointer to a XWindows structure.
9384 %
9385 %    o state: key mask.
9386 %
9387 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9388 %      to trim.
9389 %
9390 */
9391 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9392   const MagickStatusType state,const KeySym key_symbol)
9393 {
9394   unsigned int
9395     quantum;
9396
9397   /*
9398     User specified a magnify factor or position.
9399   */
9400   quantum=1;
9401   if ((state & Mod1Mask) != 0)
9402     quantum=10;
9403   switch ((int) key_symbol)
9404   {
9405     case QuitCommand:
9406     {
9407       (void) XWithdrawWindow(display,windows->magnify.id,
9408         windows->magnify.screen);
9409       break;
9410     }
9411     case XK_Home:
9412     case XK_KP_Home:
9413     {
9414       windows->magnify.x=(int) windows->image.width/2;
9415       windows->magnify.y=(int) windows->image.height/2;
9416       break;
9417     }
9418     case XK_Left:
9419     case XK_KP_Left:
9420     {
9421       if (windows->magnify.x > 0)
9422         windows->magnify.x-=quantum;
9423       break;
9424     }
9425     case XK_Up:
9426     case XK_KP_Up:
9427     {
9428       if (windows->magnify.y > 0)
9429         windows->magnify.y-=quantum;
9430       break;
9431     }
9432     case XK_Right:
9433     case XK_KP_Right:
9434     {
9435       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9436         windows->magnify.x+=quantum;
9437       break;
9438     }
9439     case XK_Down:
9440     case XK_KP_Down:
9441     {
9442       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9443         windows->magnify.y+=quantum;
9444       break;
9445     }
9446     case XK_0:
9447     case XK_1:
9448     case XK_2:
9449     case XK_3:
9450     case XK_4:
9451     case XK_5:
9452     case XK_6:
9453     case XK_7:
9454     case XK_8:
9455     case XK_9:
9456     {
9457       windows->magnify.data=(key_symbol-XK_0);
9458       break;
9459     }
9460     case XK_KP_0:
9461     case XK_KP_1:
9462     case XK_KP_2:
9463     case XK_KP_3:
9464     case XK_KP_4:
9465     case XK_KP_5:
9466     case XK_KP_6:
9467     case XK_KP_7:
9468     case XK_KP_8:
9469     case XK_KP_9:
9470     {
9471       windows->magnify.data=(key_symbol-XK_KP_0);
9472       break;
9473     }
9474     default:
9475       break;
9476   }
9477   XMakeMagnifyImage(display,windows);
9478 }
9479 \f
9480 /*
9481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9482 %                                                                             %
9483 %                                                                             %
9484 %                                                                             %
9485 +   X M a k e P a n I m a g e                                                 %
9486 %                                                                             %
9487 %                                                                             %
9488 %                                                                             %
9489 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9490 %
9491 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9492 %  icon window.
9493 %
9494 %  The format of the XMakePanImage method is:
9495 %
9496 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9497 %          XWindows *windows,Image *image)
9498 %
9499 %  A description of each parameter follows:
9500 %
9501 %    o display: Specifies a connection to an X server;  returned from
9502 %      XOpenDisplay.
9503 %
9504 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9505 %
9506 %    o windows: Specifies a pointer to a XWindows structure.
9507 %
9508 %    o image: the image.
9509 %
9510 */
9511 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9512   XWindows *windows,Image *image)
9513 {
9514   MagickStatusType
9515     status;
9516
9517   /*
9518     Create and display image for panning icon.
9519   */
9520   XSetCursorState(display,windows,MagickTrue);
9521   XCheckRefreshWindows(display,windows);
9522   windows->pan.x=(int) windows->image.x;
9523   windows->pan.y=(int) windows->image.y;
9524   status=XMakeImage(display,resource_info,&windows->pan,image,
9525     windows->pan.width,windows->pan.height);
9526   if (status == MagickFalse)
9527     ThrowXWindowFatalException(XServerError,image->exception.reason,
9528       image->exception.description);
9529   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9530     windows->pan.pixmap);
9531   (void) XClearWindow(display,windows->pan.id);
9532   XDrawPanRectangle(display,windows);
9533   XSetCursorState(display,windows,MagickFalse);
9534 }
9535 \f
9536 /*
9537 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9538 %                                                                             %
9539 %                                                                             %
9540 %                                                                             %
9541 +   X M a t t a E d i t I m a g e                                             %
9542 %                                                                             %
9543 %                                                                             %
9544 %                                                                             %
9545 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9546 %
9547 %  XMatteEditImage() allows the user to interactively change the Matte channel
9548 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9549 %  before the matte information is stored.
9550 %
9551 %  The format of the XMatteEditImage method is:
9552 %
9553 %      MagickBooleanType XMatteEditImage(Display *display,
9554 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
9555 %
9556 %  A description of each parameter follows:
9557 %
9558 %    o display: Specifies a connection to an X server;  returned from
9559 %      XOpenDisplay.
9560 %
9561 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9562 %
9563 %    o windows: Specifies a pointer to a XWindows structure.
9564 %
9565 %    o image: the image; returned from ReadImage.
9566 %
9567 */
9568 static MagickBooleanType XMatteEditImage(Display *display,
9569   XResourceInfo *resource_info,XWindows *windows,Image **image)
9570 {
9571   static char
9572     matte[MaxTextExtent] = "0";
9573
9574   static const char
9575     *MatteEditMenu[] =
9576     {
9577       "Method",
9578       "Border Color",
9579       "Fuzz",
9580       "Matte Value",
9581       "Undo",
9582       "Help",
9583       "Dismiss",
9584       (char *) NULL
9585     };
9586
9587   static const ModeType
9588     MatteEditCommands[] =
9589     {
9590       MatteEditMethod,
9591       MatteEditBorderCommand,
9592       MatteEditFuzzCommand,
9593       MatteEditValueCommand,
9594       MatteEditUndoCommand,
9595       MatteEditHelpCommand,
9596       MatteEditDismissCommand
9597     };
9598
9599   static PaintMethod
9600     method = PointMethod;
9601
9602   static XColor
9603     border_color = { 0, 0, 0, 0, 0, 0 };
9604
9605   char
9606     command[MaxTextExtent],
9607     text[MaxTextExtent];
9608
9609   Cursor
9610     cursor;
9611
9612   int
9613     entry,
9614     id,
9615     x,
9616     x_offset,
9617     y,
9618     y_offset;
9619
9620   register int
9621     i;
9622
9623   register Quantum
9624     *q;
9625
9626   unsigned int
9627     height,
9628     width;
9629
9630   size_t
9631     state;
9632
9633   XEvent
9634     event;
9635
9636   /*
9637     Map Command widget.
9638   */
9639   (void) CloneString(&windows->command.name,"Matte Edit");
9640   windows->command.data=4;
9641   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9642   (void) XMapRaised(display,windows->command.id);
9643   XClientMessage(display,windows->image.id,windows->im_protocols,
9644     windows->im_update_widget,CurrentTime);
9645   /*
9646     Make cursor.
9647   */
9648   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9649     resource_info->background_color,resource_info->foreground_color);
9650   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9651   /*
9652     Track pointer until button 1 is pressed.
9653   */
9654   XQueryPosition(display,windows->image.id,&x,&y);
9655   (void) XSelectInput(display,windows->image.id,
9656     windows->image.attributes.event_mask | PointerMotionMask);
9657   state=DefaultState;
9658   do
9659   {
9660     if (windows->info.mapped != MagickFalse)
9661       {
9662         /*
9663           Display pointer position.
9664         */
9665         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9666           x+windows->image.x,y+windows->image.y);
9667         XInfoWidget(display,windows,text);
9668       }
9669     /*
9670       Wait for next event.
9671     */
9672     XScreenEvent(display,windows,&event);
9673     if (event.xany.window == windows->command.id)
9674       {
9675         /*
9676           Select a command from the Command widget.
9677         */
9678         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9679         if (id < 0)
9680           {
9681             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9682             continue;
9683           }
9684         switch (MatteEditCommands[id])
9685         {
9686           case MatteEditMethod:
9687           {
9688             char
9689               **methods;
9690
9691             /*
9692               Select a method from the pop-up menu.
9693             */
9694             methods=GetCommandOptions(MagickMethodOptions);
9695             if (methods == (char **) NULL)
9696               break;
9697             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9698               (const char **) methods,command);
9699             if (entry >= 0)
9700               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9701                 MagickFalse,methods[entry]);
9702             methods=DestroyStringList(methods);
9703             break;
9704           }
9705           case MatteEditBorderCommand:
9706           {
9707             const char
9708               *ColorMenu[MaxNumberPens];
9709
9710             int
9711               pen_number;
9712
9713             /*
9714               Initialize menu selections.
9715             */
9716             for (i=0; i < (int) (MaxNumberPens-2); i++)
9717               ColorMenu[i]=resource_info->pen_colors[i];
9718             ColorMenu[MaxNumberPens-2]="Browser...";
9719             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9720             /*
9721               Select a pen color from the pop-up menu.
9722             */
9723             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9724               (const char **) ColorMenu,command);
9725             if (pen_number < 0)
9726               break;
9727             if (pen_number == (MaxNumberPens-2))
9728               {
9729                 static char
9730                   color_name[MaxTextExtent] = "gray";
9731
9732                 /*
9733                   Select a pen color from a dialog.
9734                 */
9735                 resource_info->pen_colors[pen_number]=color_name;
9736                 XColorBrowserWidget(display,windows,"Select",color_name);
9737                 if (*color_name == '\0')
9738                   break;
9739               }
9740             /*
9741               Set border color.
9742             */
9743             (void) XParseColor(display,windows->map_info->colormap,
9744               resource_info->pen_colors[pen_number],&border_color);
9745             break;
9746           }
9747           case MatteEditFuzzCommand:
9748           {
9749             static char
9750               fuzz[MaxTextExtent];
9751
9752             static const char
9753               *FuzzMenu[] =
9754               {
9755                 "0%",
9756                 "2%",
9757                 "5%",
9758                 "10%",
9759                 "15%",
9760                 "Dialog...",
9761                 (char *) NULL,
9762               };
9763
9764             /*
9765               Select a command from the pop-up menu.
9766             */
9767             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9768               command);
9769             if (entry < 0)
9770               break;
9771             if (entry != 5)
9772               {
9773                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
9774                   QuantumRange+1.0);
9775                 break;
9776               }
9777             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9778             (void) XDialogWidget(display,windows,"Ok",
9779               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9780             if (*fuzz == '\0')
9781               break;
9782             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9783             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9784             break;
9785           }
9786           case MatteEditValueCommand:
9787           {
9788             static char
9789               message[MaxTextExtent];
9790
9791             static const char
9792               *MatteMenu[] =
9793               {
9794                 "Opaque",
9795                 "Transparent",
9796                 "Dialog...",
9797                 (char *) NULL,
9798               };
9799
9800             /*
9801               Select a command from the pop-up menu.
9802             */
9803             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9804               command);
9805             if (entry < 0)
9806               break;
9807             if (entry != 2)
9808               {
9809                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9810                   OpaqueAlpha);
9811                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9812                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9813                     (Quantum) TransparentAlpha);
9814                 break;
9815               }
9816             (void) FormatLocaleString(message,MaxTextExtent,
9817               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9818               QuantumRange);
9819             (void) XDialogWidget(display,windows,"Matte",message,matte);
9820             if (*matte == '\0')
9821               break;
9822             break;
9823           }
9824           case MatteEditUndoCommand:
9825           {
9826             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9827               image);
9828             break;
9829           }
9830           case MatteEditHelpCommand:
9831           {
9832             XTextViewWidget(display,resource_info,windows,MagickFalse,
9833               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9834             break;
9835           }
9836           case MatteEditDismissCommand:
9837           {
9838             /*
9839               Prematurely exit.
9840             */
9841             state|=EscapeState;
9842             state|=ExitState;
9843             break;
9844           }
9845           default:
9846             break;
9847         }
9848         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9849         continue;
9850       }
9851     switch (event.type)
9852     {
9853       case ButtonPress:
9854       {
9855         if (event.xbutton.button != Button1)
9856           break;
9857         if ((event.xbutton.window != windows->image.id) &&
9858             (event.xbutton.window != windows->magnify.id))
9859           break;
9860         /*
9861           Update matte data.
9862         */
9863         x=event.xbutton.x;
9864         y=event.xbutton.y;
9865         (void) XMagickCommand(display,resource_info,windows,
9866           SaveToUndoBufferCommand,image);
9867         state|=UpdateConfigurationState;
9868         break;
9869       }
9870       case ButtonRelease:
9871       {
9872         if (event.xbutton.button != Button1)
9873           break;
9874         if ((event.xbutton.window != windows->image.id) &&
9875             (event.xbutton.window != windows->magnify.id))
9876           break;
9877         /*
9878           Update colormap information.
9879         */
9880         x=event.xbutton.x;
9881         y=event.xbutton.y;
9882         XConfigureImageColormap(display,resource_info,windows,*image);
9883         (void) XConfigureImage(display,resource_info,windows,*image);
9884         XInfoWidget(display,windows,text);
9885         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9886         state&=(~UpdateConfigurationState);
9887         break;
9888       }
9889       case Expose:
9890         break;
9891       case KeyPress:
9892       {
9893         char
9894           command[MaxTextExtent];
9895
9896         KeySym
9897           key_symbol;
9898
9899         if (event.xkey.window == windows->magnify.id)
9900           {
9901             Window
9902               window;
9903
9904             window=windows->magnify.id;
9905             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9906           }
9907         if (event.xkey.window != windows->image.id)
9908           break;
9909         /*
9910           Respond to a user key press.
9911         */
9912         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9913           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9914         switch ((int) key_symbol)
9915         {
9916           case XK_Escape:
9917           case XK_F20:
9918           {
9919             /*
9920               Prematurely exit.
9921             */
9922             state|=ExitState;
9923             break;
9924           }
9925           case XK_F1:
9926           case XK_Help:
9927           {
9928             XTextViewWidget(display,resource_info,windows,MagickFalse,
9929               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9930             break;
9931           }
9932           default:
9933           {
9934             (void) XBell(display,0);
9935             break;
9936           }
9937         }
9938         break;
9939       }
9940       case MotionNotify:
9941       {
9942         /*
9943           Map and unmap Info widget as cursor crosses its boundaries.
9944         */
9945         x=event.xmotion.x;
9946         y=event.xmotion.y;
9947         if (windows->info.mapped != MagickFalse)
9948           {
9949             if ((x < (int) (windows->info.x+windows->info.width)) &&
9950                 (y < (int) (windows->info.y+windows->info.height)))
9951               (void) XWithdrawWindow(display,windows->info.id,
9952                 windows->info.screen);
9953           }
9954         else
9955           if ((x > (int) (windows->info.x+windows->info.width)) ||
9956               (y > (int) (windows->info.y+windows->info.height)))
9957             (void) XMapWindow(display,windows->info.id);
9958         break;
9959       }
9960       default:
9961         break;
9962     }
9963     if (event.xany.window == windows->magnify.id)
9964       {
9965         x=windows->magnify.x-windows->image.x;
9966         y=windows->magnify.y-windows->image.y;
9967       }
9968     x_offset=x;
9969     y_offset=y;
9970     if ((state & UpdateConfigurationState) != 0)
9971       {
9972         CacheView
9973           *image_view;
9974
9975         ExceptionInfo
9976           *exception;
9977
9978         int
9979           x,
9980           y;
9981
9982         /*
9983           Matte edit is relative to image configuration.
9984         */
9985         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9986           MagickTrue);
9987         XPutPixel(windows->image.ximage,x_offset,y_offset,
9988           windows->pixel_info->background_color.pixel);
9989         width=(unsigned int) (*image)->columns;
9990         height=(unsigned int) (*image)->rows;
9991         x=0;
9992         y=0;
9993         if (windows->image.crop_geometry != (char *) NULL)
9994           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
9995             &height);
9996         x_offset=(int) (width*(windows->image.x+x_offset)/
9997           windows->image.ximage->width+x);
9998         y_offset=(int) (height*(windows->image.y+y_offset)/
9999           windows->image.ximage->height+y);
10000         if ((x_offset < 0) || (y_offset < 0))
10001           continue;
10002         if ((x_offset >= (int) (*image)->columns) ||
10003             (y_offset >= (int) (*image)->rows))
10004           continue;
10005         exception=(&(*image)->exception);
10006         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10007           return(MagickFalse);
10008         (*image)->matte=MagickTrue;
10009         image_view=AcquireCacheView(*image);
10010         switch (method)
10011         {
10012           case PointMethod:
10013           default:
10014           {
10015             /*
10016               Update matte information using point algorithm.
10017             */
10018             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10019               (ssize_t) y_offset,1,1,exception);
10020             if (q == (Quantum *) NULL)
10021               break;
10022             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10023             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10024             break;
10025           }
10026           case ReplaceMethod:
10027           {
10028             PixelPacket
10029               pixel,
10030               target;
10031
10032             /*
10033               Update matte information using replace algorithm.
10034             */
10035             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10036               (ssize_t) y_offset,&target,exception);
10037             for (y=0; y < (int) (*image)->rows; y++)
10038             {
10039               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10040                 (*image)->columns,1,&(*image)->exception);
10041               if (q == (Quantum *) NULL)
10042                 break;
10043               for (x=0; x < (int) (*image)->columns; x++)
10044               {
10045                 GetPixelPacket(*image,q,&pixel);
10046                 if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
10047                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10048                 q+=GetPixelChannels(*image);
10049               }
10050               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10051                 break;
10052             }
10053             break;
10054           }
10055           case FloodfillMethod:
10056           case FillToBorderMethod:
10057           {
10058             ChannelType
10059               channel_mask;
10060
10061             DrawInfo
10062               *draw_info;
10063
10064             PixelInfo
10065               target;
10066
10067             /*
10068               Update matte information using floodfill algorithm.
10069             */
10070             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10071               (ssize_t) y_offset,&target,exception);
10072             if (method == FillToBorderMethod)
10073               {
10074                 target.red=(MagickRealType) ScaleShortToQuantum(
10075                   border_color.red);
10076                 target.green=(MagickRealType) ScaleShortToQuantum(
10077                   border_color.green);
10078                 target.blue=(MagickRealType) ScaleShortToQuantum(
10079                   border_color.blue);
10080               }
10081             draw_info=CloneDrawInfo(resource_info->image_info,
10082               (DrawInfo *) NULL);
10083             draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10084               (char **) NULL));
10085             channel_mask=SetPixelChannelMask(*image,AlphaChannel); 
10086             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10087               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10088               MagickFalse : MagickTrue,exception);
10089             (void) SetPixelChannelMap(*image,channel_mask);
10090             draw_info=DestroyDrawInfo(draw_info);
10091             break;
10092           }
10093           case ResetMethod:
10094           {
10095             /*
10096               Update matte information using reset algorithm.
10097             */
10098             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10099               return(MagickFalse);
10100             for (y=0; y < (int) (*image)->rows; y++)
10101             {
10102               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10103                 (*image)->columns,1,exception);
10104               if (q == (Quantum *) NULL)
10105                 break;
10106               for (x=0; x < (int) (*image)->columns; x++)
10107               {
10108                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10109                 q+=GetPixelChannels(*image);
10110               }
10111               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10112                 break;
10113             }
10114             if (StringToLong(matte) == (long) OpaqueAlpha)
10115               (*image)->matte=MagickFalse;
10116             break;
10117           }
10118         }
10119         image_view=DestroyCacheView(image_view);
10120         state&=(~UpdateConfigurationState);
10121       }
10122   } while ((state & ExitState) == 0);
10123   (void) XSelectInput(display,windows->image.id,
10124     windows->image.attributes.event_mask);
10125   XSetCursorState(display,windows,MagickFalse);
10126   (void) XFreeCursor(display,cursor);
10127   return(MagickTrue);
10128 }
10129 \f
10130 /*
10131 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10132 %                                                                             %
10133 %                                                                             %
10134 %                                                                             %
10135 +   X O p e n I m a g e                                                       %
10136 %                                                                             %
10137 %                                                                             %
10138 %                                                                             %
10139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10140 %
10141 %  XOpenImage() loads an image from a file.
10142 %
10143 %  The format of the XOpenImage method is:
10144 %
10145 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10146 %       XWindows *windows,const unsigned int command)
10147 %
10148 %  A description of each parameter follows:
10149 %
10150 %    o display: Specifies a connection to an X server; returned from
10151 %      XOpenDisplay.
10152 %
10153 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10154 %
10155 %    o windows: Specifies a pointer to a XWindows structure.
10156 %
10157 %    o command: A value other than zero indicates that the file is selected
10158 %      from the command line argument list.
10159 %
10160 */
10161 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10162   XWindows *windows,const MagickBooleanType command)
10163 {
10164   const MagickInfo
10165     *magick_info;
10166
10167   ExceptionInfo
10168     *exception;
10169
10170   Image
10171     *nexus;
10172
10173   ImageInfo
10174     *image_info;
10175
10176   static char
10177     filename[MaxTextExtent] = "\0";
10178
10179   /*
10180     Request file name from user.
10181   */
10182   if (command == MagickFalse)
10183     XFileBrowserWidget(display,windows,"Open",filename);
10184   else
10185     {
10186       char
10187         **filelist,
10188         **files;
10189
10190       int
10191         count,
10192         status;
10193
10194       register int
10195         i,
10196         j;
10197
10198       /*
10199         Select next image from the command line.
10200       */
10201       status=XGetCommand(display,windows->image.id,&files,&count);
10202       if (status == 0)
10203         {
10204           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10205           return((Image *) NULL);
10206         }
10207       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10208       if (filelist == (char **) NULL)
10209         {
10210           ThrowXWindowFatalException(ResourceLimitError,
10211             "MemoryAllocationFailed","...");
10212           (void) XFreeStringList(files);
10213           return((Image *) NULL);
10214         }
10215       j=0;
10216       for (i=1; i < count; i++)
10217         if (*files[i] != '-')
10218           filelist[j++]=files[i];
10219       filelist[j]=(char *) NULL;
10220       XListBrowserWidget(display,windows,&windows->widget,
10221         (const char **) filelist,"Load","Select Image to Load:",filename);
10222       filelist=(char **) RelinquishMagickMemory(filelist);
10223       (void) XFreeStringList(files);
10224     }
10225   if (*filename == '\0')
10226     return((Image *) NULL);
10227   image_info=CloneImageInfo(resource_info->image_info);
10228   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10229     (void *) NULL);
10230   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10231   exception=AcquireExceptionInfo();
10232   (void) SetImageInfo(image_info,0,exception);
10233   if (LocaleCompare(image_info->magick,"X") == 0)
10234     {
10235       char
10236         seconds[MaxTextExtent];
10237
10238       /*
10239         User may want to delay the X server screen grab.
10240       */
10241       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10242       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10243         seconds);
10244       if (*seconds == '\0')
10245         return((Image *) NULL);
10246       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10247     }
10248   magick_info=GetMagickInfo(image_info->magick,exception);
10249   if ((magick_info != (const MagickInfo *) NULL) &&
10250       (magick_info->raw != MagickFalse))
10251     {
10252       char
10253         geometry[MaxTextExtent];
10254
10255       /*
10256         Request image size from the user.
10257       */
10258       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10259       if (image_info->size != (char *) NULL)
10260         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10261       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10262         geometry);
10263       (void) CloneString(&image_info->size,geometry);
10264     }
10265   /*
10266     Load the image.
10267   */
10268   XSetCursorState(display,windows,MagickTrue);
10269   XCheckRefreshWindows(display,windows);
10270   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10271   nexus=ReadImage(image_info,exception);
10272   CatchException(exception);
10273   XSetCursorState(display,windows,MagickFalse);
10274   if (nexus != (Image *) NULL)
10275     XClientMessage(display,windows->image.id,windows->im_protocols,
10276       windows->im_next_image,CurrentTime);
10277   else
10278     {
10279       char
10280         *text,
10281         **textlist;
10282
10283       /*
10284         Unknown image format.
10285       */
10286       text=FileToString(filename,~0,exception);
10287       if (text == (char *) NULL)
10288         return((Image *) NULL);
10289       textlist=StringToList(text);
10290       if (textlist != (char **) NULL)
10291         {
10292           char
10293             title[MaxTextExtent];
10294
10295           register int
10296             i;
10297
10298           (void) FormatLocaleString(title,MaxTextExtent,
10299             "Unknown format: %s",filename);
10300           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10301             (const char **) textlist);
10302           for (i=0; textlist[i] != (char *) NULL; i++)
10303             textlist[i]=DestroyString(textlist[i]);
10304           textlist=(char **) RelinquishMagickMemory(textlist);
10305         }
10306       text=DestroyString(text);
10307     }
10308   exception=DestroyExceptionInfo(exception);
10309   image_info=DestroyImageInfo(image_info);
10310   return(nexus);
10311 }
10312 \f
10313 /*
10314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10315 %                                                                             %
10316 %                                                                             %
10317 %                                                                             %
10318 +   X P a n I m a g e                                                         %
10319 %                                                                             %
10320 %                                                                             %
10321 %                                                                             %
10322 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10323 %
10324 %  XPanImage() pans the image until the mouse button is released.
10325 %
10326 %  The format of the XPanImage method is:
10327 %
10328 %      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10329 %
10330 %  A description of each parameter follows:
10331 %
10332 %    o display: Specifies a connection to an X server;  returned from
10333 %      XOpenDisplay.
10334 %
10335 %    o windows: Specifies a pointer to a XWindows structure.
10336 %
10337 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10338 %      the entire image is refreshed.
10339 %
10340 */
10341 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10342 {
10343   char
10344     text[MaxTextExtent];
10345
10346   Cursor
10347     cursor;
10348
10349   MagickRealType
10350     x_factor,
10351     y_factor;
10352
10353   RectangleInfo
10354     pan_info;
10355
10356   size_t
10357     state;
10358
10359   /*
10360     Define cursor.
10361   */
10362   if ((windows->image.ximage->width > (int) windows->image.width) &&
10363       (windows->image.ximage->height > (int) windows->image.height))
10364     cursor=XCreateFontCursor(display,XC_fleur);
10365   else
10366     if (windows->image.ximage->width > (int) windows->image.width)
10367       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10368     else
10369       if (windows->image.ximage->height > (int) windows->image.height)
10370         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10371       else
10372         cursor=XCreateFontCursor(display,XC_arrow);
10373   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10374   /*
10375     Pan image as pointer moves until the mouse button is released.
10376   */
10377   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10378   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10379   pan_info.width=windows->pan.width*windows->image.width/
10380     windows->image.ximage->width;
10381   pan_info.height=windows->pan.height*windows->image.height/
10382     windows->image.ximage->height;
10383   pan_info.x=0;
10384   pan_info.y=0;
10385   state=UpdateConfigurationState;
10386   do
10387   {
10388     switch (event->type)
10389     {
10390       case ButtonPress:
10391       {
10392         /*
10393           User choose an initial pan location.
10394         */
10395         pan_info.x=(ssize_t) event->xbutton.x;
10396         pan_info.y=(ssize_t) event->xbutton.y;
10397         state|=UpdateConfigurationState;
10398         break;
10399       }
10400       case ButtonRelease:
10401       {
10402         /*
10403           User has finished panning the image.
10404         */
10405         pan_info.x=(ssize_t) event->xbutton.x;
10406         pan_info.y=(ssize_t) event->xbutton.y;
10407         state|=UpdateConfigurationState | ExitState;
10408         break;
10409       }
10410       case MotionNotify:
10411       {
10412         pan_info.x=(ssize_t) event->xmotion.x;
10413         pan_info.y=(ssize_t) event->xmotion.y;
10414         state|=UpdateConfigurationState;
10415       }
10416       default:
10417         break;
10418     }
10419     if ((state & UpdateConfigurationState) != 0)
10420       {
10421         /*
10422           Check boundary conditions.
10423         */
10424         if (pan_info.x < (ssize_t) (pan_info.width/2))
10425           pan_info.x=0;
10426         else
10427           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10428         if (pan_info.x < 0)
10429           pan_info.x=0;
10430         else
10431           if ((int) (pan_info.x+windows->image.width) >
10432               windows->image.ximage->width)
10433             pan_info.x=(ssize_t)
10434               (windows->image.ximage->width-windows->image.width);
10435         if (pan_info.y < (ssize_t) (pan_info.height/2))
10436           pan_info.y=0;
10437         else
10438           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10439         if (pan_info.y < 0)
10440           pan_info.y=0;
10441         else
10442           if ((int) (pan_info.y+windows->image.height) >
10443               windows->image.ximage->height)
10444             pan_info.y=(ssize_t)
10445               (windows->image.ximage->height-windows->image.height);
10446         if ((windows->image.x != (int) pan_info.x) ||
10447             (windows->image.y != (int) pan_info.y))
10448           {
10449             /*
10450               Display image pan offset.
10451             */
10452             windows->image.x=(int) pan_info.x;
10453             windows->image.y=(int) pan_info.y;
10454             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10455               windows->image.width,windows->image.height,windows->image.x,
10456               windows->image.y);
10457             XInfoWidget(display,windows,text);
10458             /*
10459               Refresh Image window.
10460             */
10461             XDrawPanRectangle(display,windows);
10462             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10463           }
10464         state&=(~UpdateConfigurationState);
10465       }
10466     /*
10467       Wait for next event.
10468     */
10469     if ((state & ExitState) == 0)
10470       XScreenEvent(display,windows,event);
10471   } while ((state & ExitState) == 0);
10472   /*
10473     Restore cursor.
10474   */
10475   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10476   (void) XFreeCursor(display,cursor);
10477   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10478 }
10479 \f
10480 /*
10481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10482 %                                                                             %
10483 %                                                                             %
10484 %                                                                             %
10485 +   X P a s t e I m a g e                                                     %
10486 %                                                                             %
10487 %                                                                             %
10488 %                                                                             %
10489 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10490 %
10491 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10492 %  window image at a location the user chooses with the pointer.
10493 %
10494 %  The format of the XPasteImage method is:
10495 %
10496 %      MagickBooleanType XPasteImage(Display *display,
10497 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
10498 %
10499 %  A description of each parameter follows:
10500 %
10501 %    o display: Specifies a connection to an X server;  returned from
10502 %      XOpenDisplay.
10503 %
10504 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10505 %
10506 %    o windows: Specifies a pointer to a XWindows structure.
10507 %
10508 %    o image: the image; returned from ReadImage.
10509 %
10510 */
10511 static MagickBooleanType XPasteImage(Display *display,
10512   XResourceInfo *resource_info,XWindows *windows,Image *image)
10513 {
10514   static const char
10515     *PasteMenu[] =
10516     {
10517       "Operator",
10518       "Help",
10519       "Dismiss",
10520       (char *) NULL
10521     };
10522
10523   static const ModeType
10524     PasteCommands[] =
10525     {
10526       PasteOperatorsCommand,
10527       PasteHelpCommand,
10528       PasteDismissCommand
10529     };
10530
10531   static CompositeOperator
10532     compose = CopyCompositeOp;
10533
10534   char
10535     text[MaxTextExtent];
10536
10537   Cursor
10538     cursor;
10539
10540   Image
10541     *paste_image;
10542
10543   int
10544     entry,
10545     id,
10546     x,
10547     y;
10548
10549   MagickRealType
10550     scale_factor;
10551
10552   RectangleInfo
10553     highlight_info,
10554     paste_info;
10555
10556   unsigned int
10557     height,
10558     width;
10559
10560   size_t
10561     state;
10562
10563   XEvent
10564     event;
10565
10566   /*
10567     Copy image.
10568   */
10569   if (resource_info->copy_image == (Image *) NULL)
10570     return(MagickFalse);
10571   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10572     &image->exception);
10573   /*
10574     Map Command widget.
10575   */
10576   (void) CloneString(&windows->command.name,"Paste");
10577   windows->command.data=1;
10578   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10579   (void) XMapRaised(display,windows->command.id);
10580   XClientMessage(display,windows->image.id,windows->im_protocols,
10581     windows->im_update_widget,CurrentTime);
10582   /*
10583     Track pointer until button 1 is pressed.
10584   */
10585   XSetCursorState(display,windows,MagickFalse);
10586   XQueryPosition(display,windows->image.id,&x,&y);
10587   (void) XSelectInput(display,windows->image.id,
10588     windows->image.attributes.event_mask | PointerMotionMask);
10589   paste_info.x=(ssize_t) windows->image.x+x;
10590   paste_info.y=(ssize_t) windows->image.y+y;
10591   paste_info.width=0;
10592   paste_info.height=0;
10593   cursor=XCreateFontCursor(display,XC_ul_angle);
10594   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10595   state=DefaultState;
10596   do
10597   {
10598     if (windows->info.mapped != MagickFalse)
10599       {
10600         /*
10601           Display pointer position.
10602         */
10603         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10604           (long) paste_info.x,(long) paste_info.y);
10605         XInfoWidget(display,windows,text);
10606       }
10607     highlight_info=paste_info;
10608     highlight_info.x=paste_info.x-windows->image.x;
10609     highlight_info.y=paste_info.y-windows->image.y;
10610     XHighlightRectangle(display,windows->image.id,
10611       windows->image.highlight_context,&highlight_info);
10612     /*
10613       Wait for next event.
10614     */
10615     XScreenEvent(display,windows,&event);
10616     XHighlightRectangle(display,windows->image.id,
10617       windows->image.highlight_context,&highlight_info);
10618     if (event.xany.window == windows->command.id)
10619       {
10620         /*
10621           Select a command from the Command widget.
10622         */
10623         id=XCommandWidget(display,windows,PasteMenu,&event);
10624         if (id < 0)
10625           continue;
10626         switch (PasteCommands[id])
10627         {
10628           case PasteOperatorsCommand:
10629           {
10630             char
10631               command[MaxTextExtent],
10632               **operators;
10633
10634             /*
10635               Select a command from the pop-up menu.
10636             */
10637             operators=GetCommandOptions(MagickComposeOptions);
10638             if (operators == (char **) NULL)
10639               break;
10640             entry=XMenuWidget(display,windows,PasteMenu[id],
10641               (const char **) operators,command);
10642             if (entry >= 0)
10643               compose=(CompositeOperator) ParseCommandOption(
10644                 MagickComposeOptions,MagickFalse,operators[entry]);
10645             operators=DestroyStringList(operators);
10646             break;
10647           }
10648           case PasteHelpCommand:
10649           {
10650             XTextViewWidget(display,resource_info,windows,MagickFalse,
10651               "Help Viewer - Image Composite",ImagePasteHelp);
10652             break;
10653           }
10654           case PasteDismissCommand:
10655           {
10656             /*
10657               Prematurely exit.
10658             */
10659             state|=EscapeState;
10660             state|=ExitState;
10661             break;
10662           }
10663           default:
10664             break;
10665         }
10666         continue;
10667       }
10668     switch (event.type)
10669     {
10670       case ButtonPress:
10671       {
10672         if (image->debug != MagickFalse)
10673           (void) LogMagickEvent(X11Event,GetMagickModule(),
10674             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10675             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10676         if (event.xbutton.button != Button1)
10677           break;
10678         if (event.xbutton.window != windows->image.id)
10679           break;
10680         /*
10681           Paste rectangle is relative to image configuration.
10682         */
10683         width=(unsigned int) image->columns;
10684         height=(unsigned int) image->rows;
10685         x=0;
10686         y=0;
10687         if (windows->image.crop_geometry != (char *) NULL)
10688           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10689             &width,&height);
10690         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10691         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10692         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10693         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10694         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10695         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10696         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10697         break;
10698       }
10699       case ButtonRelease:
10700       {
10701         if (image->debug != MagickFalse)
10702           (void) LogMagickEvent(X11Event,GetMagickModule(),
10703             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10704             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10705         if (event.xbutton.button != Button1)
10706           break;
10707         if (event.xbutton.window != windows->image.id)
10708           break;
10709         if ((paste_info.width != 0) && (paste_info.height != 0))
10710           {
10711             /*
10712               User has selected the location of the paste image.
10713             */
10714             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10715             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10716             state|=ExitState;
10717           }
10718         break;
10719       }
10720       case Expose:
10721         break;
10722       case KeyPress:
10723       {
10724         char
10725           command[MaxTextExtent];
10726
10727         KeySym
10728           key_symbol;
10729
10730         int
10731           length;
10732
10733         if (event.xkey.window != windows->image.id)
10734           break;
10735         /*
10736           Respond to a user key press.
10737         */
10738         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10739           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10740         *(command+length)='\0';
10741         if (image->debug != MagickFalse)
10742           (void) LogMagickEvent(X11Event,GetMagickModule(),
10743             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10744         switch ((int) key_symbol)
10745         {
10746           case XK_Escape:
10747           case XK_F20:
10748           {
10749             /*
10750               Prematurely exit.
10751             */
10752             paste_image=DestroyImage(paste_image);
10753             state|=EscapeState;
10754             state|=ExitState;
10755             break;
10756           }
10757           case XK_F1:
10758           case XK_Help:
10759           {
10760             (void) XSetFunction(display,windows->image.highlight_context,
10761               GXcopy);
10762             XTextViewWidget(display,resource_info,windows,MagickFalse,
10763               "Help Viewer - Image Composite",ImagePasteHelp);
10764             (void) XSetFunction(display,windows->image.highlight_context,
10765               GXinvert);
10766             break;
10767           }
10768           default:
10769           {
10770             (void) XBell(display,0);
10771             break;
10772           }
10773         }
10774         break;
10775       }
10776       case MotionNotify:
10777       {
10778         /*
10779           Map and unmap Info widget as text cursor crosses its boundaries.
10780         */
10781         x=event.xmotion.x;
10782         y=event.xmotion.y;
10783         if (windows->info.mapped != MagickFalse)
10784           {
10785             if ((x < (int) (windows->info.x+windows->info.width)) &&
10786                 (y < (int) (windows->info.y+windows->info.height)))
10787               (void) XWithdrawWindow(display,windows->info.id,
10788                 windows->info.screen);
10789           }
10790         else
10791           if ((x > (int) (windows->info.x+windows->info.width)) ||
10792               (y > (int) (windows->info.y+windows->info.height)))
10793             (void) XMapWindow(display,windows->info.id);
10794         paste_info.x=(ssize_t) windows->image.x+x;
10795         paste_info.y=(ssize_t) windows->image.y+y;
10796         break;
10797       }
10798       default:
10799       {
10800         if (image->debug != MagickFalse)
10801           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10802             event.type);
10803         break;
10804       }
10805     }
10806   } while ((state & ExitState) == 0);
10807   (void) XSelectInput(display,windows->image.id,
10808     windows->image.attributes.event_mask);
10809   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10810   XSetCursorState(display,windows,MagickFalse);
10811   (void) XFreeCursor(display,cursor);
10812   if ((state & EscapeState) != 0)
10813     return(MagickTrue);
10814   /*
10815     Image pasting is relative to image configuration.
10816   */
10817   XSetCursorState(display,windows,MagickTrue);
10818   XCheckRefreshWindows(display,windows);
10819   width=(unsigned int) image->columns;
10820   height=(unsigned int) image->rows;
10821   x=0;
10822   y=0;
10823   if (windows->image.crop_geometry != (char *) NULL)
10824     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10825   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10826   paste_info.x+=x;
10827   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10828   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10829   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10830   paste_info.y+=y;
10831   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10832   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10833   /*
10834     Paste image with X Image window.
10835   */
10836   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10837   paste_image=DestroyImage(paste_image);
10838   XSetCursorState(display,windows,MagickFalse);
10839   /*
10840     Update image colormap.
10841   */
10842   XConfigureImageColormap(display,resource_info,windows,image);
10843   (void) XConfigureImage(display,resource_info,windows,image);
10844   return(MagickTrue);
10845 }
10846 \f
10847 /*
10848 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10849 %                                                                             %
10850 %                                                                             %
10851 %                                                                             %
10852 +   X P r i n t I m a g e                                                     %
10853 %                                                                             %
10854 %                                                                             %
10855 %                                                                             %
10856 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10857 %
10858 %  XPrintImage() prints an image to a Postscript printer.
10859 %
10860 %  The format of the XPrintImage method is:
10861 %
10862 %      MagickBooleanType XPrintImage(Display *display,
10863 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
10864 %
10865 %  A description of each parameter follows:
10866 %
10867 %    o display: Specifies a connection to an X server; returned from
10868 %      XOpenDisplay.
10869 %
10870 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10871 %
10872 %    o windows: Specifies a pointer to a XWindows structure.
10873 %
10874 %    o image: the image.
10875 %
10876 */
10877 static MagickBooleanType XPrintImage(Display *display,
10878   XResourceInfo *resource_info,XWindows *windows,Image *image)
10879 {
10880   char
10881     filename[MaxTextExtent],
10882     geometry[MaxTextExtent];
10883
10884   Image
10885     *print_image;
10886
10887   ImageInfo
10888     *image_info;
10889
10890   MagickStatusType
10891     status;
10892
10893   /*
10894     Request Postscript page geometry from user.
10895   */
10896   image_info=CloneImageInfo(resource_info->image_info);
10897   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10898   if (image_info->page != (char *) NULL)
10899     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10900   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10901     "Select Postscript Page Geometry:",geometry);
10902   if (*geometry == '\0')
10903     return(MagickTrue);
10904   image_info->page=GetPageGeometry(geometry);
10905   /*
10906     Apply image transforms.
10907   */
10908   XSetCursorState(display,windows,MagickTrue);
10909   XCheckRefreshWindows(display,windows);
10910   print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10911   if (print_image == (Image *) NULL)
10912     return(MagickFalse);
10913   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10914     windows->image.ximage->width,windows->image.ximage->height);
10915   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10916   /*
10917     Print image.
10918   */
10919   (void) AcquireUniqueFilename(filename);
10920   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10921     filename);
10922   status=WriteImage(image_info,print_image,&image->exception);
10923   (void) RelinquishUniqueFileResource(filename);
10924   print_image=DestroyImage(print_image);
10925   image_info=DestroyImageInfo(image_info);
10926   XSetCursorState(display,windows,MagickFalse);
10927   return(status != 0 ? MagickTrue : MagickFalse);
10928 }
10929 \f
10930 /*
10931 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10932 %                                                                             %
10933 %                                                                             %
10934 %                                                                             %
10935 +   X R O I I m a g e                                                         %
10936 %                                                                             %
10937 %                                                                             %
10938 %                                                                             %
10939 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10940 %
10941 %  XROIImage() applies an image processing technique to a region of interest.
10942 %
10943 %  The format of the XROIImage method is:
10944 %
10945 %      MagickBooleanType XROIImage(Display *display,
10946 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
10947 %
10948 %  A description of each parameter follows:
10949 %
10950 %    o display: Specifies a connection to an X server; returned from
10951 %      XOpenDisplay.
10952 %
10953 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10954 %
10955 %    o windows: Specifies a pointer to a XWindows structure.
10956 %
10957 %    o image: the image; returned from ReadImage.
10958 %
10959 */
10960 static MagickBooleanType XROIImage(Display *display,
10961   XResourceInfo *resource_info,XWindows *windows,Image **image)
10962 {
10963 #define ApplyMenus  7
10964
10965   static const char
10966     *ROIMenu[] =
10967     {
10968       "Help",
10969       "Dismiss",
10970       (char *) NULL
10971     },
10972     *ApplyMenu[] =
10973     {
10974       "File",
10975       "Edit",
10976       "Transform",
10977       "Enhance",
10978       "Effects",
10979       "F/X",
10980       "Miscellany",
10981       "Help",
10982       "Dismiss",
10983       (char *) NULL
10984     },
10985     *FileMenu[] =
10986     {
10987       "Save...",
10988       "Print...",
10989       (char *) NULL
10990     },
10991     *EditMenu[] =
10992     {
10993       "Undo",
10994       "Redo",
10995       (char *) NULL
10996     },
10997     *TransformMenu[] =
10998     {
10999       "Flop",
11000       "Flip",
11001       "Rotate Right",
11002       "Rotate Left",
11003       (char *) NULL
11004     },
11005     *EnhanceMenu[] =
11006     {
11007       "Hue...",
11008       "Saturation...",
11009       "Brightness...",
11010       "Gamma...",
11011       "Spiff",
11012       "Dull",
11013       "Contrast Stretch...",
11014       "Sigmoidal Contrast...",
11015       "Normalize",
11016       "Equalize",
11017       "Negate",
11018       "Grayscale",
11019       "Map...",
11020       "Quantize...",
11021       (char *) NULL
11022     },
11023     *EffectsMenu[] =
11024     {
11025       "Despeckle",
11026       "Emboss",
11027       "Reduce Noise",
11028       "Add Noise",
11029       "Sharpen...",
11030       "Blur...",
11031       "Threshold...",
11032       "Edge Detect...",
11033       "Spread...",
11034       "Shade...",
11035       "Raise...",
11036       "Segment...",
11037       (char *) NULL
11038     },
11039     *FXMenu[] =
11040     {
11041       "Solarize...",
11042       "Sepia Tone...",
11043       "Swirl...",
11044       "Implode...",
11045       "Vignette...",
11046       "Wave...",
11047       "Oil Paint...",
11048       "Charcoal Draw...",
11049       (char *) NULL
11050     },
11051     *MiscellanyMenu[] =
11052     {
11053       "Image Info",
11054       "Zoom Image",
11055       "Show Preview...",
11056       "Show Histogram",
11057       "Show Matte",
11058       (char *) NULL
11059     };
11060
11061   static const char
11062     **Menus[ApplyMenus] =
11063     {
11064       FileMenu,
11065       EditMenu,
11066       TransformMenu,
11067       EnhanceMenu,
11068       EffectsMenu,
11069       FXMenu,
11070       MiscellanyMenu
11071     };
11072
11073   static const CommandType
11074     ApplyCommands[] =
11075     {
11076       NullCommand,
11077       NullCommand,
11078       NullCommand,
11079       NullCommand,
11080       NullCommand,
11081       NullCommand,
11082       NullCommand,
11083       HelpCommand,
11084       QuitCommand
11085     },
11086     FileCommands[] =
11087     {
11088       SaveCommand,
11089       PrintCommand
11090     },
11091     EditCommands[] =
11092     {
11093       UndoCommand,
11094       RedoCommand
11095     },
11096     TransformCommands[] =
11097     {
11098       FlopCommand,
11099       FlipCommand,
11100       RotateRightCommand,
11101       RotateLeftCommand
11102     },
11103     EnhanceCommands[] =
11104     {
11105       HueCommand,
11106       SaturationCommand,
11107       BrightnessCommand,
11108       GammaCommand,
11109       SpiffCommand,
11110       DullCommand,
11111       ContrastStretchCommand,
11112       SigmoidalContrastCommand,
11113       NormalizeCommand,
11114       EqualizeCommand,
11115       NegateCommand,
11116       GrayscaleCommand,
11117       MapCommand,
11118       QuantizeCommand
11119     },
11120     EffectsCommands[] =
11121     {
11122       DespeckleCommand,
11123       EmbossCommand,
11124       ReduceNoiseCommand,
11125       AddNoiseCommand,
11126       SharpenCommand,
11127       BlurCommand,
11128       EdgeDetectCommand,
11129       SpreadCommand,
11130       ShadeCommand,
11131       RaiseCommand,
11132       SegmentCommand
11133     },
11134     FXCommands[] =
11135     {
11136       SolarizeCommand,
11137       SepiaToneCommand,
11138       SwirlCommand,
11139       ImplodeCommand,
11140       VignetteCommand,
11141       WaveCommand,
11142       OilPaintCommand,
11143       CharcoalDrawCommand
11144     },
11145     MiscellanyCommands[] =
11146     {
11147       InfoCommand,
11148       ZoomCommand,
11149       ShowPreviewCommand,
11150       ShowHistogramCommand,
11151       ShowMatteCommand
11152     },
11153     ROICommands[] =
11154     {
11155       ROIHelpCommand,
11156       ROIDismissCommand
11157     };
11158
11159   static const CommandType
11160     *Commands[ApplyMenus] =
11161     {
11162       FileCommands,
11163       EditCommands,
11164       TransformCommands,
11165       EnhanceCommands,
11166       EffectsCommands,
11167       FXCommands,
11168       MiscellanyCommands
11169     };
11170
11171   char
11172     command[MaxTextExtent],
11173     text[MaxTextExtent];
11174
11175   CommandType
11176     command_type;
11177
11178   Cursor
11179     cursor;
11180
11181   Image
11182     *roi_image;
11183
11184   int
11185     entry,
11186     id,
11187     x,
11188     y;
11189
11190   MagickRealType
11191     scale_factor;
11192
11193   MagickProgressMonitor
11194     progress_monitor;
11195
11196   RectangleInfo
11197     crop_info,
11198     highlight_info,
11199     roi_info;
11200
11201   unsigned int
11202     height,
11203     width;
11204
11205   size_t
11206     state;
11207
11208   XEvent
11209     event;
11210
11211   /*
11212     Map Command widget.
11213   */
11214   (void) CloneString(&windows->command.name,"ROI");
11215   windows->command.data=0;
11216   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11217   (void) XMapRaised(display,windows->command.id);
11218   XClientMessage(display,windows->image.id,windows->im_protocols,
11219     windows->im_update_widget,CurrentTime);
11220   /*
11221     Track pointer until button 1 is pressed.
11222   */
11223   XQueryPosition(display,windows->image.id,&x,&y);
11224   (void) XSelectInput(display,windows->image.id,
11225     windows->image.attributes.event_mask | PointerMotionMask);
11226   roi_info.x=(ssize_t) windows->image.x+x;
11227   roi_info.y=(ssize_t) windows->image.y+y;
11228   roi_info.width=0;
11229   roi_info.height=0;
11230   cursor=XCreateFontCursor(display,XC_fleur);
11231   state=DefaultState;
11232   do
11233   {
11234     if (windows->info.mapped != MagickFalse)
11235       {
11236         /*
11237           Display pointer position.
11238         */
11239         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11240           (long) roi_info.x,(long) roi_info.y);
11241         XInfoWidget(display,windows,text);
11242       }
11243     /*
11244       Wait for next event.
11245     */
11246     XScreenEvent(display,windows,&event);
11247     if (event.xany.window == windows->command.id)
11248       {
11249         /*
11250           Select a command from the Command widget.
11251         */
11252         id=XCommandWidget(display,windows,ROIMenu,&event);
11253         if (id < 0)
11254           continue;
11255         switch (ROICommands[id])
11256         {
11257           case ROIHelpCommand:
11258           {
11259             XTextViewWidget(display,resource_info,windows,MagickFalse,
11260               "Help Viewer - Region of Interest",ImageROIHelp);
11261             break;
11262           }
11263           case ROIDismissCommand:
11264           {
11265             /*
11266               Prematurely exit.
11267             */
11268             state|=EscapeState;
11269             state|=ExitState;
11270             break;
11271           }
11272           default:
11273             break;
11274         }
11275         continue;
11276       }
11277     switch (event.type)
11278     {
11279       case ButtonPress:
11280       {
11281         if (event.xbutton.button != Button1)
11282           break;
11283         if (event.xbutton.window != windows->image.id)
11284           break;
11285         /*
11286           Note first corner of region of interest rectangle-- exit loop.
11287         */
11288         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11289         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11290         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11291         state|=ExitState;
11292         break;
11293       }
11294       case ButtonRelease:
11295         break;
11296       case Expose:
11297         break;
11298       case KeyPress:
11299       {
11300         KeySym
11301           key_symbol;
11302
11303         if (event.xkey.window != windows->image.id)
11304           break;
11305         /*
11306           Respond to a user key press.
11307         */
11308         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11309           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11310         switch ((int) key_symbol)
11311         {
11312           case XK_Escape:
11313           case XK_F20:
11314           {
11315             /*
11316               Prematurely exit.
11317             */
11318             state|=EscapeState;
11319             state|=ExitState;
11320             break;
11321           }
11322           case XK_F1:
11323           case XK_Help:
11324           {
11325             XTextViewWidget(display,resource_info,windows,MagickFalse,
11326               "Help Viewer - Region of Interest",ImageROIHelp);
11327             break;
11328           }
11329           default:
11330           {
11331             (void) XBell(display,0);
11332             break;
11333           }
11334         }
11335         break;
11336       }
11337       case MotionNotify:
11338       {
11339         /*
11340           Map and unmap Info widget as text cursor crosses its boundaries.
11341         */
11342         x=event.xmotion.x;
11343         y=event.xmotion.y;
11344         if (windows->info.mapped != MagickFalse)
11345           {
11346             if ((x < (int) (windows->info.x+windows->info.width)) &&
11347                 (y < (int) (windows->info.y+windows->info.height)))
11348               (void) XWithdrawWindow(display,windows->info.id,
11349                 windows->info.screen);
11350           }
11351         else
11352           if ((x > (int) (windows->info.x+windows->info.width)) ||
11353               (y > (int) (windows->info.y+windows->info.height)))
11354             (void) XMapWindow(display,windows->info.id);
11355         roi_info.x=(ssize_t) windows->image.x+x;
11356         roi_info.y=(ssize_t) windows->image.y+y;
11357         break;
11358       }
11359       default:
11360         break;
11361     }
11362   } while ((state & ExitState) == 0);
11363   (void) XSelectInput(display,windows->image.id,
11364     windows->image.attributes.event_mask);
11365   if ((state & EscapeState) != 0)
11366     {
11367       /*
11368         User want to exit without region of interest.
11369       */
11370       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11371       (void) XFreeCursor(display,cursor);
11372       return(MagickTrue);
11373     }
11374   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11375   do
11376   {
11377     /*
11378       Size rectangle as pointer moves until the mouse button is released.
11379     */
11380     x=(int) roi_info.x;
11381     y=(int) roi_info.y;
11382     roi_info.width=0;
11383     roi_info.height=0;
11384     state=DefaultState;
11385     do
11386     {
11387       highlight_info=roi_info;
11388       highlight_info.x=roi_info.x-windows->image.x;
11389       highlight_info.y=roi_info.y-windows->image.y;
11390       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11391         {
11392           /*
11393             Display info and draw region of interest rectangle.
11394           */
11395           if (windows->info.mapped == MagickFalse)
11396             (void) XMapWindow(display,windows->info.id);
11397           (void) FormatLocaleString(text,MaxTextExtent,
11398             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11399             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11400           XInfoWidget(display,windows,text);
11401           XHighlightRectangle(display,windows->image.id,
11402             windows->image.highlight_context,&highlight_info);
11403         }
11404       else
11405         if (windows->info.mapped != MagickFalse)
11406           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11407       /*
11408         Wait for next event.
11409       */
11410       XScreenEvent(display,windows,&event);
11411       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11412         XHighlightRectangle(display,windows->image.id,
11413           windows->image.highlight_context,&highlight_info);
11414       switch (event.type)
11415       {
11416         case ButtonPress:
11417         {
11418           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11419           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11420           break;
11421         }
11422         case ButtonRelease:
11423         {
11424           /*
11425             User has committed to region of interest rectangle.
11426           */
11427           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11428           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11429           XSetCursorState(display,windows,MagickFalse);
11430           state|=ExitState;
11431           if (LocaleCompare(windows->command.name,"Apply") == 0)
11432             break;
11433           (void) CloneString(&windows->command.name,"Apply");
11434           windows->command.data=ApplyMenus;
11435           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11436           break;
11437         }
11438         case Expose:
11439           break;
11440         case MotionNotify:
11441         {
11442           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11443           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11444         }
11445         default:
11446           break;
11447       }
11448       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11449           ((state & ExitState) != 0))
11450         {
11451           /*
11452             Check boundary conditions.
11453           */
11454           if (roi_info.x < 0)
11455             roi_info.x=0;
11456           else
11457             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11458               roi_info.x=(ssize_t) windows->image.ximage->width;
11459           if ((int) roi_info.x < x)
11460             roi_info.width=(unsigned int) (x-roi_info.x);
11461           else
11462             {
11463               roi_info.width=(unsigned int) (roi_info.x-x);
11464               roi_info.x=(ssize_t) x;
11465             }
11466           if (roi_info.y < 0)
11467             roi_info.y=0;
11468           else
11469             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11470               roi_info.y=(ssize_t) windows->image.ximage->height;
11471           if ((int) roi_info.y < y)
11472             roi_info.height=(unsigned int) (y-roi_info.y);
11473           else
11474             {
11475               roi_info.height=(unsigned int) (roi_info.y-y);
11476               roi_info.y=(ssize_t) y;
11477             }
11478         }
11479     } while ((state & ExitState) == 0);
11480     /*
11481       Wait for user to grab a corner of the rectangle or press return.
11482     */
11483     state=DefaultState;
11484     command_type=NullCommand;
11485     (void) XMapWindow(display,windows->info.id);
11486     do
11487     {
11488       if (windows->info.mapped != MagickFalse)
11489         {
11490           /*
11491             Display pointer position.
11492           */
11493           (void) FormatLocaleString(text,MaxTextExtent,
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         }
11498       highlight_info=roi_info;
11499       highlight_info.x=roi_info.x-windows->image.x;
11500       highlight_info.y=roi_info.y-windows->image.y;
11501       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11502         {
11503           state|=EscapeState;
11504           state|=ExitState;
11505           break;
11506         }
11507       if ((state & UpdateRegionState) != 0)
11508         {
11509           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11510           switch (command_type)
11511           {
11512             case UndoCommand:
11513             case RedoCommand:
11514             {
11515               (void) XMagickCommand(display,resource_info,windows,command_type,
11516                 image);
11517               break;
11518             }
11519             default:
11520             {
11521               /*
11522                 Region of interest is relative to image configuration.
11523               */
11524               progress_monitor=SetImageProgressMonitor(*image,
11525                 (MagickProgressMonitor) NULL,(*image)->client_data);
11526               crop_info=roi_info;
11527               width=(unsigned int) (*image)->columns;
11528               height=(unsigned int) (*image)->rows;
11529               x=0;
11530               y=0;
11531               if (windows->image.crop_geometry != (char *) NULL)
11532                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11533                   &width,&height);
11534               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11535               crop_info.x+=x;
11536               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11537               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11538               scale_factor=(MagickRealType)
11539                 height/windows->image.ximage->height;
11540               crop_info.y+=y;
11541               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11542               crop_info.height=(unsigned int)
11543                 (scale_factor*crop_info.height+0.5);
11544               roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11545               (void) SetImageProgressMonitor(*image,progress_monitor,
11546                 (*image)->client_data);
11547               if (roi_image == (Image *) NULL)
11548                 continue;
11549               /*
11550                 Apply image processing technique to the region of interest.
11551               */
11552               windows->image.orphan=MagickTrue;
11553               (void) XMagickCommand(display,resource_info,windows,command_type,
11554                 &roi_image);
11555               progress_monitor=SetImageProgressMonitor(*image,
11556                 (MagickProgressMonitor) NULL,(*image)->client_data);
11557               (void) XMagickCommand(display,resource_info,windows,
11558                 SaveToUndoBufferCommand,image);
11559               windows->image.orphan=MagickFalse;
11560               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11561                 crop_info.x,crop_info.y);
11562               roi_image=DestroyImage(roi_image);
11563               (void) SetImageProgressMonitor(*image,progress_monitor,
11564                 (*image)->client_data);
11565               break;
11566             }
11567           }
11568           if (command_type != InfoCommand)
11569             {
11570               XConfigureImageColormap(display,resource_info,windows,*image);
11571               (void) XConfigureImage(display,resource_info,windows,*image);
11572             }
11573           XCheckRefreshWindows(display,windows);
11574           XInfoWidget(display,windows,text);
11575           (void) XSetFunction(display,windows->image.highlight_context,
11576             GXinvert);
11577           state&=(~UpdateRegionState);
11578         }
11579       XHighlightRectangle(display,windows->image.id,
11580         windows->image.highlight_context,&highlight_info);
11581       XScreenEvent(display,windows,&event);
11582       if (event.xany.window == windows->command.id)
11583         {
11584           /*
11585             Select a command from the Command widget.
11586           */
11587           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11588           command_type=NullCommand;
11589           id=XCommandWidget(display,windows,ApplyMenu,&event);
11590           if (id >= 0)
11591             {
11592               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11593               command_type=ApplyCommands[id];
11594               if (id < ApplyMenus)
11595                 {
11596                   /*
11597                     Select a command from a pop-up menu.
11598                   */
11599                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11600                     (const char **) Menus[id],command);
11601                   if (entry >= 0)
11602                     {
11603                       (void) CopyMagickString(command,Menus[id][entry],
11604                         MaxTextExtent);
11605                       command_type=Commands[id][entry];
11606                     }
11607                 }
11608             }
11609           (void) XSetFunction(display,windows->image.highlight_context,
11610             GXinvert);
11611           XHighlightRectangle(display,windows->image.id,
11612             windows->image.highlight_context,&highlight_info);
11613           if (command_type == HelpCommand)
11614             {
11615               (void) XSetFunction(display,windows->image.highlight_context,
11616                 GXcopy);
11617               XTextViewWidget(display,resource_info,windows,MagickFalse,
11618                 "Help Viewer - Region of Interest",ImageROIHelp);
11619               (void) XSetFunction(display,windows->image.highlight_context,
11620                 GXinvert);
11621               continue;
11622             }
11623           if (command_type == QuitCommand)
11624             {
11625               /*
11626                 exit.
11627               */
11628               state|=EscapeState;
11629               state|=ExitState;
11630               continue;
11631             }
11632           if (command_type != NullCommand)
11633             state|=UpdateRegionState;
11634           continue;
11635         }
11636       XHighlightRectangle(display,windows->image.id,
11637         windows->image.highlight_context,&highlight_info);
11638       switch (event.type)
11639       {
11640         case ButtonPress:
11641         {
11642           x=windows->image.x;
11643           y=windows->image.y;
11644           if (event.xbutton.button != Button1)
11645             break;
11646           if (event.xbutton.window != windows->image.id)
11647             break;
11648           x=windows->image.x+event.xbutton.x;
11649           y=windows->image.y+event.xbutton.y;
11650           if ((x < (int) (roi_info.x+RoiDelta)) &&
11651               (x > (int) (roi_info.x-RoiDelta)) &&
11652               (y < (int) (roi_info.y+RoiDelta)) &&
11653               (y > (int) (roi_info.y-RoiDelta)))
11654             {
11655               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11656               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11657               state|=UpdateConfigurationState;
11658               break;
11659             }
11660           if ((x < (int) (roi_info.x+RoiDelta)) &&
11661               (x > (int) (roi_info.x-RoiDelta)) &&
11662               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11663               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11664             {
11665               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11666               state|=UpdateConfigurationState;
11667               break;
11668             }
11669           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11670               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11671               (y < (int) (roi_info.y+RoiDelta)) &&
11672               (y > (int) (roi_info.y-RoiDelta)))
11673             {
11674               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11675               state|=UpdateConfigurationState;
11676               break;
11677             }
11678           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11679               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11680               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11681               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11682             {
11683               state|=UpdateConfigurationState;
11684               break;
11685             }
11686         }
11687         case ButtonRelease:
11688         {
11689           if (event.xbutton.window == windows->pan.id)
11690             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11691                 (highlight_info.y != crop_info.y-windows->image.y))
11692               XHighlightRectangle(display,windows->image.id,
11693                 windows->image.highlight_context,&highlight_info);
11694           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11695             event.xbutton.time);
11696           break;
11697         }
11698         case Expose:
11699         {
11700           if (event.xexpose.window == windows->image.id)
11701             if (event.xexpose.count == 0)
11702               {
11703                 event.xexpose.x=(int) highlight_info.x;
11704                 event.xexpose.y=(int) highlight_info.y;
11705                 event.xexpose.width=(int) highlight_info.width;
11706                 event.xexpose.height=(int) highlight_info.height;
11707                 XRefreshWindow(display,&windows->image,&event);
11708               }
11709           if (event.xexpose.window == windows->info.id)
11710             if (event.xexpose.count == 0)
11711               XInfoWidget(display,windows,text);
11712           break;
11713         }
11714         case KeyPress:
11715         {
11716           KeySym
11717             key_symbol;
11718
11719           if (event.xkey.window != windows->image.id)
11720             break;
11721           /*
11722             Respond to a user key press.
11723           */
11724           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11725             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11726           switch ((int) key_symbol)
11727           {
11728             case XK_Shift_L:
11729             case XK_Shift_R:
11730               break;
11731             case XK_Escape:
11732             case XK_F20:
11733               state|=EscapeState;
11734             case XK_Return:
11735             {
11736               state|=ExitState;
11737               break;
11738             }
11739             case XK_Home:
11740             case XK_KP_Home:
11741             {
11742               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11743               roi_info.y=(ssize_t) (windows->image.height/2L-
11744                 roi_info.height/2L);
11745               break;
11746             }
11747             case XK_Left:
11748             case XK_KP_Left:
11749             {
11750               roi_info.x--;
11751               break;
11752             }
11753             case XK_Up:
11754             case XK_KP_Up:
11755             case XK_Next:
11756             {
11757               roi_info.y--;
11758               break;
11759             }
11760             case XK_Right:
11761             case XK_KP_Right:
11762             {
11763               roi_info.x++;
11764               break;
11765             }
11766             case XK_Prior:
11767             case XK_Down:
11768             case XK_KP_Down:
11769             {
11770               roi_info.y++;
11771               break;
11772             }
11773             case XK_F1:
11774             case XK_Help:
11775             {
11776               (void) XSetFunction(display,windows->image.highlight_context,
11777                 GXcopy);
11778               XTextViewWidget(display,resource_info,windows,MagickFalse,
11779                 "Help Viewer - Region of Interest",ImageROIHelp);
11780               (void) XSetFunction(display,windows->image.highlight_context,
11781                 GXinvert);
11782               break;
11783             }
11784             default:
11785             {
11786               command_type=XImageWindowCommand(display,resource_info,windows,
11787                 event.xkey.state,key_symbol,image);
11788               if (command_type != NullCommand)
11789                 state|=UpdateRegionState;
11790               break;
11791             }
11792           }
11793           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11794             event.xkey.time);
11795           break;
11796         }
11797         case KeyRelease:
11798           break;
11799         case MotionNotify:
11800         {
11801           if (event.xbutton.window != windows->image.id)
11802             break;
11803           /*
11804             Map and unmap Info widget as text cursor crosses its boundaries.
11805           */
11806           x=event.xmotion.x;
11807           y=event.xmotion.y;
11808           if (windows->info.mapped != MagickFalse)
11809             {
11810               if ((x < (int) (windows->info.x+windows->info.width)) &&
11811                   (y < (int) (windows->info.y+windows->info.height)))
11812                 (void) XWithdrawWindow(display,windows->info.id,
11813                   windows->info.screen);
11814             }
11815           else
11816             if ((x > (int) (windows->info.x+windows->info.width)) ||
11817                 (y > (int) (windows->info.y+windows->info.height)))
11818               (void) XMapWindow(display,windows->info.id);
11819           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11820           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11821           break;
11822         }
11823         case SelectionRequest:
11824         {
11825           XSelectionEvent
11826             notify;
11827
11828           XSelectionRequestEvent
11829             *request;
11830
11831           /*
11832             Set primary selection.
11833           */
11834           (void) FormatLocaleString(text,MaxTextExtent,
11835             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11836             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11837           request=(&(event.xselectionrequest));
11838           (void) XChangeProperty(request->display,request->requestor,
11839             request->property,request->target,8,PropModeReplace,
11840             (unsigned char *) text,(int) strlen(text));
11841           notify.type=SelectionNotify;
11842           notify.display=request->display;
11843           notify.requestor=request->requestor;
11844           notify.selection=request->selection;
11845           notify.target=request->target;
11846           notify.time=request->time;
11847           if (request->property == None)
11848             notify.property=request->target;
11849           else
11850             notify.property=request->property;
11851           (void) XSendEvent(request->display,request->requestor,False,0,
11852             (XEvent *) &notify);
11853         }
11854         default:
11855           break;
11856       }
11857       if ((state & UpdateConfigurationState) != 0)
11858         {
11859           (void) XPutBackEvent(display,&event);
11860           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11861           break;
11862         }
11863     } while ((state & ExitState) == 0);
11864   } while ((state & ExitState) == 0);
11865   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11866   XSetCursorState(display,windows,MagickFalse);
11867   if ((state & EscapeState) != 0)
11868     return(MagickTrue);
11869   return(MagickTrue);
11870 }
11871 \f
11872 /*
11873 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11874 %                                                                             %
11875 %                                                                             %
11876 %                                                                             %
11877 +   X R o t a t e I m a g e                                                   %
11878 %                                                                             %
11879 %                                                                             %
11880 %                                                                             %
11881 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11882 %
11883 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11884 %  rotation angle is computed from the slope of a line drawn by the user.
11885 %
11886 %  The format of the XRotateImage method is:
11887 %
11888 %      MagickBooleanType XRotateImage(Display *display,
11889 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11890 %        Image **image)
11891 %
11892 %  A description of each parameter follows:
11893 %
11894 %    o display: Specifies a connection to an X server; returned from
11895 %      XOpenDisplay.
11896 %
11897 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11898 %
11899 %    o windows: Specifies a pointer to a XWindows structure.
11900 %
11901 %    o degrees: Specifies the number of degrees to rotate the image.
11902 %
11903 %    o image: the image.
11904 %
11905 */
11906 static MagickBooleanType XRotateImage(Display *display,
11907   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11908 {
11909   static const char
11910     *RotateMenu[] =
11911     {
11912       "Pixel Color",
11913       "Direction",
11914       "Help",
11915       "Dismiss",
11916       (char *) NULL
11917     };
11918
11919   static ModeType
11920     direction = HorizontalRotateCommand;
11921
11922   static const ModeType
11923     DirectionCommands[] =
11924     {
11925       HorizontalRotateCommand,
11926       VerticalRotateCommand
11927     },
11928     RotateCommands[] =
11929     {
11930       RotateColorCommand,
11931       RotateDirectionCommand,
11932       RotateHelpCommand,
11933       RotateDismissCommand
11934     };
11935
11936   static unsigned int
11937     pen_id = 0;
11938
11939   char
11940     command[MaxTextExtent],
11941     text[MaxTextExtent];
11942
11943   Image
11944     *rotate_image;
11945
11946   int
11947     id,
11948     x,
11949     y;
11950
11951   MagickRealType
11952     normalized_degrees;
11953
11954   register int
11955     i;
11956
11957   unsigned int
11958     height,
11959     rotations,
11960     width;
11961
11962   if (degrees == 0.0)
11963     {
11964       unsigned int
11965         distance;
11966
11967       size_t
11968         state;
11969
11970       XEvent
11971         event;
11972
11973       XSegment
11974         rotate_info;
11975
11976       /*
11977         Map Command widget.
11978       */
11979       (void) CloneString(&windows->command.name,"Rotate");
11980       windows->command.data=2;
11981       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11982       (void) XMapRaised(display,windows->command.id);
11983       XClientMessage(display,windows->image.id,windows->im_protocols,
11984         windows->im_update_widget,CurrentTime);
11985       /*
11986         Wait for first button press.
11987       */
11988       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11989       XQueryPosition(display,windows->image.id,&x,&y);
11990       rotate_info.x1=x;
11991       rotate_info.y1=y;
11992       rotate_info.x2=x;
11993       rotate_info.y2=y;
11994       state=DefaultState;
11995       do
11996       {
11997         XHighlightLine(display,windows->image.id,
11998           windows->image.highlight_context,&rotate_info);
11999         /*
12000           Wait for next event.
12001         */
12002         XScreenEvent(display,windows,&event);
12003         XHighlightLine(display,windows->image.id,
12004           windows->image.highlight_context,&rotate_info);
12005         if (event.xany.window == windows->command.id)
12006           {
12007             /*
12008               Select a command from the Command widget.
12009             */
12010             id=XCommandWidget(display,windows,RotateMenu,&event);
12011             if (id < 0)
12012               continue;
12013             (void) XSetFunction(display,windows->image.highlight_context,
12014               GXcopy);
12015             switch (RotateCommands[id])
12016             {
12017               case RotateColorCommand:
12018               {
12019                 const char
12020                   *ColorMenu[MaxNumberPens];
12021
12022                 int
12023                   pen_number;
12024
12025                 XColor
12026                   color;
12027
12028                 /*
12029                   Initialize menu selections.
12030                 */
12031                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12032                   ColorMenu[i]=resource_info->pen_colors[i];
12033                 ColorMenu[MaxNumberPens-2]="Browser...";
12034                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12035                 /*
12036                   Select a pen color from the pop-up menu.
12037                 */
12038                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12039                   (const char **) ColorMenu,command);
12040                 if (pen_number < 0)
12041                   break;
12042                 if (pen_number == (MaxNumberPens-2))
12043                   {
12044                     static char
12045                       color_name[MaxTextExtent] = "gray";
12046
12047                     /*
12048                       Select a pen color from a dialog.
12049                     */
12050                     resource_info->pen_colors[pen_number]=color_name;
12051                     XColorBrowserWidget(display,windows,"Select",color_name);
12052                     if (*color_name == '\0')
12053                       break;
12054                   }
12055                 /*
12056                   Set pen color.
12057                 */
12058                 (void) XParseColor(display,windows->map_info->colormap,
12059                   resource_info->pen_colors[pen_number],&color);
12060                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12061                   (unsigned int) MaxColors,&color);
12062                 windows->pixel_info->pen_colors[pen_number]=color;
12063                 pen_id=(unsigned int) pen_number;
12064                 break;
12065               }
12066               case RotateDirectionCommand:
12067               {
12068                 static const char
12069                   *Directions[] =
12070                   {
12071                     "horizontal",
12072                     "vertical",
12073                     (char *) NULL,
12074                   };
12075
12076                 /*
12077                   Select a command from the pop-up menu.
12078                 */
12079                 id=XMenuWidget(display,windows,RotateMenu[id],
12080                   Directions,command);
12081                 if (id >= 0)
12082                   direction=DirectionCommands[id];
12083                 break;
12084               }
12085               case RotateHelpCommand:
12086               {
12087                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12088                   "Help Viewer - Image Rotation",ImageRotateHelp);
12089                 break;
12090               }
12091               case RotateDismissCommand:
12092               {
12093                 /*
12094                   Prematurely exit.
12095                 */
12096                 state|=EscapeState;
12097                 state|=ExitState;
12098                 break;
12099               }
12100               default:
12101                 break;
12102             }
12103             (void) XSetFunction(display,windows->image.highlight_context,
12104               GXinvert);
12105             continue;
12106           }
12107         switch (event.type)
12108         {
12109           case ButtonPress:
12110           {
12111             if (event.xbutton.button != Button1)
12112               break;
12113             if (event.xbutton.window != windows->image.id)
12114               break;
12115             /*
12116               exit loop.
12117             */
12118             (void) XSetFunction(display,windows->image.highlight_context,
12119               GXcopy);
12120             rotate_info.x1=event.xbutton.x;
12121             rotate_info.y1=event.xbutton.y;
12122             state|=ExitState;
12123             break;
12124           }
12125           case ButtonRelease:
12126             break;
12127           case Expose:
12128             break;
12129           case KeyPress:
12130           {
12131             char
12132               command[MaxTextExtent];
12133
12134             KeySym
12135               key_symbol;
12136
12137             if (event.xkey.window != windows->image.id)
12138               break;
12139             /*
12140               Respond to a user key press.
12141             */
12142             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12143               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12144             switch ((int) key_symbol)
12145             {
12146               case XK_Escape:
12147               case XK_F20:
12148               {
12149                 /*
12150                   Prematurely exit.
12151                 */
12152                 state|=EscapeState;
12153                 state|=ExitState;
12154                 break;
12155               }
12156               case XK_F1:
12157               case XK_Help:
12158               {
12159                 (void) XSetFunction(display,windows->image.highlight_context,
12160                   GXcopy);
12161                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12162                   "Help Viewer - Image Rotation",ImageRotateHelp);
12163                 (void) XSetFunction(display,windows->image.highlight_context,
12164                   GXinvert);
12165                 break;
12166               }
12167               default:
12168               {
12169                 (void) XBell(display,0);
12170                 break;
12171               }
12172             }
12173             break;
12174           }
12175           case MotionNotify:
12176           {
12177             rotate_info.x1=event.xmotion.x;
12178             rotate_info.y1=event.xmotion.y;
12179           }
12180         }
12181         rotate_info.x2=rotate_info.x1;
12182         rotate_info.y2=rotate_info.y1;
12183         if (direction == HorizontalRotateCommand)
12184           rotate_info.x2+=32;
12185         else
12186           rotate_info.y2-=32;
12187       } while ((state & ExitState) == 0);
12188       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12189       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12190       if ((state & EscapeState) != 0)
12191         return(MagickTrue);
12192       /*
12193         Draw line as pointer moves until the mouse button is released.
12194       */
12195       distance=0;
12196       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12197       state=DefaultState;
12198       do
12199       {
12200         if (distance > 9)
12201           {
12202             /*
12203               Display info and draw rotation line.
12204             */
12205             if (windows->info.mapped == MagickFalse)
12206               (void) XMapWindow(display,windows->info.id);
12207             (void) FormatLocaleString(text,MaxTextExtent," %g",
12208               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12209             XInfoWidget(display,windows,text);
12210             XHighlightLine(display,windows->image.id,
12211               windows->image.highlight_context,&rotate_info);
12212           }
12213         else
12214           if (windows->info.mapped != MagickFalse)
12215             (void) XWithdrawWindow(display,windows->info.id,
12216               windows->info.screen);
12217         /*
12218           Wait for next event.
12219         */
12220         XScreenEvent(display,windows,&event);
12221         if (distance > 9)
12222           XHighlightLine(display,windows->image.id,
12223             windows->image.highlight_context,&rotate_info);
12224         switch (event.type)
12225         {
12226           case ButtonPress:
12227             break;
12228           case ButtonRelease:
12229           {
12230             /*
12231               User has committed to rotation line.
12232             */
12233             rotate_info.x2=event.xbutton.x;
12234             rotate_info.y2=event.xbutton.y;
12235             state|=ExitState;
12236             break;
12237           }
12238           case Expose:
12239             break;
12240           case MotionNotify:
12241           {
12242             rotate_info.x2=event.xmotion.x;
12243             rotate_info.y2=event.xmotion.y;
12244           }
12245           default:
12246             break;
12247         }
12248         /*
12249           Check boundary conditions.
12250         */
12251         if (rotate_info.x2 < 0)
12252           rotate_info.x2=0;
12253         else
12254           if (rotate_info.x2 > (int) windows->image.width)
12255             rotate_info.x2=(short) windows->image.width;
12256         if (rotate_info.y2 < 0)
12257           rotate_info.y2=0;
12258         else
12259           if (rotate_info.y2 > (int) windows->image.height)
12260             rotate_info.y2=(short) windows->image.height;
12261         /*
12262           Compute rotation angle from the slope of the line.
12263         */
12264         degrees=0.0;
12265         distance=(unsigned int)
12266           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12267           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12268         if (distance > 9)
12269           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12270             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12271       } while ((state & ExitState) == 0);
12272       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12273       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12274       if (distance <= 9)
12275         return(MagickTrue);
12276     }
12277   if (direction == VerticalRotateCommand)
12278     degrees-=90.0;
12279   if (degrees == 0.0)
12280     return(MagickTrue);
12281   /*
12282     Rotate image.
12283   */
12284   normalized_degrees=degrees;
12285   while (normalized_degrees < -45.0)
12286     normalized_degrees+=360.0;
12287   for (rotations=0; normalized_degrees > 45.0; rotations++)
12288     normalized_degrees-=90.0;
12289   if (normalized_degrees != 0.0)
12290     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12291   XSetCursorState(display,windows,MagickTrue);
12292   XCheckRefreshWindows(display,windows);
12293   (*image)->background_color.red=ScaleShortToQuantum(
12294     windows->pixel_info->pen_colors[pen_id].red);
12295   (*image)->background_color.green=ScaleShortToQuantum(
12296     windows->pixel_info->pen_colors[pen_id].green);
12297   (*image)->background_color.blue=ScaleShortToQuantum(
12298     windows->pixel_info->pen_colors[pen_id].blue);
12299   rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12300   XSetCursorState(display,windows,MagickFalse);
12301   if (rotate_image == (Image *) NULL)
12302     return(MagickFalse);
12303   *image=DestroyImage(*image);
12304   *image=rotate_image;
12305   if (windows->image.crop_geometry != (char *) NULL)
12306     {
12307       /*
12308         Rotate crop geometry.
12309       */
12310       width=(unsigned int) (*image)->columns;
12311       height=(unsigned int) (*image)->rows;
12312       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12313       switch (rotations % 4)
12314       {
12315         default:
12316         case 0:
12317           break;
12318         case 1:
12319         {
12320           /*
12321             Rotate 90 degrees.
12322           */
12323           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12324             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12325             (int) height-y,x);
12326           break;
12327         }
12328         case 2:
12329         {
12330           /*
12331             Rotate 180 degrees.
12332           */
12333           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12334             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12335           break;
12336         }
12337         case 3:
12338         {
12339           /*
12340             Rotate 270 degrees.
12341           */
12342           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12343             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12344           break;
12345         }
12346       }
12347     }
12348   if (windows->image.orphan != MagickFalse)
12349     return(MagickTrue);
12350   if (normalized_degrees != 0.0)
12351     {
12352       /*
12353         Update image colormap.
12354       */
12355       windows->image.window_changes.width=(int) (*image)->columns;
12356       windows->image.window_changes.height=(int) (*image)->rows;
12357       if (windows->image.crop_geometry != (char *) NULL)
12358         {
12359           /*
12360             Obtain dimensions of image from crop geometry.
12361           */
12362           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12363             &width,&height);
12364           windows->image.window_changes.width=(int) width;
12365           windows->image.window_changes.height=(int) height;
12366         }
12367       XConfigureImageColormap(display,resource_info,windows,*image);
12368     }
12369   else
12370     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12371       {
12372         windows->image.window_changes.width=windows->image.ximage->height;
12373         windows->image.window_changes.height=windows->image.ximage->width;
12374       }
12375   /*
12376     Update image configuration.
12377   */
12378   (void) XConfigureImage(display,resource_info,windows,*image);
12379   return(MagickTrue);
12380 }
12381 \f
12382 /*
12383 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12384 %                                                                             %
12385 %                                                                             %
12386 %                                                                             %
12387 +   X S a v e I m a g e                                                       %
12388 %                                                                             %
12389 %                                                                             %
12390 %                                                                             %
12391 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12392 %
12393 %  XSaveImage() saves an image to a file.
12394 %
12395 %  The format of the XSaveImage method is:
12396 %
12397 %      MagickBooleanType XSaveImage(Display *display,
12398 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
12399 %
12400 %  A description of each parameter follows:
12401 %
12402 %    o display: Specifies a connection to an X server; returned from
12403 %      XOpenDisplay.
12404 %
12405 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12406 %
12407 %    o windows: Specifies a pointer to a XWindows structure.
12408 %
12409 %    o image: the image.
12410 %
12411 */
12412 static MagickBooleanType XSaveImage(Display *display,
12413   XResourceInfo *resource_info,XWindows *windows,Image *image)
12414 {
12415   char
12416     filename[MaxTextExtent],
12417     geometry[MaxTextExtent];
12418
12419   Image
12420     *save_image;
12421
12422   ImageInfo
12423     *image_info;
12424
12425   MagickStatusType
12426     status;
12427
12428   /*
12429     Request file name from user.
12430   */
12431   if (resource_info->write_filename != (char *) NULL)
12432     (void) CopyMagickString(filename,resource_info->write_filename,
12433       MaxTextExtent);
12434   else
12435     {
12436       char
12437         path[MaxTextExtent];
12438
12439       int
12440         status;
12441
12442       GetPathComponent(image->filename,HeadPath,path);
12443       GetPathComponent(image->filename,TailPath,filename);
12444       status=chdir(path);
12445       if (status == -1)
12446         (void) ThrowMagickException(&image->exception,GetMagickModule(),
12447           FileOpenError,"UnableToOpenFile","%s",path);
12448     }
12449   XFileBrowserWidget(display,windows,"Save",filename);
12450   if (*filename == '\0')
12451     return(MagickTrue);
12452   if (IsPathAccessible(filename) != MagickFalse)
12453     {
12454       int
12455         status;
12456
12457       /*
12458         File exists-- seek user's permission before overwriting.
12459       */
12460       status=XConfirmWidget(display,windows,"Overwrite",filename);
12461       if (status <= 0)
12462         return(MagickTrue);
12463     }
12464   image_info=CloneImageInfo(resource_info->image_info);
12465   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12466   (void) SetImageInfo(image_info,1,&image->exception);
12467   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12468       (LocaleCompare(image_info->magick,"JPG") == 0))
12469     {
12470       char
12471         quality[MaxTextExtent];
12472
12473       int
12474         status;
12475
12476       /*
12477         Request JPEG quality from user.
12478       */
12479       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12480         image->quality);
12481       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12482         quality);
12483       if (*quality == '\0')
12484         return(MagickTrue);
12485       image->quality=StringToUnsignedLong(quality);
12486       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12487     }
12488   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12489       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12490       (LocaleCompare(image_info->magick,"PS") == 0) ||
12491       (LocaleCompare(image_info->magick,"PS2") == 0))
12492     {
12493       char
12494         geometry[MaxTextExtent];
12495
12496       /*
12497         Request page geometry from user.
12498       */
12499       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12500       if (LocaleCompare(image_info->magick,"PDF") == 0)
12501         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12502       if (image_info->page != (char *) NULL)
12503         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12504       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12505         "Select page geometry:",geometry);
12506       if (*geometry != '\0')
12507         image_info->page=GetPageGeometry(geometry);
12508     }
12509   /*
12510     Apply image transforms.
12511   */
12512   XSetCursorState(display,windows,MagickTrue);
12513   XCheckRefreshWindows(display,windows);
12514   save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12515   if (save_image == (Image *) NULL)
12516     return(MagickFalse);
12517   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12518     windows->image.ximage->width,windows->image.ximage->height);
12519   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12520   /*
12521     Write image.
12522   */
12523   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12524   status=WriteImage(image_info,save_image,&image->exception);
12525   if (status != MagickFalse)
12526     image->taint=MagickFalse;
12527   save_image=DestroyImage(save_image);
12528   image_info=DestroyImageInfo(image_info);
12529   XSetCursorState(display,windows,MagickFalse);
12530   return(status != 0 ? MagickTrue : MagickFalse);
12531 }
12532 \f
12533 /*
12534 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12535 %                                                                             %
12536 %                                                                             %
12537 %                                                                             %
12538 +   X S c r e e n E v e n t                                                   %
12539 %                                                                             %
12540 %                                                                             %
12541 %                                                                             %
12542 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12543 %
12544 %  XScreenEvent() handles global events associated with the Pan and Magnify
12545 %  windows.
12546 %
12547 %  The format of the XScreenEvent function is:
12548 %
12549 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12550 %
12551 %  A description of each parameter follows:
12552 %
12553 %    o display: Specifies a pointer to the Display structure;  returned from
12554 %      XOpenDisplay.
12555 %
12556 %    o windows: Specifies a pointer to a XWindows structure.
12557 %
12558 %    o event: Specifies a pointer to a X11 XEvent structure.
12559 %
12560 %
12561 */
12562
12563 #if defined(__cplusplus) || defined(c_plusplus)
12564 extern "C" {
12565 #endif
12566
12567 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12568 {
12569   register XWindows
12570     *windows;
12571
12572   windows=(XWindows *) data;
12573   if ((event->type == ClientMessage) &&
12574       (event->xclient.window == windows->image.id))
12575     return(MagickFalse);
12576   return(MagickTrue);
12577 }
12578
12579 #if defined(__cplusplus) || defined(c_plusplus)
12580 }
12581 #endif
12582
12583 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12584 {
12585   register int
12586     x,
12587     y;
12588
12589   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12590   if (event->xany.window == windows->command.id)
12591     return;
12592   switch (event->type)
12593   {
12594     case ButtonPress:
12595     case ButtonRelease:
12596     {
12597       if ((event->xbutton.button == Button3) &&
12598           (event->xbutton.state & Mod1Mask))
12599         {
12600           /*
12601             Convert Alt-Button3 to Button2.
12602           */
12603           event->xbutton.button=Button2;
12604           event->xbutton.state&=(~Mod1Mask);
12605         }
12606       if (event->xbutton.window == windows->backdrop.id)
12607         {
12608           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12609             event->xbutton.time);
12610           break;
12611         }
12612       if (event->xbutton.window == windows->pan.id)
12613         {
12614           XPanImage(display,windows,event);
12615           break;
12616         }
12617       if (event->xbutton.window == windows->image.id)
12618         if (event->xbutton.button == Button2)
12619           {
12620             /*
12621               Update magnified image.
12622             */
12623             x=event->xbutton.x;
12624             y=event->xbutton.y;
12625             if (x < 0)
12626               x=0;
12627             else
12628               if (x >= (int) windows->image.width)
12629                 x=(int) (windows->image.width-1);
12630             windows->magnify.x=(int) windows->image.x+x;
12631             if (y < 0)
12632               y=0;
12633             else
12634              if (y >= (int) windows->image.height)
12635                y=(int) (windows->image.height-1);
12636             windows->magnify.y=windows->image.y+y;
12637             if (windows->magnify.mapped == MagickFalse)
12638               (void) XMapRaised(display,windows->magnify.id);
12639             XMakeMagnifyImage(display,windows);
12640             if (event->type == ButtonRelease)
12641               (void) XWithdrawWindow(display,windows->info.id,
12642                 windows->info.screen);
12643             break;
12644           }
12645       break;
12646     }
12647     case ClientMessage:
12648     {
12649       /*
12650         If client window delete message, exit.
12651       */
12652       if (event->xclient.message_type != windows->wm_protocols)
12653         break;
12654       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12655         break;
12656       if (event->xclient.window == windows->magnify.id)
12657         {
12658           (void) XWithdrawWindow(display,windows->magnify.id,
12659             windows->magnify.screen);
12660           break;
12661         }
12662       break;
12663     }
12664     case ConfigureNotify:
12665     {
12666       if (event->xconfigure.window == windows->magnify.id)
12667         {
12668           unsigned int
12669             magnify;
12670
12671           /*
12672             Magnify window has a new configuration.
12673           */
12674           windows->magnify.width=(unsigned int) event->xconfigure.width;
12675           windows->magnify.height=(unsigned int) event->xconfigure.height;
12676           if (windows->magnify.mapped == MagickFalse)
12677             break;
12678           magnify=1;
12679           while ((int) magnify <= event->xconfigure.width)
12680             magnify<<=1;
12681           while ((int) magnify <= event->xconfigure.height)
12682             magnify<<=1;
12683           magnify>>=1;
12684           if (((int) magnify != event->xconfigure.width) ||
12685               ((int) magnify != event->xconfigure.height))
12686             {
12687               XWindowChanges
12688                 window_changes;
12689
12690               window_changes.width=(int) magnify;
12691               window_changes.height=(int) magnify;
12692               (void) XReconfigureWMWindow(display,windows->magnify.id,
12693                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12694                 &window_changes);
12695               break;
12696             }
12697           XMakeMagnifyImage(display,windows);
12698           break;
12699         }
12700       break;
12701     }
12702     case Expose:
12703     {
12704       if (event->xexpose.window == windows->image.id)
12705         {
12706           XRefreshWindow(display,&windows->image,event);
12707           break;
12708         }
12709       if (event->xexpose.window == windows->pan.id)
12710         if (event->xexpose.count == 0)
12711           {
12712             XDrawPanRectangle(display,windows);
12713             break;
12714           }
12715       if (event->xexpose.window == windows->magnify.id)
12716         if (event->xexpose.count == 0)
12717           {
12718             XMakeMagnifyImage(display,windows);
12719             break;
12720           }
12721       break;
12722     }
12723     case KeyPress:
12724     {
12725       char
12726         command[MaxTextExtent];
12727
12728       KeySym
12729         key_symbol;
12730
12731       if (event->xkey.window != windows->magnify.id)
12732         break;
12733       /*
12734         Respond to a user key press.
12735       */
12736       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12737         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12738       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12739       break;
12740     }
12741     case MapNotify:
12742     {
12743       if (event->xmap.window == windows->magnify.id)
12744         {
12745           windows->magnify.mapped=MagickTrue;
12746           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12747           break;
12748         }
12749       if (event->xmap.window == windows->info.id)
12750         {
12751           windows->info.mapped=MagickTrue;
12752           break;
12753         }
12754       break;
12755     }
12756     case MotionNotify:
12757     {
12758       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12759       if (event->xmotion.window == windows->image.id)
12760         if (windows->magnify.mapped != MagickFalse)
12761           {
12762             /*
12763               Update magnified image.
12764             */
12765             x=event->xmotion.x;
12766             y=event->xmotion.y;
12767             if (x < 0)
12768               x=0;
12769             else
12770               if (x >= (int) windows->image.width)
12771                 x=(int) (windows->image.width-1);
12772             windows->magnify.x=(int) windows->image.x+x;
12773             if (y < 0)
12774               y=0;
12775             else
12776              if (y >= (int) windows->image.height)
12777                y=(int) (windows->image.height-1);
12778             windows->magnify.y=windows->image.y+y;
12779             XMakeMagnifyImage(display,windows);
12780           }
12781       break;
12782     }
12783     case UnmapNotify:
12784     {
12785       if (event->xunmap.window == windows->magnify.id)
12786         {
12787           windows->magnify.mapped=MagickFalse;
12788           break;
12789         }
12790       if (event->xunmap.window == windows->info.id)
12791         {
12792           windows->info.mapped=MagickFalse;
12793           break;
12794         }
12795       break;
12796     }
12797     default:
12798       break;
12799   }
12800 }
12801 \f
12802 /*
12803 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12804 %                                                                             %
12805 %                                                                             %
12806 %                                                                             %
12807 +   X S e t C r o p G e o m e t r y                                           %
12808 %                                                                             %
12809 %                                                                             %
12810 %                                                                             %
12811 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12812 %
12813 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12814 %  and translates it to a cropping geometry relative to the image.
12815 %
12816 %  The format of the XSetCropGeometry method is:
12817 %
12818 %      void XSetCropGeometry(Display *display,XWindows *windows,
12819 %        RectangleInfo *crop_info,Image *image)
12820 %
12821 %  A description of each parameter follows:
12822 %
12823 %    o display: Specifies a connection to an X server; returned from
12824 %      XOpenDisplay.
12825 %
12826 %    o windows: Specifies a pointer to a XWindows structure.
12827 %
12828 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12829 %      Image window to crop.
12830 %
12831 %    o image: the image.
12832 %
12833 */
12834 static void XSetCropGeometry(Display *display,XWindows *windows,
12835   RectangleInfo *crop_info,Image *image)
12836 {
12837   char
12838     text[MaxTextExtent];
12839
12840   int
12841     x,
12842     y;
12843
12844   MagickRealType
12845     scale_factor;
12846
12847   unsigned int
12848     height,
12849     width;
12850
12851   if (windows->info.mapped != MagickFalse)
12852     {
12853       /*
12854         Display info on cropping rectangle.
12855       */
12856       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12857         (double) crop_info->width,(double) crop_info->height,(double)
12858         crop_info->x,(double) crop_info->y);
12859       XInfoWidget(display,windows,text);
12860     }
12861   /*
12862     Cropping geometry is relative to any previous crop geometry.
12863   */
12864   x=0;
12865   y=0;
12866   width=(unsigned int) image->columns;
12867   height=(unsigned int) image->rows;
12868   if (windows->image.crop_geometry != (char *) NULL)
12869     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12870   else
12871     windows->image.crop_geometry=AcquireString((char *) NULL);
12872   /*
12873     Define the crop geometry string from the cropping rectangle.
12874   */
12875   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12876   if (crop_info->x > 0)
12877     x+=(int) (scale_factor*crop_info->x+0.5);
12878   width=(unsigned int) (scale_factor*crop_info->width+0.5);
12879   if (width == 0)
12880     width=1;
12881   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12882   if (crop_info->y > 0)
12883     y+=(int) (scale_factor*crop_info->y+0.5);
12884   height=(unsigned int) (scale_factor*crop_info->height+0.5);
12885   if (height == 0)
12886     height=1;
12887   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12888     "%ux%u%+d%+d",width,height,x,y);
12889 }
12890 \f
12891 /*
12892 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12893 %                                                                             %
12894 %                                                                             %
12895 %                                                                             %
12896 +   X T i l e I m a g e                                                       %
12897 %                                                                             %
12898 %                                                                             %
12899 %                                                                             %
12900 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12901 %
12902 %  XTileImage() loads or deletes a selected tile from a visual image directory.
12903 %  The load or delete command is chosen from a menu.
12904 %
12905 %  The format of the XTileImage method is:
12906 %
12907 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
12908 %        XWindows *windows,Image *image,XEvent *event)
12909 %
12910 %  A description of each parameter follows:
12911 %
12912 %    o tile_image:  XTileImage reads or deletes the tile image
12913 %      and returns it.  A null image is returned if an error occurs.
12914 %
12915 %    o display: Specifies a connection to an X server;  returned from
12916 %      XOpenDisplay.
12917 %
12918 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12919 %
12920 %    o windows: Specifies a pointer to a XWindows structure.
12921 %
12922 %    o image: the image; returned from ReadImage.
12923 %
12924 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
12925 %      the entire image is refreshed.
12926 %
12927 */
12928 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12929   XWindows *windows,Image *image,XEvent *event)
12930 {
12931   static const char
12932     *VerbMenu[] =
12933     {
12934       "Load",
12935       "Next",
12936       "Former",
12937       "Delete",
12938       "Update",
12939       (char *) NULL,
12940     };
12941
12942   static const ModeType
12943     TileCommands[] =
12944     {
12945       TileLoadCommand,
12946       TileNextCommand,
12947       TileFormerCommand,
12948       TileDeleteCommand,
12949       TileUpdateCommand
12950     };
12951
12952   char
12953     command[MaxTextExtent],
12954     filename[MaxTextExtent];
12955
12956   Image
12957     *tile_image;
12958
12959   int
12960     id,
12961     status,
12962     tile,
12963     x,
12964     y;
12965
12966   MagickRealType
12967     scale_factor;
12968
12969   register char
12970     *p,
12971     *q;
12972
12973   register int
12974     i;
12975
12976   unsigned int
12977     height,
12978     width;
12979
12980   /*
12981     Tile image is relative to montage image configuration.
12982   */
12983   x=0;
12984   y=0;
12985   width=(unsigned int) image->columns;
12986   height=(unsigned int) image->rows;
12987   if (windows->image.crop_geometry != (char *) NULL)
12988     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12989   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12990   event->xbutton.x+=windows->image.x;
12991   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
12992   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12993   event->xbutton.y+=windows->image.y;
12994   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
12995   /*
12996     Determine size and location of each tile in the visual image directory.
12997   */
12998   width=(unsigned int) image->columns;
12999   height=(unsigned int) image->rows;
13000   x=0;
13001   y=0;
13002   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13003   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13004     (event->xbutton.x-x)/width;
13005   if (tile < 0)
13006     {
13007       /*
13008         Button press is outside any tile.
13009       */
13010       (void) XBell(display,0);
13011       return((Image *) NULL);
13012     }
13013   /*
13014     Determine file name from the tile directory.
13015   */
13016   p=image->directory;
13017   for (i=tile; (i != 0) && (*p != '\0'); )
13018   {
13019     if (*p == '\n')
13020       i--;
13021     p++;
13022   }
13023   if (*p == '\0')
13024     {
13025       /*
13026         Button press is outside any tile.
13027       */
13028       (void) XBell(display,0);
13029       return((Image *) NULL);
13030     }
13031   /*
13032     Select a command from the pop-up menu.
13033   */
13034   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13035   if (id < 0)
13036     return((Image *) NULL);
13037   q=p;
13038   while ((*q != '\n') && (*q != '\0'))
13039     q++;
13040   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13041   /*
13042     Perform command for the selected tile.
13043   */
13044   XSetCursorState(display,windows,MagickTrue);
13045   XCheckRefreshWindows(display,windows);
13046   tile_image=NewImageList();
13047   switch (TileCommands[id])
13048   {
13049     case TileLoadCommand:
13050     {
13051       /*
13052         Load tile image.
13053       */
13054       XCheckRefreshWindows(display,windows);
13055       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13056         MaxTextExtent);
13057       (void) CopyMagickString(resource_info->image_info->filename,filename,
13058         MaxTextExtent);
13059       tile_image=ReadImage(resource_info->image_info,&image->exception);
13060       CatchException(&image->exception);
13061       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13062       break;
13063     }
13064     case TileNextCommand:
13065     {
13066       /*
13067         Display next image.
13068       */
13069       XClientMessage(display,windows->image.id,windows->im_protocols,
13070         windows->im_next_image,CurrentTime);
13071       break;
13072     }
13073     case TileFormerCommand:
13074     {
13075       /*
13076         Display former image.
13077       */
13078       XClientMessage(display,windows->image.id,windows->im_protocols,
13079         windows->im_former_image,CurrentTime);
13080       break;
13081     }
13082     case TileDeleteCommand:
13083     {
13084       /*
13085         Delete tile image.
13086       */
13087       if (IsPathAccessible(filename) == MagickFalse)
13088         {
13089           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13090           break;
13091         }
13092       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13093       if (status <= 0)
13094         break;
13095       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13096       if (status != MagickFalse)
13097         {
13098           XNoticeWidget(display,windows,"Unable to delete image file:",
13099             filename);
13100           break;
13101         }
13102     }
13103     case TileUpdateCommand:
13104     {
13105       ExceptionInfo
13106         *exception;
13107
13108       int
13109         x_offset,
13110         y_offset;
13111
13112       PixelPacket
13113         pixel;
13114
13115       register int
13116         j;
13117
13118       register Quantum
13119         *s;
13120
13121       /*
13122         Ensure all the images exist.
13123       */
13124       tile=0;
13125       for (p=image->directory; *p != '\0'; p++)
13126       {
13127         CacheView
13128           *image_view;
13129
13130         q=p;
13131         while ((*q != '\n') && (*q != '\0'))
13132           q++;
13133         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13134         p=q;
13135         if (IsPathAccessible(filename) != MagickFalse)
13136           {
13137             tile++;
13138             continue;
13139           }
13140         /*
13141           Overwrite tile with background color.
13142         */
13143         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13144         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13145         exception=(&image->exception);
13146         image_view=AcquireCacheView(image);
13147         (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13148         for (i=0; i < (int) height; i++)
13149         {
13150           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13151             y_offset+i,width,1,exception);
13152           if (s == (Quantum *) NULL)
13153             break;
13154           for (j=0; j < (int) width; j++)
13155           {
13156             SetPixelPacket(image,&pixel,s);
13157             s+=GetPixelChannels(image);
13158           }
13159           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13160             break;
13161         }
13162         image_view=DestroyCacheView(image_view);
13163         tile++;
13164       }
13165       windows->image.window_changes.width=(int) image->columns;
13166       windows->image.window_changes.height=(int) image->rows;
13167       XConfigureImageColormap(display,resource_info,windows,image);
13168       (void) XConfigureImage(display,resource_info,windows,image);
13169       break;
13170     }
13171     default:
13172       break;
13173   }
13174   XSetCursorState(display,windows,MagickFalse);
13175   return(tile_image);
13176 }
13177 \f
13178 /*
13179 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13180 %                                                                             %
13181 %                                                                             %
13182 %                                                                             %
13183 +   X T r a n s l a t e I m a g e                                             %
13184 %                                                                             %
13185 %                                                                             %
13186 %                                                                             %
13187 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13188 %
13189 %  XTranslateImage() translates the image within an Image window by one pixel
13190 %  as specified by the key symbol.  If the image has a `montage string the
13191 %  translation is respect to the width and height contained within the string.
13192 %
13193 %  The format of the XTranslateImage method is:
13194 %
13195 %      void XTranslateImage(Display *display,XWindows *windows,
13196 %        Image *image,const KeySym key_symbol)
13197 %
13198 %  A description of each parameter follows:
13199 %
13200 %    o display: Specifies a connection to an X server; returned from
13201 %      XOpenDisplay.
13202 %
13203 %    o windows: Specifies a pointer to a XWindows structure.
13204 %
13205 %    o image: the image.
13206 %
13207 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13208 %      to trim.
13209 %
13210 */
13211 static void XTranslateImage(Display *display,XWindows *windows,
13212   Image *image,const KeySym key_symbol)
13213 {
13214   char
13215     text[MaxTextExtent];
13216
13217   int
13218     x,
13219     y;
13220
13221   unsigned int
13222     x_offset,
13223     y_offset;
13224
13225   /*
13226     User specified a pan position offset.
13227   */
13228   x_offset=windows->image.width;
13229   y_offset=windows->image.height;
13230   if (image->montage != (char *) NULL)
13231     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13232   switch ((int) key_symbol)
13233   {
13234     case XK_Home:
13235     case XK_KP_Home:
13236     {
13237       windows->image.x=(int) windows->image.width/2;
13238       windows->image.y=(int) windows->image.height/2;
13239       break;
13240     }
13241     case XK_Left:
13242     case XK_KP_Left:
13243     {
13244       windows->image.x-=x_offset;
13245       break;
13246     }
13247     case XK_Next:
13248     case XK_Up:
13249     case XK_KP_Up:
13250     {
13251       windows->image.y-=y_offset;
13252       break;
13253     }
13254     case XK_Right:
13255     case XK_KP_Right:
13256     {
13257       windows->image.x+=x_offset;
13258       break;
13259     }
13260     case XK_Prior:
13261     case XK_Down:
13262     case XK_KP_Down:
13263     {
13264       windows->image.y+=y_offset;
13265       break;
13266     }
13267     default:
13268       return;
13269   }
13270   /*
13271     Check boundary conditions.
13272   */
13273   if (windows->image.x < 0)
13274     windows->image.x=0;
13275   else
13276     if ((int) (windows->image.x+windows->image.width) >
13277         windows->image.ximage->width)
13278       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13279   if (windows->image.y < 0)
13280     windows->image.y=0;
13281   else
13282     if ((int) (windows->image.y+windows->image.height) >
13283         windows->image.ximage->height)
13284       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13285   /*
13286     Refresh Image window.
13287   */
13288   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13289     windows->image.width,windows->image.height,windows->image.x,
13290     windows->image.y);
13291   XInfoWidget(display,windows,text);
13292   XCheckRefreshWindows(display,windows);
13293   XDrawPanRectangle(display,windows);
13294   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13295   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13296 }
13297 \f
13298 /*
13299 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13300 %                                                                             %
13301 %                                                                             %
13302 %                                                                             %
13303 +   X T r i m I m a g e                                                       %
13304 %                                                                             %
13305 %                                                                             %
13306 %                                                                             %
13307 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13308 %
13309 %  XTrimImage() trims the edges from the Image window.
13310 %
13311 %  The format of the XTrimImage method is:
13312 %
13313 %      MagickBooleanType XTrimImage(Display *display,
13314 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
13315 %
13316 %  A description of each parameter follows:
13317 %
13318 %    o display: Specifies a connection to an X server; returned from
13319 %      XOpenDisplay.
13320 %
13321 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13322 %
13323 %    o windows: Specifies a pointer to a XWindows structure.
13324 %
13325 %    o image: the image.
13326 %
13327 */
13328 static MagickBooleanType XTrimImage(Display *display,
13329   XResourceInfo *resource_info,XWindows *windows,Image *image)
13330 {
13331   RectangleInfo
13332     trim_info;
13333
13334   register int
13335     x,
13336     y;
13337
13338   size_t
13339     background,
13340     pixel;
13341
13342   /*
13343     Trim edges from image.
13344   */
13345   XSetCursorState(display,windows,MagickTrue);
13346   XCheckRefreshWindows(display,windows);
13347   /*
13348     Crop the left edge.
13349   */
13350   background=XGetPixel(windows->image.ximage,0,0);
13351   trim_info.width=(size_t) windows->image.ximage->width;
13352   for (x=0; x < windows->image.ximage->width; x++)
13353   {
13354     for (y=0; y < windows->image.ximage->height; y++)
13355     {
13356       pixel=XGetPixel(windows->image.ximage,x,y);
13357       if (pixel != background)
13358         break;
13359     }
13360     if (y < windows->image.ximage->height)
13361       break;
13362   }
13363   trim_info.x=(ssize_t) x;
13364   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13365     {
13366       XSetCursorState(display,windows,MagickFalse);
13367       return(MagickFalse);
13368     }
13369   /*
13370     Crop the right edge.
13371   */
13372   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13373   for (x=windows->image.ximage->width-1; x != 0; x--)
13374   {
13375     for (y=0; y < windows->image.ximage->height; y++)
13376     {
13377       pixel=XGetPixel(windows->image.ximage,x,y);
13378       if (pixel != background)
13379         break;
13380     }
13381     if (y < windows->image.ximage->height)
13382       break;
13383   }
13384   trim_info.width=(size_t) (x-trim_info.x+1);
13385   /*
13386     Crop the top edge.
13387   */
13388   background=XGetPixel(windows->image.ximage,0,0);
13389   trim_info.height=(size_t) windows->image.ximage->height;
13390   for (y=0; y < windows->image.ximage->height; y++)
13391   {
13392     for (x=0; x < windows->image.ximage->width; x++)
13393     {
13394       pixel=XGetPixel(windows->image.ximage,x,y);
13395       if (pixel != background)
13396         break;
13397     }
13398     if (x < windows->image.ximage->width)
13399       break;
13400   }
13401   trim_info.y=(ssize_t) y;
13402   /*
13403     Crop the bottom edge.
13404   */
13405   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13406   for (y=windows->image.ximage->height-1; y != 0; y--)
13407   {
13408     for (x=0; x < windows->image.ximage->width; x++)
13409     {
13410       pixel=XGetPixel(windows->image.ximage,x,y);
13411       if (pixel != background)
13412         break;
13413     }
13414     if (x < windows->image.ximage->width)
13415       break;
13416   }
13417   trim_info.height=(size_t) y-trim_info.y+1;
13418   if (((unsigned int) trim_info.width != windows->image.width) ||
13419       ((unsigned int) trim_info.height != windows->image.height))
13420     {
13421       /*
13422         Reconfigure Image window as defined by the trimming rectangle.
13423       */
13424       XSetCropGeometry(display,windows,&trim_info,image);
13425       windows->image.window_changes.width=(int) trim_info.width;
13426       windows->image.window_changes.height=(int) trim_info.height;
13427       (void) XConfigureImage(display,resource_info,windows,image);
13428     }
13429   XSetCursorState(display,windows,MagickFalse);
13430   return(MagickTrue);
13431 }
13432 \f
13433 /*
13434 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13435 %                                                                             %
13436 %                                                                             %
13437 %                                                                             %
13438 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13439 %                                                                             %
13440 %                                                                             %
13441 %                                                                             %
13442 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13443 %
13444 %  XVisualDirectoryImage() creates a Visual Image Directory.
13445 %
13446 %  The format of the XVisualDirectoryImage method is:
13447 %
13448 %      Image *XVisualDirectoryImage(Display *display,
13449 %        XResourceInfo *resource_info,XWindows *windows)
13450 %
13451 %  A description of each parameter follows:
13452 %
13453 %    o nexus: Method XVisualDirectoryImage returns a visual image
13454 %      directory if it can be created successfully.  Otherwise a null image
13455 %      is returned.
13456 %
13457 %    o display: Specifies a connection to an X server; returned from
13458 %      XOpenDisplay.
13459 %
13460 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13461 %
13462 %    o windows: Specifies a pointer to a XWindows structure.
13463 %
13464 */
13465 static Image *XVisualDirectoryImage(Display *display,
13466   XResourceInfo *resource_info,XWindows *windows)
13467 {
13468 #define TileImageTag  "Scale/Image"
13469 #define XClientName  "montage"
13470
13471   char
13472     **filelist;
13473
13474   ExceptionInfo
13475     *exception;
13476
13477   Image
13478     *images,
13479     *montage_image,
13480     *next_image,
13481     *thumbnail_image;
13482
13483   ImageInfo
13484     *read_info;
13485
13486   int
13487     number_files;
13488
13489   MagickBooleanType
13490     backdrop;
13491
13492   MagickStatusType
13493     status;
13494
13495   MontageInfo
13496     *montage_info;
13497
13498   RectangleInfo
13499     geometry;
13500
13501   register int
13502     i;
13503
13504   static char
13505     filename[MaxTextExtent] = "\0",
13506     filenames[MaxTextExtent] = "*";
13507
13508   XResourceInfo
13509     background_resources;
13510
13511   /*
13512     Request file name from user.
13513   */
13514   XFileBrowserWidget(display,windows,"Directory",filenames);
13515   if (*filenames == '\0')
13516     return((Image *) NULL);
13517   /*
13518     Expand the filenames.
13519   */
13520   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13521   if (filelist == (char **) NULL)
13522     {
13523       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13524         filenames);
13525       return((Image *) NULL);
13526     }
13527   number_files=1;
13528   filelist[0]=filenames;
13529   status=ExpandFilenames(&number_files,&filelist);
13530   if ((status == MagickFalse) || (number_files == 0))
13531     {
13532       if (number_files == 0)
13533         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13534       else
13535         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13536           filenames);
13537       return((Image *) NULL);
13538     }
13539   /*
13540     Set image background resources.
13541   */
13542   background_resources=(*resource_info);
13543   background_resources.window_id=AcquireString("");
13544   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13545     "0x%lx",windows->image.id);
13546   background_resources.backdrop=MagickTrue;
13547   /*
13548     Read each image and convert them to a tile.
13549   */
13550   backdrop=(windows->visual_info->klass == TrueColor) ||
13551     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13552   read_info=CloneImageInfo(resource_info->image_info);
13553   (void) SetImageOption(read_info,"jpeg:size","120x120");
13554   (void) CloneString(&read_info->size,DefaultTileGeometry);
13555   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13556     (void *) NULL);
13557   images=NewImageList();
13558   exception=AcquireExceptionInfo();
13559   XSetCursorState(display,windows,MagickTrue);
13560   XCheckRefreshWindows(display,windows);
13561   for (i=0; i < (int) number_files; i++)
13562   {
13563     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13564     filelist[i]=DestroyString(filelist[i]);
13565     *read_info->magick='\0';
13566     next_image=ReadImage(read_info,exception);
13567     CatchException(exception);
13568     if (next_image != (Image *) NULL)
13569       {
13570         (void) DeleteImageProperty(next_image,"label");
13571         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13572           read_info,next_image,DefaultTileLabel));
13573         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13574           exception);
13575         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13576           geometry.height,exception);
13577         if (thumbnail_image != (Image *) NULL)
13578           {
13579             next_image=DestroyImage(next_image);
13580             next_image=thumbnail_image;
13581           }
13582         if (backdrop)
13583           {
13584             (void) XDisplayBackgroundImage(display,&background_resources,
13585               next_image);
13586             XSetCursorState(display,windows,MagickTrue);
13587           }
13588         AppendImageToList(&images,next_image);
13589         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13590           {
13591             MagickBooleanType
13592               proceed;
13593
13594             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13595               (MagickSizeType) number_files);
13596             if (proceed == MagickFalse)
13597               break;
13598           }
13599       }
13600   }
13601   exception=DestroyExceptionInfo(exception);
13602   filelist=(char **) RelinquishMagickMemory(filelist);
13603   if (images == (Image *) NULL)
13604     {
13605       read_info=DestroyImageInfo(read_info);
13606       XSetCursorState(display,windows,MagickFalse);
13607       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13608       return((Image *) NULL);
13609     }
13610   /*
13611     Create the Visual Image Directory.
13612   */
13613   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13614   montage_info->pointsize=10;
13615   if (resource_info->font != (char *) NULL)
13616     (void) CloneString(&montage_info->font,resource_info->font);
13617   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13618   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13619     images),&images->exception);
13620   images=DestroyImageList(images);
13621   montage_info=DestroyMontageInfo(montage_info);
13622   read_info=DestroyImageInfo(read_info);
13623   XSetCursorState(display,windows,MagickFalse);
13624   if (montage_image == (Image *) NULL)
13625     return(montage_image);
13626   XClientMessage(display,windows->image.id,windows->im_protocols,
13627     windows->im_next_image,CurrentTime);
13628   return(montage_image);
13629 }
13630 \f
13631 /*
13632 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13633 %                                                                             %
13634 %                                                                             %
13635 %                                                                             %
13636 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13637 %                                                                             %
13638 %                                                                             %
13639 %                                                                             %
13640 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13641 %
13642 %  XDisplayBackgroundImage() displays an image in the background of a window.
13643 %
13644 %  The format of the XDisplayBackgroundImage method is:
13645 %
13646 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13647 %        XResourceInfo *resource_info,Image *image)
13648 %
13649 %  A description of each parameter follows:
13650 %
13651 %    o display: Specifies a connection to an X server;  returned from
13652 %      XOpenDisplay.
13653 %
13654 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13655 %
13656 %    o image: the image.
13657 %
13658 */
13659 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13660   XResourceInfo *resource_info,Image *image)
13661 {
13662   char
13663     geometry[MaxTextExtent],
13664     visual_type[MaxTextExtent];
13665
13666   int
13667     height,
13668     status,
13669     width;
13670
13671   RectangleInfo
13672     geometry_info;
13673
13674   static XPixelInfo
13675     pixel;
13676
13677   static XStandardColormap
13678     *map_info;
13679
13680   static XVisualInfo
13681     *visual_info = (XVisualInfo *) NULL;
13682
13683   static XWindowInfo
13684     window_info;
13685
13686   size_t
13687     delay;
13688
13689   Window
13690     root_window;
13691
13692   XGCValues
13693     context_values;
13694
13695   XResourceInfo
13696     resources;
13697
13698   XWindowAttributes
13699     window_attributes;
13700
13701   /*
13702     Determine target window.
13703   */
13704   assert(image != (Image *) NULL);
13705   assert(image->signature == MagickSignature);
13706   if (image->debug != MagickFalse)
13707     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13708   resources=(*resource_info);
13709   window_info.id=(Window) NULL;
13710   root_window=XRootWindow(display,XDefaultScreen(display));
13711   if (LocaleCompare(resources.window_id,"root") == 0)
13712     window_info.id=root_window;
13713   else
13714     {
13715       if (isdigit((unsigned char) *resources.window_id) != 0)
13716         window_info.id=XWindowByID(display,root_window,
13717           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13718       if (window_info.id == (Window) NULL)
13719         window_info.id=XWindowByName(display,root_window,resources.window_id);
13720     }
13721   if (window_info.id == (Window) NULL)
13722     {
13723       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13724         resources.window_id);
13725       return(MagickFalse);
13726     }
13727   /*
13728     Determine window visual id.
13729   */
13730   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13731   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13732   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13733   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13734   if (status != 0)
13735     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13736       XVisualIDFromVisual(window_attributes.visual));
13737   if (visual_info == (XVisualInfo *) NULL)
13738     {
13739       /*
13740         Allocate standard colormap.
13741       */
13742       map_info=XAllocStandardColormap();
13743       if (map_info == (XStandardColormap *) NULL)
13744         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13745           image->filename);
13746       map_info->colormap=(Colormap) NULL;
13747       pixel.pixels=(unsigned long *) NULL;
13748       /*
13749         Initialize visual info.
13750       */
13751       resources.map_type=(char *) NULL;
13752       resources.visual_type=visual_type;
13753       visual_info=XBestVisualInfo(display,map_info,&resources);
13754       if (visual_info == (XVisualInfo *) NULL)
13755         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13756           resources.visual_type);
13757       /*
13758         Initialize window info.
13759       */
13760       window_info.ximage=(XImage *) NULL;
13761       window_info.matte_image=(XImage *) NULL;
13762       window_info.pixmap=(Pixmap) NULL;
13763       window_info.matte_pixmap=(Pixmap) NULL;
13764     }
13765   /*
13766     Free previous root colors.
13767   */
13768   if (window_info.id == root_window)
13769     (void) XDestroyWindowColors(display,root_window);
13770   /*
13771     Initialize Standard Colormap.
13772   */
13773   resources.colormap=SharedColormap;
13774   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13775   /*
13776     Graphic context superclass.
13777   */
13778   context_values.background=pixel.background_color.pixel;
13779   context_values.foreground=pixel.foreground_color.pixel;
13780   pixel.annotate_context=XCreateGC(display,window_info.id,
13781     (size_t) (GCBackground | GCForeground),&context_values);
13782   if (pixel.annotate_context == (GC) NULL)
13783     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13784       image->filename);
13785   /*
13786     Initialize Image window attributes.
13787   */
13788   window_info.name=AcquireString("\0");
13789   window_info.icon_name=AcquireString("\0");
13790   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13791     &resources,&window_info);
13792   /*
13793     Create the X image.
13794   */
13795   window_info.width=(unsigned int) image->columns;
13796   window_info.height=(unsigned int) image->rows;
13797   if ((image->columns != window_info.width) ||
13798       (image->rows != window_info.height))
13799     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13800       image->filename);
13801   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13802     window_attributes.width,window_attributes.height);
13803   geometry_info.width=window_info.width;
13804   geometry_info.height=window_info.height;
13805   geometry_info.x=(ssize_t) window_info.x;
13806   geometry_info.y=(ssize_t) window_info.y;
13807   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13808     &geometry_info.width,&geometry_info.height);
13809   window_info.width=(unsigned int) geometry_info.width;
13810   window_info.height=(unsigned int) geometry_info.height;
13811   window_info.x=(int) geometry_info.x;
13812   window_info.y=(int) geometry_info.y;
13813   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13814     window_info.height);
13815   if (status == MagickFalse)
13816     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13817       image->filename);
13818   window_info.x=0;
13819   window_info.y=0;
13820   if (image->debug != MagickFalse)
13821     {
13822       (void) LogMagickEvent(X11Event,GetMagickModule(),
13823         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13824         (double) image->columns,(double) image->rows);
13825       if (image->colors != 0)
13826         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13827           image->colors);
13828       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13829     }
13830   /*
13831     Adjust image dimensions as specified by backdrop or geometry options.
13832   */
13833   width=(int) window_info.width;
13834   height=(int) window_info.height;
13835   if (resources.backdrop != MagickFalse)
13836     {
13837       /*
13838         Center image on window.
13839       */
13840       window_info.x=(window_attributes.width/2)-
13841         (window_info.ximage->width/2);
13842       window_info.y=(window_attributes.height/2)-
13843         (window_info.ximage->height/2);
13844       width=window_attributes.width;
13845       height=window_attributes.height;
13846     }
13847   if ((resources.image_geometry != (char *) NULL) &&
13848       (*resources.image_geometry != '\0'))
13849     {
13850       char
13851         default_geometry[MaxTextExtent];
13852
13853       int
13854         flags,
13855         gravity;
13856
13857       XSizeHints
13858         *size_hints;
13859
13860       /*
13861         User specified geometry.
13862       */
13863       size_hints=XAllocSizeHints();
13864       if (size_hints == (XSizeHints *) NULL)
13865         ThrowXWindowFatalException(ResourceLimitFatalError,
13866           "MemoryAllocationFailed",image->filename);
13867       size_hints->flags=0L;
13868       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13869         width,height);
13870       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13871         default_geometry,window_info.border_width,size_hints,&window_info.x,
13872         &window_info.y,&width,&height,&gravity);
13873       if (flags & (XValue | YValue))
13874         {
13875           width=window_attributes.width;
13876           height=window_attributes.height;
13877         }
13878       (void) XFree((void *) size_hints);
13879     }
13880   /*
13881     Create the X pixmap.
13882   */
13883   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13884     (unsigned int) height,window_info.depth);
13885   if (window_info.pixmap == (Pixmap) NULL)
13886     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13887       image->filename);
13888   /*
13889     Display pixmap on the window.
13890   */
13891   if (((unsigned int) width > window_info.width) ||
13892       ((unsigned int) height > window_info.height))
13893     (void) XFillRectangle(display,window_info.pixmap,
13894       window_info.annotate_context,0,0,(unsigned int) width,
13895       (unsigned int) height);
13896   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13897     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13898     window_info.width,(unsigned int) window_info.height);
13899   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13900   (void) XClearWindow(display,window_info.id);
13901   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13902   XDelay(display,delay == 0UL ? 10UL : delay);
13903   (void) XSync(display,MagickFalse);
13904   return(window_info.id == root_window ? MagickTrue : MagickFalse);
13905 }
13906 \f
13907 /*
13908 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13909 %                                                                             %
13910 %                                                                             %
13911 %                                                                             %
13912 +   X D i s p l a y I m a g e                                                 %
13913 %                                                                             %
13914 %                                                                             %
13915 %                                                                             %
13916 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13917 %
13918 %  XDisplayImage() displays an image via X11.  A new image is created and
13919 %  returned if the user interactively transforms the displayed image.
13920 %
13921 %  The format of the XDisplayImage method is:
13922 %
13923 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13924 %        char **argv,int argc,Image **image,size_t *state)
13925 %
13926 %  A description of each parameter follows:
13927 %
13928 %    o nexus:  Method XDisplayImage returns an image when the
13929 %      user chooses 'Open Image' from the command menu or picks a tile
13930 %      from the image directory.  Otherwise a null image is returned.
13931 %
13932 %    o display: Specifies a connection to an X server;  returned from
13933 %      XOpenDisplay.
13934 %
13935 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13936 %
13937 %    o argv: Specifies the application's argument list.
13938 %
13939 %    o argc: Specifies the number of arguments.
13940 %
13941 %    o image: Specifies an address to an address of an Image structure;
13942 %
13943 */
13944 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13945   char **argv,int argc,Image **image,size_t *state)
13946 {
13947 #define MagnifySize  256  /* must be a power of 2 */
13948 #define MagickMenus  10
13949 #define MagickTitle  "Commands"
13950
13951   static const char
13952     *CommandMenu[] =
13953     {
13954       "File",
13955       "Edit",
13956       "View",
13957       "Transform",
13958       "Enhance",
13959       "Effects",
13960       "F/X",
13961       "Image Edit",
13962       "Miscellany",
13963       "Help",
13964       (char *) NULL
13965     },
13966     *FileMenu[] =
13967     {
13968       "Open...",
13969       "Next",
13970       "Former",
13971       "Select...",
13972       "Save...",
13973       "Print...",
13974       "Delete...",
13975       "New...",
13976       "Visual Directory...",
13977       "Quit",
13978       (char *) NULL
13979     },
13980     *EditMenu[] =
13981     {
13982       "Undo",
13983       "Redo",
13984       "Cut",
13985       "Copy",
13986       "Paste",
13987       (char *) NULL
13988     },
13989     *ViewMenu[] =
13990     {
13991       "Half Size",
13992       "Original Size",
13993       "Double Size",
13994       "Resize...",
13995       "Apply",
13996       "Refresh",
13997       "Restore",
13998       (char *) NULL
13999     },
14000     *TransformMenu[] =
14001     {
14002       "Crop",
14003       "Chop",
14004       "Flop",
14005       "Flip",
14006       "Rotate Right",
14007       "Rotate Left",
14008       "Rotate...",
14009       "Shear...",
14010       "Roll...",
14011       "Trim Edges",
14012       (char *) NULL
14013     },
14014     *EnhanceMenu[] =
14015     {
14016       "Hue...",
14017       "Saturation...",
14018       "Brightness...",
14019       "Gamma...",
14020       "Spiff",
14021       "Dull",
14022       "Contrast Stretch...",
14023       "Sigmoidal Contrast...",
14024       "Normalize",
14025       "Equalize",
14026       "Negate",
14027       "Grayscale",
14028       "Map...",
14029       "Quantize...",
14030       (char *) NULL
14031     },
14032     *EffectsMenu[] =
14033     {
14034       "Despeckle",
14035       "Emboss",
14036       "Reduce Noise",
14037       "Add Noise...",
14038       "Sharpen...",
14039       "Blur...",
14040       "Threshold...",
14041       "Edge Detect...",
14042       "Spread...",
14043       "Shade...",
14044       "Raise...",
14045       "Segment...",
14046       (char *) NULL
14047     },
14048     *FXMenu[] =
14049     {
14050       "Solarize...",
14051       "Sepia Tone...",
14052       "Swirl...",
14053       "Implode...",
14054       "Vignette...",
14055       "Wave...",
14056       "Oil Paint...",
14057       "Charcoal Draw...",
14058       (char *) NULL
14059     },
14060     *ImageEditMenu[] =
14061     {
14062       "Annotate...",
14063       "Draw...",
14064       "Color...",
14065       "Matte...",
14066       "Composite...",
14067       "Add Border...",
14068       "Add Frame...",
14069       "Comment...",
14070       "Launch...",
14071       "Region of Interest...",
14072       (char *) NULL
14073     },
14074     *MiscellanyMenu[] =
14075     {
14076       "Image Info",
14077       "Zoom Image",
14078       "Show Preview...",
14079       "Show Histogram",
14080       "Show Matte",
14081       "Background...",
14082       "Slide Show...",
14083       "Preferences...",
14084       (char *) NULL
14085     },
14086     *HelpMenu[] =
14087     {
14088       "Overview",
14089       "Browse Documentation",
14090       "About Display",
14091       (char *) NULL
14092     },
14093     *ShortCutsMenu[] =
14094     {
14095       "Next",
14096       "Former",
14097       "Open...",
14098       "Save...",
14099       "Print...",
14100       "Undo",
14101       "Restore",
14102       "Image Info",
14103       "Quit",
14104       (char *) NULL
14105     },
14106     *VirtualMenu[] =
14107     {
14108       "Image Info",
14109       "Print",
14110       "Next",
14111       "Quit",
14112       (char *) NULL
14113     };
14114
14115   static const char
14116     **Menus[MagickMenus] =
14117     {
14118       FileMenu,
14119       EditMenu,
14120       ViewMenu,
14121       TransformMenu,
14122       EnhanceMenu,
14123       EffectsMenu,
14124       FXMenu,
14125       ImageEditMenu,
14126       MiscellanyMenu,
14127       HelpMenu
14128     };
14129
14130   static CommandType
14131     CommandMenus[] =
14132     {
14133       NullCommand,
14134       NullCommand,
14135       NullCommand,
14136       NullCommand,
14137       NullCommand,
14138       NullCommand,
14139       NullCommand,
14140       NullCommand,
14141       NullCommand,
14142       NullCommand,
14143     },
14144     FileCommands[] =
14145     {
14146       OpenCommand,
14147       NextCommand,
14148       FormerCommand,
14149       SelectCommand,
14150       SaveCommand,
14151       PrintCommand,
14152       DeleteCommand,
14153       NewCommand,
14154       VisualDirectoryCommand,
14155       QuitCommand
14156     },
14157     EditCommands[] =
14158     {
14159       UndoCommand,
14160       RedoCommand,
14161       CutCommand,
14162       CopyCommand,
14163       PasteCommand
14164     },
14165     ViewCommands[] =
14166     {
14167       HalfSizeCommand,
14168       OriginalSizeCommand,
14169       DoubleSizeCommand,
14170       ResizeCommand,
14171       ApplyCommand,
14172       RefreshCommand,
14173       RestoreCommand
14174     },
14175     TransformCommands[] =
14176     {
14177       CropCommand,
14178       ChopCommand,
14179       FlopCommand,
14180       FlipCommand,
14181       RotateRightCommand,
14182       RotateLeftCommand,
14183       RotateCommand,
14184       ShearCommand,
14185       RollCommand,
14186       TrimCommand
14187     },
14188     EnhanceCommands[] =
14189     {
14190       HueCommand,
14191       SaturationCommand,
14192       BrightnessCommand,
14193       GammaCommand,
14194       SpiffCommand,
14195       DullCommand,
14196       ContrastStretchCommand,
14197       SigmoidalContrastCommand,
14198       NormalizeCommand,
14199       EqualizeCommand,
14200       NegateCommand,
14201       GrayscaleCommand,
14202       MapCommand,
14203       QuantizeCommand
14204     },
14205     EffectsCommands[] =
14206     {
14207       DespeckleCommand,
14208       EmbossCommand,
14209       ReduceNoiseCommand,
14210       AddNoiseCommand,
14211       SharpenCommand,
14212       BlurCommand,
14213       ThresholdCommand,
14214       EdgeDetectCommand,
14215       SpreadCommand,
14216       ShadeCommand,
14217       RaiseCommand,
14218       SegmentCommand
14219     },
14220     FXCommands[] =
14221     {
14222       SolarizeCommand,
14223       SepiaToneCommand,
14224       SwirlCommand,
14225       ImplodeCommand,
14226       VignetteCommand,
14227       WaveCommand,
14228       OilPaintCommand,
14229       CharcoalDrawCommand
14230     },
14231     ImageEditCommands[] =
14232     {
14233       AnnotateCommand,
14234       DrawCommand,
14235       ColorCommand,
14236       MatteCommand,
14237       CompositeCommand,
14238       AddBorderCommand,
14239       AddFrameCommand,
14240       CommentCommand,
14241       LaunchCommand,
14242       RegionofInterestCommand
14243     },
14244     MiscellanyCommands[] =
14245     {
14246       InfoCommand,
14247       ZoomCommand,
14248       ShowPreviewCommand,
14249       ShowHistogramCommand,
14250       ShowMatteCommand,
14251       BackgroundCommand,
14252       SlideShowCommand,
14253       PreferencesCommand
14254     },
14255     HelpCommands[] =
14256     {
14257       HelpCommand,
14258       BrowseDocumentationCommand,
14259       VersionCommand
14260     },
14261     ShortCutsCommands[] =
14262     {
14263       NextCommand,
14264       FormerCommand,
14265       OpenCommand,
14266       SaveCommand,
14267       PrintCommand,
14268       UndoCommand,
14269       RestoreCommand,
14270       InfoCommand,
14271       QuitCommand
14272     },
14273     VirtualCommands[] =
14274     {
14275       InfoCommand,
14276       PrintCommand,
14277       NextCommand,
14278       QuitCommand
14279     };
14280
14281   static CommandType
14282     *Commands[MagickMenus] =
14283     {
14284       FileCommands,
14285       EditCommands,
14286       ViewCommands,
14287       TransformCommands,
14288       EnhanceCommands,
14289       EffectsCommands,
14290       FXCommands,
14291       ImageEditCommands,
14292       MiscellanyCommands,
14293       HelpCommands
14294     };
14295
14296   char
14297     command[MaxTextExtent],
14298     *directory,
14299     geometry[MaxTextExtent],
14300     resource_name[MaxTextExtent];
14301
14302   CommandType
14303     command_type;
14304
14305   Image
14306     *display_image,
14307     *nexus;
14308
14309   int
14310     entry,
14311     id;
14312
14313   KeySym
14314     key_symbol;
14315
14316   MagickStatusType
14317     context_mask,
14318     status;
14319
14320   RectangleInfo
14321     geometry_info;
14322
14323   register int
14324     i;
14325
14326   static char
14327     working_directory[MaxTextExtent];
14328
14329   static XPoint
14330     vid_info;
14331
14332   static XWindowInfo
14333     *magick_windows[MaxXWindows];
14334
14335   static unsigned int
14336     number_windows;
14337
14338   struct stat
14339     attributes;
14340
14341   time_t
14342     timer,
14343     timestamp,
14344     update_time;
14345
14346   unsigned int
14347     height,
14348     width;
14349
14350   size_t
14351     delay;
14352
14353   WarningHandler
14354     warning_handler;
14355
14356   Window
14357     root_window;
14358
14359   XClassHint
14360     *class_hints;
14361
14362   XEvent
14363     event;
14364
14365   XFontStruct
14366     *font_info;
14367
14368   XGCValues
14369     context_values;
14370
14371   XPixelInfo
14372     *icon_pixel,
14373     *pixel;
14374
14375   XResourceInfo
14376     *icon_resources;
14377
14378   XStandardColormap
14379     *icon_map,
14380     *map_info;
14381
14382   XVisualInfo
14383     *icon_visual,
14384     *visual_info;
14385
14386   XWindowChanges
14387     window_changes;
14388
14389   XWindows
14390     *windows;
14391
14392   XWMHints
14393     *manager_hints;
14394
14395   assert(image != (Image **) NULL);
14396   assert((*image)->signature == MagickSignature);
14397   if ((*image)->debug != MagickFalse)
14398     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14399   display_image=(*image);
14400   warning_handler=(WarningHandler) NULL;
14401   windows=XSetWindows((XWindows *) ~0);
14402   if (windows != (XWindows *) NULL)
14403     {
14404       int
14405         status;
14406
14407       status=chdir(working_directory);
14408       if (status == -1)
14409         (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14410           FileOpenError,"UnableToOpenFile","%s",working_directory);
14411       warning_handler=resource_info->display_warnings ?
14412         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14413       warning_handler=resource_info->display_warnings ?
14414         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14415     }
14416   else
14417     {
14418       /*
14419         Allocate windows structure.
14420       */
14421       resource_info->colors=display_image->colors;
14422       windows=XSetWindows(XInitializeWindows(display,resource_info));
14423       if (windows == (XWindows *) NULL)
14424         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14425           (*image)->filename);
14426       /*
14427         Initialize window id's.
14428       */
14429       number_windows=0;
14430       magick_windows[number_windows++]=(&windows->icon);
14431       magick_windows[number_windows++]=(&windows->backdrop);
14432       magick_windows[number_windows++]=(&windows->image);
14433       magick_windows[number_windows++]=(&windows->info);
14434       magick_windows[number_windows++]=(&windows->command);
14435       magick_windows[number_windows++]=(&windows->widget);
14436       magick_windows[number_windows++]=(&windows->popup);
14437       magick_windows[number_windows++]=(&windows->magnify);
14438       magick_windows[number_windows++]=(&windows->pan);
14439       for (i=0; i < (int) number_windows; i++)
14440         magick_windows[i]->id=(Window) NULL;
14441       vid_info.x=0;
14442       vid_info.y=0;
14443     }
14444   /*
14445     Initialize font info.
14446   */
14447   if (windows->font_info != (XFontStruct *) NULL)
14448     (void) XFreeFont(display,windows->font_info);
14449   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14450   if (windows->font_info == (XFontStruct *) NULL)
14451     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14452       resource_info->font);
14453   /*
14454     Initialize Standard Colormap.
14455   */
14456   map_info=windows->map_info;
14457   icon_map=windows->icon_map;
14458   visual_info=windows->visual_info;
14459   icon_visual=windows->icon_visual;
14460   pixel=windows->pixel_info;
14461   icon_pixel=windows->icon_pixel;
14462   font_info=windows->font_info;
14463   icon_resources=windows->icon_resources;
14464   class_hints=windows->class_hints;
14465   manager_hints=windows->manager_hints;
14466   root_window=XRootWindow(display,visual_info->screen);
14467   nexus=NewImageList();
14468   if (display_image->debug != MagickFalse)
14469     {
14470       (void) LogMagickEvent(X11Event,GetMagickModule(),
14471         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14472         (double) display_image->scene,(double) display_image->columns,
14473         (double) display_image->rows);
14474       if (display_image->colors != 0)
14475         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14476           display_image->colors);
14477       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14478         display_image->magick);
14479     }
14480   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14481     map_info,pixel);
14482   display_image->taint=MagickFalse;
14483   /*
14484     Initialize graphic context.
14485   */
14486   windows->context.id=(Window) NULL;
14487   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14488     resource_info,&windows->context);
14489   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14490   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14491   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14492   manager_hints->flags=InputHint | StateHint;
14493   manager_hints->input=MagickFalse;
14494   manager_hints->initial_state=WithdrawnState;
14495   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14496     &windows->context);
14497   if (display_image->debug != MagickFalse)
14498     (void) LogMagickEvent(X11Event,GetMagickModule(),
14499       "Window id: 0x%lx (context)",windows->context.id);
14500   context_values.background=pixel->background_color.pixel;
14501   context_values.font=font_info->fid;
14502   context_values.foreground=pixel->foreground_color.pixel;
14503   context_values.graphics_exposures=MagickFalse;
14504   context_mask=(MagickStatusType)
14505     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14506   if (pixel->annotate_context != (GC) NULL)
14507     (void) XFreeGC(display,pixel->annotate_context);
14508   pixel->annotate_context=XCreateGC(display,windows->context.id,
14509     context_mask,&context_values);
14510   if (pixel->annotate_context == (GC) NULL)
14511     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14512       display_image->filename);
14513   context_values.background=pixel->depth_color.pixel;
14514   if (pixel->widget_context != (GC) NULL)
14515     (void) XFreeGC(display,pixel->widget_context);
14516   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14517     &context_values);
14518   if (pixel->widget_context == (GC) NULL)
14519     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14520       display_image->filename);
14521   context_values.background=pixel->foreground_color.pixel;
14522   context_values.foreground=pixel->background_color.pixel;
14523   context_values.plane_mask=context_values.background ^
14524     context_values.foreground;
14525   if (pixel->highlight_context != (GC) NULL)
14526     (void) XFreeGC(display,pixel->highlight_context);
14527   pixel->highlight_context=XCreateGC(display,windows->context.id,
14528     (size_t) (context_mask | GCPlaneMask),&context_values);
14529   if (pixel->highlight_context == (GC) NULL)
14530     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14531       display_image->filename);
14532   (void) XDestroyWindow(display,windows->context.id);
14533   /*
14534     Initialize icon window.
14535   */
14536   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14537     icon_resources,&windows->icon);
14538   windows->icon.geometry=resource_info->icon_geometry;
14539   XBestIconSize(display,&windows->icon,display_image);
14540   windows->icon.attributes.colormap=XDefaultColormap(display,
14541     icon_visual->screen);
14542   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14543   manager_hints->flags=InputHint | StateHint;
14544   manager_hints->input=MagickFalse;
14545   manager_hints->initial_state=IconicState;
14546   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14547     &windows->icon);
14548   if (display_image->debug != MagickFalse)
14549     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14550       windows->icon.id);
14551   /*
14552     Initialize graphic context for icon window.
14553   */
14554   if (icon_pixel->annotate_context != (GC) NULL)
14555     (void) XFreeGC(display,icon_pixel->annotate_context);
14556   context_values.background=icon_pixel->background_color.pixel;
14557   context_values.foreground=icon_pixel->foreground_color.pixel;
14558   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14559     (size_t) (GCBackground | GCForeground),&context_values);
14560   if (icon_pixel->annotate_context == (GC) NULL)
14561     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14562       display_image->filename);
14563   windows->icon.annotate_context=icon_pixel->annotate_context;
14564   /*
14565     Initialize Image window.
14566   */
14567   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14568     &windows->image);
14569   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14570   if (resource_info->use_shared_memory == MagickFalse)
14571     windows->image.shared_memory=MagickFalse;
14572   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14573     {
14574       char
14575         *title;
14576
14577       title=InterpretImageProperties(resource_info->image_info,display_image,
14578         resource_info->title);
14579       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14580       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14581       title=DestroyString(title);
14582     }
14583   else
14584     {
14585       char
14586         filename[MaxTextExtent];
14587
14588       /*
14589         Window name is the base of the filename.
14590       */
14591       GetPathComponent(display_image->magick_filename,TailPath,filename);
14592       if (GetImageListLength(display_image) == 1)
14593         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14594           "%s: %s",MagickPackageName,filename);
14595       else
14596         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14597           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14598           (double) display_image->scene,(double) GetImageListLength(
14599           display_image));
14600       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14601     }
14602   if (resource_info->immutable)
14603     windows->image.immutable=MagickTrue;
14604   windows->image.use_pixmap=resource_info->use_pixmap;
14605   windows->image.geometry=resource_info->image_geometry;
14606   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14607     XDisplayWidth(display,visual_info->screen),
14608     XDisplayHeight(display,visual_info->screen));
14609   geometry_info.width=display_image->columns;
14610   geometry_info.height=display_image->rows;
14611   geometry_info.x=0;
14612   geometry_info.y=0;
14613   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14614     &geometry_info.width,&geometry_info.height);
14615   windows->image.width=(unsigned int) geometry_info.width;
14616   windows->image.height=(unsigned int) geometry_info.height;
14617   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14618     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14619     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14620     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14621   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14622     resource_info,&windows->backdrop);
14623   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14624     {
14625       /*
14626         Initialize backdrop window.
14627       */
14628       windows->backdrop.x=0;
14629       windows->backdrop.y=0;
14630       (void) CloneString(&windows->backdrop.name,"Backdrop");
14631       windows->backdrop.flags=(size_t) (USSize | USPosition);
14632       windows->backdrop.width=(unsigned int)
14633         XDisplayWidth(display,visual_info->screen);
14634       windows->backdrop.height=(unsigned int)
14635         XDisplayHeight(display,visual_info->screen);
14636       windows->backdrop.border_width=0;
14637       windows->backdrop.immutable=MagickTrue;
14638       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14639         ButtonReleaseMask;
14640       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14641         StructureNotifyMask;
14642       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14643       manager_hints->icon_window=windows->icon.id;
14644       manager_hints->input=MagickTrue;
14645       manager_hints->initial_state=resource_info->iconic ? IconicState :
14646         NormalState;
14647       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14648         &windows->backdrop);
14649       if (display_image->debug != MagickFalse)
14650         (void) LogMagickEvent(X11Event,GetMagickModule(),
14651           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14652       (void) XMapWindow(display,windows->backdrop.id);
14653       (void) XClearWindow(display,windows->backdrop.id);
14654       if (windows->image.id != (Window) NULL)
14655         {
14656           (void) XDestroyWindow(display,windows->image.id);
14657           windows->image.id=(Window) NULL;
14658         }
14659       /*
14660         Position image in the center the backdrop.
14661       */
14662       windows->image.flags|=USPosition;
14663       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14664         (windows->image.width/2);
14665       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14666         (windows->image.height/2);
14667     }
14668   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14669   manager_hints->icon_window=windows->icon.id;
14670   manager_hints->input=MagickTrue;
14671   manager_hints->initial_state=resource_info->iconic ? IconicState :
14672     NormalState;
14673   if (windows->group_leader.id != (Window) NULL)
14674     {
14675       /*
14676         Follow the leader.
14677       */
14678       manager_hints->flags|=WindowGroupHint;
14679       manager_hints->window_group=windows->group_leader.id;
14680       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14681       if (display_image->debug != MagickFalse)
14682         (void) LogMagickEvent(X11Event,GetMagickModule(),
14683           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14684     }
14685   XMakeWindow(display,
14686     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14687     argv,argc,class_hints,manager_hints,&windows->image);
14688   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14689     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14690   if (windows->group_leader.id != (Window) NULL)
14691     (void) XSetTransientForHint(display,windows->image.id,
14692       windows->group_leader.id);
14693   if (display_image->debug != MagickFalse)
14694     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14695       windows->image.id);
14696   /*
14697     Initialize Info widget.
14698   */
14699   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14700     &windows->info);
14701   (void) CloneString(&windows->info.name,"Info");
14702   (void) CloneString(&windows->info.icon_name,"Info");
14703   windows->info.border_width=1;
14704   windows->info.x=2;
14705   windows->info.y=2;
14706   windows->info.flags|=PPosition;
14707   windows->info.attributes.win_gravity=UnmapGravity;
14708   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14709     StructureNotifyMask;
14710   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14711   manager_hints->input=MagickFalse;
14712   manager_hints->initial_state=NormalState;
14713   manager_hints->window_group=windows->image.id;
14714   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14715     &windows->info);
14716   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14717     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14718   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14719     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14720   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14721   if (windows->image.mapped != MagickFalse)
14722     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14723   if (display_image->debug != MagickFalse)
14724     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14725       windows->info.id);
14726   /*
14727     Initialize Command widget.
14728   */
14729   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14730     resource_info,&windows->command);
14731   windows->command.data=MagickMenus;
14732   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14733   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14734     resource_info->client_name);
14735   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14736     resource_name,"geometry",(char *) NULL);
14737   (void) CloneString(&windows->command.name,MagickTitle);
14738   windows->command.border_width=0;
14739   windows->command.flags|=PPosition;
14740   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14741     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14742     OwnerGrabButtonMask | StructureNotifyMask;
14743   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14744   manager_hints->input=MagickTrue;
14745   manager_hints->initial_state=NormalState;
14746   manager_hints->window_group=windows->image.id;
14747   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14748     &windows->command);
14749   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14750     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14751     HighlightHeight);
14752   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14753     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14754   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14755   if (windows->command.mapped != MagickFalse)
14756     (void) XMapRaised(display,windows->command.id);
14757   if (display_image->debug != MagickFalse)
14758     (void) LogMagickEvent(X11Event,GetMagickModule(),
14759       "Window id: 0x%lx (command)",windows->command.id);
14760   /*
14761     Initialize Widget window.
14762   */
14763   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14764     resource_info,&windows->widget);
14765   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14766     resource_info->client_name);
14767   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14768     resource_name,"geometry",(char *) NULL);
14769   windows->widget.border_width=0;
14770   windows->widget.flags|=PPosition;
14771   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14772     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14773     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14774     StructureNotifyMask;
14775   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14776   manager_hints->input=MagickTrue;
14777   manager_hints->initial_state=NormalState;
14778   manager_hints->window_group=windows->image.id;
14779   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14780     &windows->widget);
14781   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14782     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14783   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14784     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14785   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14786   if (display_image->debug != MagickFalse)
14787     (void) LogMagickEvent(X11Event,GetMagickModule(),
14788       "Window id: 0x%lx (widget)",windows->widget.id);
14789   /*
14790     Initialize popup window.
14791   */
14792   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14793     resource_info,&windows->popup);
14794   windows->popup.border_width=0;
14795   windows->popup.flags|=PPosition;
14796   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14797     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14798     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14799   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14800   manager_hints->input=MagickTrue;
14801   manager_hints->initial_state=NormalState;
14802   manager_hints->window_group=windows->image.id;
14803   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14804     &windows->popup);
14805   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14806     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14807   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14808     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14809   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14810   if (display_image->debug != MagickFalse)
14811     (void) LogMagickEvent(X11Event,GetMagickModule(),
14812       "Window id: 0x%lx (pop up)",windows->popup.id);
14813   /*
14814     Initialize Magnify window and cursor.
14815   */
14816   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14817     resource_info,&windows->magnify);
14818   if (resource_info->use_shared_memory == MagickFalse)
14819     windows->magnify.shared_memory=MagickFalse;
14820   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14821     resource_info->client_name);
14822   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14823     resource_name,"geometry",(char *) NULL);
14824   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14825     resource_info->magnify);
14826   if (windows->magnify.cursor != (Cursor) NULL)
14827     (void) XFreeCursor(display,windows->magnify.cursor);
14828   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14829     map_info->colormap,resource_info->background_color,
14830     resource_info->foreground_color);
14831   if (windows->magnify.cursor == (Cursor) NULL)
14832     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14833       display_image->filename);
14834   windows->magnify.width=MagnifySize;
14835   windows->magnify.height=MagnifySize;
14836   windows->magnify.flags|=PPosition;
14837   windows->magnify.min_width=MagnifySize;
14838   windows->magnify.min_height=MagnifySize;
14839   windows->magnify.width_inc=MagnifySize;
14840   windows->magnify.height_inc=MagnifySize;
14841   windows->magnify.data=resource_info->magnify;
14842   windows->magnify.attributes.cursor=windows->magnify.cursor;
14843   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14844     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14845     StructureNotifyMask;
14846   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14847   manager_hints->input=MagickTrue;
14848   manager_hints->initial_state=NormalState;
14849   manager_hints->window_group=windows->image.id;
14850   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14851     &windows->magnify);
14852   if (display_image->debug != MagickFalse)
14853     (void) LogMagickEvent(X11Event,GetMagickModule(),
14854       "Window id: 0x%lx (magnify)",windows->magnify.id);
14855   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14856   /*
14857     Initialize panning window.
14858   */
14859   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14860     resource_info,&windows->pan);
14861   (void) CloneString(&windows->pan.name,"Pan Icon");
14862   windows->pan.width=windows->icon.width;
14863   windows->pan.height=windows->icon.height;
14864   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14865     resource_info->client_name);
14866   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14867     resource_name,"geometry",(char *) NULL);
14868   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14869     &windows->pan.width,&windows->pan.height);
14870   windows->pan.flags|=PPosition;
14871   windows->pan.immutable=MagickTrue;
14872   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14873     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14874     StructureNotifyMask;
14875   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14876   manager_hints->input=MagickFalse;
14877   manager_hints->initial_state=NormalState;
14878   manager_hints->window_group=windows->image.id;
14879   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14880     &windows->pan);
14881   if (display_image->debug != MagickFalse)
14882     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14883       windows->pan.id);
14884   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14885   if (windows->info.mapped != MagickFalse)
14886     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14887   if ((windows->image.mapped == MagickFalse) ||
14888       (windows->backdrop.id != (Window) NULL))
14889     (void) XMapWindow(display,windows->image.id);
14890   /*
14891     Set our progress monitor and warning handlers.
14892   */
14893   if (warning_handler == (WarningHandler) NULL)
14894     {
14895       warning_handler=resource_info->display_warnings ?
14896         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14897       warning_handler=resource_info->display_warnings ?
14898         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14899     }
14900   /*
14901     Initialize Image and Magnify X images.
14902   */
14903   windows->image.x=0;
14904   windows->image.y=0;
14905   windows->magnify.shape=MagickFalse;
14906   width=(unsigned int) display_image->columns;
14907   height=(unsigned int) display_image->rows;
14908   if ((display_image->columns != width) || (display_image->rows != height))
14909     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14910       display_image->filename);
14911   status=XMakeImage(display,resource_info,&windows->image,display_image,
14912     width,height);
14913   if (status == MagickFalse)
14914     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14915       display_image->filename);
14916   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14917     windows->magnify.width,windows->magnify.height);
14918   if (status == MagickFalse)
14919     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14920       display_image->filename);
14921   if (windows->magnify.mapped != MagickFalse)
14922     (void) XMapRaised(display,windows->magnify.id);
14923   if (windows->pan.mapped != MagickFalse)
14924     (void) XMapRaised(display,windows->pan.id);
14925   windows->image.window_changes.width=(int) display_image->columns;
14926   windows->image.window_changes.height=(int) display_image->rows;
14927   (void) XConfigureImage(display,resource_info,windows,display_image);
14928   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14929   (void) XSync(display,MagickFalse);
14930   /*
14931     Respond to events.
14932   */
14933   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14934   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14935   update_time=0;
14936   if (resource_info->update != MagickFalse)
14937     {
14938       MagickBooleanType
14939         status;
14940
14941       /*
14942         Determine when file data was last modified.
14943       */
14944       status=GetPathAttributes(display_image->filename,&attributes);
14945       if (status != MagickFalse)
14946         update_time=attributes.st_mtime;
14947     }
14948   *state&=(~FormerImageState);
14949   *state&=(~MontageImageState);
14950   *state&=(~NextImageState);
14951   do
14952   {
14953     /*
14954       Handle a window event.
14955     */
14956     if (windows->image.mapped != MagickFalse)
14957       if ((display_image->delay != 0) || (resource_info->update != 0))
14958         {
14959           if (timer < time((time_t *) NULL))
14960             {
14961               if (resource_info->update == MagickFalse)
14962                 *state|=NextImageState | ExitState;
14963               else
14964                 {
14965                   MagickBooleanType
14966                     status;
14967
14968                   /*
14969                     Determine if image file was modified.
14970                   */
14971                   status=GetPathAttributes(display_image->filename,&attributes);
14972                   if (status != MagickFalse)
14973                     if (update_time != attributes.st_mtime)
14974                       {
14975                         /*
14976                           Redisplay image.
14977                         */
14978                         (void) FormatLocaleString(
14979                           resource_info->image_info->filename,MaxTextExtent,
14980                           "%s:%s",display_image->magick,
14981                           display_image->filename);
14982                         nexus=ReadImage(resource_info->image_info,
14983                           &display_image->exception);
14984                         if (nexus != (Image *) NULL)
14985                           {
14986                             nexus=DestroyImage(nexus);
14987                             *state|=NextImageState | ExitState;
14988                           }
14989                       }
14990                   delay=display_image->delay/MagickMax(
14991                     display_image->ticks_per_second,1L);
14992                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14993                 }
14994             }
14995           if (XEventsQueued(display,QueuedAfterFlush) == 0)
14996             {
14997               /*
14998                 Do not block if delay > 0.
14999               */
15000               XDelay(display,SuspendTime << 2);
15001               continue;
15002             }
15003         }
15004     timestamp=time((time_t *) NULL);
15005     (void) XNextEvent(display,&event);
15006     if (windows->image.stasis == MagickFalse)
15007       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15008         MagickTrue : MagickFalse;
15009     if (windows->magnify.stasis == MagickFalse)
15010       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15011         MagickTrue : MagickFalse;
15012     if (event.xany.window == windows->command.id)
15013       {
15014         /*
15015           Select a command from the Command widget.
15016         */
15017         id=XCommandWidget(display,windows,CommandMenu,&event);
15018         if (id < 0)
15019           continue;
15020         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15021         command_type=CommandMenus[id];
15022         if (id < MagickMenus)
15023           {
15024             /*
15025               Select a command from a pop-up menu.
15026             */
15027             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15028               command);
15029             if (entry < 0)
15030               continue;
15031             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15032             command_type=Commands[id][entry];
15033           }
15034         if (command_type != NullCommand)
15035           nexus=XMagickCommand(display,resource_info,windows,command_type,
15036             &display_image);
15037         continue;
15038       }
15039     switch (event.type)
15040     {
15041       case ButtonPress:
15042       {
15043         if (display_image->debug != MagickFalse)
15044           (void) LogMagickEvent(X11Event,GetMagickModule(),
15045             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15046             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15047         if ((event.xbutton.button == Button3) &&
15048             (event.xbutton.state & Mod1Mask))
15049           {
15050             /*
15051               Convert Alt-Button3 to Button2.
15052             */
15053             event.xbutton.button=Button2;
15054             event.xbutton.state&=(~Mod1Mask);
15055           }
15056         if (event.xbutton.window == windows->backdrop.id)
15057           {
15058             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15059               event.xbutton.time);
15060             break;
15061           }
15062         if (event.xbutton.window == windows->image.id)
15063           {
15064             switch (event.xbutton.button)
15065             {
15066               case Button1:
15067               {
15068                 if (resource_info->immutable)
15069                   {
15070                     /*
15071                       Select a command from the Virtual menu.
15072                     */
15073                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15074                       command);
15075                     if (entry >= 0)
15076                       nexus=XMagickCommand(display,resource_info,windows,
15077                         VirtualCommands[entry],&display_image);
15078                     break;
15079                   }
15080                 /*
15081                   Map/unmap Command widget.
15082                 */
15083                 if (windows->command.mapped != MagickFalse)
15084                   (void) XWithdrawWindow(display,windows->command.id,
15085                     windows->command.screen);
15086                 else
15087                   {
15088                     (void) XCommandWidget(display,windows,CommandMenu,
15089                       (XEvent *) NULL);
15090                     (void) XMapRaised(display,windows->command.id);
15091                   }
15092                 break;
15093               }
15094               case Button2:
15095               {
15096                 /*
15097                   User pressed the image magnify button.
15098                 */
15099                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15100                   &display_image);
15101                 XMagnifyImage(display,windows,&event);
15102                 break;
15103               }
15104               case Button3:
15105               {
15106                 if (resource_info->immutable)
15107                   {
15108                     /*
15109                       Select a command from the Virtual menu.
15110                     */
15111                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15112                       command);
15113                     if (entry >= 0)
15114                       nexus=XMagickCommand(display,resource_info,windows,
15115                         VirtualCommands[entry],&display_image);
15116                     break;
15117                   }
15118                 if (display_image->montage != (char *) NULL)
15119                   {
15120                     /*
15121                       Open or delete a tile from a visual image directory.
15122                     */
15123                     nexus=XTileImage(display,resource_info,windows,
15124                       display_image,&event);
15125                     if (nexus != (Image *) NULL)
15126                       *state|=MontageImageState | NextImageState | ExitState;
15127                     vid_info.x=(short int) windows->image.x;
15128                     vid_info.y=(short int) windows->image.y;
15129                     break;
15130                   }
15131                 /*
15132                   Select a command from the Short Cuts menu.
15133                 */
15134                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15135                   command);
15136                 if (entry >= 0)
15137                   nexus=XMagickCommand(display,resource_info,windows,
15138                     ShortCutsCommands[entry],&display_image);
15139                 break;
15140               }
15141               case Button4:
15142               {
15143                 /*
15144                   Wheel up.
15145                 */
15146                 XTranslateImage(display,windows,*image,XK_Up);
15147                 break;
15148               }
15149               case Button5:
15150               {
15151                 /*
15152                   Wheel down.
15153                 */
15154                 XTranslateImage(display,windows,*image,XK_Down);
15155                 break;
15156               }
15157               default:
15158                 break;
15159             }
15160             break;
15161           }
15162         if (event.xbutton.window == windows->magnify.id)
15163           {
15164             int
15165               factor;
15166
15167             static const char
15168               *MagnifyMenu[] =
15169               {
15170                 "2",
15171                 "4",
15172                 "5",
15173                 "6",
15174                 "7",
15175                 "8",
15176                 "9",
15177                 "3",
15178                 (char *) NULL,
15179               };
15180
15181             static KeySym
15182               MagnifyCommands[] =
15183               {
15184                 XK_2,
15185                 XK_4,
15186                 XK_5,
15187                 XK_6,
15188                 XK_7,
15189                 XK_8,
15190                 XK_9,
15191                 XK_3
15192               };
15193
15194             /*
15195               Select a magnify factor from the pop-up menu.
15196             */
15197             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15198             if (factor >= 0)
15199               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15200             break;
15201           }
15202         if (event.xbutton.window == windows->pan.id)
15203           {
15204             switch (event.xbutton.button)
15205             {
15206               case Button4:
15207               {
15208                 /*
15209                   Wheel up.
15210                 */
15211                 XTranslateImage(display,windows,*image,XK_Up);
15212                 break;
15213               }
15214               case Button5:
15215               {
15216                 /*
15217                   Wheel down.
15218                 */
15219                 XTranslateImage(display,windows,*image,XK_Down);
15220                 break;
15221               }
15222               default:
15223               {
15224                 XPanImage(display,windows,&event);
15225                 break;
15226               }
15227             }
15228             break;
15229           }
15230         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15231           1L);
15232         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15233         break;
15234       }
15235       case ButtonRelease:
15236       {
15237         if (display_image->debug != MagickFalse)
15238           (void) LogMagickEvent(X11Event,GetMagickModule(),
15239             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15240             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15241         break;
15242       }
15243       case ClientMessage:
15244       {
15245         if (display_image->debug != MagickFalse)
15246           (void) LogMagickEvent(X11Event,GetMagickModule(),
15247             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15248             event.xclient.message_type,event.xclient.format,(unsigned long)
15249             event.xclient.data.l[0]);
15250         if (event.xclient.message_type == windows->im_protocols)
15251           {
15252             if (*event.xclient.data.l == (long) windows->im_update_widget)
15253               {
15254                 (void) CloneString(&windows->command.name,MagickTitle);
15255                 windows->command.data=MagickMenus;
15256                 (void) XCommandWidget(display,windows,CommandMenu,
15257                   (XEvent *) NULL);
15258                 break;
15259               }
15260             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15261               {
15262                 /*
15263                   Update graphic context and window colormap.
15264                 */
15265                 for (i=0; i < (int) number_windows; i++)
15266                 {
15267                   if (magick_windows[i]->id == windows->icon.id)
15268                     continue;
15269                   context_values.background=pixel->background_color.pixel;
15270                   context_values.foreground=pixel->foreground_color.pixel;
15271                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15272                     context_mask,&context_values);
15273                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15274                     context_mask,&context_values);
15275                   context_values.background=pixel->foreground_color.pixel;
15276                   context_values.foreground=pixel->background_color.pixel;
15277                   context_values.plane_mask=context_values.background ^
15278                     context_values.foreground;
15279                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15280                     (size_t) (context_mask | GCPlaneMask),
15281                     &context_values);
15282                   magick_windows[i]->attributes.background_pixel=
15283                     pixel->background_color.pixel;
15284                   magick_windows[i]->attributes.border_pixel=
15285                     pixel->border_color.pixel;
15286                   magick_windows[i]->attributes.colormap=map_info->colormap;
15287                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15288                     (unsigned long) magick_windows[i]->mask,
15289                     &magick_windows[i]->attributes);
15290                 }
15291                 if (windows->pan.mapped != MagickFalse)
15292                   {
15293                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15294                       windows->pan.pixmap);
15295                     (void) XClearWindow(display,windows->pan.id);
15296                     XDrawPanRectangle(display,windows);
15297                   }
15298                 if (windows->backdrop.id != (Window) NULL)
15299                   (void) XInstallColormap(display,map_info->colormap);
15300                 break;
15301               }
15302             if (*event.xclient.data.l == (long) windows->im_former_image)
15303               {
15304                 *state|=FormerImageState | ExitState;
15305                 break;
15306               }
15307             if (*event.xclient.data.l == (long) windows->im_next_image)
15308               {
15309                 *state|=NextImageState | ExitState;
15310                 break;
15311               }
15312             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15313               {
15314                 *state|=RetainColorsState;
15315                 break;
15316               }
15317             if (*event.xclient.data.l == (long) windows->im_exit)
15318               {
15319                 *state|=ExitState;
15320                 break;
15321               }
15322             break;
15323           }
15324         if (event.xclient.message_type == windows->dnd_protocols)
15325           {
15326             Atom
15327               selection,
15328               type;
15329
15330             int
15331               format,
15332               status;
15333
15334             unsigned char
15335               *data;
15336
15337             unsigned long
15338               after,
15339               length;
15340
15341             /*
15342               Display image named by the Drag-and-Drop selection.
15343             */
15344             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15345               break;
15346             selection=XInternAtom(display,"DndSelection",MagickFalse);
15347             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15348               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15349               &length,&after,&data);
15350             if ((status != Success) || (length == 0))
15351               break;
15352             if (*event.xclient.data.l == 2)
15353               {
15354                 /*
15355                   Offix DND.
15356                 */
15357                 (void) CopyMagickString(resource_info->image_info->filename,
15358                   (char *) data,MaxTextExtent);
15359               }
15360             else
15361               {
15362                 /*
15363                   XDND.
15364                 */
15365                 if (strncmp((char *) data, "file:", 5) != 0)
15366                   {
15367                     (void) XFree((void *) data);
15368                     break;
15369                   }
15370                 (void) CopyMagickString(resource_info->image_info->filename,
15371                   ((char *) data)+5,MaxTextExtent);
15372               }
15373             nexus=ReadImage(resource_info->image_info,
15374               &display_image->exception);
15375             CatchException(&display_image->exception);
15376             if (nexus != (Image *) NULL)
15377               *state|=NextImageState | ExitState;
15378             (void) XFree((void *) data);
15379             break;
15380           }
15381         /*
15382           If client window delete message, exit.
15383         */
15384         if (event.xclient.message_type != windows->wm_protocols)
15385           break;
15386         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15387           break;
15388         (void) XWithdrawWindow(display,event.xclient.window,
15389           visual_info->screen);
15390         if (event.xclient.window == windows->image.id)
15391           {
15392             *state|=ExitState;
15393             break;
15394           }
15395         if (event.xclient.window == windows->pan.id)
15396           {
15397             /*
15398               Restore original image size when pan window is deleted.
15399             */
15400             windows->image.window_changes.width=windows->image.ximage->width;
15401             windows->image.window_changes.height=windows->image.ximage->height;
15402             (void) XConfigureImage(display,resource_info,windows,
15403               display_image);
15404           }
15405         break;
15406       }
15407       case ConfigureNotify:
15408       {
15409         if (display_image->debug != MagickFalse)
15410           (void) LogMagickEvent(X11Event,GetMagickModule(),
15411             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15412             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15413             event.xconfigure.y,event.xconfigure.send_event);
15414         if (event.xconfigure.window == windows->image.id)
15415           {
15416             /*
15417               Image window has a new configuration.
15418             */
15419             if (event.xconfigure.send_event != 0)
15420               {
15421                 XWindowChanges
15422                   window_changes;
15423
15424                 /*
15425                   Position the transient windows relative of the Image window.
15426                 */
15427                 if (windows->command.geometry == (char *) NULL)
15428                   if (windows->command.mapped == MagickFalse)
15429                     {
15430                       windows->command.x=event.xconfigure.x-
15431                         windows->command.width-25;
15432                       windows->command.y=event.xconfigure.y;
15433                       XConstrainWindowPosition(display,&windows->command);
15434                       window_changes.x=windows->command.x;
15435                       window_changes.y=windows->command.y;
15436                       (void) XReconfigureWMWindow(display,windows->command.id,
15437                         windows->command.screen,(unsigned int) (CWX | CWY),
15438                         &window_changes);
15439                     }
15440                 if (windows->widget.geometry == (char *) NULL)
15441                   if (windows->widget.mapped == MagickFalse)
15442                     {
15443                       windows->widget.x=event.xconfigure.x+
15444                         event.xconfigure.width/10;
15445                       windows->widget.y=event.xconfigure.y+
15446                         event.xconfigure.height/10;
15447                       XConstrainWindowPosition(display,&windows->widget);
15448                       window_changes.x=windows->widget.x;
15449                       window_changes.y=windows->widget.y;
15450                       (void) XReconfigureWMWindow(display,windows->widget.id,
15451                         windows->widget.screen,(unsigned int) (CWX | CWY),
15452                         &window_changes);
15453                     }
15454                 if (windows->magnify.geometry == (char *) NULL)
15455                   if (windows->magnify.mapped == MagickFalse)
15456                     {
15457                       windows->magnify.x=event.xconfigure.x+
15458                         event.xconfigure.width+25;
15459                       windows->magnify.y=event.xconfigure.y;
15460                       XConstrainWindowPosition(display,&windows->magnify);
15461                       window_changes.x=windows->magnify.x;
15462                       window_changes.y=windows->magnify.y;
15463                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15464                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15465                         &window_changes);
15466                     }
15467                 if (windows->pan.geometry == (char *) NULL)
15468                   if (windows->pan.mapped == MagickFalse)
15469                     {
15470                       windows->pan.x=event.xconfigure.x+
15471                         event.xconfigure.width+25;
15472                       windows->pan.y=event.xconfigure.y+
15473                         windows->magnify.height+50;
15474                       XConstrainWindowPosition(display,&windows->pan);
15475                       window_changes.x=windows->pan.x;
15476                       window_changes.y=windows->pan.y;
15477                       (void) XReconfigureWMWindow(display,windows->pan.id,
15478                         windows->pan.screen,(unsigned int) (CWX | CWY),
15479                         &window_changes);
15480                     }
15481               }
15482             if ((event.xconfigure.width == (int) windows->image.width) &&
15483                 (event.xconfigure.height == (int) windows->image.height))
15484               break;
15485             windows->image.width=(unsigned int) event.xconfigure.width;
15486             windows->image.height=(unsigned int) event.xconfigure.height;
15487             windows->image.x=0;
15488             windows->image.y=0;
15489             if (display_image->montage != (char *) NULL)
15490               {
15491                 windows->image.x=vid_info.x;
15492                 windows->image.y=vid_info.y;
15493               }
15494             if ((windows->image.mapped != MagickFalse) &&
15495                 (windows->image.stasis != MagickFalse))
15496               {
15497                 /*
15498                   Update image window configuration.
15499                 */
15500                 windows->image.window_changes.width=event.xconfigure.width;
15501                 windows->image.window_changes.height=event.xconfigure.height;
15502                 (void) XConfigureImage(display,resource_info,windows,
15503                   display_image);
15504               }
15505             /*
15506               Update pan window configuration.
15507             */
15508             if ((event.xconfigure.width < windows->image.ximage->width) ||
15509                 (event.xconfigure.height < windows->image.ximage->height))
15510               {
15511                 (void) XMapRaised(display,windows->pan.id);
15512                 XDrawPanRectangle(display,windows);
15513               }
15514             else
15515               if (windows->pan.mapped != MagickFalse)
15516                 (void) XWithdrawWindow(display,windows->pan.id,
15517                   windows->pan.screen);
15518             break;
15519           }
15520         if (event.xconfigure.window == windows->magnify.id)
15521           {
15522             unsigned int
15523               magnify;
15524
15525             /*
15526               Magnify window has a new configuration.
15527             */
15528             windows->magnify.width=(unsigned int) event.xconfigure.width;
15529             windows->magnify.height=(unsigned int) event.xconfigure.height;
15530             if (windows->magnify.mapped == MagickFalse)
15531               break;
15532             magnify=1;
15533             while ((int) magnify <= event.xconfigure.width)
15534               magnify<<=1;
15535             while ((int) magnify <= event.xconfigure.height)
15536               magnify<<=1;
15537             magnify>>=1;
15538             if (((int) magnify != event.xconfigure.width) ||
15539                 ((int) magnify != event.xconfigure.height))
15540               {
15541                 window_changes.width=(int) magnify;
15542                 window_changes.height=(int) magnify;
15543                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15544                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15545                   &window_changes);
15546                 break;
15547               }
15548             if ((windows->magnify.mapped != MagickFalse) &&
15549                 (windows->magnify.stasis != MagickFalse))
15550               {
15551                 status=XMakeImage(display,resource_info,&windows->magnify,
15552                   display_image,windows->magnify.width,windows->magnify.height);
15553                 XMakeMagnifyImage(display,windows);
15554               }
15555             break;
15556           }
15557         if ((windows->magnify.mapped != MagickFalse) &&
15558             (event.xconfigure.window == windows->pan.id))
15559           {
15560             /*
15561               Pan icon window has a new configuration.
15562             */
15563             if (event.xconfigure.send_event != 0)
15564               {
15565                 windows->pan.x=event.xconfigure.x;
15566                 windows->pan.y=event.xconfigure.y;
15567               }
15568             windows->pan.width=(unsigned int) event.xconfigure.width;
15569             windows->pan.height=(unsigned int) event.xconfigure.height;
15570             break;
15571           }
15572         if (event.xconfigure.window == windows->icon.id)
15573           {
15574             /*
15575               Icon window has a new configuration.
15576             */
15577             windows->icon.width=(unsigned int) event.xconfigure.width;
15578             windows->icon.height=(unsigned int) event.xconfigure.height;
15579             break;
15580           }
15581         break;
15582       }
15583       case DestroyNotify:
15584       {
15585         /*
15586           Group leader has exited.
15587         */
15588         if (display_image->debug != MagickFalse)
15589           (void) LogMagickEvent(X11Event,GetMagickModule(),
15590             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15591         if (event.xdestroywindow.window == windows->group_leader.id)
15592           {
15593             *state|=ExitState;
15594             break;
15595           }
15596         break;
15597       }
15598       case EnterNotify:
15599       {
15600         /*
15601           Selectively install colormap.
15602         */
15603         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15604           if (event.xcrossing.mode != NotifyUngrab)
15605             XInstallColormap(display,map_info->colormap);
15606         break;
15607       }
15608       case Expose:
15609       {
15610         if (display_image->debug != MagickFalse)
15611           (void) LogMagickEvent(X11Event,GetMagickModule(),
15612             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15613             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15614             event.xexpose.y);
15615         /*
15616           Refresh windows that are now exposed.
15617         */
15618         if ((event.xexpose.window == windows->image.id) &&
15619             (windows->image.mapped != MagickFalse))
15620           {
15621             XRefreshWindow(display,&windows->image,&event);
15622             delay=display_image->delay/MagickMax(
15623               display_image->ticks_per_second,1L);
15624             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15625             break;
15626           }
15627         if ((event.xexpose.window == windows->magnify.id) &&
15628             (windows->magnify.mapped != MagickFalse))
15629           {
15630             XMakeMagnifyImage(display,windows);
15631             break;
15632           }
15633         if (event.xexpose.window == windows->pan.id)
15634           {
15635             XDrawPanRectangle(display,windows);
15636             break;
15637           }
15638         if (event.xexpose.window == windows->icon.id)
15639           {
15640             XRefreshWindow(display,&windows->icon,&event);
15641             break;
15642           }
15643         break;
15644       }
15645       case KeyPress:
15646       {
15647         int
15648           length;
15649
15650         /*
15651           Respond to a user key press.
15652         */
15653         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15654           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15655         *(command+length)='\0';
15656         if (display_image->debug != MagickFalse)
15657           (void) LogMagickEvent(X11Event,GetMagickModule(),
15658             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15659             key_symbol,command);
15660         if (event.xkey.window == windows->image.id)
15661           {
15662             command_type=XImageWindowCommand(display,resource_info,windows,
15663               event.xkey.state,key_symbol,&display_image);
15664             if (command_type != NullCommand)
15665               nexus=XMagickCommand(display,resource_info,windows,command_type,
15666                 &display_image);
15667           }
15668         if (event.xkey.window == windows->magnify.id)
15669           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15670         if (event.xkey.window == windows->pan.id)
15671           {
15672             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15673               (void) XWithdrawWindow(display,windows->pan.id,
15674                 windows->pan.screen);
15675             else
15676               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15677                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15678                   "Help Viewer - Image Pan",ImagePanHelp);
15679               else
15680                 XTranslateImage(display,windows,*image,key_symbol);
15681           }
15682         delay=display_image->delay/MagickMax(
15683           display_image->ticks_per_second,1L);
15684         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15685         break;
15686       }
15687       case KeyRelease:
15688       {
15689         /*
15690           Respond to a user key release.
15691         */
15692         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15693           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15694         if (display_image->debug != MagickFalse)
15695           (void) LogMagickEvent(X11Event,GetMagickModule(),
15696             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15697         break;
15698       }
15699       case LeaveNotify:
15700       {
15701         /*
15702           Selectively uninstall colormap.
15703         */
15704         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15705           if (event.xcrossing.mode != NotifyUngrab)
15706             XUninstallColormap(display,map_info->colormap);
15707         break;
15708       }
15709       case MapNotify:
15710       {
15711         if (display_image->debug != MagickFalse)
15712           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15713             event.xmap.window);
15714         if (event.xmap.window == windows->backdrop.id)
15715           {
15716             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15717               CurrentTime);
15718             windows->backdrop.mapped=MagickTrue;
15719             break;
15720           }
15721         if (event.xmap.window == windows->image.id)
15722           {
15723             if (windows->backdrop.id != (Window) NULL)
15724               (void) XInstallColormap(display,map_info->colormap);
15725             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15726               {
15727                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15728                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15729               }
15730             if (((int) windows->image.width < windows->image.ximage->width) ||
15731                 ((int) windows->image.height < windows->image.ximage->height))
15732               (void) XMapRaised(display,windows->pan.id);
15733             windows->image.mapped=MagickTrue;
15734             break;
15735           }
15736         if (event.xmap.window == windows->magnify.id)
15737           {
15738             XMakeMagnifyImage(display,windows);
15739             windows->magnify.mapped=MagickTrue;
15740             (void) XWithdrawWindow(display,windows->info.id,
15741               windows->info.screen);
15742             break;
15743           }
15744         if (event.xmap.window == windows->pan.id)
15745           {
15746             XMakePanImage(display,resource_info,windows,display_image);
15747             windows->pan.mapped=MagickTrue;
15748             break;
15749           }
15750         if (event.xmap.window == windows->info.id)
15751           {
15752             windows->info.mapped=MagickTrue;
15753             break;
15754           }
15755         if (event.xmap.window == windows->icon.id)
15756           {
15757             MagickBooleanType
15758               taint;
15759
15760             /*
15761               Create an icon image.
15762             */
15763             taint=display_image->taint;
15764             XMakeStandardColormap(display,icon_visual,icon_resources,
15765               display_image,icon_map,icon_pixel);
15766             (void) XMakeImage(display,icon_resources,&windows->icon,
15767               display_image,windows->icon.width,windows->icon.height);
15768             display_image->taint=taint;
15769             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15770               windows->icon.pixmap);
15771             (void) XClearWindow(display,windows->icon.id);
15772             (void) XWithdrawWindow(display,windows->info.id,
15773               windows->info.screen);
15774             windows->icon.mapped=MagickTrue;
15775             break;
15776           }
15777         if (event.xmap.window == windows->command.id)
15778           {
15779             windows->command.mapped=MagickTrue;
15780             break;
15781           }
15782         if (event.xmap.window == windows->popup.id)
15783           {
15784             windows->popup.mapped=MagickTrue;
15785             break;
15786           }
15787         if (event.xmap.window == windows->widget.id)
15788           {
15789             windows->widget.mapped=MagickTrue;
15790             break;
15791           }
15792         break;
15793       }
15794       case MappingNotify:
15795       {
15796         (void) XRefreshKeyboardMapping(&event.xmapping);
15797         break;
15798       }
15799       case NoExpose:
15800         break;
15801       case PropertyNotify:
15802       {
15803         Atom
15804           type;
15805
15806         int
15807           format,
15808           status;
15809
15810         unsigned char
15811           *data;
15812
15813         unsigned long
15814           after,
15815           length;
15816
15817         if (display_image->debug != MagickFalse)
15818           (void) LogMagickEvent(X11Event,GetMagickModule(),
15819             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15820             event.xproperty.atom,event.xproperty.state);
15821         if (event.xproperty.atom != windows->im_remote_command)
15822           break;
15823         /*
15824           Display image named by the remote command protocol.
15825         */
15826         status=XGetWindowProperty(display,event.xproperty.window,
15827           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15828           AnyPropertyType,&type,&format,&length,&after,&data);
15829         if ((status != Success) || (length == 0))
15830           break;
15831         if (LocaleCompare((char *) data,"-quit") == 0)
15832           {
15833             XClientMessage(display,windows->image.id,windows->im_protocols,
15834               windows->im_exit,CurrentTime);
15835             (void) XFree((void *) data);
15836             break;
15837           }
15838         (void) CopyMagickString(resource_info->image_info->filename,
15839           (char *) data,MaxTextExtent);
15840         (void) XFree((void *) data);
15841         nexus=ReadImage(resource_info->image_info,&display_image->exception);
15842         CatchException(&display_image->exception);
15843         if (nexus != (Image *) NULL)
15844           *state|=NextImageState | ExitState;
15845         break;
15846       }
15847       case ReparentNotify:
15848       {
15849         if (display_image->debug != MagickFalse)
15850           (void) LogMagickEvent(X11Event,GetMagickModule(),
15851             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15852             event.xreparent.window);
15853         break;
15854       }
15855       case UnmapNotify:
15856       {
15857         if (display_image->debug != MagickFalse)
15858           (void) LogMagickEvent(X11Event,GetMagickModule(),
15859             "Unmap Notify: 0x%lx",event.xunmap.window);
15860         if (event.xunmap.window == windows->backdrop.id)
15861           {
15862             windows->backdrop.mapped=MagickFalse;
15863             break;
15864           }
15865         if (event.xunmap.window == windows->image.id)
15866           {
15867             windows->image.mapped=MagickFalse;
15868             break;
15869           }
15870         if (event.xunmap.window == windows->magnify.id)
15871           {
15872             windows->magnify.mapped=MagickFalse;
15873             break;
15874           }
15875         if (event.xunmap.window == windows->pan.id)
15876           {
15877             windows->pan.mapped=MagickFalse;
15878             break;
15879           }
15880         if (event.xunmap.window == windows->info.id)
15881           {
15882             windows->info.mapped=MagickFalse;
15883             break;
15884           }
15885         if (event.xunmap.window == windows->icon.id)
15886           {
15887             if (map_info->colormap == icon_map->colormap)
15888               XConfigureImageColormap(display,resource_info,windows,
15889                 display_image);
15890             (void) XFreeStandardColormap(display,icon_visual,icon_map,
15891               icon_pixel);
15892             windows->icon.mapped=MagickFalse;
15893             break;
15894           }
15895         if (event.xunmap.window == windows->command.id)
15896           {
15897             windows->command.mapped=MagickFalse;
15898             break;
15899           }
15900         if (event.xunmap.window == windows->popup.id)
15901           {
15902             if (windows->backdrop.id != (Window) NULL)
15903               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15904                 CurrentTime);
15905             windows->popup.mapped=MagickFalse;
15906             break;
15907           }
15908         if (event.xunmap.window == windows->widget.id)
15909           {
15910             if (windows->backdrop.id != (Window) NULL)
15911               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15912                 CurrentTime);
15913             windows->widget.mapped=MagickFalse;
15914             break;
15915           }
15916         break;
15917       }
15918       default:
15919       {
15920         if (display_image->debug != MagickFalse)
15921           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15922             event.type);
15923         break;
15924       }
15925     }
15926   } while (!(*state & ExitState));
15927   if ((*state & ExitState) == 0)
15928     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15929       &display_image);
15930   else
15931     if (resource_info->confirm_edit != MagickFalse)
15932       {
15933         /*
15934           Query user if image has changed.
15935         */
15936         if ((resource_info->immutable == MagickFalse) &&
15937             (display_image->taint != MagickFalse))
15938           {
15939             int
15940               status;
15941
15942             status=XConfirmWidget(display,windows,"Your image changed.",
15943               "Do you want to save it");
15944             if (status == 0)
15945               *state&=(~ExitState);
15946             else
15947               if (status > 0)
15948                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15949                   &display_image);
15950           }
15951       }
15952   if ((windows->visual_info->klass == GrayScale) ||
15953       (windows->visual_info->klass == PseudoColor) ||
15954       (windows->visual_info->klass == DirectColor))
15955     {
15956       /*
15957         Withdraw pan and Magnify window.
15958       */
15959       if (windows->info.mapped != MagickFalse)
15960         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15961       if (windows->magnify.mapped != MagickFalse)
15962         (void) XWithdrawWindow(display,windows->magnify.id,
15963           windows->magnify.screen);
15964       if (windows->command.mapped != MagickFalse)
15965         (void) XWithdrawWindow(display,windows->command.id,
15966           windows->command.screen);
15967     }
15968   if (windows->pan.mapped != MagickFalse)
15969     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15970   if (resource_info->backdrop == MagickFalse)
15971     if (windows->backdrop.mapped)
15972       {
15973         (void) XWithdrawWindow(display,windows->backdrop.id,
15974           windows->backdrop.screen);
15975         (void) XDestroyWindow(display,windows->backdrop.id);
15976         windows->backdrop.id=(Window) NULL;
15977         (void) XWithdrawWindow(display,windows->image.id,
15978           windows->image.screen);
15979         (void) XDestroyWindow(display,windows->image.id);
15980         windows->image.id=(Window) NULL;
15981       }
15982   XSetCursorState(display,windows,MagickTrue);
15983   XCheckRefreshWindows(display,windows);
15984   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15985     *state&=(~ExitState);
15986   if (*state & ExitState)
15987     {
15988       /*
15989         Free Standard Colormap.
15990       */
15991       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
15992       if (resource_info->map_type == (char *) NULL)
15993         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
15994       /*
15995         Free X resources.
15996       */
15997       if (resource_info->copy_image != (Image *) NULL)
15998         {
15999           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16000           resource_info->copy_image=NewImageList();
16001         }
16002       DestroyXResources();
16003     }
16004   (void) XSync(display,MagickFalse);
16005   /*
16006     Restore our progress monitor and warning handlers.
16007   */
16008   (void) SetErrorHandler(warning_handler);
16009   (void) SetWarningHandler(warning_handler);
16010   /*
16011     Change to home directory.
16012   */
16013   directory=getcwd(working_directory,MaxTextExtent);
16014   (void) directory;
16015   {
16016     int
16017       status;
16018
16019     status=chdir(resource_info->home_directory);
16020     if (status == -1)
16021       (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16022         FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16023   }
16024   *image=display_image;
16025   return(nexus);
16026 }
16027 #else
16028 \f
16029 /*
16030 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16031 %                                                                             %
16032 %                                                                             %
16033 %                                                                             %
16034 +   D i s p l a y I m a g e s                                                 %
16035 %                                                                             %
16036 %                                                                             %
16037 %                                                                             %
16038 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16039 %
16040 %  DisplayImages() displays an image sequence to any X window screen.  It
16041 %  returns a value other than 0 if successful.  Check the exception member
16042 %  of image to determine the reason for any failure.
16043 %
16044 %  The format of the DisplayImages method is:
16045 %
16046 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16047 %        Image *images)
16048 %
16049 %  A description of each parameter follows:
16050 %
16051 %    o image_info: the image info.
16052 %
16053 %    o image: the image.
16054 %
16055 */
16056 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16057   Image *image)
16058 {
16059   assert(image_info != (const ImageInfo *) NULL);
16060   assert(image_info->signature == MagickSignature);
16061   assert(image != (Image *) NULL);
16062   assert(image->signature == MagickSignature);
16063   if (image->debug != MagickFalse)
16064     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16065   (void) ThrowMagickException(&image->exception,GetMagickModule(),
16066     MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16067     image->filename);
16068   return(MagickFalse);
16069 }
16070 \f
16071 /*
16072 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16073 %                                                                             %
16074 %                                                                             %
16075 %                                                                             %
16076 +   R e m o t e D i s p l a y C o m m a n d                                   %
16077 %                                                                             %
16078 %                                                                             %
16079 %                                                                             %
16080 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16081 %
16082 %  RemoteDisplayCommand() encourages a remote display program to display the
16083 %  specified image filename.
16084 %
16085 %  The format of the RemoteDisplayCommand method is:
16086 %
16087 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16088 %        const char *window,const char *filename,ExceptionInfo *exception)
16089 %
16090 %  A description of each parameter follows:
16091 %
16092 %    o image_info: the image info.
16093 %
16094 %    o window: Specifies the name or id of an X window.
16095 %
16096 %    o filename: the name of the image filename to display.
16097 %
16098 %    o exception: return any errors or warnings in this structure.
16099 %
16100 */
16101 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16102   const char *window,const char *filename,ExceptionInfo *exception)
16103 {
16104   assert(image_info != (const ImageInfo *) NULL);
16105   assert(image_info->signature == MagickSignature);
16106   assert(filename != (char *) NULL);
16107   (void) window;
16108   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16109   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16110     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16111   return(MagickFalse);
16112 }
16113 #endif