]> granicus.if.org Git - imagemagick/blob - magick/display.c
(no commit message)
[imagemagick] / magick / 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-2010 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 "magick/studio.h"
43 #include "magick/artifact.h"
44 #include "magick/blob.h"
45 #include "magick/cache.h"
46 #include "magick/client.h"
47 #include "magick/color.h"
48 #include "magick/colorspace.h"
49 #include "magick/composite.h"
50 #include "magick/constitute.h"
51 #include "magick/decorate.h"
52 #include "magick/delegate.h"
53 #include "magick/display.h"
54 #include "magick/display-private.h"
55 #include "magick/draw.h"
56 #include "magick/effect.h"
57 #include "magick/enhance.h"
58 #include "magick/exception.h"
59 #include "magick/exception-private.h"
60 #include "magick/fx.h"
61 #include "magick/geometry.h"
62 #include "magick/image.h"
63 #include "magick/image-private.h"
64 #include "magick/list.h"
65 #include "magick/log.h"
66 #include "magick/magick.h"
67 #include "magick/memory_.h"
68 #include "magick/monitor.h"
69 #include "magick/monitor-private.h"
70 #include "magick/montage.h"
71 #include "magick/option.h"
72 #include "magick/paint.h"
73 #include "magick/pixel.h"
74 #include "magick/pixel-private.h"
75 #include "magick/PreRvIcccm.h"
76 #include "magick/property.h"
77 #include "magick/quantum.h"
78 #include "magick/resize.h"
79 #include "magick/resource_.h"
80 #include "magick/shear.h"
81 #include "magick/segment.h"
82 #include "magick/string_.h"
83 #include "magick/string-private.h"
84 #include "magick/transform.h"
85 #include "magick/threshold.h"
86 #include "magick/utility.h"
87 #include "magick/version.h"
88 #include "magick/widget.h"
89 #include "magick/xwindow-private.h"
90 \f
91 #if defined(MAGICKCORE_X11_DELEGATE)
92 /*
93   Define declarations.
94 */
95 #define MaxColors  MagickMin(windows->visual_info->colormap_size,256L)
96 \f
97 /*
98   Constant declarations.
99 */
100 static const unsigned char
101   HighlightBitmap[8] =
102   {
103     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
104   },
105   ShadowBitmap[8] =
106   {
107     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
108   };
109
110 static const char
111   *PageSizes[] =
112   {
113     "Letter",
114     "Tabloid",
115     "Ledger",
116     "Legal",
117     "Statement",
118     "Executive",
119     "A3",
120     "A4",
121     "A5",
122     "B4",
123     "B5",
124     "Folio",
125     "Quarto",
126     "10x14",
127     (char *) NULL
128   };
129 \f
130 /*
131   Help widget declarations.
132 */
133 static const char
134   *ImageAnnotateHelp[] =
135   {
136     "In annotate mode, the Command widget has these options:",
137     "",
138     "    Font Name",
139     "      fixed",
140     "      variable",
141     "      5x8",
142     "      6x10",
143     "      7x13bold",
144     "      8x13bold",
145     "      9x15bold",
146     "      10x20",
147     "      12x24",
148     "      Browser...",
149     "    Font Color",
150     "      black",
151     "      blue",
152     "      cyan",
153     "      green",
154     "      gray",
155     "      red",
156     "      magenta",
157     "      yellow",
158     "      white",
159     "      transparent",
160     "      Browser...",
161     "    Font Color",
162     "      black",
163     "      blue",
164     "      cyan",
165     "      green",
166     "      gray",
167     "      red",
168     "      magenta",
169     "      yellow",
170     "      white",
171     "      transparent",
172     "      Browser...",
173     "    Rotate Text",
174     "      -90",
175     "      -45",
176     "      -30",
177     "      0",
178     "      30",
179     "      45",
180     "      90",
181     "      180",
182     "      Dialog...",
183     "    Help",
184     "    Dismiss",
185     "",
186     "Choose a font name from the Font Name sub-menu.  Additional",
187     "font names can be specified with the font browser.  You can",
188     "change the menu names by setting the X resources font1",
189     "through font9.",
190     "",
191     "Choose a font color from the Font Color sub-menu.",
192     "Additional font colors can be specified with the color",
193     "browser.  You can change the menu colors by setting the X",
194     "resources pen1 through pen9.",
195     "",
196     "If you select the color browser and press Grab, you can",
197     "choose the font color by moving the pointer to the desired",
198     "color on the screen and press any button.",
199     "",
200     "If you choose to rotate the text, choose Rotate Text from the",
201     "menu and select an angle.  Typically you will only want to",
202     "rotate one line of text at a time.  Depending on the angle you",
203     "choose, subsequent lines may end up overwriting each other.",
204     "",
205     "Choosing a font and its color is optional.  The default font",
206     "is fixed and the default color is black.  However, you must",
207     "choose a location to begin entering text and press button 1.",
208     "An underscore character will appear at the location of the",
209     "pointer.  The cursor changes to a pencil to indicate you are",
210     "in text mode.  To exit immediately, press Dismiss.",
211     "",
212     "In text mode, any key presses will display the character at",
213     "the location of the underscore and advance the underscore",
214     "cursor.  Enter your text and once completed press Apply to",
215     "finish your image annotation.  To correct errors press BACK",
216     "SPACE.  To delete an entire line of text, press DELETE.  Any",
217     "text that exceeds the boundaries of the image window is",
218     "automagically continued onto the next line.",
219     "",
220     "The actual color you request for the font is saved in the",
221     "image.  However, the color that appears in your image window",
222     "may be different.  For example, on a monochrome screen the",
223     "text will appear black or white even if you choose the color",
224     "red as the font color.  However, the image saved to a file",
225     "with -write is written with red lettering.  To assure the",
226     "correct color text in the final image, any PseudoClass image",
227     "is promoted to DirectClass (see miff(5)).  To force a",
228     "PseudoClass image to remain PseudoClass, use -colors.",
229     (char *) NULL,
230   },
231   *ImageChopHelp[] =
232   {
233     "In chop mode, the Command widget has these options:",
234     "",
235     "    Direction",
236     "      horizontal",
237     "      vertical",
238     "    Help",
239     "    Dismiss",
240     "",
241     "If the you choose the horizontal direction (this the",
242     "default), the area of the image between the two horizontal",
243     "endpoints of the chop line is removed.  Otherwise, the area",
244     "of the image between the two vertical endpoints of the chop",
245     "line is removed.",
246     "",
247     "Select a location within the image window to begin your chop,",
248     "press and hold any button.  Next, move the pointer to",
249     "another location in the image.  As you move a line will",
250     "connect the initial location and the pointer.  When you",
251     "release the button, the area within the image to chop is",
252     "determined by which direction you choose from the Command",
253     "widget.",
254     "",
255     "To cancel the image chopping, move the pointer back to the",
256     "starting point of the line and release the button.",
257     (char *) NULL,
258   },
259   *ImageColorEditHelp[] =
260   {
261     "In color edit mode, the Command widget has these options:",
262     "",
263     "    Method",
264     "      point",
265     "      replace",
266     "      floodfill",
267     "      filltoborder",
268     "      reset",
269     "    Pixel Color",
270     "      black",
271     "      blue",
272     "      cyan",
273     "      green",
274     "      gray",
275     "      red",
276     "      magenta",
277     "      yellow",
278     "      white",
279     "      Browser...",
280     "    Border Color",
281     "      black",
282     "      blue",
283     "      cyan",
284     "      green",
285     "      gray",
286     "      red",
287     "      magenta",
288     "      yellow",
289     "      white",
290     "      Browser...",
291     "    Fuzz",
292     "      0%",
293     "      2%",
294     "      5%",
295     "      10%",
296     "      15%",
297     "      Dialog...",
298     "    Undo",
299     "    Help",
300     "    Dismiss",
301     "",
302     "Choose a color editing method from the Method sub-menu",
303     "of the Command widget.  The point method recolors any pixel",
304     "selected with the pointer until the button is released.  The",
305     "replace method recolors any pixel that matches the color of",
306     "the pixel you select with a button press.  Floodfill recolors",
307     "any pixel that matches the color of the pixel you select with",
308     "a button press and is a neighbor.  Whereas filltoborder recolors",
309     "any neighbor pixel that is not the border color.  Finally reset",
310     "changes the entire image to the designated color.",
311     "",
312     "Next, choose a pixel color from the Pixel Color sub-menu.",
313     "Additional pixel colors can be specified with the color",
314     "browser.  You can change the menu colors by setting the X",
315     "resources pen1 through pen9.",
316     "",
317     "Now press button 1 to select a pixel within the image window",
318     "to change its color.  Additional pixels may be recolored as",
319     "prescribed by the method you choose.",
320     "",
321     "If the Magnify widget is mapped, it can be helpful in positioning",
322     "your pointer within the image (refer to button 2).",
323     "",
324     "The actual color you request for the pixels is saved in the",
325     "image.  However, the color that appears in your image window",
326     "may be different.  For example, on a monochrome screen the",
327     "pixel will appear black or white even if you choose the",
328     "color red as the pixel color.  However, the image saved to a",
329     "file with -write is written with red pixels.  To assure the",
330     "correct color text in the final image, any PseudoClass image",
331     "is promoted to DirectClass (see miff(5)).  To force a",
332     "PseudoClass image to remain PseudoClass, use -colors.",
333     (char *) NULL,
334   },
335   *ImageCompositeHelp[] =
336   {
337     "First a widget window is displayed requesting you to enter an",
338     "image name. Press Composite, Grab or type a file name.",
339     "Press Cancel if you choose not to create a composite image.",
340     "When you choose Grab, move the pointer to the desired window",
341     "and press any button.",
342     "",
343     "If the Composite image does not have any matte information,",
344     "you are informed and the file browser is displayed again.",
345     "Enter the name of a mask image.  The image is typically",
346     "grayscale and the same size as the composite image.  If the",
347     "image is not grayscale, it is converted to grayscale and the",
348     "resulting intensities are used as matte information.",
349     "",
350     "A small window appears showing the location of the cursor in",
351     "the image window. You are now in composite mode.  To exit",
352     "immediately, press Dismiss.  In composite mode, the Command",
353     "widget has these options:",
354     "",
355     "    Operators",
356     "      Over",
357     "      In",
358     "      Out",
359     "      Atop",
360     "      Xor",
361     "      Plus",
362     "      Minus",
363     "      Add",
364     "      Subtract",
365     "      Difference",
366     "      Multiply",
367     "      Bumpmap",
368     "      Copy",
369     "      CopyRed",
370     "      CopyGreen",
371     "      CopyBlue",
372     "      CopyOpacity",
373     "      Clear",
374     "    Dissolve",
375     "    Displace",
376     "    Help",
377     "    Dismiss",
378     "",
379     "Choose a composite operation from the Operators sub-menu of",
380     "the Command widget.  How each operator behaves is described",
381     "below.  Image window is the image currently displayed on",
382     "your X server and image is the image obtained with the File",
383     "Browser widget.",
384     "",
385     "Over     The result is the union of the two image shapes,",
386     "         with image obscuring image window in the region of",
387     "         overlap.",
388     "",
389     "In       The result is simply image cut by the shape of",
390     "         image window.  None of the image data of image",
391     "         window is in the result.",
392     "",
393     "Out      The resulting image is image with the shape of",
394     "         image window cut out.",
395     "",
396     "Atop     The result is the same shape as image image window,",
397     "         with image obscuring image window where the image",
398     "         shapes overlap.  Note this differs from over",
399     "         because the portion of image outside image window's",
400     "         shape does not appear in the result.",
401     "",
402     "Xor      The result is the image data from both image and",
403     "         image window that is outside the overlap region.",
404     "         The overlap region is blank.",
405     "",
406     "Plus     The result is just the sum of the image data.",
407     "         Output values are cropped to QuantumRange (no overflow).",
408     "",
409     "Minus    The result of image - image window, with underflow",
410     "         cropped to zero.",
411     "",
412     "Add      The result of image + image window, with overflow",
413     "         wrapping around (mod 256).",
414     "",
415     "Subtract The result of image - image window, with underflow",
416     "         wrapping around (mod 256).  The add and subtract",
417     "         operators can be used to perform reversible",
418     "         transformations.",
419     "",
420     "Difference",
421     "         The result of abs(image - image window).  This",
422     "         useful for comparing two very similar images.",
423     "",
424     "Multiply",
425     "         The result of image * image window.  This",
426     "         useful for the creation of drop-shadows.",
427     "",
428     "Bumpmap  The result of surface normals from image * image",
429     "         window.",
430     "",
431     "Copy     The resulting image is image window replaced with",
432     "         image.  Here the matte information is ignored.",
433     "",
434     "CopyRed  The red layer of the image window is replace with",
435     "         the red layer of the image.  The other layers are",
436     "         untouched.",
437     "",
438     "CopyGreen",
439     "         The green layer of the image window is replace with",
440     "         the green layer of the image.  The other layers are",
441     "         untouched.",
442     "",
443     "CopyBlue The blue layer of the image window is replace with",
444     "         the blue layer of the image.  The other layers are",
445     "         untouched.",
446     "",
447     "CopyOpacity",
448     "         The matte layer of the image window is replace with",
449     "         the matte layer of the image.  The other layers are",
450     "         untouched.",
451     "",
452     "The image compositor requires a matte, or alpha channel in",
453     "the image for some operations.  This extra channel usually",
454     "defines a mask which represents a sort of a cookie-cutter",
455     "for the image.  This the case when matte is opaque (full",
456     "coverage) for pixels inside the shape, zero outside, and",
457     "between 0 and QuantumRange on the boundary.  If image does not",
458     "have a matte channel, it is initialized with 0 for any pixel",
459     "matching in color to pixel location (0,0), otherwise QuantumRange.",
460     "",
461     "If you choose Dissolve, the composite operator becomes Over.  The",
462     "image matte channel percent transparency is initialized to factor.",
463     "The image window is initialized to (100-factor). Where factor is the",
464     "value you specify in the Dialog widget.",
465     "",
466     "Displace shifts the image pixels as defined by a displacement",
467     "map.  With this option, image is used as a displacement map.",
468     "Black, within the displacement map, is a maximum positive",
469     "displacement.  White is a maximum negative displacement and",
470     "middle gray is neutral.  The displacement is scaled to determine",
471     "the pixel shift.  By default, the displacement applies in both the",
472     "horizontal and vertical directions.  However, if you specify a mask,",
473     "image is the horizontal X displacement and mask the vertical Y",
474     "displacement.",
475     "",
476     "Note that matte information for image window is not retained",
477     "for colormapped X server visuals (e.g. StaticColor,",
478     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
479     "behavior may require a TrueColor or DirectColor visual or a",
480     "Standard Colormap.",
481     "",
482     "Choosing a composite operator is optional.  The default",
483     "operator is replace.  However, you must choose a location to",
484     "composite your image and press button 1.  Press and hold the",
485     "button before releasing and an outline of the image will",
486     "appear to help you identify your location.",
487     "",
488     "The actual colors of the composite image is saved.  However,",
489     "the color that appears in image window may be different.",
490     "For example, on a monochrome screen image window will appear",
491     "black or white even though your composited image may have",
492     "many colors.  If the image is saved to a file it is written",
493     "with the correct colors.  To assure the correct colors are",
494     "saved in the final image, any PseudoClass image is promoted",
495     "to DirectClass (see miff(5)).  To force a PseudoClass image",
496     "to remain PseudoClass, use -colors.",
497     (char *) NULL,
498   },
499   *ImageCutHelp[] =
500   {
501     "In cut mode, the Command widget has these options:",
502     "",
503     "    Help",
504     "    Dismiss",
505     "",
506     "To define a cut region, press button 1 and drag.  The",
507     "cut region is defined by a highlighted rectangle that",
508     "expands or contracts as it follows the pointer.  Once you",
509     "are satisfied with the cut region, release the button.",
510     "You are now in rectify mode.  In rectify mode, the Command",
511     "widget has these options:",
512     "",
513     "    Cut",
514     "    Help",
515     "    Dismiss",
516     "",
517     "You can make adjustments by moving the pointer to one of the",
518     "cut rectangle corners, pressing a button, and dragging.",
519     "Finally, press Cut to commit your copy region.  To",
520     "exit without cutting the image, press Dismiss.",
521     (char *) NULL,
522   },
523   *ImageCopyHelp[] =
524   {
525     "In copy mode, the Command widget has these options:",
526     "",
527     "    Help",
528     "    Dismiss",
529     "",
530     "To define a copy region, press button 1 and drag.  The",
531     "copy region is defined by a highlighted rectangle that",
532     "expands or contracts as it follows the pointer.  Once you",
533     "are satisfied with the copy region, release the button.",
534     "You are now in rectify mode.  In rectify mode, the Command",
535     "widget has these options:",
536     "",
537     "    Copy",
538     "    Help",
539     "    Dismiss",
540     "",
541     "You can make adjustments by moving the pointer to one of the",
542     "copy rectangle corners, pressing a button, and dragging.",
543     "Finally, press Copy to commit your copy region.  To",
544     "exit without copying the image, press Dismiss.",
545     (char *) NULL,
546   },
547   *ImageCropHelp[] =
548   {
549     "In crop mode, the Command widget has these options:",
550     "",
551     "    Help",
552     "    Dismiss",
553     "",
554     "To define a cropping region, press button 1 and drag.  The",
555     "cropping region is defined by a highlighted rectangle that",
556     "expands or contracts as it follows the pointer.  Once you",
557     "are satisfied with the cropping region, release the button.",
558     "You are now in rectify mode.  In rectify mode, the Command",
559     "widget has these options:",
560     "",
561     "    Crop",
562     "    Help",
563     "    Dismiss",
564     "",
565     "You can make adjustments by moving the pointer to one of the",
566     "cropping rectangle corners, pressing a button, and dragging.",
567     "Finally, press Crop to commit your cropping region.  To",
568     "exit without cropping the image, press Dismiss.",
569     (char *) NULL,
570   },
571   *ImageDrawHelp[] =
572   {
573     "The cursor changes to a crosshair to indicate you are in",
574     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
575     "the Command widget has these options:",
576     "",
577     "    Element",
578     "      point",
579     "      line",
580     "      rectangle",
581     "      fill rectangle",
582     "      circle",
583     "      fill circle",
584     "      ellipse",
585     "      fill ellipse",
586     "      polygon",
587     "      fill polygon",
588     "    Color",
589     "      black",
590     "      blue",
591     "      cyan",
592     "      green",
593     "      gray",
594     "      red",
595     "      magenta",
596     "      yellow",
597     "      white",
598     "      transparent",
599     "      Browser...",
600     "    Stipple",
601     "      Brick",
602     "      Diagonal",
603     "      Scales",
604     "      Vertical",
605     "      Wavy",
606     "      Translucent",
607     "      Opaque",
608     "      Open...",
609     "    Width",
610     "      1",
611     "      2",
612     "      4",
613     "      8",
614     "      16",
615     "      Dialog...",
616     "    Undo",
617     "    Help",
618     "    Dismiss",
619     "",
620     "Choose a drawing primitive from the Element sub-menu.",
621     "",
622     "Choose a color from the Color sub-menu.  Additional",
623     "colors can be specified with the color browser.",
624     "",
625     "If you choose the color browser and press Grab, you can",
626     "select the color by moving the pointer to the desired",
627     "color on the screen and press any button.  The transparent",
628     "color updates the image matte channel and is useful for",
629     "image compositing.",
630     "",
631     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
632     "Additional stipples can be specified with the file browser.",
633     "Stipples obtained from the file browser must be on disk in the",
634     "X11 bitmap format.",
635     "",
636     "Choose a width, if appropriate, from the Width sub-menu.  To",
637     "choose a specific width select the Dialog widget.",
638     "",
639     "Choose a point in the Image window and press button 1 and",
640     "hold.  Next, move the pointer to another location in the",
641     "image.  As you move, a line connects the initial location and",
642     "the pointer.  When you release the button, the image is",
643     "updated with the primitive you just drew.  For polygons, the",
644     "image is updated when you press and release the button without",
645     "moving the pointer.",
646     "",
647     "To cancel image drawing, move the pointer back to the",
648     "starting point of the line and release the button.",
649     (char *) NULL,
650   },
651   *DisplayHelp[] =
652   {
653     "BUTTONS",
654     "  The effects of each button press is described below.  Three",
655     "  buttons are required.  If you have a two button mouse,",
656     "  button 1 and 3 are returned.  Press ALT and button 3 to",
657     "  simulate button 2.",
658     "",
659     "  1    Press this button to map or unmap the Command widget.",
660     "",
661     "  2    Press and drag to define a region of the image to",
662     "       magnify.",
663     "",
664     "  3    Press and drag to choose from a select set of commands.",
665     "       This button behaves differently if the image being",
666     "       displayed is a visual image directory.  Here, choose a",
667     "       particular tile of the directory and press this button and",
668     "       drag to select a command from a pop-up menu.  Choose from",
669     "       these menu items:",
670     "",
671     "           Open",
672     "           Next",
673     "           Former",
674     "           Delete",
675     "           Update",
676     "",
677     "       If you choose Open, the image represented by the tile is",
678     "       displayed.  To return to the visual image directory, choose",
679     "       Next from the Command widget.  Next and Former moves to the",
680     "       next or former image respectively.  Choose Delete to delete",
681     "       a particular image tile.  Finally, choose Update to",
682     "       synchronize all the image tiles with their respective",
683     "       images.",
684     "",
685     "COMMAND WIDGET",
686     "  The Command widget lists a number of sub-menus and commands.",
687     "  They are",
688     "",
689     "      File",
690     "        Open...",
691     "        Next",
692     "        Former",
693     "        Select...",
694     "        Save...",
695     "        Print...",
696     "        Delete...",
697     "        New...",
698     "        Visual Directory...",
699     "        Quit",
700     "      Edit",
701     "        Undo",
702     "        Redo",
703     "        Cut",
704     "        Copy",
705     "        Paste",
706     "      View",
707     "        Half Size",
708     "        Original Size",
709     "        Double Size",
710     "        Resize...",
711     "        Apply",
712     "        Refresh",
713     "        Restore",
714     "      Transform",
715     "        Crop",
716     "        Chop",
717     "        Flop",
718     "        Flip",
719     "        Rotate Right",
720     "        Rotate Left",
721     "        Rotate...",
722     "        Shear...",
723     "        Roll...",
724     "        Trim Edges",
725     "      Enhance",
726     "        Brightness...",
727     "        Saturation...",
728     "        Hue...",
729     "        Gamma...",
730     "        Sharpen...",
731     "        Dull",
732     "        Contrast Stretch...",
733     "        Sigmoidal Contrast...",
734     "        Normalize",
735     "        Equalize",
736     "        Negate",
737     "        Grayscale",
738     "        Map...",
739     "        Quantize...",
740     "      Effects",
741     "        Despeckle",
742     "        Emboss",
743     "        Reduce Noise",
744     "        Add Noise",
745     "        Sharpen...",
746     "        Blur...",
747     "        Threshold...",
748     "        Edge Detect...",
749     "        Spread...",
750     "        Shade...",
751     "        Painting...",
752     "        Segment...",
753     "      F/X",
754     "        Solarize...",
755     "        Sepia Tone...",
756     "        Swirl...",
757     "        Implode...",
758     "        Vignette...",
759     "        Wave...",
760     "        Oil Painting...",
761     "        Charcoal Drawing...",
762     "      Image Edit",
763     "        Annotate...",
764     "        Draw...",
765     "        Color...",
766     "        Matte...",
767     "        Composite...",
768     "        Add Border...",
769     "        Add Frame...",
770     "        Comment...",
771     "        Launch...",
772     "        Region of Interest...",
773     "      Miscellany",
774     "        Image Info",
775     "        Zoom Image",
776     "        Show Preview...",
777     "        Show Histogram",
778     "        Show Matte",
779     "        Background...",
780     "        Slide Show",
781     "        Preferences...",
782     "      Help",
783     "        Overview",
784     "        Browse Documentation",
785     "        About Display",
786     "",
787     "  Menu items with a indented triangle have a sub-menu.  They",
788     "  are represented above as the indented items.  To access a",
789     "  sub-menu item, move the pointer to the appropriate menu and",
790     "  press a button and drag.  When you find the desired sub-menu",
791     "  item, release the button and the command is executed.  Move",
792     "  the pointer away from the sub-menu if you decide not to",
793     "  execute a particular command.",
794     "",
795     "KEYBOARD ACCELERATORS",
796     "  Accelerators are one or two key presses that effect a",
797     "  particular command.  The keyboard accelerators that",
798     "  display(1) understands is:",
799     "",
800     "  Ctl+O     Press to open an image from a file.",
801     "",
802     "  space     Press to display the next image.",
803     "",
804     "            If the image is a multi-paged document such as a Postscript",
805     "            document, you can skip ahead several pages by preceding",
806     "            this command with a number.  For example to display the",
807     "            third page beyond the current page, press 3<space>.",
808     "",
809     "  backspace Press to display the former image.",
810     "",
811     "            If the image is a multi-paged document such as a Postscript",
812     "            document, you can skip behind several pages by preceding",
813     "            this command with a number.  For example to display the",
814     "            third page preceding the current page, press 3<backspace>.",
815     "",
816     "  Ctl+S     Press to write the image to a file.",
817     "",
818     "  Ctl+P     Press to print the image to a Postscript printer.",
819     "",
820     "  Ctl+D     Press to delete an image file.",
821     "",
822     "  Ctl+N     Press to create a blank canvas.",
823     "",
824     "  Ctl+Q     Press to discard all images and exit program.",
825     "",
826     "  Ctl+Z     Press to undo last image transformation.",
827     "",
828     "  Ctl+R     Press to redo last image transformation.",
829     "",
830     "  Ctl+X     Press to cut a region of the image.",
831     "",
832     "  Ctl+C     Press to copy a region of the image.",
833     "",
834     "  Ctl+V     Press to paste a region to the image.",
835     "",
836     "  <         Press to half the image size.",
837     "",
838     "  -         Press to return to the original image size.",
839     "",
840     "  >         Press to double the image size.",
841     "",
842     "  %         Press to resize the image to a width and height you",
843     "            specify.",
844     "",
845     "Cmd-A       Press to make any image transformations permanent."
846     "",
847     "            By default, any image size transformations are applied",
848     "            to the original image to create the image displayed on",
849     "            the X server.  However, the transformations are not",
850     "            permanent (i.e. the original image does not change",
851     "            size only the X image does).  For example, if you",
852     "            press > the X image will appear to double in size,",
853     "            but the original image will in fact remain the same size.",
854     "            To force the original image to double in size, press >",
855     "            followed by Cmd-A.",
856     "",
857     "  @         Press to refresh the image window.",
858     "",
859     "  C         Press to cut out a rectangular region of the image.",
860     "",
861     "  [         Press to chop the image.",
862     "",
863     "  H         Press to flop image in the horizontal direction.",
864     "",
865     "  V         Press to flip image in the vertical direction.",
866     "",
867     "  /         Press to rotate the image 90 degrees clockwise.",
868     "",
869     " \\         Press to rotate the image 90 degrees counter-clockwise.",
870     "",
871     "  *         Press to rotate the image the number of degrees you",
872     "            specify.",
873     "",
874     "  S         Press to shear the image the number of degrees you",
875     "            specify.",
876     "",
877     "  R         Press to roll the image.",
878     "",
879     "  T         Press to trim the image edges.",
880     "",
881     "  Shft-H    Press to vary the image hue.",
882     "",
883     "  Shft-S    Press to vary the color saturation.",
884     "",
885     "  Shft-L    Press to vary the color brightness.",
886     "",
887     "  Shft-G    Press to gamma correct the image.",
888     "",
889     "  Shft-C    Press to sharpen the image contrast.",
890     "",
891     "  Shft-Z    Press to dull the image contrast.",
892     "",
893     "  =         Press to perform histogram equalization on the image.",
894     "",
895     "  Shft-N    Press to perform histogram normalization on the image.",
896     "",
897     "  Shft-~    Press to negate the colors of the image.",
898     "",
899     "  .         Press to convert the image colors to gray.",
900     "",
901     "  Shft-#    Press to set the maximum number of unique colors in the",
902     "            image.",
903     "",
904     "  F2        Press to reduce the speckles in an image.",
905     "",
906     "  F3        Press to eliminate peak noise from an image.",
907     "",
908     "  F4        Press to add noise to an image.",
909     "",
910     "  F5        Press to sharpen an image.",
911     "",
912     "  F6        Press to delete an image file.",
913     "",
914     "  F7        Press to threshold the image.",
915     "",
916     "  F8        Press to detect edges within an image.",
917     "",
918     "  F9        Press to emboss an image.",
919     "",
920     "  F10       Press to displace pixels by a random amount.",
921     "",
922     "  F11       Press to negate all pixels above the threshold level.",
923     "",
924     "  F12       Press to shade the image using a distant light source.",
925     "",
926     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
927     "",
928     "  F14       Press to segment the image by color.",
929     "",
930     "  Meta-S    Press to swirl image pixels about the center.",
931     "",
932     "  Meta-I    Press to implode image pixels about the center.",
933     "",
934     "  Meta-W    Press to alter an image along a sine wave.",
935     "",
936     "  Meta-P    Press to simulate an oil painting.",
937     "",
938     "  Meta-C    Press to simulate a charcoal drawing.",
939     "",
940     "  Alt-A     Press to annotate the image with text.",
941     "",
942     "  Alt-D     Press to draw on an image.",
943     "",
944     "  Alt-P     Press to edit an image pixel color.",
945     "",
946     "  Alt-M     Press to edit the image matte information.",
947     "",
948     "  Alt-V     Press to composite the image with another.",
949     "",
950     "  Alt-B     Press to add a border to the image.",
951     "",
952     "  Alt-F     Press to add an ornamental border to the image.",
953     "",
954     "  Alt-Shft-!",
955     "            Press to add an image comment.",
956     "",
957     "  Ctl-A     Press to apply image processing techniques to a region",
958     "            of interest.",
959     "",
960     "  Shft-?    Press to display information about the image.",
961     "",
962     "  Shft-+    Press to map the zoom image window.",
963     "",
964     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
965     "",
966     "  F1        Press to display helpful information about display(1).",
967     "",
968     "  Find      Press to browse documentation about ImageMagick.",
969     "",
970     "  1-9       Press to change the level of magnification.",
971     "",
972     "  Use the arrow keys to move the image one pixel up, down,",
973     "  left, or right within the magnify window.  Be sure to first",
974     "  map the magnify window by pressing button 2.",
975     "",
976     "  Press ALT and one of the arrow keys to trim off one pixel",
977     "  from any side of the image.",
978     (char *) NULL,
979   },
980   *ImageMatteEditHelp[] =
981   {
982     "Matte information within an image is useful for some",
983     "operations such as image compositing (See IMAGE",
984     "COMPOSITING).  This extra channel usually defines a mask",
985     "which represents a sort of a cookie-cutter for the image.",
986     "This the case when matte is opaque (full coverage) for",
987     "pixels inside the shape, zero outside, and between 0 and",
988     "QuantumRange on the boundary.",
989     "",
990     "A small window appears showing the location of the cursor in",
991     "the image window. You are now in matte edit mode.  To exit",
992     "immediately, press Dismiss.  In matte edit mode, the Command",
993     "widget has these options:",
994     "",
995     "    Method",
996     "      point",
997     "      replace",
998     "      floodfill",
999     "      filltoborder",
1000     "      reset",
1001     "    Border Color",
1002     "      black",
1003     "      blue",
1004     "      cyan",
1005     "      green",
1006     "      gray",
1007     "      red",
1008     "      magenta",
1009     "      yellow",
1010     "      white",
1011     "      Browser...",
1012     "    Fuzz",
1013     "      0%",
1014     "      2%",
1015     "      5%",
1016     "      10%",
1017     "      15%",
1018     "      Dialog...",
1019     "    Matte",
1020     "      Opaque",
1021     "      Transparent",
1022     "      Dialog...",
1023     "    Undo",
1024     "    Help",
1025     "    Dismiss",
1026     "",
1027     "Choose a matte editing method from the Method sub-menu of",
1028     "the Command widget.  The point method changes the matte value",
1029     "of any pixel selected with the pointer until the button is",
1030     "is released.  The replace method changes the matte value of",
1031     "any pixel that matches the color of the pixel you select with",
1032     "a button press.  Floodfill changes the matte value of any pixel",
1033     "that matches the color of the pixel you select with a button",
1034     "press and is a neighbor.  Whereas filltoborder changes the matte",
1035     "value any neighbor pixel that is not the border color.  Finally",
1036     "reset changes the entire image to the designated matte value.",
1037     "",
1038     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1039     "select the Dialog entry.  Here a dialog appears requesting a matte",
1040     "value.  The value you select is assigned as the opacity value of the",
1041     "selected pixel or pixels.",
1042     "",
1043     "Now, press any button to select a pixel within the image",
1044     "window to change its matte value.",
1045     "",
1046     "If the Magnify widget is mapped, it can be helpful in positioning",
1047     "your pointer within the image (refer to button 2).",
1048     "",
1049     "Matte information is only valid in a DirectClass image.",
1050     "Therefore, any PseudoClass image is promoted to DirectClass",
1051     "(see miff(5)).  Note that matte information for PseudoClass",
1052     "is not retained for colormapped X server visuals (e.g.",
1053     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1054     "immediately save your image to a file (refer to Write).",
1055     "Correct matte editing behavior may require a TrueColor or",
1056     "DirectColor visual or a Standard Colormap.",
1057     (char *) NULL,
1058   },
1059   *ImagePanHelp[] =
1060   {
1061     "When an image exceeds the width or height of the X server",
1062     "screen, display maps a small panning icon.  The rectangle",
1063     "within the panning icon shows the area that is currently",
1064     "displayed in the image window.  To pan about the image,",
1065     "press any button and drag the pointer within the panning",
1066     "icon.  The pan rectangle moves with the pointer and the",
1067     "image window is updated to reflect the location of the",
1068     "rectangle within the panning icon.  When you have selected",
1069     "the area of the image you wish to view, release the button.",
1070     "",
1071     "Use the arrow keys to pan the image one pixel up, down,",
1072     "left, or right within the image window.",
1073     "",
1074     "The panning icon is withdrawn if the image becomes smaller",
1075     "than the dimensions of the X server screen.",
1076     (char *) NULL,
1077   },
1078   *ImagePasteHelp[] =
1079   {
1080     "A small window appears showing the location of the cursor in",
1081     "the image window. You are now in paste mode.  To exit",
1082     "immediately, press Dismiss.  In paste mode, the Command",
1083     "widget has these options:",
1084     "",
1085     "    Operators",
1086     "      over",
1087     "      in",
1088     "      out",
1089     "      atop",
1090     "      xor",
1091     "      plus",
1092     "      minus",
1093     "      add",
1094     "      subtract",
1095     "      difference",
1096     "      replace",
1097     "    Help",
1098     "    Dismiss",
1099     "",
1100     "Choose a composite operation from the Operators sub-menu of",
1101     "the Command widget.  How each operator behaves is described",
1102     "below.  Image window is the image currently displayed on",
1103     "your X server and image is the image obtained with the File",
1104     "Browser widget.",
1105     "",
1106     "Over     The result is the union of the two image shapes,",
1107     "         with image obscuring image window in the region of",
1108     "         overlap.",
1109     "",
1110     "In       The result is simply image cut by the shape of",
1111     "         image window.  None of the image data of image",
1112     "         window is in the result.",
1113     "",
1114     "Out      The resulting image is image with the shape of",
1115     "         image window cut out.",
1116     "",
1117     "Atop     The result is the same shape as image image window,",
1118     "         with image obscuring image window where the image",
1119     "         shapes overlap.  Note this differs from over",
1120     "         because the portion of image outside image window's",
1121     "         shape does not appear in the result.",
1122     "",
1123     "Xor      The result is the image data from both image and",
1124     "         image window that is outside the overlap region.",
1125     "         The overlap region is blank.",
1126     "",
1127     "Plus     The result is just the sum of the image data.",
1128     "         Output values are cropped to QuantumRange (no overflow).",
1129     "         This operation is independent of the matte",
1130     "         channels.",
1131     "",
1132     "Minus    The result of image - image window, with underflow",
1133     "         cropped to zero.",
1134     "",
1135     "Add      The result of image + image window, with overflow",
1136     "         wrapping around (mod 256).",
1137     "",
1138     "Subtract The result of image - image window, with underflow",
1139     "         wrapping around (mod 256).  The add and subtract",
1140     "         operators can be used to perform reversible",
1141     "         transformations.",
1142     "",
1143     "Difference",
1144     "         The result of abs(image - image window).  This",
1145     "         useful for comparing two very similar images.",
1146     "",
1147     "Copy     The resulting image is image window replaced with",
1148     "         image.  Here the matte information is ignored.",
1149     "",
1150     "CopyRed  The red layer of the image window is replace with",
1151     "         the red layer of the image.  The other layers are",
1152     "         untouched.",
1153     "",
1154     "CopyGreen",
1155     "         The green layer of the image window is replace with",
1156     "         the green layer of the image.  The other layers are",
1157     "         untouched.",
1158     "",
1159     "CopyBlue The blue layer of the image window is replace with",
1160     "         the blue layer of the image.  The other layers are",
1161     "         untouched.",
1162     "",
1163     "CopyOpacity",
1164     "         The matte layer of the image window is replace with",
1165     "         the matte layer of the image.  The other layers are",
1166     "         untouched.",
1167     "",
1168     "The image compositor requires a matte, or alpha channel in",
1169     "the image for some operations.  This extra channel usually",
1170     "defines a mask which represents a sort of a cookie-cutter",
1171     "for the image.  This the case when matte is opaque (full",
1172     "coverage) for pixels inside the shape, zero outside, and",
1173     "between 0 and QuantumRange on the boundary.  If image does not",
1174     "have a matte channel, it is initialized with 0 for any pixel",
1175     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1176     "",
1177     "Note that matte information for image window is not retained",
1178     "for colormapped X server visuals (e.g. StaticColor,",
1179     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1180     "behavior may require a TrueColor or DirectColor visual or a",
1181     "Standard Colormap.",
1182     "",
1183     "Choosing a composite operator is optional.  The default",
1184     "operator is replace.  However, you must choose a location to",
1185     "paste your image and press button 1.  Press and hold the",
1186     "button before releasing and an outline of the image will",
1187     "appear to help you identify your location.",
1188     "",
1189     "The actual colors of the pasted image is saved.  However,",
1190     "the color that appears in image window may be different.",
1191     "For example, on a monochrome screen image window will appear",
1192     "black or white even though your pasted image may have",
1193     "many colors.  If the image is saved to a file it is written",
1194     "with the correct colors.  To assure the correct colors are",
1195     "saved in the final image, any PseudoClass image is promoted",
1196     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1197     "to remain PseudoClass, use -colors.",
1198     (char *) NULL,
1199   },
1200   *ImageROIHelp[] =
1201   {
1202     "In region of interest mode, the Command widget has these",
1203     "options:",
1204     "",
1205     "    Help",
1206     "    Dismiss",
1207     "",
1208     "To define a region of interest, press button 1 and drag.",
1209     "The region of interest is defined by a highlighted rectangle",
1210     "that expands or contracts as it follows the pointer.  Once",
1211     "you are satisfied with the region of interest, release the",
1212     "button.  You are now in apply mode.  In apply mode the",
1213     "Command widget has these options:",
1214     "",
1215     "      File",
1216     "        Save...",
1217     "        Print...",
1218     "      Edit",
1219     "        Undo",
1220     "        Redo",
1221     "      Transform",
1222     "        Flop",
1223     "        Flip",
1224     "        Rotate Right",
1225     "        Rotate Left",
1226     "      Enhance",
1227     "        Hue...",
1228     "        Saturation...",
1229     "        Brightness...",
1230     "        Gamma...",
1231     "        Spiff",
1232     "        Dull",
1233     "        Contrast Stretch",
1234     "        Sigmoidal Contrast...",
1235     "        Normalize",
1236     "        Equalize",
1237     "        Negate",
1238     "        Grayscale",
1239     "        Map...",
1240     "        Quantize...",
1241     "      Effects",
1242     "        Despeckle",
1243     "        Emboss",
1244     "        Reduce Noise",
1245     "        Sharpen...",
1246     "        Blur...",
1247     "        Threshold...",
1248     "        Edge Detect...",
1249     "        Spread...",
1250     "        Shade...",
1251     "        Raise...",
1252     "        Segment...",
1253     "      F/X",
1254     "        Solarize...",
1255     "        Sepia Tone...",
1256     "        Swirl...",
1257     "        Implode...",
1258     "        Vignette...",
1259     "        Wave...",
1260     "        Oil Painting...",
1261     "        Charcoal Drawing...",
1262     "      Miscellany",
1263     "        Image Info",
1264     "        Zoom Image",
1265     "        Show Preview...",
1266     "        Show Histogram",
1267     "        Show Matte",
1268     "      Help",
1269     "      Dismiss",
1270     "",
1271     "You can make adjustments to the region of interest by moving",
1272     "the pointer to one of the rectangle corners, pressing a",
1273     "button, and dragging.  Finally, choose an image processing",
1274     "technique from the Command widget.  You can choose more than",
1275     "one image processing technique to apply to an area.",
1276     "Alternatively, you can move the region of interest before",
1277     "applying another image processing technique.  To exit, press",
1278     "Dismiss.",
1279     (char *) NULL,
1280   },
1281   *ImageRotateHelp[] =
1282   {
1283     "In rotate mode, the Command widget has these options:",
1284     "",
1285     "    Pixel Color",
1286     "      black",
1287     "      blue",
1288     "      cyan",
1289     "      green",
1290     "      gray",
1291     "      red",
1292     "      magenta",
1293     "      yellow",
1294     "      white",
1295     "      Browser...",
1296     "    Direction",
1297     "      horizontal",
1298     "      vertical",
1299     "    Help",
1300     "    Dismiss",
1301     "",
1302     "Choose a background color from the Pixel Color sub-menu.",
1303     "Additional background colors can be specified with the color",
1304     "browser.  You can change the menu colors by setting the X",
1305     "resources pen1 through pen9.",
1306     "",
1307     "If you choose the color browser and press Grab, you can",
1308     "select the background color by moving the pointer to the",
1309     "desired color on the screen and press any button.",
1310     "",
1311     "Choose a point in the image window and press this button and",
1312     "hold.  Next, move the pointer to another location in the",
1313     "image.  As you move a line connects the initial location and",
1314     "the pointer.  When you release the button, the degree of",
1315     "image rotation is determined by the slope of the line you",
1316     "just drew.  The slope is relative to the direction you",
1317     "choose from the Direction sub-menu of the Command widget.",
1318     "",
1319     "To cancel the image rotation, move the pointer back to the",
1320     "starting point of the line and release the button.",
1321     (char *) NULL,
1322   };
1323 \f
1324 /*
1325   Enumeration declarations.
1326 */
1327 typedef enum
1328 {
1329   CopyMode,
1330   CropMode,
1331   CutMode
1332 } ClipboardMode;
1333
1334 typedef enum
1335 {
1336   OpenCommand,
1337   NextCommand,
1338   FormerCommand,
1339   SelectCommand,
1340   SaveCommand,
1341   PrintCommand,
1342   DeleteCommand,
1343   NewCommand,
1344   VisualDirectoryCommand,
1345   QuitCommand,
1346   UndoCommand,
1347   RedoCommand,
1348   CutCommand,
1349   CopyCommand,
1350   PasteCommand,
1351   HalfSizeCommand,
1352   OriginalSizeCommand,
1353   DoubleSizeCommand,
1354   ResizeCommand,
1355   ApplyCommand,
1356   RefreshCommand,
1357   RestoreCommand,
1358   CropCommand,
1359   ChopCommand,
1360   FlopCommand,
1361   FlipCommand,
1362   RotateRightCommand,
1363   RotateLeftCommand,
1364   RotateCommand,
1365   ShearCommand,
1366   RollCommand,
1367   TrimCommand,
1368   HueCommand,
1369   SaturationCommand,
1370   BrightnessCommand,
1371   GammaCommand,
1372   SpiffCommand,
1373   DullCommand,
1374   ContrastStretchCommand,
1375   SigmoidalContrastCommand,
1376   NormalizeCommand,
1377   EqualizeCommand,
1378   NegateCommand,
1379   GrayscaleCommand,
1380   MapCommand,
1381   QuantizeCommand,
1382   DespeckleCommand,
1383   EmbossCommand,
1384   ReduceNoiseCommand,
1385   AddNoiseCommand,
1386   SharpenCommand,
1387   BlurCommand,
1388   ThresholdCommand,
1389   EdgeDetectCommand,
1390   SpreadCommand,
1391   ShadeCommand,
1392   RaiseCommand,
1393   SegmentCommand,
1394   SolarizeCommand,
1395   SepiaToneCommand,
1396   SwirlCommand,
1397   ImplodeCommand,
1398   VignetteCommand,
1399   WaveCommand,
1400   OilPaintCommand,
1401   CharcoalDrawCommand,
1402   AnnotateCommand,
1403   DrawCommand,
1404   ColorCommand,
1405   MatteCommand,
1406   CompositeCommand,
1407   AddBorderCommand,
1408   AddFrameCommand,
1409   CommentCommand,
1410   LaunchCommand,
1411   RegionofInterestCommand,
1412   ROIHelpCommand,
1413   ROIDismissCommand,
1414   InfoCommand,
1415   ZoomCommand,
1416   ShowPreviewCommand,
1417   ShowHistogramCommand,
1418   ShowMatteCommand,
1419   BackgroundCommand,
1420   SlideShowCommand,
1421   PreferencesCommand,
1422   HelpCommand,
1423   BrowseDocumentationCommand,
1424   VersionCommand,
1425   SaveToUndoBufferCommand,
1426   FreeBuffersCommand,
1427   NullCommand
1428 } CommandType;
1429
1430 typedef enum
1431 {
1432   AnnotateNameCommand,
1433   AnnotateFontColorCommand,
1434   AnnotateBackgroundColorCommand,
1435   AnnotateRotateCommand,
1436   AnnotateHelpCommand,
1437   AnnotateDismissCommand,
1438   TextHelpCommand,
1439   TextApplyCommand,
1440   ChopDirectionCommand,
1441   ChopHelpCommand,
1442   ChopDismissCommand,
1443   HorizontalChopCommand,
1444   VerticalChopCommand,
1445   ColorEditMethodCommand,
1446   ColorEditColorCommand,
1447   ColorEditBorderCommand,
1448   ColorEditFuzzCommand,
1449   ColorEditUndoCommand,
1450   ColorEditHelpCommand,
1451   ColorEditDismissCommand,
1452   CompositeOperatorsCommand,
1453   CompositeDissolveCommand,
1454   CompositeDisplaceCommand,
1455   CompositeHelpCommand,
1456   CompositeDismissCommand,
1457   CropHelpCommand,
1458   CropDismissCommand,
1459   RectifyCopyCommand,
1460   RectifyHelpCommand,
1461   RectifyDismissCommand,
1462   DrawElementCommand,
1463   DrawColorCommand,
1464   DrawStippleCommand,
1465   DrawWidthCommand,
1466   DrawUndoCommand,
1467   DrawHelpCommand,
1468   DrawDismissCommand,
1469   MatteEditMethod,
1470   MatteEditBorderCommand,
1471   MatteEditFuzzCommand,
1472   MatteEditValueCommand,
1473   MatteEditUndoCommand,
1474   MatteEditHelpCommand,
1475   MatteEditDismissCommand,
1476   PasteOperatorsCommand,
1477   PasteHelpCommand,
1478   PasteDismissCommand,
1479   RotateColorCommand,
1480   RotateDirectionCommand,
1481   RotateCropCommand,
1482   RotateSharpenCommand,
1483   RotateHelpCommand,
1484   RotateDismissCommand,
1485   HorizontalRotateCommand,
1486   VerticalRotateCommand,
1487   TileLoadCommand,
1488   TileNextCommand,
1489   TileFormerCommand,
1490   TileDeleteCommand,
1491   TileUpdateCommand
1492 } ModeType;
1493 \f
1494 /*
1495   Stipples.
1496 */
1497 #define BricksWidth  20
1498 #define BricksHeight  20
1499 #define DiagonalWidth  16
1500 #define DiagonalHeight  16
1501 #define HighlightWidth  8
1502 #define HighlightHeight  8
1503 #define ScalesWidth  16
1504 #define ScalesHeight  16
1505 #define ShadowWidth  8
1506 #define ShadowHeight  8
1507 #define VerticalWidth  16
1508 #define VerticalHeight  16
1509 #define WavyWidth  16
1510 #define WavyHeight  16
1511 \f
1512 /*
1513   Constant declaration.
1514 */
1515 static const int
1516   RoiDelta = 8;
1517
1518 static const unsigned char
1519   BricksBitmap[] =
1520   {
1521     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1522     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1523     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1524     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1525     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1526   },
1527   DiagonalBitmap[] =
1528   {
1529     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1530     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1531     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1532   },
1533   ScalesBitmap[] =
1534   {
1535     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1536     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1537     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1538   },
1539   VerticalBitmap[] =
1540   {
1541     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1542     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1543     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1544   },
1545   WavyBitmap[] =
1546   {
1547     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1548     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1549     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1550   };
1551 \f
1552 /*
1553   Function prototypes.
1554 */
1555 static CommandType
1556   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1557     const MagickStatusType,KeySym,Image **);
1558
1559 static Image
1560   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1561     Image **),
1562   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1563   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
1564   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
1565
1566 static MagickBooleanType
1567   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
1568   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1569   XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
1570   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
1571   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
1572   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1573   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
1574   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
1575   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1576   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
1577   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
1578   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
1579   XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
1580   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
1581   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
1582
1583 static void
1584   XDrawPanRectangle(Display *,XWindows *),
1585   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **),
1586   XMagnifyImage(Display *,XWindows *,XEvent *),
1587   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
1588   XPanImage(Display *,XWindows *,XEvent *),
1589   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1590     const KeySym),
1591   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1592   XScreenEvent(Display *,XWindows *,XEvent *),
1593   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1594 \f
1595 /*
1596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1597 %                                                                             %
1598 %                                                                             %
1599 %                                                                             %
1600 %   D i s p l a y I m a g e s                                                 %
1601 %                                                                             %
1602 %                                                                             %
1603 %                                                                             %
1604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1605 %
1606 %  DisplayImages() displays an image sequence to any X window screen.  It
1607 %  returns a value other than 0 if successful.  Check the exception member
1608 %  of image to determine the reason for any failure.
1609 %
1610 %  The format of the DisplayImages method is:
1611 %
1612 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1613 %        Image *images)
1614 %
1615 %  A description of each parameter follows:
1616 %
1617 %    o image_info: the image info.
1618 %
1619 %    o image: the image.
1620 %
1621 */
1622 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1623   Image *images)
1624 {
1625   char
1626     *argv[1];
1627
1628   Display
1629     *display;
1630
1631   Image
1632     *image;
1633
1634   register long
1635     i;
1636
1637   unsigned long
1638     state;
1639
1640   XrmDatabase
1641     resource_database;
1642
1643   XResourceInfo
1644     resource_info;
1645
1646   assert(image_info != (const ImageInfo *) NULL);
1647   assert(image_info->signature == MagickSignature);
1648   assert(images != (Image *) NULL);
1649   assert(images->signature == MagickSignature);
1650   if (images->debug != MagickFalse)
1651     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1652   display=XOpenDisplay(image_info->server_name);
1653   if (display == (Display *) NULL)
1654     {
1655       (void) ThrowMagickException(&images->exception,GetMagickModule(),
1656         XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
1657         image_info->server_name));
1658       return(MagickFalse);
1659     }
1660   if (images->exception.severity != UndefinedException)
1661     CatchException(&images->exception);
1662   (void) XSetErrorHandler(XError);
1663   resource_database=XGetResourceDatabase(display,GetClientName());
1664   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1665   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1666   if (image_info->page != (char *) NULL)
1667     resource_info.image_geometry=AcquireString(image_info->page);
1668   resource_info.immutable=MagickTrue;
1669   argv[0]=AcquireString(GetClientName());
1670   state=DefaultState;
1671   for (i=0; (state & ExitState) == 0; i++)
1672   {
1673     if ((images->iterations != 0) && (i >= (long) images->iterations))
1674       break;
1675     image=GetImageFromList(images,i % GetImageListLength(images));
1676     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
1677   }
1678   argv[0]=DestroyString(argv[0]);
1679   (void) XCloseDisplay(display);
1680   XDestroyResourceInfo(&resource_info);
1681   if (images->exception.severity != UndefinedException)
1682     return(MagickFalse);
1683   return(MagickTrue);
1684 }
1685 \f
1686 /*
1687 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1688 %                                                                             %
1689 %                                                                             %
1690 %                                                                             %
1691 %   R e m o t e D i s p l a y C o m m a n d                                   %
1692 %                                                                             %
1693 %                                                                             %
1694 %                                                                             %
1695 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1696 %
1697 %  RemoteDisplayCommand() encourages a remote display program to display the
1698 %  specified image filename.
1699 %
1700 %  The format of the RemoteDisplayCommand method is:
1701 %
1702 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1703 %        const char *window,const char *filename,ExceptionInfo *exception)
1704 %
1705 %  A description of each parameter follows:
1706 %
1707 %    o image_info: the image info.
1708 %
1709 %    o window: Specifies the name or id of an X window.
1710 %
1711 %    o filename: the name of the image filename to display.
1712 %
1713 %    o exception: return any errors or warnings in this structure.
1714 %
1715 */
1716 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1717   const char *window,const char *filename,ExceptionInfo *exception)
1718 {
1719   Display
1720     *display;
1721
1722   MagickStatusType
1723     status;
1724
1725   assert(image_info != (const ImageInfo *) NULL);
1726   assert(image_info->signature == MagickSignature);
1727   assert(filename != (char *) NULL);
1728   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1729   display=XOpenDisplay(image_info->server_name);
1730   if (display == (Display *) NULL)
1731     {
1732       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1733         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1734       return(MagickFalse);
1735     }
1736   (void) XSetErrorHandler(XError);
1737   status=XRemoteCommand(display,window,filename);
1738   (void) XCloseDisplay(display);
1739   return(status != 0 ? MagickTrue : MagickFalse);
1740 }
1741 \f
1742 /*
1743 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1744 %                                                                             %
1745 %                                                                             %
1746 %                                                                             %
1747 +   X A n n o t a t e E d i t I m a g e                                       %
1748 %                                                                             %
1749 %                                                                             %
1750 %                                                                             %
1751 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1752 %
1753 %  XAnnotateEditImage() annotates the image with text.
1754 %
1755 %  The format of the XAnnotateEditImage method is:
1756 %
1757 %      MagickBooleanType XAnnotateEditImage(Display *display,
1758 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
1759 %
1760 %  A description of each parameter follows:
1761 %
1762 %    o display: Specifies a connection to an X server;  returned from
1763 %      XOpenDisplay.
1764 %
1765 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1766 %
1767 %    o windows: Specifies a pointer to a XWindows structure.
1768 %
1769 %    o image: the image; returned from ReadImage.
1770 %
1771 */
1772
1773 static inline long MagickMax(const long x,const long y)
1774 {
1775   if (x > y)
1776     return(x);
1777   return(y);
1778 }
1779
1780 static inline long MagickMin(const long x,const long y)
1781 {
1782   if (x < y)
1783     return(x);
1784   return(y);
1785 }
1786
1787 static MagickBooleanType XAnnotateEditImage(Display *display,
1788   XResourceInfo *resource_info,XWindows *windows,Image *image)
1789 {
1790   static const char
1791     *AnnotateMenu[] =
1792     {
1793       "Font Name",
1794       "Font Color",
1795       "Box Color",
1796       "Rotate Text",
1797       "Help",
1798       "Dismiss",
1799       (char *) NULL
1800     },
1801     *TextMenu[] =
1802     {
1803       "Help",
1804       "Apply",
1805       (char *) NULL
1806     };
1807
1808   static const ModeType
1809     AnnotateCommands[] =
1810     {
1811       AnnotateNameCommand,
1812       AnnotateFontColorCommand,
1813       AnnotateBackgroundColorCommand,
1814       AnnotateRotateCommand,
1815       AnnotateHelpCommand,
1816       AnnotateDismissCommand
1817     },
1818     TextCommands[] =
1819     {
1820       TextHelpCommand,
1821       TextApplyCommand
1822     };
1823
1824   static MagickBooleanType
1825     transparent_box = MagickTrue,
1826     transparent_pen = MagickFalse;
1827
1828   static MagickRealType
1829     degrees = 0.0;
1830
1831   static unsigned int
1832     box_id = MaxNumberPens-2,
1833     font_id = 0,
1834     pen_id = 0;
1835
1836   char
1837     command[MaxTextExtent],
1838     text[MaxTextExtent];
1839
1840   const char
1841     *ColorMenu[MaxNumberPens+1];
1842
1843   Cursor
1844     cursor;
1845
1846   GC
1847     annotate_context;
1848
1849   int
1850     id,
1851     pen_number,
1852     status,
1853     x,
1854     y;
1855
1856   KeySym
1857     key_symbol;
1858
1859   register char
1860     *p;
1861
1862   register long
1863     i;
1864
1865   unsigned int
1866     height,
1867     width;
1868
1869   unsigned long
1870     state;
1871
1872   XAnnotateInfo
1873     *annotate_info,
1874     *previous_info;
1875
1876   XColor
1877     color;
1878
1879   XFontStruct
1880     *font_info;
1881
1882   XEvent
1883     event,
1884     text_event;
1885
1886   /*
1887     Map Command widget.
1888   */
1889   (void) CloneString(&windows->command.name,"Annotate");
1890   windows->command.data=4;
1891   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1892   (void) XMapRaised(display,windows->command.id);
1893   XClientMessage(display,windows->image.id,windows->im_protocols,
1894     windows->im_update_widget,CurrentTime);
1895   /*
1896     Track pointer until button 1 is pressed.
1897   */
1898   XQueryPosition(display,windows->image.id,&x,&y);
1899   (void) XSelectInput(display,windows->image.id,
1900     windows->image.attributes.event_mask | PointerMotionMask);
1901   cursor=XCreateFontCursor(display,XC_left_side);
1902   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1903   state=DefaultState;
1904   do
1905   {
1906     if (windows->info.mapped != MagickFalse)
1907       {
1908         /*
1909           Display pointer position.
1910         */
1911         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
1912           x+windows->image.x,y+windows->image.y);
1913         XInfoWidget(display,windows,text);
1914       }
1915     /*
1916       Wait for next event.
1917     */
1918     XScreenEvent(display,windows,&event);
1919     if (event.xany.window == windows->command.id)
1920       {
1921         /*
1922           Select a command from the Command widget.
1923         */
1924         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1925         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1926         if (id < 0)
1927           continue;
1928         switch (AnnotateCommands[id])
1929         {
1930           case AnnotateNameCommand:
1931           {
1932             const char
1933               *FontMenu[MaxNumberFonts];
1934
1935             int
1936               font_number;
1937
1938             /*
1939               Initialize menu selections.
1940             */
1941             for (i=0; i < MaxNumberFonts; i++)
1942               FontMenu[i]=resource_info->font_name[i];
1943             FontMenu[MaxNumberFonts-2]="Browser...";
1944             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1945             /*
1946               Select a font name from the pop-up menu.
1947             */
1948             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1949               (const char **) FontMenu,command);
1950             if (font_number < 0)
1951               break;
1952             if (font_number == (MaxNumberFonts-2))
1953               {
1954                 static char
1955                   font_name[MaxTextExtent] = "fixed";
1956
1957                 /*
1958                   Select a font name from a browser.
1959                 */
1960                 resource_info->font_name[font_number]=font_name;
1961                 XFontBrowserWidget(display,windows,"Select",font_name);
1962                 if (*font_name == '\0')
1963                   break;
1964               }
1965             /*
1966               Initialize font info.
1967             */
1968             font_info=XLoadQueryFont(display,resource_info->font_name[
1969               font_number]);
1970             if (font_info == (XFontStruct *) NULL)
1971               {
1972                 XNoticeWidget(display,windows,"Unable to load font:",
1973                   resource_info->font_name[font_number]);
1974                 break;
1975               }
1976             font_id=(unsigned int) font_number;
1977             (void) XFreeFont(display,font_info);
1978             break;
1979           }
1980           case AnnotateFontColorCommand:
1981           {
1982             /*
1983               Initialize menu selections.
1984             */
1985             for (i=0; i < (int) (MaxNumberPens-2); i++)
1986               ColorMenu[i]=resource_info->pen_colors[i];
1987             ColorMenu[MaxNumberPens-2]="transparent";
1988             ColorMenu[MaxNumberPens-1]="Browser...";
1989             ColorMenu[MaxNumberPens]=(const char *) NULL;
1990             /*
1991               Select a pen color from the pop-up menu.
1992             */
1993             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1994               (const char **) ColorMenu,command);
1995             if (pen_number < 0)
1996               break;
1997             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1998               MagickFalse;
1999             if (transparent_pen != MagickFalse)
2000               break;
2001             if (pen_number == (MaxNumberPens-1))
2002               {
2003                 static char
2004                   color_name[MaxTextExtent] = "gray";
2005
2006                 /*
2007                   Select a pen color from a dialog.
2008                 */
2009                 resource_info->pen_colors[pen_number]=color_name;
2010                 XColorBrowserWidget(display,windows,"Select",color_name);
2011                 if (*color_name == '\0')
2012                   break;
2013               }
2014             /*
2015               Set pen color.
2016             */
2017             (void) XParseColor(display,windows->map_info->colormap,
2018               resource_info->pen_colors[pen_number],&color);
2019             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2020               (unsigned int) MaxColors,&color);
2021             windows->pixel_info->pen_colors[pen_number]=color;
2022             pen_id=(unsigned int) pen_number;
2023             break;
2024           }
2025           case AnnotateBackgroundColorCommand:
2026           {
2027             /*
2028               Initialize menu selections.
2029             */
2030             for (i=0; i < (int) (MaxNumberPens-2); i++)
2031               ColorMenu[i]=resource_info->pen_colors[i];
2032             ColorMenu[MaxNumberPens-2]="transparent";
2033             ColorMenu[MaxNumberPens-1]="Browser...";
2034             ColorMenu[MaxNumberPens]=(const char *) NULL;
2035             /*
2036               Select a pen color from the pop-up menu.
2037             */
2038             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2039               (const char **) ColorMenu,command);
2040             if (pen_number < 0)
2041               break;
2042             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2043               MagickFalse;
2044             if (transparent_box != MagickFalse)
2045               break;
2046             if (pen_number == (MaxNumberPens-1))
2047               {
2048                 static char
2049                   color_name[MaxTextExtent] = "gray";
2050
2051                 /*
2052                   Select a pen color from a dialog.
2053                 */
2054                 resource_info->pen_colors[pen_number]=color_name;
2055                 XColorBrowserWidget(display,windows,"Select",color_name);
2056                 if (*color_name == '\0')
2057                   break;
2058               }
2059             /*
2060               Set pen color.
2061             */
2062             (void) XParseColor(display,windows->map_info->colormap,
2063               resource_info->pen_colors[pen_number],&color);
2064             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2065               (unsigned int) MaxColors,&color);
2066             windows->pixel_info->pen_colors[pen_number]=color;
2067             box_id=(unsigned int) pen_number;
2068             break;
2069           }
2070           case AnnotateRotateCommand:
2071           {
2072             int
2073               entry;
2074
2075             static char
2076               angle[MaxTextExtent] = "30.0";
2077
2078             static const char
2079               *RotateMenu[] =
2080               {
2081                 "-90",
2082                 "-45",
2083                 "-30",
2084                 "0",
2085                 "30",
2086                 "45",
2087                 "90",
2088                 "180",
2089                 "Dialog...",
2090                 (char *) NULL,
2091               };
2092
2093             /*
2094               Select a command from the pop-up menu.
2095             */
2096             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2097               command);
2098             if (entry < 0)
2099               break;
2100             if (entry != 8)
2101               {
2102                 degrees=StringToDouble(RotateMenu[entry]);
2103                 break;
2104               }
2105             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2106               angle);
2107             if (*angle == '\0')
2108               break;
2109             degrees=StringToDouble(angle);
2110             break;
2111           }
2112           case AnnotateHelpCommand:
2113           {
2114             XTextViewWidget(display,resource_info,windows,MagickFalse,
2115               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2116             break;
2117           }
2118           case AnnotateDismissCommand:
2119           {
2120             /*
2121               Prematurely exit.
2122             */
2123             state|=EscapeState;
2124             state|=ExitState;
2125             break;
2126           }
2127           default:
2128             break;
2129         }
2130         continue;
2131       }
2132     switch (event.type)
2133     {
2134       case ButtonPress:
2135       {
2136         if (event.xbutton.button != Button1)
2137           break;
2138         if (event.xbutton.window != windows->image.id)
2139           break;
2140         /*
2141           Change to text entering mode.
2142         */
2143         x=event.xbutton.x;
2144         y=event.xbutton.y;
2145         state|=ExitState;
2146         break;
2147       }
2148       case ButtonRelease:
2149         break;
2150       case Expose:
2151         break;
2152       case KeyPress:
2153       {
2154         if (event.xkey.window != windows->image.id)
2155           break;
2156         /*
2157           Respond to a user key press.
2158         */
2159         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2160           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2161         switch ((int) key_symbol)
2162         {
2163           case XK_Escape:
2164           case XK_F20:
2165           {
2166             /*
2167               Prematurely exit.
2168             */
2169             state|=EscapeState;
2170             state|=ExitState;
2171             break;
2172           }
2173           case XK_F1:
2174           case XK_Help:
2175           {
2176             XTextViewWidget(display,resource_info,windows,MagickFalse,
2177               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2178             break;
2179           }
2180           default:
2181           {
2182             (void) XBell(display,0);
2183             break;
2184           }
2185         }
2186         break;
2187       }
2188       case MotionNotify:
2189       {
2190         /*
2191           Map and unmap Info widget as cursor crosses its boundaries.
2192         */
2193         x=event.xmotion.x;
2194         y=event.xmotion.y;
2195         if (windows->info.mapped != MagickFalse)
2196           {
2197             if ((x < (int) (windows->info.x+windows->info.width)) &&
2198                 (y < (int) (windows->info.y+windows->info.height)))
2199               (void) XWithdrawWindow(display,windows->info.id,
2200                 windows->info.screen);
2201           }
2202         else
2203           if ((x > (int) (windows->info.x+windows->info.width)) ||
2204               (y > (int) (windows->info.y+windows->info.height)))
2205             (void) XMapWindow(display,windows->info.id);
2206         break;
2207       }
2208       default:
2209         break;
2210     }
2211   } while ((state & ExitState) == 0);
2212   (void) XSelectInput(display,windows->image.id,
2213     windows->image.attributes.event_mask);
2214   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2215   if ((state & EscapeState) != 0)
2216     return(MagickTrue);
2217   /*
2218     Set font info and check boundary conditions.
2219   */
2220   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2221   if (font_info == (XFontStruct *) NULL)
2222     {
2223       XNoticeWidget(display,windows,"Unable to load font:",
2224         resource_info->font_name[font_id]);
2225       font_info=windows->font_info;
2226     }
2227   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2228     x=(int) windows->image.width-font_info->max_bounds.width;
2229   if (y < (int) (font_info->ascent+font_info->descent))
2230     y=(int) font_info->ascent+font_info->descent;
2231   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2232       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2233     return(MagickFalse);
2234   /*
2235     Initialize annotate structure.
2236   */
2237   annotate_info=(XAnnotateInfo *) AcquireAlignedMemory(1,sizeof(*annotate_info));
2238   if (annotate_info == (XAnnotateInfo *) NULL)
2239     return(MagickFalse);
2240   XGetAnnotateInfo(annotate_info);
2241   annotate_info->x=x;
2242   annotate_info->y=y;
2243   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2244     annotate_info->stencil=OpaqueStencil;
2245   else
2246     if (transparent_box == MagickFalse)
2247       annotate_info->stencil=BackgroundStencil;
2248     else
2249       annotate_info->stencil=ForegroundStencil;
2250   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2251   annotate_info->degrees=degrees;
2252   annotate_info->font_info=font_info;
2253   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2254     windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
2255     sizeof(*annotate_info->text));
2256   if (annotate_info->text == (char *) NULL)
2257     return(MagickFalse);
2258   /*
2259     Create cursor and set graphic context.
2260   */
2261   cursor=XCreateFontCursor(display,XC_pencil);
2262   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2263   annotate_context=windows->image.annotate_context;
2264   (void) XSetFont(display,annotate_context,font_info->fid);
2265   (void) XSetBackground(display,annotate_context,
2266     windows->pixel_info->pen_colors[box_id].pixel);
2267   (void) XSetForeground(display,annotate_context,
2268     windows->pixel_info->pen_colors[pen_id].pixel);
2269   /*
2270     Begin annotating the image with text.
2271   */
2272   (void) CloneString(&windows->command.name,"Text");
2273   windows->command.data=0;
2274   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2275   state=DefaultState;
2276   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2277   text_event.xexpose.width=(int) font_info->max_bounds.width;
2278   text_event.xexpose.height=font_info->max_bounds.ascent+
2279     font_info->max_bounds.descent;
2280   p=annotate_info->text;
2281   do
2282   {
2283     /*
2284       Display text cursor.
2285     */
2286     *p='\0';
2287     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2288     /*
2289       Wait for next event.
2290     */
2291     XScreenEvent(display,windows,&event);
2292     if (event.xany.window == windows->command.id)
2293       {
2294         /*
2295           Select a command from the Command widget.
2296         */
2297         (void) XSetBackground(display,annotate_context,
2298           windows->pixel_info->background_color.pixel);
2299         (void) XSetForeground(display,annotate_context,
2300           windows->pixel_info->foreground_color.pixel);
2301         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2302         (void) XSetBackground(display,annotate_context,
2303           windows->pixel_info->pen_colors[box_id].pixel);
2304         (void) XSetForeground(display,annotate_context,
2305           windows->pixel_info->pen_colors[pen_id].pixel);
2306         if (id < 0)
2307           continue;
2308         switch (TextCommands[id])
2309         {
2310           case TextHelpCommand:
2311           {
2312             XTextViewWidget(display,resource_info,windows,MagickFalse,
2313               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2314             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2315             break;
2316           }
2317           case TextApplyCommand:
2318           {
2319             /*
2320               Finished annotating.
2321             */
2322             annotate_info->width=(unsigned int) XTextWidth(font_info,
2323               annotate_info->text,(int) strlen(annotate_info->text));
2324             XRefreshWindow(display,&windows->image,&text_event);
2325             state|=ExitState;
2326             break;
2327           }
2328           default:
2329             break;
2330         }
2331         continue;
2332       }
2333     /*
2334       Erase text cursor.
2335     */
2336     text_event.xexpose.x=x;
2337     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2338     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2339       (unsigned int) text_event.xexpose.width,(unsigned int)
2340       text_event.xexpose.height,MagickFalse);
2341     XRefreshWindow(display,&windows->image,&text_event);
2342     switch (event.type)
2343     {
2344       case ButtonPress:
2345       {
2346         if (event.xbutton.window != windows->image.id)
2347           break;
2348         if (event.xbutton.button == Button2)
2349           {
2350             /*
2351               Request primary selection.
2352             */
2353             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2354               windows->image.id,CurrentTime);
2355             break;
2356           }
2357         break;
2358       }
2359       case Expose:
2360       {
2361         if (event.xexpose.count == 0)
2362           {
2363             XAnnotateInfo
2364               *text_info;
2365
2366             /*
2367               Refresh Image window.
2368             */
2369             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2370             text_info=annotate_info;
2371             while (text_info != (XAnnotateInfo *) NULL)
2372             {
2373               if (annotate_info->stencil == ForegroundStencil)
2374                 (void) XDrawString(display,windows->image.id,annotate_context,
2375                   text_info->x,text_info->y,text_info->text,
2376                   (int) strlen(text_info->text));
2377               else
2378                 (void) XDrawImageString(display,windows->image.id,
2379                   annotate_context,text_info->x,text_info->y,text_info->text,
2380                   (int) strlen(text_info->text));
2381               text_info=text_info->previous;
2382             }
2383             (void) XDrawString(display,windows->image.id,annotate_context,
2384               x,y,"_",1);
2385           }
2386         break;
2387       }
2388       case KeyPress:
2389       {
2390         int
2391           length;
2392
2393         if (event.xkey.window != windows->image.id)
2394           break;
2395         /*
2396           Respond to a user key press.
2397         */
2398         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2399           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2400         *(command+length)='\0';
2401         if (((event.xkey.state & ControlMask) != 0) ||
2402             ((event.xkey.state & Mod1Mask) != 0))
2403           state|=ModifierState;
2404         if ((state & ModifierState) != 0)
2405           switch ((int) key_symbol)
2406           {
2407             case XK_u:
2408             case XK_U:
2409             {
2410               key_symbol=DeleteCommand;
2411               break;
2412             }
2413             default:
2414               break;
2415           }
2416         switch ((int) key_symbol)
2417         {
2418           case XK_BackSpace:
2419           {
2420             /*
2421               Erase one character.
2422             */
2423             if (p == annotate_info->text)
2424               {
2425                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2426                   break;
2427                 else
2428                   {
2429                     /*
2430                       Go to end of the previous line of text.
2431                     */
2432                     annotate_info=annotate_info->previous;
2433                     p=annotate_info->text;
2434                     x=annotate_info->x+annotate_info->width;
2435                     y=annotate_info->y;
2436                     if (annotate_info->width != 0)
2437                       p+=strlen(annotate_info->text);
2438                     break;
2439                   }
2440               }
2441             p--;
2442             x-=XTextWidth(font_info,p,1);
2443             text_event.xexpose.x=x;
2444             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2445             XRefreshWindow(display,&windows->image,&text_event);
2446             break;
2447           }
2448           case XK_bracketleft:
2449           {
2450             key_symbol=XK_Escape;
2451             break;
2452           }
2453           case DeleteCommand:
2454           {
2455             /*
2456               Erase the entire line of text.
2457             */
2458             while (p != annotate_info->text)
2459             {
2460               p--;
2461               x-=XTextWidth(font_info,p,1);
2462               text_event.xexpose.x=x;
2463               XRefreshWindow(display,&windows->image,&text_event);
2464             }
2465             break;
2466           }
2467           case XK_Escape:
2468           case XK_F20:
2469           {
2470             /*
2471               Finished annotating.
2472             */
2473             annotate_info->width=(unsigned int) XTextWidth(font_info,
2474               annotate_info->text,(int) strlen(annotate_info->text));
2475             XRefreshWindow(display,&windows->image,&text_event);
2476             state|=ExitState;
2477             break;
2478           }
2479           default:
2480           {
2481             /*
2482               Draw a single character on the Image window.
2483             */
2484             if ((state & ModifierState) != 0)
2485               break;
2486             if (*command == '\0')
2487               break;
2488             *p=(*command);
2489             if (annotate_info->stencil == ForegroundStencil)
2490               (void) XDrawString(display,windows->image.id,annotate_context,
2491                 x,y,p,1);
2492             else
2493               (void) XDrawImageString(display,windows->image.id,
2494                 annotate_context,x,y,p,1);
2495             x+=XTextWidth(font_info,p,1);
2496             p++;
2497             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2498               break;
2499           }
2500           case XK_Return:
2501           case XK_KP_Enter:
2502           {
2503             /*
2504               Advance to the next line of text.
2505             */
2506             *p='\0';
2507             annotate_info->width=(unsigned int) XTextWidth(font_info,
2508               annotate_info->text,(int) strlen(annotate_info->text));
2509             if (annotate_info->next != (XAnnotateInfo *) NULL)
2510               {
2511                 /*
2512                   Line of text already exists.
2513                 */
2514                 annotate_info=annotate_info->next;
2515                 x=annotate_info->x;
2516                 y=annotate_info->y;
2517                 p=annotate_info->text;
2518                 break;
2519               }
2520             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2521               sizeof(*annotate_info->next));
2522             if (annotate_info->next == (XAnnotateInfo *) NULL)
2523               return(MagickFalse);
2524             *annotate_info->next=(*annotate_info);
2525             annotate_info->next->previous=annotate_info;
2526             annotate_info=annotate_info->next;
2527             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2528               windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
2529               sizeof(*annotate_info->text));
2530             if (annotate_info->text == (char *) NULL)
2531               return(MagickFalse);
2532             annotate_info->y+=annotate_info->height;
2533             if (annotate_info->y > (int) windows->image.height)
2534               annotate_info->y=(int) annotate_info->height;
2535             annotate_info->next=(XAnnotateInfo *) NULL;
2536             x=annotate_info->x;
2537             y=annotate_info->y;
2538             p=annotate_info->text;
2539             break;
2540           }
2541         }
2542         break;
2543       }
2544       case KeyRelease:
2545       {
2546         /*
2547           Respond to a user key release.
2548         */
2549         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2550           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2551         state&=(~ModifierState);
2552         break;
2553       }
2554       case SelectionNotify:
2555       {
2556         Atom
2557           type;
2558
2559         int
2560           format;
2561
2562         unsigned char
2563           *data;
2564
2565         unsigned long
2566           after,
2567           length;
2568
2569         /*
2570           Obtain response from primary selection.
2571         */
2572         if (event.xselection.property == (Atom) None)
2573           break;
2574         status=XGetWindowProperty(display,event.xselection.requestor,
2575           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2576           &type,&format,&length,&after,&data);
2577         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2578             (length == 0))
2579           break;
2580         /*
2581           Annotate Image window with primary selection.
2582         */
2583         for (i=0; i < (long) length; i++)
2584         {
2585           if ((char) data[i] != '\n')
2586             {
2587               /*
2588                 Draw a single character on the Image window.
2589               */
2590               *p=(char) data[i];
2591               (void) XDrawString(display,windows->image.id,annotate_context,
2592                 x,y,p,1);
2593               x+=XTextWidth(font_info,p,1);
2594               p++;
2595               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2596                 continue;
2597             }
2598           /*
2599             Advance to the next line of text.
2600           */
2601           *p='\0';
2602           annotate_info->width=(unsigned int) XTextWidth(font_info,
2603             annotate_info->text,(int) strlen(annotate_info->text));
2604           if (annotate_info->next != (XAnnotateInfo *) NULL)
2605             {
2606               /*
2607                 Line of text already exists.
2608               */
2609               annotate_info=annotate_info->next;
2610               x=annotate_info->x;
2611               y=annotate_info->y;
2612               p=annotate_info->text;
2613               continue;
2614             }
2615           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2616             sizeof(*annotate_info->next));
2617           if (annotate_info->next == (XAnnotateInfo *) NULL)
2618             return(MagickFalse);
2619           *annotate_info->next=(*annotate_info);
2620           annotate_info->next->previous=annotate_info;
2621           annotate_info=annotate_info->next;
2622           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2623             windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
2624             sizeof(*annotate_info->text));
2625           if (annotate_info->text == (char *) NULL)
2626             return(MagickFalse);
2627           annotate_info->y+=annotate_info->height;
2628           if (annotate_info->y > (int) windows->image.height)
2629             annotate_info->y=(int) annotate_info->height;
2630           annotate_info->next=(XAnnotateInfo *) NULL;
2631           x=annotate_info->x;
2632           y=annotate_info->y;
2633           p=annotate_info->text;
2634         }
2635         (void) XFree((void *) data);
2636         break;
2637       }
2638       default:
2639         break;
2640     }
2641   } while ((state & ExitState) == 0);
2642   (void) XFreeCursor(display,cursor);
2643   /*
2644     Annotation is relative to image configuration.
2645   */
2646   width=(unsigned int) image->columns;
2647   height=(unsigned int) image->rows;
2648   x=0;
2649   y=0;
2650   if (windows->image.crop_geometry != (char *) NULL)
2651     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2652   /*
2653     Initialize annotated image.
2654   */
2655   XSetCursorState(display,windows,MagickTrue);
2656   XCheckRefreshWindows(display,windows);
2657   while (annotate_info != (XAnnotateInfo *) NULL)
2658   {
2659     if (annotate_info->width == 0)
2660       {
2661         /*
2662           No text on this line--  go to the next line of text.
2663         */
2664         previous_info=annotate_info->previous;
2665         annotate_info->text=(char *)
2666           RelinquishMagickMemory(annotate_info->text);
2667         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2668         annotate_info=previous_info;
2669         continue;
2670       }
2671     /*
2672       Determine pixel index for box and pen color.
2673     */
2674     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2675     if (windows->pixel_info->colors != 0)
2676       for (i=0; i < (long) windows->pixel_info->colors; i++)
2677         if (windows->pixel_info->pixels[i] ==
2678             windows->pixel_info->pen_colors[box_id].pixel)
2679           {
2680             windows->pixel_info->box_index=(unsigned short) i;
2681             break;
2682           }
2683     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2684     if (windows->pixel_info->colors != 0)
2685       for (i=0; i < (long) windows->pixel_info->colors; i++)
2686         if (windows->pixel_info->pixels[i] ==
2687             windows->pixel_info->pen_colors[pen_id].pixel)
2688           {
2689             windows->pixel_info->pen_index=(unsigned short) i;
2690             break;
2691           }
2692     /*
2693       Define the annotate geometry string.
2694     */
2695     annotate_info->x=(int)
2696       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2697     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2698       windows->image.y)/windows->image.ximage->height;
2699     (void) FormatMagickString(annotate_info->geometry,MaxTextExtent,
2700       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2701       height*annotate_info->height/windows->image.ximage->height,
2702       annotate_info->x+x,annotate_info->y+y);
2703     /*
2704       Annotate image with text.
2705     */
2706     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2707     if (status == 0)
2708       return(MagickFalse);
2709     /*
2710       Free up memory.
2711     */
2712     previous_info=annotate_info->previous;
2713     annotate_info->text=DestroyString(annotate_info->text);
2714     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2715     annotate_info=previous_info;
2716   }
2717   (void) XSetForeground(display,annotate_context,
2718     windows->pixel_info->foreground_color.pixel);
2719   (void) XSetBackground(display,annotate_context,
2720     windows->pixel_info->background_color.pixel);
2721   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2722   XSetCursorState(display,windows,MagickFalse);
2723   (void) XFreeFont(display,font_info);
2724   /*
2725     Update image configuration.
2726   */
2727   XConfigureImageColormap(display,resource_info,windows,image);
2728   (void) XConfigureImage(display,resource_info,windows,image);
2729   return(MagickTrue);
2730 }
2731 \f
2732 /*
2733 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2734 %                                                                             %
2735 %                                                                             %
2736 %                                                                             %
2737 +   X B a c k g r o u n d I m a g e                                           %
2738 %                                                                             %
2739 %                                                                             %
2740 %                                                                             %
2741 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2742 %
2743 %  XBackgroundImage() displays the image in the background of a window.
2744 %
2745 %  The format of the XBackgroundImage method is:
2746 %
2747 %      MagickBooleanType XBackgroundImage(Display *display,
2748 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
2749 %
2750 %  A description of each parameter follows:
2751 %
2752 %    o display: Specifies a connection to an X server; returned from
2753 %      XOpenDisplay.
2754 %
2755 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2756 %
2757 %    o windows: Specifies a pointer to a XWindows structure.
2758 %
2759 %    o image: the image.
2760 %
2761 */
2762 static MagickBooleanType XBackgroundImage(Display *display,
2763   XResourceInfo *resource_info,XWindows *windows,Image **image)
2764 {
2765 #define BackgroundImageTag  "Background/Image"
2766
2767   int
2768     status;
2769
2770   static char
2771     window_id[MaxTextExtent] = "root";
2772
2773   XResourceInfo
2774     background_resources;
2775
2776   /*
2777     Put image in background.
2778   */
2779   status=XDialogWidget(display,windows,"Background",
2780     "Enter window id (id 0x00 selects window with pointer):",window_id);
2781   if (*window_id == '\0')
2782     return(MagickFalse);
2783   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
2784   XInfoWidget(display,windows,BackgroundImageTag);
2785   XSetCursorState(display,windows,MagickTrue);
2786   XCheckRefreshWindows(display,windows);
2787   background_resources=(*resource_info);
2788   background_resources.window_id=window_id;
2789   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2790   status=XDisplayBackgroundImage(display,&background_resources,*image);
2791   if (status != MagickFalse)
2792     XClientMessage(display,windows->image.id,windows->im_protocols,
2793       windows->im_retain_colors,CurrentTime);
2794   XSetCursorState(display,windows,MagickFalse);
2795   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
2796   return(MagickTrue);
2797 }
2798 \f
2799 /*
2800 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2801 %                                                                             %
2802 %                                                                             %
2803 %                                                                             %
2804 +   X C h o p I m a g e                                                       %
2805 %                                                                             %
2806 %                                                                             %
2807 %                                                                             %
2808 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2809 %
2810 %  XChopImage() chops the X image.
2811 %
2812 %  The format of the XChopImage method is:
2813 %
2814 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2815 %      XWindows *windows,Image **image)
2816 %
2817 %  A description of each parameter follows:
2818 %
2819 %    o display: Specifies a connection to an X server; returned from
2820 %      XOpenDisplay.
2821 %
2822 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2823 %
2824 %    o windows: Specifies a pointer to a XWindows structure.
2825 %
2826 %    o image: the image.
2827 %
2828 */
2829 static MagickBooleanType XChopImage(Display *display,
2830   XResourceInfo *resource_info,XWindows *windows,Image **image)
2831 {
2832   static const char
2833     *ChopMenu[] =
2834     {
2835       "Direction",
2836       "Help",
2837       "Dismiss",
2838       (char *) NULL
2839     };
2840
2841   static ModeType
2842     direction = HorizontalChopCommand;
2843
2844   static const ModeType
2845     ChopCommands[] =
2846     {
2847       ChopDirectionCommand,
2848       ChopHelpCommand,
2849       ChopDismissCommand
2850     },
2851     DirectionCommands[] =
2852     {
2853       HorizontalChopCommand,
2854       VerticalChopCommand
2855     };
2856
2857   char
2858     text[MaxTextExtent];
2859
2860   Image
2861     *chop_image;
2862
2863   int
2864     id,
2865     x,
2866     y;
2867
2868   MagickRealType
2869     scale_factor;
2870
2871   RectangleInfo
2872     chop_info;
2873
2874   unsigned int
2875     distance,
2876     height,
2877     width;
2878
2879   unsigned long
2880     state;
2881
2882   XEvent
2883     event;
2884
2885   XSegment
2886     segment_info;
2887
2888   /*
2889     Map Command widget.
2890   */
2891   (void) CloneString(&windows->command.name,"Chop");
2892   windows->command.data=1;
2893   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2894   (void) XMapRaised(display,windows->command.id);
2895   XClientMessage(display,windows->image.id,windows->im_protocols,
2896     windows->im_update_widget,CurrentTime);
2897   /*
2898     Track pointer until button 1 is pressed.
2899   */
2900   XQueryPosition(display,windows->image.id,&x,&y);
2901   (void) XSelectInput(display,windows->image.id,
2902     windows->image.attributes.event_mask | PointerMotionMask);
2903   state=DefaultState;
2904   do
2905   {
2906     if (windows->info.mapped != MagickFalse)
2907       {
2908         /*
2909           Display pointer position.
2910         */
2911         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
2912           x+windows->image.x,y+windows->image.y);
2913         XInfoWidget(display,windows,text);
2914       }
2915     /*
2916       Wait for next event.
2917     */
2918     XScreenEvent(display,windows,&event);
2919     if (event.xany.window == windows->command.id)
2920       {
2921         /*
2922           Select a command from the Command widget.
2923         */
2924         id=XCommandWidget(display,windows,ChopMenu,&event);
2925         if (id < 0)
2926           continue;
2927         switch (ChopCommands[id])
2928         {
2929           case ChopDirectionCommand:
2930           {
2931             char
2932               command[MaxTextExtent];
2933
2934             static const char
2935               *Directions[] =
2936               {
2937                 "horizontal",
2938                 "vertical",
2939                 (char *) NULL,
2940               };
2941
2942             /*
2943               Select a command from the pop-up menu.
2944             */
2945             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2946             if (id >= 0)
2947               direction=DirectionCommands[id];
2948             break;
2949           }
2950           case ChopHelpCommand:
2951           {
2952             XTextViewWidget(display,resource_info,windows,MagickFalse,
2953               "Help Viewer - Image Chop",ImageChopHelp);
2954             break;
2955           }
2956           case ChopDismissCommand:
2957           {
2958             /*
2959               Prematurely exit.
2960             */
2961             state|=EscapeState;
2962             state|=ExitState;
2963             break;
2964           }
2965           default:
2966             break;
2967         }
2968         continue;
2969       }
2970     switch (event.type)
2971     {
2972       case ButtonPress:
2973       {
2974         if (event.xbutton.button != Button1)
2975           break;
2976         if (event.xbutton.window != windows->image.id)
2977           break;
2978         /*
2979           User has committed to start point of chopping line.
2980         */
2981         segment_info.x1=(short int) event.xbutton.x;
2982         segment_info.x2=(short int) event.xbutton.x;
2983         segment_info.y1=(short int) event.xbutton.y;
2984         segment_info.y2=(short int) event.xbutton.y;
2985         state|=ExitState;
2986         break;
2987       }
2988       case ButtonRelease:
2989         break;
2990       case Expose:
2991         break;
2992       case KeyPress:
2993       {
2994         char
2995           command[MaxTextExtent];
2996
2997         KeySym
2998           key_symbol;
2999
3000         if (event.xkey.window != windows->image.id)
3001           break;
3002         /*
3003           Respond to a user key press.
3004         */
3005         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3006           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3007         switch ((int) key_symbol)
3008         {
3009           case XK_Escape:
3010           case XK_F20:
3011           {
3012             /*
3013               Prematurely exit.
3014             */
3015             state|=EscapeState;
3016             state|=ExitState;
3017             break;
3018           }
3019           case XK_F1:
3020           case XK_Help:
3021           {
3022             (void) XSetFunction(display,windows->image.highlight_context,
3023               GXcopy);
3024             XTextViewWidget(display,resource_info,windows,MagickFalse,
3025               "Help Viewer - Image Chop",ImageChopHelp);
3026             (void) XSetFunction(display,windows->image.highlight_context,
3027               GXinvert);
3028             break;
3029           }
3030           default:
3031           {
3032             (void) XBell(display,0);
3033             break;
3034           }
3035         }
3036         break;
3037       }
3038       case MotionNotify:
3039       {
3040         /*
3041           Map and unmap Info widget as text cursor crosses its boundaries.
3042         */
3043         x=event.xmotion.x;
3044         y=event.xmotion.y;
3045         if (windows->info.mapped != MagickFalse)
3046           {
3047             if ((x < (int) (windows->info.x+windows->info.width)) &&
3048                 (y < (int) (windows->info.y+windows->info.height)))
3049               (void) XWithdrawWindow(display,windows->info.id,
3050                 windows->info.screen);
3051           }
3052         else
3053           if ((x > (int) (windows->info.x+windows->info.width)) ||
3054               (y > (int) (windows->info.y+windows->info.height)))
3055             (void) XMapWindow(display,windows->info.id);
3056       }
3057     }
3058   } while ((state & ExitState) == 0);
3059   (void) XSelectInput(display,windows->image.id,
3060     windows->image.attributes.event_mask);
3061   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3062   if ((state & EscapeState) != 0)
3063     return(MagickTrue);
3064   /*
3065     Draw line as pointer moves until the mouse button is released.
3066   */
3067   chop_info.width=0;
3068   chop_info.height=0;
3069   chop_info.x=0;
3070   chop_info.y=0;
3071   distance=0;
3072   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3073   state=DefaultState;
3074   do
3075   {
3076     if (distance > 9)
3077       {
3078         /*
3079           Display info and draw chopping line.
3080         */
3081         if (windows->info.mapped == MagickFalse)
3082           (void) XMapWindow(display,windows->info.id);
3083         (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
3084           chop_info.width,chop_info.height,chop_info.x,chop_info.y);
3085         XInfoWidget(display,windows,text);
3086         XHighlightLine(display,windows->image.id,
3087           windows->image.highlight_context,&segment_info);
3088       }
3089     else
3090       if (windows->info.mapped != MagickFalse)
3091         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3092     /*
3093       Wait for next event.
3094     */
3095     XScreenEvent(display,windows,&event);
3096     if (distance > 9)
3097       XHighlightLine(display,windows->image.id,
3098         windows->image.highlight_context,&segment_info);
3099     switch (event.type)
3100     {
3101       case ButtonPress:
3102       {
3103         segment_info.x2=(short int) event.xmotion.x;
3104         segment_info.y2=(short int) event.xmotion.y;
3105         break;
3106       }
3107       case ButtonRelease:
3108       {
3109         /*
3110           User has committed to chopping line.
3111         */
3112         segment_info.x2=(short int) event.xbutton.x;
3113         segment_info.y2=(short int) event.xbutton.y;
3114         state|=ExitState;
3115         break;
3116       }
3117       case Expose:
3118         break;
3119       case MotionNotify:
3120       {
3121         segment_info.x2=(short int) event.xmotion.x;
3122         segment_info.y2=(short int) event.xmotion.y;
3123       }
3124       default:
3125         break;
3126     }
3127     /*
3128       Check boundary conditions.
3129     */
3130     if (segment_info.x2 < 0)
3131       segment_info.x2=0;
3132     else
3133       if (segment_info.x2 > windows->image.ximage->width)
3134         segment_info.x2=windows->image.ximage->width;
3135     if (segment_info.y2 < 0)
3136       segment_info.y2=0;
3137     else
3138       if (segment_info.y2 > windows->image.ximage->height)
3139         segment_info.y2=windows->image.ximage->height;
3140     distance=(unsigned int)
3141       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3142        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3143     /*
3144       Compute chopping geometry.
3145     */
3146     if (direction == HorizontalChopCommand)
3147       {
3148         chop_info.width=(unsigned long) (segment_info.x2-segment_info.x1+1);
3149         chop_info.x=windows->image.x+segment_info.x1;
3150         chop_info.height=0;
3151         chop_info.y=0;
3152         if (segment_info.x1 > (int) segment_info.x2)
3153           {
3154             chop_info.width=(unsigned long) (segment_info.x1-segment_info.x2+1);
3155             chop_info.x=windows->image.x+segment_info.x2;
3156           }
3157       }
3158     else
3159       {
3160         chop_info.width=0;
3161         chop_info.height=(unsigned long) (segment_info.y2-segment_info.y1+1);
3162         chop_info.x=0;
3163         chop_info.y=windows->image.y+segment_info.y1;
3164         if (segment_info.y1 > segment_info.y2)
3165           {
3166             chop_info.height=(unsigned long)
3167               (segment_info.y1-segment_info.y2+1);
3168             chop_info.y=windows->image.y+segment_info.y2;
3169           }
3170       }
3171   } while ((state & ExitState) == 0);
3172   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3173   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3174   if (distance <= 9)
3175     return(MagickTrue);
3176   /*
3177     Image chopping is relative to image configuration.
3178   */
3179   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
3180   XSetCursorState(display,windows,MagickTrue);
3181   XCheckRefreshWindows(display,windows);
3182   windows->image.window_changes.width=windows->image.ximage->width-
3183     (unsigned int) chop_info.width;
3184   windows->image.window_changes.height=windows->image.ximage->height-
3185     (unsigned int) chop_info.height;
3186   width=(unsigned int) (*image)->columns;
3187   height=(unsigned int) (*image)->rows;
3188   x=0;
3189   y=0;
3190   if (windows->image.crop_geometry != (char *) NULL)
3191     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3192   scale_factor=(MagickRealType) width/windows->image.ximage->width;
3193   chop_info.x+=x;
3194   chop_info.x=(int) (scale_factor*chop_info.x+0.5);
3195   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3196   scale_factor=(MagickRealType) height/windows->image.ximage->height;
3197   chop_info.y+=y;
3198   chop_info.y=(int) (scale_factor*chop_info.y+0.5);
3199   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3200   /*
3201     Chop image.
3202   */
3203   chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
3204   XSetCursorState(display,windows,MagickFalse);
3205   if (chop_image == (Image *) NULL)
3206     return(MagickFalse);
3207   *image=DestroyImage(*image);
3208   *image=chop_image;
3209   /*
3210     Update image configuration.
3211   */
3212   XConfigureImageColormap(display,resource_info,windows,*image);
3213   (void) XConfigureImage(display,resource_info,windows,*image);
3214   return(MagickTrue);
3215 }
3216 \f
3217 /*
3218 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3219 %                                                                             %
3220 %                                                                             %
3221 %                                                                             %
3222 +   X C o l o r E d i t I m a g e                                             %
3223 %                                                                             %
3224 %                                                                             %
3225 %                                                                             %
3226 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3227 %
3228 %  XColorEditImage() allows the user to interactively change the color of one
3229 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3230 %
3231 %  The format of the XColorEditImage method is:
3232 %
3233 %      MagickBooleanType XColorEditImage(Display *display,
3234 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
3235 %
3236 %  A description of each parameter follows:
3237 %
3238 %    o display: Specifies a connection to an X server;  returned from
3239 %      XOpenDisplay.
3240 %
3241 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3242 %
3243 %    o windows: Specifies a pointer to a XWindows structure.
3244 %
3245 %    o image: the image; returned from ReadImage.
3246 %
3247 */
3248
3249
3250 static MagickBooleanType XColorEditImage(Display *display,
3251   XResourceInfo *resource_info,XWindows *windows,Image **image)
3252 {
3253   static const char
3254     *ColorEditMenu[] =
3255     {
3256       "Method",
3257       "Pixel Color",
3258       "Border Color",
3259       "Fuzz",
3260       "Undo",
3261       "Help",
3262       "Dismiss",
3263       (char *) NULL
3264     };
3265
3266   static const ModeType
3267     ColorEditCommands[] =
3268     {
3269       ColorEditMethodCommand,
3270       ColorEditColorCommand,
3271       ColorEditBorderCommand,
3272       ColorEditFuzzCommand,
3273       ColorEditUndoCommand,
3274       ColorEditHelpCommand,
3275       ColorEditDismissCommand
3276     };
3277
3278   static PaintMethod
3279     method = PointMethod;
3280
3281   static unsigned int
3282     pen_id = 0;
3283
3284   static XColor
3285     border_color = { 0, 0, 0, 0, 0, 0 };
3286
3287   char
3288     command[MaxTextExtent],
3289     text[MaxTextExtent];
3290
3291   Cursor
3292     cursor;
3293
3294   ExceptionInfo
3295     *exception;
3296
3297   int
3298     entry,
3299     id,
3300     x,
3301     x_offset,
3302     y,
3303     y_offset;
3304
3305   register PixelPacket
3306     *q;
3307
3308   register long
3309     i;
3310
3311   unsigned int
3312     height,
3313     width;
3314
3315   unsigned long
3316     state;
3317
3318   XColor
3319     color;
3320
3321   XEvent
3322     event;
3323
3324   /*
3325     Map Command widget.
3326   */
3327   (void) CloneString(&windows->command.name,"Color Edit");
3328   windows->command.data=4;
3329   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3330   (void) XMapRaised(display,windows->command.id);
3331   XClientMessage(display,windows->image.id,windows->im_protocols,
3332     windows->im_update_widget,CurrentTime);
3333   /*
3334     Make cursor.
3335   */
3336   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3337     resource_info->background_color,resource_info->foreground_color);
3338   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3339   /*
3340     Track pointer until button 1 is pressed.
3341   */
3342   XQueryPosition(display,windows->image.id,&x,&y);
3343   (void) XSelectInput(display,windows->image.id,
3344     windows->image.attributes.event_mask | PointerMotionMask);
3345   state=DefaultState;
3346   do
3347   {
3348     if (windows->info.mapped != MagickFalse)
3349       {
3350         /*
3351           Display pointer position.
3352         */
3353         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
3354           x+windows->image.x,y+windows->image.y);
3355         XInfoWidget(display,windows,text);
3356       }
3357     /*
3358       Wait for next event.
3359     */
3360     XScreenEvent(display,windows,&event);
3361     if (event.xany.window == windows->command.id)
3362       {
3363         /*
3364           Select a command from the Command widget.
3365         */
3366         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3367         if (id < 0)
3368           {
3369             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3370             continue;
3371           }
3372         switch (ColorEditCommands[id])
3373         {
3374           case ColorEditMethodCommand:
3375           {
3376             char
3377               **methods;
3378
3379             /*
3380               Select a method from the pop-up menu.
3381             */
3382             methods=(char **) GetMagickOptions(MagickMethodOptions);
3383             if (methods == (char **) NULL)
3384               break;
3385             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3386               (const char **) methods,command);
3387             if (entry >= 0)
3388               method=(PaintMethod) ParseMagickOption(MagickMethodOptions,
3389                 MagickFalse,methods[entry]);
3390             methods=DestroyStringList(methods);
3391             break;
3392           }
3393           case ColorEditColorCommand:
3394           {
3395             const char
3396               *ColorMenu[MaxNumberPens];
3397
3398             int
3399               pen_number;
3400
3401             /*
3402               Initialize menu selections.
3403             */
3404             for (i=0; i < (int) (MaxNumberPens-2); i++)
3405               ColorMenu[i]=resource_info->pen_colors[i];
3406             ColorMenu[MaxNumberPens-2]="Browser...";
3407             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3408             /*
3409               Select a pen color from the pop-up menu.
3410             */
3411             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3412               (const char **) ColorMenu,command);
3413             if (pen_number < 0)
3414               break;
3415             if (pen_number == (MaxNumberPens-2))
3416               {
3417                 static char
3418                   color_name[MaxTextExtent] = "gray";
3419
3420                 /*
3421                   Select a pen color from a dialog.
3422                 */
3423                 resource_info->pen_colors[pen_number]=color_name;
3424                 XColorBrowserWidget(display,windows,"Select",color_name);
3425                 if (*color_name == '\0')
3426                   break;
3427               }
3428             /*
3429               Set pen color.
3430             */
3431             (void) XParseColor(display,windows->map_info->colormap,
3432               resource_info->pen_colors[pen_number],&color);
3433             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3434               (unsigned int) MaxColors,&color);
3435             windows->pixel_info->pen_colors[pen_number]=color;
3436             pen_id=(unsigned int) pen_number;
3437             break;
3438           }
3439           case ColorEditBorderCommand:
3440           {
3441             const char
3442               *ColorMenu[MaxNumberPens];
3443
3444             int
3445               pen_number;
3446
3447             /*
3448               Initialize menu selections.
3449             */
3450             for (i=0; i < (int) (MaxNumberPens-2); i++)
3451               ColorMenu[i]=resource_info->pen_colors[i];
3452             ColorMenu[MaxNumberPens-2]="Browser...";
3453             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3454             /*
3455               Select a pen color from the pop-up menu.
3456             */
3457             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3458               (const char **) ColorMenu,command);
3459             if (pen_number < 0)
3460               break;
3461             if (pen_number == (MaxNumberPens-2))
3462               {
3463                 static char
3464                   color_name[MaxTextExtent] = "gray";
3465
3466                 /*
3467                   Select a pen color from a dialog.
3468                 */
3469                 resource_info->pen_colors[pen_number]=color_name;
3470                 XColorBrowserWidget(display,windows,"Select",color_name);
3471                 if (*color_name == '\0')
3472                   break;
3473               }
3474             /*
3475               Set border color.
3476             */
3477             (void) XParseColor(display,windows->map_info->colormap,
3478               resource_info->pen_colors[pen_number],&border_color);
3479             break;
3480           }
3481           case ColorEditFuzzCommand:
3482           {
3483             static char
3484               fuzz[MaxTextExtent];
3485
3486             static const char
3487               *FuzzMenu[] =
3488               {
3489                 "0%",
3490                 "2%",
3491                 "5%",
3492                 "10%",
3493                 "15%",
3494                 "Dialog...",
3495                 (char *) NULL,
3496               };
3497
3498             /*
3499               Select a command from the pop-up menu.
3500             */
3501             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3502               command);
3503             if (entry < 0)
3504               break;
3505             if (entry != 5)
3506               {
3507                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
3508                   QuantumRange+1.0);
3509                 break;
3510               }
3511             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3512             (void) XDialogWidget(display,windows,"Ok",
3513               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3514             if (*fuzz == '\0')
3515               break;
3516             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3517             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
3518             break;
3519           }
3520           case ColorEditUndoCommand:
3521           {
3522             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3523               image);
3524             break;
3525           }
3526           case ColorEditHelpCommand:
3527           default:
3528           {
3529             XTextViewWidget(display,resource_info,windows,MagickFalse,
3530               "Help Viewer - Image Annotation",ImageColorEditHelp);
3531             break;
3532           }
3533           case ColorEditDismissCommand:
3534           {
3535             /*
3536               Prematurely exit.
3537             */
3538             state|=EscapeState;
3539             state|=ExitState;
3540             break;
3541           }
3542         }
3543         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3544         continue;
3545       }
3546     switch (event.type)
3547     {
3548       case ButtonPress:
3549       {
3550         if (event.xbutton.button != Button1)
3551           break;
3552         if ((event.xbutton.window != windows->image.id) &&
3553             (event.xbutton.window != windows->magnify.id))
3554           break;
3555         /*
3556           exit loop.
3557         */
3558         x=event.xbutton.x;
3559         y=event.xbutton.y;
3560         (void) XMagickCommand(display,resource_info,windows,
3561           SaveToUndoBufferCommand,image);
3562         state|=UpdateConfigurationState;
3563         break;
3564       }
3565       case ButtonRelease:
3566       {
3567         if (event.xbutton.button != Button1)
3568           break;
3569         if ((event.xbutton.window != windows->image.id) &&
3570             (event.xbutton.window != windows->magnify.id))
3571           break;
3572         /*
3573           Update colormap information.
3574         */
3575         x=event.xbutton.x;
3576         y=event.xbutton.y;
3577         XConfigureImageColormap(display,resource_info,windows,*image);
3578         (void) XConfigureImage(display,resource_info,windows,*image);
3579         XInfoWidget(display,windows,text);
3580         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3581         state&=(~UpdateConfigurationState);
3582         break;
3583       }
3584       case Expose:
3585         break;
3586       case KeyPress:
3587       {
3588         KeySym
3589           key_symbol;
3590
3591         if (event.xkey.window == windows->magnify.id)
3592           {
3593             Window
3594               window;
3595
3596             window=windows->magnify.id;
3597             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3598           }
3599         if (event.xkey.window != windows->image.id)
3600           break;
3601         /*
3602           Respond to a user key press.
3603         */
3604         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3605           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3606         switch ((int) key_symbol)
3607         {
3608           case XK_Escape:
3609           case XK_F20:
3610           {
3611             /*
3612               Prematurely exit.
3613             */
3614             state|=ExitState;
3615             break;
3616           }
3617           case XK_F1:
3618           case XK_Help:
3619           {
3620             XTextViewWidget(display,resource_info,windows,MagickFalse,
3621               "Help Viewer - Image Annotation",ImageColorEditHelp);
3622             break;
3623           }
3624           default:
3625           {
3626             (void) XBell(display,0);
3627             break;
3628           }
3629         }
3630         break;
3631       }
3632       case MotionNotify:
3633       {
3634         /*
3635           Map and unmap Info widget as cursor crosses its boundaries.
3636         */
3637         x=event.xmotion.x;
3638         y=event.xmotion.y;
3639         if (windows->info.mapped != MagickFalse)
3640           {
3641             if ((x < (int) (windows->info.x+windows->info.width)) &&
3642                 (y < (int) (windows->info.y+windows->info.height)))
3643               (void) XWithdrawWindow(display,windows->info.id,
3644                 windows->info.screen);
3645           }
3646         else
3647           if ((x > (int) (windows->info.x+windows->info.width)) ||
3648               (y > (int) (windows->info.y+windows->info.height)))
3649             (void) XMapWindow(display,windows->info.id);
3650         break;
3651       }
3652       default:
3653         break;
3654     }
3655     if (event.xany.window == windows->magnify.id)
3656       {
3657         x=windows->magnify.x-windows->image.x;
3658         y=windows->magnify.y-windows->image.y;
3659       }
3660     x_offset=x;
3661     y_offset=y;
3662     if ((state & UpdateConfigurationState) != 0)
3663       {
3664         int
3665           x,
3666           y;
3667
3668         /*
3669           Pixel edit is relative to image configuration.
3670         */
3671         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3672           MagickTrue);
3673         color=windows->pixel_info->pen_colors[pen_id];
3674         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3675         width=(unsigned int) (*image)->columns;
3676         height=(unsigned int) (*image)->rows;
3677         x=0;
3678         y=0;
3679         if (windows->image.crop_geometry != (char *) NULL)
3680           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3681             &width,&height);
3682         x_offset=(int)
3683           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3684         y_offset=(int)
3685           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3686         if ((x_offset < 0) || (y_offset < 0))
3687           continue;
3688         if ((x_offset >= (long) (*image)->columns) ||
3689             (y_offset >= (long) (*image)->rows))
3690           continue;
3691         exception=(&(*image)->exception);
3692         switch (method)
3693         {
3694           case PointMethod:
3695           default:
3696           {
3697             /*
3698               Update color information using point algorithm.
3699             */
3700             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3701               return(MagickFalse);
3702             q=GetAuthenticPixels(*image,x_offset,y_offset,1,1,exception);
3703             if (q == (PixelPacket *) NULL)
3704               break;
3705             q->red=ScaleShortToQuantum(color.red);
3706             q->green=ScaleShortToQuantum(color.green);
3707             q->blue=ScaleShortToQuantum(color.blue);
3708             (void) SyncAuthenticPixels(*image,&(*image)->exception);
3709             break;
3710           }
3711           case ReplaceMethod:
3712           {
3713             PixelPacket
3714               target;
3715
3716             /*
3717               Update color information using replace algorithm.
3718             */
3719             (void) GetOneVirtualPixel(*image,x_offset,y_offset,&target,
3720               &(*image)->exception);
3721             if ((*image)->storage_class == DirectClass)
3722               {
3723                 for (y=0; y < (long) (*image)->rows; y++)
3724                 {
3725                   q=GetAuthenticPixels(*image,0,y,(*image)->columns,1,
3726                     exception);
3727                   if (q == (PixelPacket *) NULL)
3728                     break;
3729                   for (x=0; x < (int) (*image)->columns; x++)
3730                   {
3731                     if (IsColorSimilar(*image,q,&target))
3732                       {
3733                         q->red=ScaleShortToQuantum(color.red);
3734                         q->green=ScaleShortToQuantum(color.green);
3735                         q->blue=ScaleShortToQuantum(color.blue);
3736                       }
3737                     q++;
3738                   }
3739                   if (SyncAuthenticPixels(*image,exception) == MagickFalse)
3740                     break;
3741                 }
3742               }
3743             else
3744               {
3745                 for (i=0; i < (int) (*image)->colors; i++)
3746                   if (IsColorSimilar(*image,(*image)->colormap+i,&target))
3747                     {
3748                       (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
3749                       (*image)->colormap[i].green=ScaleShortToQuantum(
3750                         color.green);
3751                       (*image)->colormap[i].blue=ScaleShortToQuantum(
3752                         color.blue);
3753                     }
3754                 (void) SyncImage(*image);
3755               }
3756             break;
3757           }
3758           case FloodfillMethod:
3759           case FillToBorderMethod:
3760           {
3761             DrawInfo
3762               *draw_info;
3763
3764             MagickPixelPacket
3765               target;
3766
3767             /*
3768               Update color information using floodfill algorithm.
3769             */
3770             (void) GetOneVirtualMagickPixel(*image,x_offset,y_offset,&target,
3771               exception);
3772             if (method == FillToBorderMethod)
3773               {
3774                 target.red=(MagickRealType)
3775                   ScaleShortToQuantum(border_color.red);
3776                 target.green=(MagickRealType)
3777                   ScaleShortToQuantum(border_color.green);
3778                 target.blue=(MagickRealType)
3779                   ScaleShortToQuantum(border_color.blue);
3780               }
3781             draw_info=CloneDrawInfo(resource_info->image_info,
3782               (DrawInfo *) NULL);
3783             (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3784               &draw_info->fill,exception);
3785             (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
3786               x_offset,y_offset,method == FloodfillMethod ? MagickFalse :
3787               MagickTrue);
3788             draw_info=DestroyDrawInfo(draw_info);
3789             break;
3790           }
3791           case ResetMethod:
3792           {
3793             /*
3794               Update color information using reset algorithm.
3795             */
3796             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3797               return(MagickFalse);
3798             for (y=0; y < (long) (*image)->rows; y++)
3799             {
3800               q=QueueAuthenticPixels(*image,0,y,(*image)->columns,1,exception);
3801               if (q == (PixelPacket *) NULL)
3802                 break;
3803               for (x=0; x < (int) (*image)->columns; x++)
3804               {
3805                 q->red=ScaleShortToQuantum(color.red);
3806                 q->green=ScaleShortToQuantum(color.green);
3807                 q->blue=ScaleShortToQuantum(color.blue);
3808                 q++;
3809               }
3810               if (SyncAuthenticPixels(*image,exception) == MagickFalse)
3811                 break;
3812             }
3813             break;
3814           }
3815         }
3816         state&=(~UpdateConfigurationState);
3817       }
3818   } while ((state & ExitState) == 0);
3819   (void) XSelectInput(display,windows->image.id,
3820     windows->image.attributes.event_mask);
3821   XSetCursorState(display,windows,MagickFalse);
3822   (void) XFreeCursor(display,cursor);
3823   return(MagickTrue);
3824 }
3825 \f
3826 /*
3827 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3828 %                                                                             %
3829 %                                                                             %
3830 %                                                                             %
3831 +   X C o m p o s i t e I m a g e                                             %
3832 %                                                                             %
3833 %                                                                             %
3834 %                                                                             %
3835 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3836 %
3837 %  XCompositeImage() requests an image name from the user, reads the image and
3838 %  composites it with the X window image at a location the user chooses with
3839 %  the pointer.
3840 %
3841 %  The format of the XCompositeImage method is:
3842 %
3843 %      MagickBooleanType XCompositeImage(Display *display,
3844 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
3845 %
3846 %  A description of each parameter follows:
3847 %
3848 %    o display: Specifies a connection to an X server;  returned from
3849 %      XOpenDisplay.
3850 %
3851 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3852 %
3853 %    o windows: Specifies a pointer to a XWindows structure.
3854 %
3855 %    o image: the image; returned from ReadImage.
3856 %
3857 */
3858 static MagickBooleanType XCompositeImage(Display *display,
3859   XResourceInfo *resource_info,XWindows *windows,Image *image)
3860 {
3861   static char
3862     displacement_geometry[MaxTextExtent] = "30x30",
3863     filename[MaxTextExtent] = "\0";
3864
3865   static const char
3866     *CompositeMenu[] =
3867     {
3868       "Operators",
3869       "Dissolve",
3870       "Displace",
3871       "Help",
3872       "Dismiss",
3873       (char *) NULL
3874     };
3875
3876   static CompositeOperator
3877     compose = CopyCompositeOp;
3878
3879   static const ModeType
3880     CompositeCommands[] =
3881     {
3882       CompositeOperatorsCommand,
3883       CompositeDissolveCommand,
3884       CompositeDisplaceCommand,
3885       CompositeHelpCommand,
3886       CompositeDismissCommand
3887     };
3888
3889   char
3890     text[MaxTextExtent];
3891
3892   Cursor
3893     cursor;
3894
3895   Image
3896     *composite_image;
3897
3898   int
3899     entry,
3900     id,
3901     x,
3902     y;
3903
3904   MagickRealType
3905     blend,
3906     scale_factor;
3907
3908   RectangleInfo
3909     highlight_info,
3910     composite_info;
3911
3912   unsigned int
3913     height,
3914     width;
3915
3916   unsigned long
3917     state;
3918
3919   XEvent
3920     event;
3921
3922   /*
3923     Request image file name from user.
3924   */
3925   XFileBrowserWidget(display,windows,"Composite",filename);
3926   if (*filename == '\0')
3927     return(MagickTrue);
3928   /*
3929     Read image.
3930   */
3931   XSetCursorState(display,windows,MagickTrue);
3932   XCheckRefreshWindows(display,windows);
3933   (void) CopyMagickString(resource_info->image_info->filename,filename,
3934     MaxTextExtent);
3935   composite_image=ReadImage(resource_info->image_info,&image->exception);
3936   CatchException(&image->exception);
3937   XSetCursorState(display,windows,MagickFalse);
3938   if (composite_image == (Image *) NULL)
3939     return(MagickFalse);
3940   /*
3941     Map Command widget.
3942   */
3943   (void) CloneString(&windows->command.name,"Composite");
3944   windows->command.data=1;
3945   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3946   (void) XMapRaised(display,windows->command.id);
3947   XClientMessage(display,windows->image.id,windows->im_protocols,
3948     windows->im_update_widget,CurrentTime);
3949   /*
3950     Track pointer until button 1 is pressed.
3951   */
3952   XQueryPosition(display,windows->image.id,&x,&y);
3953   (void) XSelectInput(display,windows->image.id,
3954     windows->image.attributes.event_mask | PointerMotionMask);
3955   composite_info.x=windows->image.x+x;
3956   composite_info.y=windows->image.y+y;
3957   composite_info.width=0;
3958   composite_info.height=0;
3959   cursor=XCreateFontCursor(display,XC_ul_angle);
3960   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3961   blend=0.0;
3962   state=DefaultState;
3963   do
3964   {
3965     if (windows->info.mapped != MagickFalse)
3966       {
3967         /*
3968           Display pointer position.
3969         */
3970         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
3971           composite_info.x,composite_info.y);
3972         XInfoWidget(display,windows,text);
3973       }
3974     highlight_info=composite_info;
3975     highlight_info.x=composite_info.x-windows->image.x;
3976     highlight_info.y=composite_info.y-windows->image.y;
3977     XHighlightRectangle(display,windows->image.id,
3978       windows->image.highlight_context,&highlight_info);
3979     /*
3980       Wait for next event.
3981     */
3982     XScreenEvent(display,windows,&event);
3983     XHighlightRectangle(display,windows->image.id,
3984       windows->image.highlight_context,&highlight_info);
3985     if (event.xany.window == windows->command.id)
3986       {
3987         /*
3988           Select a command from the Command widget.
3989         */
3990         id=XCommandWidget(display,windows,CompositeMenu,&event);
3991         if (id < 0)
3992           continue;
3993         switch (CompositeCommands[id])
3994         {
3995           case CompositeOperatorsCommand:
3996           {
3997             char
3998               command[MaxTextExtent],
3999               **operators;
4000
4001             /*
4002               Select a command from the pop-up menu.
4003             */
4004             operators=GetMagickOptions(MagickComposeOptions);
4005             if (operators == (char **) NULL)
4006               break;
4007             entry=XMenuWidget(display,windows,CompositeMenu[id],
4008               (const char **) operators,command);
4009             if (entry >= 0)
4010               compose=(CompositeOperator) ParseMagickOption(
4011                 MagickComposeOptions,MagickFalse,operators[entry]);
4012             operators=DestroyStringList(operators);
4013             break;
4014           }
4015           case CompositeDissolveCommand:
4016           {
4017             static char
4018               factor[MaxTextExtent] = "20.0";
4019
4020             /*
4021               Dissolve the two images a given percent.
4022             */
4023             (void) XSetFunction(display,windows->image.highlight_context,
4024               GXcopy);
4025             (void) XDialogWidget(display,windows,"Dissolve",
4026               "Enter the blend factor (0.0 - 99.9%):",factor);
4027             (void) XSetFunction(display,windows->image.highlight_context,
4028               GXinvert);
4029             if (*factor == '\0')
4030               break;
4031             blend=StringToDouble(factor);
4032             compose=DissolveCompositeOp;
4033             break;
4034           }
4035           case CompositeDisplaceCommand:
4036           {
4037             /*
4038               Get horizontal and vertical scale displacement geometry.
4039             */
4040             (void) XSetFunction(display,windows->image.highlight_context,
4041               GXcopy);
4042             (void) XDialogWidget(display,windows,"Displace",
4043               "Enter the horizontal and vertical scale:",displacement_geometry);
4044             (void) XSetFunction(display,windows->image.highlight_context,
4045               GXinvert);
4046             if (*displacement_geometry == '\0')
4047               break;
4048             compose=DisplaceCompositeOp;
4049             break;
4050           }
4051           case CompositeHelpCommand:
4052           {
4053             (void) XSetFunction(display,windows->image.highlight_context,
4054               GXcopy);
4055             XTextViewWidget(display,resource_info,windows,MagickFalse,
4056               "Help Viewer - Image Composite",ImageCompositeHelp);
4057             (void) XSetFunction(display,windows->image.highlight_context,
4058               GXinvert);
4059             break;
4060           }
4061           case CompositeDismissCommand:
4062           {
4063             /*
4064               Prematurely exit.
4065             */
4066             state|=EscapeState;
4067             state|=ExitState;
4068             break;
4069           }
4070           default:
4071             break;
4072         }
4073         continue;
4074       }
4075     switch (event.type)
4076     {
4077       case ButtonPress:
4078       {
4079         if (image->debug != MagickFalse)
4080           (void) LogMagickEvent(X11Event,GetMagickModule(),
4081             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4082             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4083         if (event.xbutton.button != Button1)
4084           break;
4085         if (event.xbutton.window != windows->image.id)
4086           break;
4087         /*
4088           Change cursor.
4089         */
4090         composite_info.width=composite_image->columns;
4091         composite_info.height=composite_image->rows;
4092         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4093         composite_info.x=windows->image.x+event.xbutton.x;
4094         composite_info.y=windows->image.y+event.xbutton.y;
4095         break;
4096       }
4097       case ButtonRelease:
4098       {
4099         if (image->debug != MagickFalse)
4100           (void) LogMagickEvent(X11Event,GetMagickModule(),
4101             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4102             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4103         if (event.xbutton.button != Button1)
4104           break;
4105         if (event.xbutton.window != windows->image.id)
4106           break;
4107         if ((composite_info.width != 0) && (composite_info.height != 0))
4108           {
4109             /*
4110               User has selected the location of the composite image.
4111             */
4112             composite_info.x=windows->image.x+event.xbutton.x;
4113             composite_info.y=windows->image.y+event.xbutton.y;
4114             state|=ExitState;
4115           }
4116         break;
4117       }
4118       case Expose:
4119         break;
4120       case KeyPress:
4121       {
4122         char
4123           command[MaxTextExtent];
4124
4125         KeySym
4126           key_symbol;
4127
4128         int
4129           length;
4130
4131         if (event.xkey.window != windows->image.id)
4132           break;
4133         /*
4134           Respond to a user key press.
4135         */
4136         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4137           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4138         *(command+length)='\0';
4139         if (image->debug != MagickFalse)
4140           (void) LogMagickEvent(X11Event,GetMagickModule(),
4141             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4142         switch ((int) key_symbol)
4143         {
4144           case XK_Escape:
4145           case XK_F20:
4146           {
4147             /*
4148               Prematurely exit.
4149             */
4150             composite_image=DestroyImage(composite_image);
4151             state|=EscapeState;
4152             state|=ExitState;
4153             break;
4154           }
4155           case XK_F1:
4156           case XK_Help:
4157           {
4158             (void) XSetFunction(display,windows->image.highlight_context,
4159               GXcopy);
4160             XTextViewWidget(display,resource_info,windows,MagickFalse,
4161               "Help Viewer - Image Composite",ImageCompositeHelp);
4162             (void) XSetFunction(display,windows->image.highlight_context,
4163               GXinvert);
4164             break;
4165           }
4166           default:
4167           {
4168             (void) XBell(display,0);
4169             break;
4170           }
4171         }
4172         break;
4173       }
4174       case MotionNotify:
4175       {
4176         /*
4177           Map and unmap Info widget as text cursor crosses its boundaries.
4178         */
4179         x=event.xmotion.x;
4180         y=event.xmotion.y;
4181         if (windows->info.mapped != MagickFalse)
4182           {
4183             if ((x < (int) (windows->info.x+windows->info.width)) &&
4184                 (y < (int) (windows->info.y+windows->info.height)))
4185               (void) XWithdrawWindow(display,windows->info.id,
4186                 windows->info.screen);
4187           }
4188         else
4189           if ((x > (int) (windows->info.x+windows->info.width)) ||
4190               (y > (int) (windows->info.y+windows->info.height)))
4191             (void) XMapWindow(display,windows->info.id);
4192         composite_info.x=windows->image.x+x;
4193         composite_info.y=windows->image.y+y;
4194         break;
4195       }
4196       default:
4197       {
4198         if (image->debug != MagickFalse)
4199           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4200             event.type);
4201         break;
4202       }
4203     }
4204   } while ((state & ExitState) == 0);
4205   (void) XSelectInput(display,windows->image.id,
4206     windows->image.attributes.event_mask);
4207   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4208   XSetCursorState(display,windows,MagickFalse);
4209   (void) XFreeCursor(display,cursor);
4210   if ((state & EscapeState) != 0)
4211     return(MagickTrue);
4212   /*
4213     Image compositing is relative to image configuration.
4214   */
4215   XSetCursorState(display,windows,MagickTrue);
4216   XCheckRefreshWindows(display,windows);
4217   width=(unsigned int) image->columns;
4218   height=(unsigned int) image->rows;
4219   x=0;
4220   y=0;
4221   if (windows->image.crop_geometry != (char *) NULL)
4222     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4223   scale_factor=(MagickRealType) width/windows->image.ximage->width;
4224   composite_info.x+=x;
4225   composite_info.x=(int) (scale_factor*composite_info.x+0.5);
4226   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4227   scale_factor=(MagickRealType) height/windows->image.ximage->height;
4228   composite_info.y+=y;
4229   composite_info.y=(int) (scale_factor*composite_info.y+0.5);
4230   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4231   if ((composite_info.width != composite_image->columns) ||
4232       (composite_info.height != composite_image->rows))
4233     {
4234       Image
4235         *resize_image;
4236
4237       /*
4238         Scale composite image.
4239       */
4240       resize_image=ZoomImage(composite_image,composite_info.width,
4241         composite_info.height,&image->exception);
4242       composite_image=DestroyImage(composite_image);
4243       if (resize_image == (Image *) NULL)
4244         {
4245           XSetCursorState(display,windows,MagickFalse);
4246           return(MagickFalse);
4247         }
4248       composite_image=resize_image;
4249     }
4250   if (compose == DisplaceCompositeOp)
4251     (void) SetImageArtifact(composite_image,"compose:args",
4252       displacement_geometry);
4253   if (blend != 0.0)
4254     {
4255       ExceptionInfo
4256         *exception;
4257
4258       int
4259         y;
4260
4261       Quantum
4262         opacity;
4263
4264       register int
4265         x;
4266
4267       register PixelPacket
4268         *q;
4269
4270       /*
4271         Create mattes for blending.
4272       */
4273       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4274       opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4275         ((long) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4276       if (SetImageStorageClass(image,DirectClass) == MagickFalse)
4277         return(MagickFalse);
4278       image->matte=MagickTrue;
4279       exception=(&image->exception);
4280       for (y=0; y < (long) image->rows; y++)
4281       {
4282         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4283         if (q == (PixelPacket *) NULL)
4284           break;
4285         for (x=0; x < (int) image->columns; x++)
4286         {
4287           q->opacity=opacity;
4288           q++;
4289         }
4290         if (SyncAuthenticPixels(image,exception) == MagickFalse)
4291           break;
4292       }
4293     }
4294   /*
4295     Composite image with X Image window.
4296   */
4297   (void) CompositeImage(image,compose,composite_image,composite_info.x,
4298     composite_info.y);
4299   composite_image=DestroyImage(composite_image);
4300   XSetCursorState(display,windows,MagickFalse);
4301   /*
4302     Update image configuration.
4303   */
4304   XConfigureImageColormap(display,resource_info,windows,image);
4305   (void) XConfigureImage(display,resource_info,windows,image);
4306   return(MagickTrue);
4307 }
4308 \f
4309 /*
4310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4311 %                                                                             %
4312 %                                                                             %
4313 %                                                                             %
4314 +   X C o n f i g u r e I m a g e                                             %
4315 %                                                                             %
4316 %                                                                             %
4317 %                                                                             %
4318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4319 %
4320 %  XConfigureImage() creates a new X image.  It also notifies the window
4321 %  manager of the new image size and configures the transient widows.
4322 %
4323 %  The format of the XConfigureImage method is:
4324 %
4325 %      MagickBooleanType XConfigureImage(Display *display,
4326 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
4327 %
4328 %  A description of each parameter follows:
4329 %
4330 %    o display: Specifies a connection to an X server; returned from
4331 %      XOpenDisplay.
4332 %
4333 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4334 %
4335 %    o windows: Specifies a pointer to a XWindows structure.
4336 %
4337 %    o image: the image.
4338 %
4339 %
4340 */
4341 static MagickBooleanType XConfigureImage(Display *display,
4342   XResourceInfo *resource_info,XWindows *windows,Image *image)
4343 {
4344   char
4345     geometry[MaxTextExtent];
4346
4347   long
4348     x,
4349     y;
4350
4351   MagickStatusType
4352     status;
4353
4354   unsigned long
4355     mask,
4356     height,
4357     width;
4358
4359   XSizeHints
4360     *size_hints;
4361
4362   XWindowChanges
4363     window_changes;
4364
4365   /*
4366     Dismiss if window dimensions are zero.
4367   */
4368   width=(unsigned int) windows->image.window_changes.width;
4369   height=(unsigned int) windows->image.window_changes.height;
4370   if (image->debug != MagickFalse)
4371     (void) LogMagickEvent(X11Event,GetMagickModule(),
4372       "Configure Image: %dx%d=>%lux%lu",windows->image.ximage->width,
4373       windows->image.ximage->height,width,height);
4374   if ((width*height) == 0)
4375     return(MagickTrue);
4376   x=0;
4377   y=0;
4378   /*
4379     Resize image to fit Image window dimensions.
4380   */
4381   XSetCursorState(display,windows,MagickTrue);
4382   (void) XFlush(display);
4383   if (((int) width != windows->image.ximage->width) ||
4384       ((int) height != windows->image.ximage->height))
4385     image->taint=MagickTrue;
4386   windows->magnify.x=(int)
4387     width*windows->magnify.x/windows->image.ximage->width;
4388   windows->magnify.y=(int)
4389     height*windows->magnify.y/windows->image.ximage->height;
4390   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4391   windows->image.y=(int)
4392     (height*windows->image.y/windows->image.ximage->height);
4393   status=XMakeImage(display,resource_info,&windows->image,image,
4394     (unsigned int) width,(unsigned int) height);
4395   if (status == MagickFalse)
4396     XNoticeWidget(display,windows,"Unable to configure X image:",
4397       windows->image.name);
4398   /*
4399     Notify window manager of the new configuration.
4400   */
4401   if (resource_info->image_geometry != (char *) NULL)
4402     (void) FormatMagickString(geometry,MaxTextExtent,"%s>!",
4403       resource_info->image_geometry);
4404   else
4405     (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4406       XDisplayWidth(display,windows->image.screen),
4407       XDisplayHeight(display,windows->image.screen));
4408   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4409   window_changes.width=(int) width;
4410   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4411     window_changes.width=XDisplayWidth(display,windows->image.screen);
4412   window_changes.height=(int) height;
4413   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4414     window_changes.height=XDisplayHeight(display,windows->image.screen);
4415   mask=(unsigned long) (CWWidth | CWHeight);
4416   if (resource_info->backdrop)
4417     {
4418       mask|=CWX | CWY;
4419       window_changes.x=(int)
4420         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4421       window_changes.y=(int)
4422         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4423     }
4424   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4425     (unsigned int) mask,&window_changes);
4426   (void) XClearWindow(display,windows->image.id);
4427   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4428   /*
4429     Update Magnify window configuration.
4430   */
4431   if (windows->magnify.mapped != MagickFalse)
4432     XMakeMagnifyImage(display,windows);
4433   windows->pan.crop_geometry=windows->image.crop_geometry;
4434   XBestIconSize(display,&windows->pan,image);
4435   while (((windows->pan.width << 1) < MaxIconSize) &&
4436          ((windows->pan.height << 1) < MaxIconSize))
4437   {
4438     windows->pan.width<<=1;
4439     windows->pan.height<<=1;
4440   }
4441   if (windows->pan.geometry != (char *) NULL)
4442     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4443       &windows->pan.width,&windows->pan.height);
4444   window_changes.width=(int) windows->pan.width;
4445   window_changes.height=(int) windows->pan.height;
4446   size_hints=XAllocSizeHints();
4447   if (size_hints != (XSizeHints *) NULL)
4448     {
4449       /*
4450         Set new size hints.
4451       */
4452       size_hints->flags=PSize | PMinSize | PMaxSize;
4453       size_hints->width=window_changes.width;
4454       size_hints->height=window_changes.height;
4455       size_hints->min_width=size_hints->width;
4456       size_hints->min_height=size_hints->height;
4457       size_hints->max_width=size_hints->width;
4458       size_hints->max_height=size_hints->height;
4459       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4460       (void) XFree((void *) size_hints);
4461     }
4462   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4463     (unsigned int) (CWWidth | CWHeight),&window_changes);
4464   /*
4465     Update icon window configuration.
4466   */
4467   windows->icon.crop_geometry=windows->image.crop_geometry;
4468   XBestIconSize(display,&windows->icon,image);
4469   window_changes.width=(int) windows->icon.width;
4470   window_changes.height=(int) windows->icon.height;
4471   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4472     (unsigned int) (CWWidth | CWHeight),&window_changes);
4473   XSetCursorState(display,windows,MagickFalse);
4474   return(status != 0 ? MagickTrue : MagickFalse);
4475 }
4476 \f
4477 /*
4478 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4479 %                                                                             %
4480 %                                                                             %
4481 %                                                                             %
4482 +   X C r o p I m a g e                                                       %
4483 %                                                                             %
4484 %                                                                             %
4485 %                                                                             %
4486 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4487 %
4488 %  XCropImage() allows the user to select a region of the image and crop, copy,
4489 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4490 %  the image with XPasteImage.
4491 %
4492 %  The format of the XCropImage method is:
4493 %
4494 %      MagickBooleanType XCropImage(Display *display,
4495 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4496 %        const ClipboardMode mode)
4497 %
4498 %  A description of each parameter follows:
4499 %
4500 %    o display: Specifies a connection to an X server; returned from
4501 %      XOpenDisplay.
4502 %
4503 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4504 %
4505 %    o windows: Specifies a pointer to a XWindows structure.
4506 %
4507 %    o image: the image; returned from ReadImage.
4508 %
4509 %    o mode: This unsigned value specified whether the image should be
4510 %      cropped, copied, or cut.
4511 %
4512 */
4513 static MagickBooleanType XCropImage(Display *display,
4514   XResourceInfo *resource_info,XWindows *windows,Image *image,
4515   const ClipboardMode mode)
4516 {
4517   static const char
4518     *CropModeMenu[] =
4519     {
4520       "Help",
4521       "Dismiss",
4522       (char *) NULL
4523     },
4524     *RectifyModeMenu[] =
4525     {
4526       "Crop",
4527       "Help",
4528       "Dismiss",
4529       (char *) NULL
4530     };
4531
4532   static const ModeType
4533     CropCommands[] =
4534     {
4535       CropHelpCommand,
4536       CropDismissCommand
4537     },
4538     RectifyCommands[] =
4539     {
4540       RectifyCopyCommand,
4541       RectifyHelpCommand,
4542       RectifyDismissCommand
4543     };
4544
4545   char
4546     command[MaxTextExtent],
4547     text[MaxTextExtent];
4548
4549   Cursor
4550     cursor;
4551
4552   ExceptionInfo
4553     *exception;
4554
4555   int
4556     id,
4557     x,
4558     y;
4559
4560   KeySym
4561     key_symbol;
4562
4563   Image
4564     *crop_image;
4565
4566   MagickRealType
4567     scale_factor;
4568
4569   RectangleInfo
4570     crop_info,
4571     highlight_info;
4572
4573   register PixelPacket
4574     *q;
4575
4576   unsigned int
4577     height,
4578     width;
4579
4580   unsigned long
4581     state;
4582
4583   XEvent
4584     event;
4585
4586   /*
4587     Map Command widget.
4588   */
4589   switch (mode)
4590   {
4591     case CopyMode:
4592     {
4593       (void) CloneString(&windows->command.name,"Copy");
4594       break;
4595     }
4596     case CropMode:
4597     {
4598       (void) CloneString(&windows->command.name,"Crop");
4599       break;
4600     }
4601     case CutMode:
4602     {
4603       (void) CloneString(&windows->command.name,"Cut");
4604       break;
4605     }
4606   }
4607   RectifyModeMenu[0]=windows->command.name;
4608   windows->command.data=0;
4609   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4610   (void) XMapRaised(display,windows->command.id);
4611   XClientMessage(display,windows->image.id,windows->im_protocols,
4612     windows->im_update_widget,CurrentTime);
4613   /*
4614     Track pointer until button 1 is pressed.
4615   */
4616   XQueryPosition(display,windows->image.id,&x,&y);
4617   (void) XSelectInput(display,windows->image.id,
4618     windows->image.attributes.event_mask | PointerMotionMask);
4619   crop_info.x=windows->image.x+x;
4620   crop_info.y=windows->image.y+y;
4621   crop_info.width=0;
4622   crop_info.height=0;
4623   cursor=XCreateFontCursor(display,XC_fleur);
4624   state=DefaultState;
4625   do
4626   {
4627     if (windows->info.mapped != MagickFalse)
4628       {
4629         /*
4630           Display pointer position.
4631         */
4632         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
4633           crop_info.x,crop_info.y);
4634         XInfoWidget(display,windows,text);
4635       }
4636     /*
4637       Wait for next event.
4638     */
4639     XScreenEvent(display,windows,&event);
4640     if (event.xany.window == windows->command.id)
4641       {
4642         /*
4643           Select a command from the Command widget.
4644         */
4645         id=XCommandWidget(display,windows,CropModeMenu,&event);
4646         if (id < 0)
4647           continue;
4648         switch (CropCommands[id])
4649         {
4650           case CropHelpCommand:
4651           {
4652             switch (mode)
4653             {
4654               case CopyMode:
4655               {
4656                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4657                   "Help Viewer - Image Copy",ImageCopyHelp);
4658                 break;
4659               }
4660               case CropMode:
4661               {
4662                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4663                   "Help Viewer - Image Crop",ImageCropHelp);
4664                 break;
4665               }
4666               case CutMode:
4667               {
4668                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4669                   "Help Viewer - Image Cut",ImageCutHelp);
4670                 break;
4671               }
4672             }
4673             break;
4674           }
4675           case CropDismissCommand:
4676           {
4677             /*
4678               Prematurely exit.
4679             */
4680             state|=EscapeState;
4681             state|=ExitState;
4682             break;
4683           }
4684           default:
4685             break;
4686         }
4687         continue;
4688       }
4689     switch (event.type)
4690     {
4691       case ButtonPress:
4692       {
4693         if (event.xbutton.button != Button1)
4694           break;
4695         if (event.xbutton.window != windows->image.id)
4696           break;
4697         /*
4698           Note first corner of cropping rectangle-- exit loop.
4699         */
4700         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4701         crop_info.x=windows->image.x+event.xbutton.x;
4702         crop_info.y=windows->image.y+event.xbutton.y;
4703         state|=ExitState;
4704         break;
4705       }
4706       case ButtonRelease:
4707         break;
4708       case Expose:
4709         break;
4710       case KeyPress:
4711       {
4712         if (event.xkey.window != windows->image.id)
4713           break;
4714         /*
4715           Respond to a user key press.
4716         */
4717         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4718           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4719         switch ((int) key_symbol)
4720         {
4721           case XK_Escape:
4722           case XK_F20:
4723           {
4724             /*
4725               Prematurely exit.
4726             */
4727             state|=EscapeState;
4728             state|=ExitState;
4729             break;
4730           }
4731           case XK_F1:
4732           case XK_Help:
4733           {
4734             switch (mode)
4735             {
4736               case CopyMode:
4737               {
4738                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4739                   "Help Viewer - Image Copy",ImageCopyHelp);
4740                 break;
4741               }
4742               case CropMode:
4743               {
4744                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4745                   "Help Viewer - Image Crop",ImageCropHelp);
4746                 break;
4747               }
4748               case CutMode:
4749               {
4750                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4751                   "Help Viewer - Image Cut",ImageCutHelp);
4752                 break;
4753               }
4754             }
4755             break;
4756           }
4757           default:
4758           {
4759             (void) XBell(display,0);
4760             break;
4761           }
4762         }
4763         break;
4764       }
4765       case MotionNotify:
4766       {
4767         if (event.xmotion.window != windows->image.id)
4768           break;
4769         /*
4770           Map and unmap Info widget as text cursor crosses its boundaries.
4771         */
4772         x=event.xmotion.x;
4773         y=event.xmotion.y;
4774         if (windows->info.mapped != MagickFalse)
4775           {
4776             if ((x < (int) (windows->info.x+windows->info.width)) &&
4777                 (y < (int) (windows->info.y+windows->info.height)))
4778               (void) XWithdrawWindow(display,windows->info.id,
4779                 windows->info.screen);
4780           }
4781         else
4782           if ((x > (int) (windows->info.x+windows->info.width)) ||
4783               (y > (int) (windows->info.y+windows->info.height)))
4784             (void) XMapWindow(display,windows->info.id);
4785         crop_info.x=windows->image.x+x;
4786         crop_info.y=windows->image.y+y;
4787         break;
4788       }
4789       default:
4790         break;
4791     }
4792   } while ((state & ExitState) == 0);
4793   (void) XSelectInput(display,windows->image.id,
4794     windows->image.attributes.event_mask);
4795   if ((state & EscapeState) != 0)
4796     {
4797       /*
4798         User want to exit without cropping.
4799       */
4800       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4801       (void) XFreeCursor(display,cursor);
4802       return(MagickTrue);
4803     }
4804   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4805   do
4806   {
4807     /*
4808       Size rectangle as pointer moves until the mouse button is released.
4809     */
4810     x=(int) crop_info.x;
4811     y=(int) crop_info.y;
4812     crop_info.width=0;
4813     crop_info.height=0;
4814     state=DefaultState;
4815     do
4816     {
4817       highlight_info=crop_info;
4818       highlight_info.x=crop_info.x-windows->image.x;
4819       highlight_info.y=crop_info.y-windows->image.y;
4820       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4821         {
4822           /*
4823             Display info and draw cropping rectangle.
4824           */
4825           if (windows->info.mapped == MagickFalse)
4826             (void) XMapWindow(display,windows->info.id);
4827           (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
4828             crop_info.width,crop_info.height,crop_info.x,crop_info.y);
4829           XInfoWidget(display,windows,text);
4830           XHighlightRectangle(display,windows->image.id,
4831             windows->image.highlight_context,&highlight_info);
4832         }
4833       else
4834         if (windows->info.mapped != MagickFalse)
4835           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4836       /*
4837         Wait for next event.
4838       */
4839       XScreenEvent(display,windows,&event);
4840       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4841         XHighlightRectangle(display,windows->image.id,
4842           windows->image.highlight_context,&highlight_info);
4843       switch (event.type)
4844       {
4845         case ButtonPress:
4846         {
4847           crop_info.x=windows->image.x+event.xbutton.x;
4848           crop_info.y=windows->image.y+event.xbutton.y;
4849           break;
4850         }
4851         case ButtonRelease:
4852         {
4853           /*
4854             User has committed to cropping rectangle.
4855           */
4856           crop_info.x=windows->image.x+event.xbutton.x;
4857           crop_info.y=windows->image.y+event.xbutton.y;
4858           XSetCursorState(display,windows,MagickFalse);
4859           state|=ExitState;
4860           windows->command.data=0;
4861           (void) XCommandWidget(display,windows,RectifyModeMenu,
4862             (XEvent *) NULL);
4863           break;
4864         }
4865         case Expose:
4866           break;
4867         case MotionNotify:
4868         {
4869           crop_info.x=windows->image.x+event.xmotion.x;
4870           crop_info.y=windows->image.y+event.xmotion.y;
4871         }
4872         default:
4873           break;
4874       }
4875       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4876           ((state & ExitState) != 0))
4877         {
4878           /*
4879             Check boundary conditions.
4880           */
4881           if (crop_info.x < 0)
4882             crop_info.x=0;
4883           else
4884             if (crop_info.x > (int) windows->image.ximage->width)
4885               crop_info.x=windows->image.ximage->width;
4886           if ((int) crop_info.x < x)
4887             crop_info.width=(unsigned int) (x-crop_info.x);
4888           else
4889             {
4890               crop_info.width=(unsigned int) (crop_info.x-x);
4891               crop_info.x=x;
4892             }
4893           if (crop_info.y < 0)
4894             crop_info.y=0;
4895           else
4896             if (crop_info.y > (int) windows->image.ximage->height)
4897               crop_info.y=windows->image.ximage->height;
4898           if ((int) crop_info.y < y)
4899             crop_info.height=(unsigned int) (y-crop_info.y);
4900           else
4901             {
4902               crop_info.height=(unsigned int) (crop_info.y-y);
4903               crop_info.y=y;
4904             }
4905         }
4906     } while ((state & ExitState) == 0);
4907     /*
4908       Wait for user to grab a corner of the rectangle or press return.
4909     */
4910     state=DefaultState;
4911     (void) XMapWindow(display,windows->info.id);
4912     do
4913     {
4914       if (windows->info.mapped != MagickFalse)
4915         {
4916           /*
4917             Display pointer position.
4918           */
4919           (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
4920             crop_info.width,crop_info.height,crop_info.x,crop_info.y);
4921           XInfoWidget(display,windows,text);
4922         }
4923       highlight_info=crop_info;
4924       highlight_info.x=crop_info.x-windows->image.x;
4925       highlight_info.y=crop_info.y-windows->image.y;
4926       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4927         {
4928           state|=EscapeState;
4929           state|=ExitState;
4930           break;
4931         }
4932       XHighlightRectangle(display,windows->image.id,
4933         windows->image.highlight_context,&highlight_info);
4934       XScreenEvent(display,windows,&event);
4935       if (event.xany.window == windows->command.id)
4936         {
4937           /*
4938             Select a command from the Command widget.
4939           */
4940           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4941           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4942           (void) XSetFunction(display,windows->image.highlight_context,
4943             GXinvert);
4944           XHighlightRectangle(display,windows->image.id,
4945             windows->image.highlight_context,&highlight_info);
4946           if (id >= 0)
4947             switch (RectifyCommands[id])
4948             {
4949               case RectifyCopyCommand:
4950               {
4951                 state|=ExitState;
4952                 break;
4953               }
4954               case RectifyHelpCommand:
4955               {
4956                 (void) XSetFunction(display,windows->image.highlight_context,
4957                   GXcopy);
4958                 switch (mode)
4959                 {
4960                   case CopyMode:
4961                   {
4962                     XTextViewWidget(display,resource_info,windows,MagickFalse,
4963                       "Help Viewer - Image Copy",ImageCopyHelp);
4964                     break;
4965                   }
4966                   case CropMode:
4967                   {
4968                     XTextViewWidget(display,resource_info,windows,MagickFalse,
4969                       "Help Viewer - Image Crop",ImageCropHelp);
4970                     break;
4971                   }
4972                   case CutMode:
4973                   {
4974                     XTextViewWidget(display,resource_info,windows,MagickFalse,
4975                       "Help Viewer - Image Cut",ImageCutHelp);
4976                     break;
4977                   }
4978                 }
4979                 (void) XSetFunction(display,windows->image.highlight_context,
4980                   GXinvert);
4981                 break;
4982               }
4983               case RectifyDismissCommand:
4984               {
4985                 /*
4986                   Prematurely exit.
4987                 */
4988                 state|=EscapeState;
4989                 state|=ExitState;
4990                 break;
4991               }
4992               default:
4993                 break;
4994             }
4995           continue;
4996         }
4997       XHighlightRectangle(display,windows->image.id,
4998         windows->image.highlight_context,&highlight_info);
4999       switch (event.type)
5000       {
5001         case ButtonPress:
5002         {
5003           if (event.xbutton.button != Button1)
5004             break;
5005           if (event.xbutton.window != windows->image.id)
5006             break;
5007           x=windows->image.x+event.xbutton.x;
5008           y=windows->image.y+event.xbutton.y;
5009           if ((x < (int) (crop_info.x+RoiDelta)) &&
5010               (x > (int) (crop_info.x-RoiDelta)) &&
5011               (y < (int) (crop_info.y+RoiDelta)) &&
5012               (y > (int) (crop_info.y-RoiDelta)))
5013             {
5014               crop_info.x=(long) (crop_info.x+crop_info.width);
5015               crop_info.y=(long) (crop_info.y+crop_info.height);
5016               state|=UpdateConfigurationState;
5017               break;
5018             }
5019           if ((x < (int) (crop_info.x+RoiDelta)) &&
5020               (x > (int) (crop_info.x-RoiDelta)) &&
5021               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5022               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5023             {
5024               crop_info.x=(long) (crop_info.x+crop_info.width);
5025               state|=UpdateConfigurationState;
5026               break;
5027             }
5028           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5029               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5030               (y < (int) (crop_info.y+RoiDelta)) &&
5031               (y > (int) (crop_info.y-RoiDelta)))
5032             {
5033               crop_info.y=(long) (crop_info.y+crop_info.height);
5034               state|=UpdateConfigurationState;
5035               break;
5036             }
5037           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5038               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5039               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5040               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5041             {
5042               state|=UpdateConfigurationState;
5043               break;
5044             }
5045         }
5046         case ButtonRelease:
5047         {
5048           if (event.xbutton.window == windows->pan.id)
5049             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5050                 (highlight_info.y != crop_info.y-windows->image.y))
5051               XHighlightRectangle(display,windows->image.id,
5052                 windows->image.highlight_context,&highlight_info);
5053           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5054             event.xbutton.time);
5055           break;
5056         }
5057         case Expose:
5058         {
5059           if (event.xexpose.window == windows->image.id)
5060             if (event.xexpose.count == 0)
5061               {
5062                 event.xexpose.x=(int) highlight_info.x;
5063                 event.xexpose.y=(int) highlight_info.y;
5064                 event.xexpose.width=(int) highlight_info.width;
5065                 event.xexpose.height=(int) highlight_info.height;
5066                 XRefreshWindow(display,&windows->image,&event);
5067               }
5068           if (event.xexpose.window == windows->info.id)
5069             if (event.xexpose.count == 0)
5070               XInfoWidget(display,windows,text);
5071           break;
5072         }
5073         case KeyPress:
5074         {
5075           if (event.xkey.window != windows->image.id)
5076             break;
5077           /*
5078             Respond to a user key press.
5079           */
5080           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5081             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5082           switch ((int) key_symbol)
5083           {
5084             case XK_Escape:
5085             case XK_F20:
5086               state|=EscapeState;
5087             case XK_Return:
5088             {
5089               state|=ExitState;
5090               break;
5091             }
5092             case XK_Home:
5093             case XK_KP_Home:
5094             {
5095               crop_info.x=(long) (windows->image.width/2L-crop_info.width/2L);
5096               crop_info.y=(long) (windows->image.height/2L-crop_info.height/2L);
5097               break;
5098             }
5099             case XK_Left:
5100             case XK_KP_Left:
5101             {
5102               crop_info.x--;
5103               break;
5104             }
5105             case XK_Up:
5106             case XK_KP_Up:
5107             case XK_Next:
5108             {
5109               crop_info.y--;
5110               break;
5111             }
5112             case XK_Right:
5113             case XK_KP_Right:
5114             {
5115               crop_info.x++;
5116               break;
5117             }
5118             case XK_Prior:
5119             case XK_Down:
5120             case XK_KP_Down:
5121             {
5122               crop_info.y++;
5123               break;
5124             }
5125             case XK_F1:
5126             case XK_Help:
5127             {
5128               (void) XSetFunction(display,windows->image.highlight_context,
5129                 GXcopy);
5130               switch (mode)
5131               {
5132                 case CopyMode:
5133                 {
5134                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5135                     "Help Viewer - Image Copy",ImageCopyHelp);
5136                   break;
5137                 }
5138                 case CropMode:
5139                 {
5140                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5141                     "Help Viewer - Image Cropg",ImageCropHelp);
5142                   break;
5143                 }
5144                 case CutMode:
5145                 {
5146                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5147                     "Help Viewer - Image Cutg",ImageCutHelp);
5148                   break;
5149                 }
5150               }
5151               (void) XSetFunction(display,windows->image.highlight_context,
5152                 GXinvert);
5153               break;
5154             }
5155             default:
5156             {
5157               (void) XBell(display,0);
5158               break;
5159             }
5160           }
5161           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5162             event.xkey.time);
5163           break;
5164         }
5165         case KeyRelease:
5166           break;
5167         case MotionNotify:
5168         {
5169           if (event.xmotion.window != windows->image.id)
5170             break;
5171           /*
5172             Map and unmap Info widget as text cursor crosses its boundaries.
5173           */
5174           x=event.xmotion.x;
5175           y=event.xmotion.y;
5176           if (windows->info.mapped != MagickFalse)
5177             {
5178               if ((x < (int) (windows->info.x+windows->info.width)) &&
5179                   (y < (int) (windows->info.y+windows->info.height)))
5180                 (void) XWithdrawWindow(display,windows->info.id,
5181                   windows->info.screen);
5182             }
5183           else
5184             if ((x > (int) (windows->info.x+windows->info.width)) ||
5185                 (y > (int) (windows->info.y+windows->info.height)))
5186               (void) XMapWindow(display,windows->info.id);
5187           crop_info.x=windows->image.x+event.xmotion.x;
5188           crop_info.y=windows->image.y+event.xmotion.y;
5189           break;
5190         }
5191         case SelectionRequest:
5192         {
5193           XSelectionEvent
5194             notify;
5195
5196           XSelectionRequestEvent
5197             *request;
5198
5199           /*
5200             Set primary selection.
5201           */
5202           (void) FormatMagickString(text,MaxTextExtent,"%lux%lu%+ld%+ld",
5203             crop_info.width,crop_info.height,crop_info.x,crop_info.y);
5204           request=(&(event.xselectionrequest));
5205           (void) XChangeProperty(request->display,request->requestor,
5206             request->property,request->target,8,PropModeReplace,
5207             (unsigned char *) text,(int) strlen(text));
5208           notify.type=SelectionNotify;
5209           notify.display=request->display;
5210           notify.requestor=request->requestor;
5211           notify.selection=request->selection;
5212           notify.target=request->target;
5213           notify.time=request->time;
5214           if (request->property == None)
5215             notify.property=request->target;
5216           else
5217             notify.property=request->property;
5218           (void) XSendEvent(request->display,request->requestor,False,0,
5219             (XEvent *) &notify);
5220         }
5221         default:
5222           break;
5223       }
5224       if ((state & UpdateConfigurationState) != 0)
5225         {
5226           (void) XPutBackEvent(display,&event);
5227           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5228           break;
5229         }
5230     } while ((state & ExitState) == 0);
5231   } while ((state & ExitState) == 0);
5232   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5233   XSetCursorState(display,windows,MagickFalse);
5234   if ((state & EscapeState) != 0)
5235     return(MagickTrue);
5236   if (mode == CropMode)
5237     if (((int) crop_info.width != windows->image.ximage->width) ||
5238         ((int) crop_info.height != windows->image.ximage->height))
5239       {
5240         /*
5241           Reconfigure Image window as defined by cropping rectangle.
5242         */
5243         XSetCropGeometry(display,windows,&crop_info,image);
5244         windows->image.window_changes.width=(int) crop_info.width;
5245         windows->image.window_changes.height=(int) crop_info.height;
5246         (void) XConfigureImage(display,resource_info,windows,image);
5247         return(MagickTrue);
5248       }
5249   /*
5250     Copy image before applying image transforms.
5251   */
5252   XSetCursorState(display,windows,MagickTrue);
5253   XCheckRefreshWindows(display,windows);
5254   width=(unsigned int) image->columns;
5255   height=(unsigned int) image->rows;
5256   x=0;
5257   y=0;
5258   if (windows->image.crop_geometry != (char *) NULL)
5259     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5260   scale_factor=(MagickRealType) width/windows->image.ximage->width;
5261   crop_info.x+=x;
5262   crop_info.x=(int) (scale_factor*crop_info.x+0.5);
5263   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5264   scale_factor=(MagickRealType) height/windows->image.ximage->height;
5265   crop_info.y+=y;
5266   crop_info.y=(int) (scale_factor*crop_info.y+0.5);
5267   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5268   crop_image=CropImage(image,&crop_info,&image->exception);
5269   XSetCursorState(display,windows,MagickFalse);
5270   if (crop_image == (Image *) NULL)
5271     return(MagickFalse);
5272   if (resource_info->copy_image != (Image *) NULL)
5273     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5274   resource_info->copy_image=crop_image;
5275   if (mode == CopyMode)
5276     {
5277       (void) XConfigureImage(display,resource_info,windows,image);
5278       return(MagickTrue);
5279     }
5280   /*
5281     Cut image.
5282   */
5283   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
5284     return(MagickFalse);
5285   image->matte=MagickTrue;
5286   exception=(&image->exception);
5287   for (y=0; y < (long) crop_info.height; y++)
5288   {
5289     q=GetAuthenticPixels(image,crop_info.x,y+crop_info.y,crop_info.width,1,
5290       exception);
5291     if (q == (PixelPacket *) NULL)
5292       break;
5293     for (x=0; x < (int) crop_info.width; x++)
5294     {
5295       q->opacity=(Quantum) TransparentOpacity;
5296       q++;
5297     }
5298     if (SyncAuthenticPixels(image,exception) == MagickFalse)
5299       break;
5300   }
5301   /*
5302     Update image configuration.
5303   */
5304   XConfigureImageColormap(display,resource_info,windows,image);
5305   (void) XConfigureImage(display,resource_info,windows,image);
5306   return(MagickTrue);
5307 }
5308 \f
5309 /*
5310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5311 %                                                                             %
5312 %                                                                             %
5313 %                                                                             %
5314 +   X D r a w I m a g e                                                       %
5315 %                                                                             %
5316 %                                                                             %
5317 %                                                                             %
5318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5319 %
5320 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5321 %  the image.
5322 %
5323 %  The format of the XDrawEditImage method is:
5324 %
5325 %      MagickBooleanType XDrawEditImage(Display *display,
5326 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
5327 %
5328 %  A description of each parameter follows:
5329 %
5330 %    o display: Specifies a connection to an X server; returned from
5331 %      XOpenDisplay.
5332 %
5333 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5334 %
5335 %    o windows: Specifies a pointer to a XWindows structure.
5336 %
5337 %    o image: the image.
5338 %
5339 */
5340 static MagickBooleanType XDrawEditImage(Display *display,
5341   XResourceInfo *resource_info,XWindows *windows,Image **image)
5342 {
5343   static const char
5344     *DrawMenu[] =
5345     {
5346       "Element",
5347       "Color",
5348       "Stipple",
5349       "Width",
5350       "Undo",
5351       "Help",
5352       "Dismiss",
5353       (char *) NULL
5354     };
5355
5356   static ElementType
5357     element = PointElement;
5358
5359   static const ModeType
5360     DrawCommands[] =
5361     {
5362       DrawElementCommand,
5363       DrawColorCommand,
5364       DrawStippleCommand,
5365       DrawWidthCommand,
5366       DrawUndoCommand,
5367       DrawHelpCommand,
5368       DrawDismissCommand
5369     };
5370
5371   static Pixmap
5372     stipple = (Pixmap) NULL;
5373
5374   static unsigned int
5375     pen_id = 0,
5376     line_width = 1;
5377
5378   char
5379     command[MaxTextExtent],
5380     text[MaxTextExtent];
5381
5382   Cursor
5383     cursor;
5384
5385   int
5386     entry,
5387     id,
5388     number_coordinates,
5389     x,
5390     y;
5391
5392   MagickRealType
5393     degrees;
5394
5395   MagickStatusType
5396     status;
5397
5398   RectangleInfo
5399     rectangle_info;
5400
5401   register int
5402     i;
5403
5404   unsigned int
5405     distance,
5406     height,
5407     max_coordinates,
5408     width;
5409
5410   unsigned long
5411     state;
5412
5413   Window
5414     root_window;
5415
5416   XDrawInfo
5417     draw_info;
5418
5419   XEvent
5420     event;
5421
5422   XPoint
5423     *coordinate_info;
5424
5425   XSegment
5426     line_info;
5427
5428   /*
5429     Allocate polygon info.
5430   */
5431   max_coordinates=2048;
5432   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5433     sizeof(*coordinate_info));
5434   if (coordinate_info == (XPoint *) NULL)
5435     {
5436       (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
5437         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5438       return(MagickFalse);
5439     }
5440   /*
5441     Map Command widget.
5442   */
5443   (void) CloneString(&windows->command.name,"Draw");
5444   windows->command.data=4;
5445   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5446   (void) XMapRaised(display,windows->command.id);
5447   XClientMessage(display,windows->image.id,windows->im_protocols,
5448     windows->im_update_widget,CurrentTime);
5449   /*
5450     Wait for first button press.
5451   */
5452   root_window=XRootWindow(display,XDefaultScreen(display));
5453   draw_info.stencil=OpaqueStencil;
5454   status=MagickTrue;
5455   cursor=XCreateFontCursor(display,XC_tcross);
5456   for ( ; ; )
5457   {
5458     XQueryPosition(display,windows->image.id,&x,&y);
5459     (void) XSelectInput(display,windows->image.id,
5460       windows->image.attributes.event_mask | PointerMotionMask);
5461     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5462     state=DefaultState;
5463     do
5464     {
5465       if (windows->info.mapped != MagickFalse)
5466         {
5467           /*
5468             Display pointer position.
5469           */
5470           (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
5471             x+windows->image.x,y+windows->image.y);
5472           XInfoWidget(display,windows,text);
5473         }
5474       /*
5475         Wait for next event.
5476       */
5477       XScreenEvent(display,windows,&event);
5478       if (event.xany.window == windows->command.id)
5479         {
5480           /*
5481             Select a command from the Command widget.
5482           */
5483           id=XCommandWidget(display,windows,DrawMenu,&event);
5484           if (id < 0)
5485             continue;
5486           switch (DrawCommands[id])
5487           {
5488             case DrawElementCommand:
5489             {
5490               static const char
5491                 *Elements[] =
5492                 {
5493                   "point",
5494                   "line",
5495                   "rectangle",
5496                   "fill rectangle",
5497                   "circle",
5498                   "fill circle",
5499                   "ellipse",
5500                   "fill ellipse",
5501                   "polygon",
5502                   "fill polygon",
5503                   (char *) NULL,
5504                 };
5505
5506               /*
5507                 Select a command from the pop-up menu.
5508               */
5509               element=(ElementType) (XMenuWidget(display,windows,
5510                 DrawMenu[id],Elements,command)+1);
5511               break;
5512             }
5513             case DrawColorCommand:
5514             {
5515               const char
5516                 *ColorMenu[MaxNumberPens+1];
5517
5518               int
5519                 pen_number;
5520
5521               MagickBooleanType
5522                 transparent;
5523
5524               XColor
5525                 color;
5526
5527               /*
5528                 Initialize menu selections.
5529               */
5530               for (i=0; i < (int) (MaxNumberPens-2); i++)
5531                 ColorMenu[i]=resource_info->pen_colors[i];
5532               ColorMenu[MaxNumberPens-2]="transparent";
5533               ColorMenu[MaxNumberPens-1]="Browser...";
5534               ColorMenu[MaxNumberPens]=(char *) NULL;
5535               /*
5536                 Select a pen color from the pop-up menu.
5537               */
5538               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5539                 (const char **) ColorMenu,command);
5540               if (pen_number < 0)
5541                 break;
5542               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5543                 MagickFalse;
5544               if (transparent != MagickFalse)
5545                 {
5546                   draw_info.stencil=TransparentStencil;
5547                   break;
5548                 }
5549               if (pen_number == (MaxNumberPens-1))
5550                 {
5551                   static char
5552                     color_name[MaxTextExtent] = "gray";
5553
5554                   /*
5555                     Select a pen color from a dialog.
5556                   */
5557                   resource_info->pen_colors[pen_number]=color_name;
5558                   XColorBrowserWidget(display,windows,"Select",color_name);
5559                   if (*color_name == '\0')
5560                     break;
5561                 }
5562               /*
5563                 Set pen color.
5564               */
5565               (void) XParseColor(display,windows->map_info->colormap,
5566                 resource_info->pen_colors[pen_number],&color);
5567               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5568                 (unsigned int) MaxColors,&color);
5569               windows->pixel_info->pen_colors[pen_number]=color;
5570               pen_id=(unsigned int) pen_number;
5571               draw_info.stencil=OpaqueStencil;
5572               break;
5573             }
5574             case DrawStippleCommand:
5575             {
5576               Image
5577                 *stipple_image;
5578
5579               ImageInfo
5580                 *image_info;
5581
5582               int
5583                 status;
5584
5585               static char
5586                 filename[MaxTextExtent] = "\0";
5587
5588               static const char
5589                 *StipplesMenu[] =
5590                 {
5591                   "Brick",
5592                   "Diagonal",
5593                   "Scales",
5594                   "Vertical",
5595                   "Wavy",
5596                   "Translucent",
5597                   "Opaque",
5598                   (char *) NULL,
5599                   (char *) NULL,
5600                 };
5601
5602               /*
5603                 Select a command from the pop-up menu.
5604               */
5605               StipplesMenu[7]="Open...";
5606               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5607                 command);
5608               if (entry < 0)
5609                 break;
5610               if (stipple != (Pixmap) NULL)
5611                 (void) XFreePixmap(display,stipple);
5612               stipple=(Pixmap) NULL;
5613               if (entry == 6)
5614                 break;
5615               if (entry != 7)
5616                 {
5617                   switch (entry)
5618                   {
5619                     case 0:
5620                     {
5621                       stipple=XCreateBitmapFromData(display,root_window,
5622                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5623                       break;
5624                     }
5625                     case 1:
5626                     {
5627                       stipple=XCreateBitmapFromData(display,root_window,
5628                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5629                       break;
5630                     }
5631                     case 2:
5632                     {
5633                       stipple=XCreateBitmapFromData(display,root_window,
5634                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5635                       break;
5636                     }
5637                     case 3:
5638                     {
5639                       stipple=XCreateBitmapFromData(display,root_window,
5640                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5641                       break;
5642                     }
5643                     case 4:
5644                     {
5645                       stipple=XCreateBitmapFromData(display,root_window,
5646                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5647                       break;
5648                     }
5649                     case 5:
5650                     default:
5651                     {
5652                       stipple=XCreateBitmapFromData(display,root_window,
5653                         (char *) HighlightBitmap,HighlightWidth,
5654                         HighlightHeight);
5655                       break;
5656                     }
5657                   }
5658                   break;
5659                 }
5660               XFileBrowserWidget(display,windows,"Stipple",filename);
5661               if (*filename == '\0')
5662                 break;
5663               /*
5664                 Read image.
5665               */
5666               XSetCursorState(display,windows,MagickTrue);
5667               XCheckRefreshWindows(display,windows);
5668               image_info=AcquireImageInfo();
5669               (void) CopyMagickString(image_info->filename,filename,
5670                 MaxTextExtent);
5671               stipple_image=ReadImage(image_info,&(*image)->exception);
5672               CatchException(&(*image)->exception);
5673               XSetCursorState(display,windows,MagickFalse);
5674               if (stipple_image == (Image *) NULL)
5675                 break;
5676               (void) AcquireUniqueFileResource(filename);
5677               (void) FormatMagickString(stipple_image->filename,MaxTextExtent,
5678                 "xbm:%s",filename);
5679               (void) WriteImage(image_info,stipple_image);
5680               stipple_image=DestroyImage(stipple_image);
5681               image_info=DestroyImageInfo(image_info);
5682               status=XReadBitmapFile(display,root_window,filename,&width,
5683                 &height,&stipple,&x,&y);
5684               (void) RelinquishUniqueFileResource(filename);
5685               if ((status != BitmapSuccess) != 0)
5686                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5687                   filename);
5688               break;
5689             }
5690             case DrawWidthCommand:
5691             {
5692               static char
5693                 width[MaxTextExtent] = "0";
5694
5695               static const char
5696                 *WidthsMenu[] =
5697                 {
5698                   "1",
5699                   "2",
5700                   "4",
5701                   "8",
5702                   "16",
5703                   "Dialog...",
5704                   (char *) NULL,
5705                 };
5706
5707               /*
5708                 Select a command from the pop-up menu.
5709               */
5710               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5711                 command);
5712               if (entry < 0)
5713                 break;
5714               if (entry != 5)
5715                 {
5716                   line_width=(unsigned int) StringToUnsignedLong(WidthsMenu[entry]);
5717                   break;
5718                 }
5719               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5720                 width);
5721               if (*width == '\0')
5722                 break;
5723               line_width=(unsigned int) StringToUnsignedLong(width);
5724               break;
5725             }
5726             case DrawUndoCommand:
5727             {
5728               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5729                 image);
5730               break;
5731             }
5732             case DrawHelpCommand:
5733             {
5734               XTextViewWidget(display,resource_info,windows,MagickFalse,
5735                 "Help Viewer - Image Rotation",ImageDrawHelp);
5736               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5737               break;
5738             }
5739             case DrawDismissCommand:
5740             {
5741               /*
5742                 Prematurely exit.
5743               */
5744               state|=EscapeState;
5745               state|=ExitState;
5746               break;
5747             }
5748             default:
5749               break;
5750           }
5751           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5752           continue;
5753         }
5754       switch (event.type)
5755       {
5756         case ButtonPress:
5757         {
5758           if (event.xbutton.button != Button1)
5759             break;
5760           if (event.xbutton.window != windows->image.id)
5761             break;
5762           /*
5763             exit loop.
5764           */
5765           x=event.xbutton.x;
5766           y=event.xbutton.y;
5767           state|=ExitState;
5768           break;
5769         }
5770         case ButtonRelease:
5771           break;
5772         case Expose:
5773           break;
5774         case KeyPress:
5775         {
5776           KeySym
5777             key_symbol;
5778
5779           if (event.xkey.window != windows->image.id)
5780             break;
5781           /*
5782             Respond to a user key press.
5783           */
5784           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5785             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5786           switch ((int) key_symbol)
5787           {
5788             case XK_Escape:
5789             case XK_F20:
5790             {
5791               /*
5792                 Prematurely exit.
5793               */
5794               state|=EscapeState;
5795               state|=ExitState;
5796               break;
5797             }
5798             case XK_F1:
5799             case XK_Help:
5800             {
5801               XTextViewWidget(display,resource_info,windows,MagickFalse,
5802                 "Help Viewer - Image Rotation",ImageDrawHelp);
5803               break;
5804             }
5805             default:
5806             {
5807               (void) XBell(display,0);
5808               break;
5809             }
5810           }
5811           break;
5812         }
5813         case MotionNotify:
5814         {
5815           /*
5816             Map and unmap Info widget as text cursor crosses its boundaries.
5817           */
5818           x=event.xmotion.x;
5819           y=event.xmotion.y;
5820           if (windows->info.mapped != MagickFalse)
5821             {
5822               if ((x < (int) (windows->info.x+windows->info.width)) &&
5823                   (y < (int) (windows->info.y+windows->info.height)))
5824                 (void) XWithdrawWindow(display,windows->info.id,
5825                   windows->info.screen);
5826             }
5827           else
5828             if ((x > (int) (windows->info.x+windows->info.width)) ||
5829                 (y > (int) (windows->info.y+windows->info.height)))
5830               (void) XMapWindow(display,windows->info.id);
5831           break;
5832         }
5833       }
5834     } while ((state & ExitState) == 0);
5835     (void) XSelectInput(display,windows->image.id,
5836       windows->image.attributes.event_mask);
5837     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5838     if ((state & EscapeState) != 0)
5839       break;
5840     /*
5841       Draw element as pointer moves until the button is released.
5842     */
5843     distance=0;
5844     degrees=0.0;
5845     line_info.x1=x;
5846     line_info.y1=y;
5847     line_info.x2=x;
5848     line_info.y2=y;
5849     rectangle_info.x=x;
5850     rectangle_info.y=y;
5851     rectangle_info.width=0;
5852     rectangle_info.height=0;
5853     number_coordinates=1;
5854     coordinate_info->x=x;
5855     coordinate_info->y=y;
5856     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5857     state=DefaultState;
5858     do
5859     {
5860       switch (element)
5861       {
5862         case PointElement:
5863         default:
5864         {
5865           if (number_coordinates > 1)
5866             {
5867               (void) XDrawLines(display,windows->image.id,
5868                 windows->image.highlight_context,coordinate_info,
5869                 number_coordinates,CoordModeOrigin);
5870               (void) FormatMagickString(text,MaxTextExtent," %+d%+d",
5871                 coordinate_info[number_coordinates-1].x,
5872                 coordinate_info[number_coordinates-1].y);
5873               XInfoWidget(display,windows,text);
5874             }
5875           break;
5876         }
5877         case LineElement:
5878         {
5879           if (distance > 9)
5880             {
5881               /*
5882                 Display angle of the line.
5883               */
5884               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5885                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5886               (void) FormatMagickString(text,MaxTextExtent," %g",
5887                 (double) degrees);
5888               XInfoWidget(display,windows,text);
5889               XHighlightLine(display,windows->image.id,
5890                 windows->image.highlight_context,&line_info);
5891             }
5892           else
5893             if (windows->info.mapped != MagickFalse)
5894               (void) XWithdrawWindow(display,windows->info.id,
5895                 windows->info.screen);
5896           break;
5897         }
5898         case RectangleElement:
5899         case FillRectangleElement:
5900         {
5901           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5902             {
5903               /*
5904                 Display info and draw drawing rectangle.
5905               */
5906               (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
5907                 rectangle_info.width,rectangle_info.height,rectangle_info.x,
5908                 rectangle_info.y);
5909               XInfoWidget(display,windows,text);
5910               XHighlightRectangle(display,windows->image.id,
5911                 windows->image.highlight_context,&rectangle_info);
5912             }
5913           else
5914             if (windows->info.mapped != MagickFalse)
5915               (void) XWithdrawWindow(display,windows->info.id,
5916                 windows->info.screen);
5917           break;
5918         }
5919         case CircleElement:
5920         case FillCircleElement:
5921         case EllipseElement:
5922         case FillEllipseElement:
5923         {
5924           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5925             {
5926               /*
5927                 Display info and draw drawing rectangle.
5928               */
5929               (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
5930                 rectangle_info.width,rectangle_info.height,rectangle_info.x,
5931                 rectangle_info.y);
5932               XInfoWidget(display,windows,text);
5933               XHighlightEllipse(display,windows->image.id,
5934                 windows->image.highlight_context,&rectangle_info);
5935             }
5936           else
5937             if (windows->info.mapped != MagickFalse)
5938               (void) XWithdrawWindow(display,windows->info.id,
5939                 windows->info.screen);
5940           break;
5941         }
5942         case PolygonElement:
5943         case FillPolygonElement:
5944         {
5945           if (number_coordinates > 1)
5946             (void) XDrawLines(display,windows->image.id,
5947               windows->image.highlight_context,coordinate_info,
5948               number_coordinates,CoordModeOrigin);
5949           if (distance > 9)
5950             {
5951               /*
5952                 Display angle of the line.
5953               */
5954               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5955                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5956               (void) FormatMagickString(text,MaxTextExtent," %g",
5957                 (double) degrees);
5958               XInfoWidget(display,windows,text);
5959               XHighlightLine(display,windows->image.id,
5960                 windows->image.highlight_context,&line_info);
5961             }
5962           else
5963             if (windows->info.mapped != MagickFalse)
5964               (void) XWithdrawWindow(display,windows->info.id,
5965                 windows->info.screen);
5966           break;
5967         }
5968       }
5969       /*
5970         Wait for next event.
5971       */
5972       XScreenEvent(display,windows,&event);
5973       switch (element)
5974       {
5975         case PointElement:
5976         default:
5977         {
5978           if (number_coordinates > 1)
5979             (void) XDrawLines(display,windows->image.id,
5980               windows->image.highlight_context,coordinate_info,
5981               number_coordinates,CoordModeOrigin);
5982           break;
5983         }
5984         case LineElement:
5985         {
5986           if (distance > 9)
5987             XHighlightLine(display,windows->image.id,
5988               windows->image.highlight_context,&line_info);
5989           break;
5990         }
5991         case RectangleElement:
5992         case FillRectangleElement:
5993         {
5994           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5995             XHighlightRectangle(display,windows->image.id,
5996               windows->image.highlight_context,&rectangle_info);
5997           break;
5998         }
5999         case CircleElement:
6000         case FillCircleElement:
6001         case EllipseElement:
6002         case FillEllipseElement:
6003         {
6004           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6005             XHighlightEllipse(display,windows->image.id,
6006               windows->image.highlight_context,&rectangle_info);
6007           break;
6008         }
6009         case PolygonElement:
6010         case FillPolygonElement:
6011         {
6012           if (number_coordinates > 1)
6013             (void) XDrawLines(display,windows->image.id,
6014               windows->image.highlight_context,coordinate_info,
6015               number_coordinates,CoordModeOrigin);
6016           if (distance > 9)
6017             XHighlightLine(display,windows->image.id,
6018               windows->image.highlight_context,&line_info);
6019           break;
6020         }
6021       }
6022       switch (event.type)
6023       {
6024         case ButtonPress:
6025           break;
6026         case ButtonRelease:
6027         {
6028           /*
6029             User has committed to element.
6030           */
6031           line_info.x2=event.xbutton.x;
6032           line_info.y2=event.xbutton.y;
6033           rectangle_info.x=event.xbutton.x;
6034           rectangle_info.y=event.xbutton.y;
6035           coordinate_info[number_coordinates].x=event.xbutton.x;
6036           coordinate_info[number_coordinates].y=event.xbutton.y;
6037           if (((element != PolygonElement) &&
6038                (element != FillPolygonElement)) || (distance <= 9))
6039             {
6040               state|=ExitState;
6041               break;
6042             }
6043           number_coordinates++;
6044           if (number_coordinates < (int) max_coordinates)
6045             {
6046               line_info.x1=event.xbutton.x;
6047               line_info.y1=event.xbutton.y;
6048               break;
6049             }
6050           max_coordinates<<=1;
6051           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6052             max_coordinates,sizeof(*coordinate_info));
6053           if (coordinate_info == (XPoint *) NULL)
6054             (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6055               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6056           break;
6057         }
6058         case Expose:
6059           break;
6060         case MotionNotify:
6061         {
6062           if (event.xmotion.window != windows->image.id)
6063             break;
6064           if (element != PointElement)
6065             {
6066               line_info.x2=event.xmotion.x;
6067               line_info.y2=event.xmotion.y;
6068               rectangle_info.x=event.xmotion.x;
6069               rectangle_info.y=event.xmotion.y;
6070               break;
6071             }
6072           coordinate_info[number_coordinates].x=event.xbutton.x;
6073           coordinate_info[number_coordinates].y=event.xbutton.y;
6074           number_coordinates++;
6075           if (number_coordinates < (int) max_coordinates)
6076             break;
6077           max_coordinates<<=1;
6078           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6079             max_coordinates,sizeof(*coordinate_info));
6080           if (coordinate_info == (XPoint *) NULL)
6081             (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6082               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6083           break;
6084         }
6085         default:
6086           break;
6087       }
6088       /*
6089         Check boundary conditions.
6090       */
6091       if (line_info.x2 < 0)
6092         line_info.x2=0;
6093       else
6094         if (line_info.x2 > (int) windows->image.width)
6095           line_info.x2=(short) windows->image.width;
6096       if (line_info.y2 < 0)
6097         line_info.y2=0;
6098       else
6099         if (line_info.y2 > (int) windows->image.height)
6100           line_info.y2=(short) windows->image.height;
6101       distance=(unsigned int)
6102         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6103          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6104       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6105           ((state & ExitState) != 0))
6106         {
6107           if (rectangle_info.x < 0)
6108             rectangle_info.x=0;
6109           else
6110             if (rectangle_info.x > (int) windows->image.width)
6111               rectangle_info.x=(long) windows->image.width;
6112           if ((int) rectangle_info.x < x)
6113             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6114           else
6115             {
6116               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6117               rectangle_info.x=x;
6118             }
6119           if (rectangle_info.y < 0)
6120             rectangle_info.y=0;
6121           else
6122             if (rectangle_info.y > (int) windows->image.height)
6123               rectangle_info.y=(long) windows->image.height;
6124           if ((int) rectangle_info.y < y)
6125             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6126           else
6127             {
6128               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6129               rectangle_info.y=y;
6130             }
6131         }
6132     } while ((state & ExitState) == 0);
6133     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6134     if ((element == PointElement) || (element == PolygonElement) ||
6135         (element == FillPolygonElement))
6136       {
6137         /*
6138           Determine polygon bounding box.
6139         */
6140         rectangle_info.x=coordinate_info->x;
6141         rectangle_info.y=coordinate_info->y;
6142         x=coordinate_info->x;
6143         y=coordinate_info->y;
6144         for (i=1; i < number_coordinates; i++)
6145         {
6146           if (coordinate_info[i].x > x)
6147             x=coordinate_info[i].x;
6148           if (coordinate_info[i].y > y)
6149             y=coordinate_info[i].y;
6150           if (coordinate_info[i].x < rectangle_info.x)
6151             rectangle_info.x=MagickMax(coordinate_info[i].x,0);
6152           if (coordinate_info[i].y < rectangle_info.y)
6153             rectangle_info.y=MagickMax(coordinate_info[i].y,0);
6154         }
6155         rectangle_info.width=(unsigned long) (x-rectangle_info.x);
6156         rectangle_info.height=(unsigned long) (y-rectangle_info.y);
6157         for (i=0; i < number_coordinates; i++)
6158         {
6159           coordinate_info[i].x-=rectangle_info.x;
6160           coordinate_info[i].y-=rectangle_info.y;
6161         }
6162       }
6163     else
6164       if (distance <= 9)
6165         continue;
6166       else
6167         if ((element == RectangleElement) ||
6168             (element == CircleElement) || (element == EllipseElement))
6169           {
6170             rectangle_info.width--;
6171             rectangle_info.height--;
6172           }
6173     /*
6174       Drawing is relative to image configuration.
6175     */
6176     draw_info.x=(int) rectangle_info.x;
6177     draw_info.y=(int) rectangle_info.y;
6178     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6179       image);
6180     width=(unsigned int) (*image)->columns;
6181     height=(unsigned int) (*image)->rows;
6182     x=0;
6183     y=0;
6184     if (windows->image.crop_geometry != (char *) NULL)
6185       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6186     draw_info.x+=windows->image.x-(line_width/2);
6187     if (draw_info.x < 0)
6188       draw_info.x=0;
6189     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6190     draw_info.y+=windows->image.y-(line_width/2);
6191     if (draw_info.y < 0)
6192       draw_info.y=0;
6193     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6194     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6195     if (draw_info.width > (unsigned int) (*image)->columns)
6196       draw_info.width=(unsigned int) (*image)->columns;
6197     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6198     if (draw_info.height > (unsigned int) (*image)->rows)
6199       draw_info.height=(unsigned int) (*image)->rows;
6200     (void) FormatMagickString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6201       width*draw_info.width/windows->image.ximage->width,
6202       height*draw_info.height/windows->image.ximage->height,
6203       draw_info.x+x,draw_info.y+y);
6204     /*
6205       Initialize drawing attributes.
6206     */
6207     draw_info.degrees=0.0;
6208     draw_info.element=element;
6209     draw_info.stipple=stipple;
6210     draw_info.line_width=line_width;
6211     draw_info.line_info=line_info;
6212     if (line_info.x1 > (int) (line_width/2))
6213       draw_info.line_info.x1=(short) line_width/2;
6214     if (line_info.y1 > (int) (line_width/2))
6215       draw_info.line_info.y1=(short) line_width/2;
6216     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6217     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6218     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6219       {
6220         draw_info.line_info.x2=(-draw_info.line_info.x2);
6221         draw_info.line_info.y2=(-draw_info.line_info.y2);
6222       }
6223     if (draw_info.line_info.x2 < 0)
6224       {
6225         draw_info.line_info.x2=(-draw_info.line_info.x2);
6226         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6227       }
6228     if (draw_info.line_info.y2 < 0)
6229       {
6230         draw_info.line_info.y2=(-draw_info.line_info.y2);
6231         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6232       }
6233     draw_info.rectangle_info=rectangle_info;
6234     if (draw_info.rectangle_info.x > (int) (line_width/2))
6235       draw_info.rectangle_info.x=(long) line_width/2;
6236     if (draw_info.rectangle_info.y > (int) (line_width/2))
6237       draw_info.rectangle_info.y=(long) line_width/2;
6238     draw_info.number_coordinates=(unsigned int) number_coordinates;
6239     draw_info.coordinate_info=coordinate_info;
6240     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6241     /*
6242       Draw element on image.
6243     */
6244     XSetCursorState(display,windows,MagickTrue);
6245     XCheckRefreshWindows(display,windows);
6246     status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6247     XSetCursorState(display,windows,MagickFalse);
6248     /*
6249       Update image colormap and return to image drawing.
6250     */
6251     XConfigureImageColormap(display,resource_info,windows,*image);
6252     (void) XConfigureImage(display,resource_info,windows,*image);
6253   }
6254   XSetCursorState(display,windows,MagickFalse);
6255   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6256   return(status != 0 ? MagickTrue : MagickFalse);
6257 }
6258 \f
6259 /*
6260 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6261 %                                                                             %
6262 %                                                                             %
6263 %                                                                             %
6264 +   X D r a w P a n R e c t a n g l e                                         %
6265 %                                                                             %
6266 %                                                                             %
6267 %                                                                             %
6268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6269 %
6270 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6271 %  displays a zoom image and the rectangle shows which portion of the image is
6272 %  displayed in the Image window.
6273 %
6274 %  The format of the XDrawPanRectangle method is:
6275 %
6276 %      XDrawPanRectangle(Display *display,XWindows *windows)
6277 %
6278 %  A description of each parameter follows:
6279 %
6280 %    o display: Specifies a connection to an X server;  returned from
6281 %      XOpenDisplay.
6282 %
6283 %    o windows: Specifies a pointer to a XWindows structure.
6284 %
6285 */
6286 static void XDrawPanRectangle(Display *display,XWindows *windows)
6287 {
6288   MagickRealType
6289     scale_factor;
6290
6291   RectangleInfo
6292     highlight_info;
6293
6294   /*
6295     Determine dimensions of the panning rectangle.
6296   */
6297   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6298   highlight_info.x=(int) (scale_factor*windows->image.x+0.5);
6299   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6300   scale_factor=(MagickRealType)
6301     windows->pan.height/windows->image.ximage->height;
6302   highlight_info.y=(int) (scale_factor*windows->image.y+0.5);
6303   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6304   /*
6305     Display the panning rectangle.
6306   */
6307   (void) XClearWindow(display,windows->pan.id);
6308   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6309     &highlight_info);
6310 }
6311 \f
6312 /*
6313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6314 %                                                                             %
6315 %                                                                             %
6316 %                                                                             %
6317 +   X I m a g e C a c h e                                                     %
6318 %                                                                             %
6319 %                                                                             %
6320 %                                                                             %
6321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6322 %
6323 %  XImageCache() handles the creation, manipulation, and destruction of the
6324 %  image cache (undo and redo buffers).
6325 %
6326 %  The format of the XImageCache method is:
6327 %
6328 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6329 %        XWindows *windows,const CommandType command,Image **image)
6330 %
6331 %  A description of each parameter follows:
6332 %
6333 %    o display: Specifies a connection to an X server; returned from
6334 %      XOpenDisplay.
6335 %
6336 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6337 %
6338 %    o windows: Specifies a pointer to a XWindows structure.
6339 %
6340 %    o command: Specifies a command to perform.
6341 %
6342 %    o image: the image;  XImageCache
6343 %      may transform the image and return a new image pointer.
6344 %
6345 */
6346 static void XImageCache(Display *display,XResourceInfo *resource_info,
6347   XWindows *windows,const CommandType command,Image **image)
6348 {
6349   Image
6350     *cache_image;
6351
6352   static Image
6353     *redo_image = (Image *) NULL,
6354     *undo_image = (Image *) NULL;
6355
6356   switch (command)
6357   {
6358     case FreeBuffersCommand:
6359     {
6360       /*
6361         Free memory from the undo and redo cache.
6362       */
6363       while (undo_image != (Image *) NULL)
6364       {
6365         cache_image=undo_image;
6366         undo_image=GetPreviousImageInList(undo_image);
6367         cache_image->list=DestroyImage(cache_image->list);
6368         cache_image=DestroyImage(cache_image);
6369       }
6370       undo_image=NewImageList();
6371       if (redo_image != (Image *) NULL)
6372         redo_image=DestroyImage(redo_image);
6373       redo_image=NewImageList();
6374       return;
6375     }
6376     case UndoCommand:
6377     {
6378       /*
6379         Undo the last image transformation.
6380       */
6381       if (undo_image == (Image *) NULL)
6382         {
6383           (void) XBell(display,0);
6384           return;
6385         }
6386       cache_image=undo_image;
6387       undo_image=GetPreviousImageInList(undo_image);
6388       windows->image.window_changes.width=(int) cache_image->columns;
6389       windows->image.window_changes.height=(int) cache_image->rows;
6390       if (windows->image.crop_geometry != (char *) NULL)
6391         windows->image.crop_geometry=(char *)
6392           RelinquishMagickMemory(windows->image.crop_geometry);
6393       windows->image.crop_geometry=cache_image->geometry;
6394       if (redo_image != (Image *) NULL)
6395         redo_image=DestroyImage(redo_image);
6396       redo_image=(*image);
6397       *image=cache_image->list;
6398       cache_image=DestroyImage(cache_image);
6399       if (windows->image.orphan != MagickFalse)
6400         return;
6401       XConfigureImageColormap(display,resource_info,windows,*image);
6402       (void) XConfigureImage(display,resource_info,windows,*image);
6403       return;
6404     }
6405     case CutCommand:
6406     case PasteCommand:
6407     case ApplyCommand:
6408     case HalfSizeCommand:
6409     case OriginalSizeCommand:
6410     case DoubleSizeCommand:
6411     case ResizeCommand:
6412     case TrimCommand:
6413     case CropCommand:
6414     case ChopCommand:
6415     case FlipCommand:
6416     case FlopCommand:
6417     case RotateRightCommand:
6418     case RotateLeftCommand:
6419     case RotateCommand:
6420     case ShearCommand:
6421     case RollCommand:
6422     case NegateCommand:
6423     case ContrastStretchCommand:
6424     case SigmoidalContrastCommand:
6425     case NormalizeCommand:
6426     case EqualizeCommand:
6427     case HueCommand:
6428     case SaturationCommand:
6429     case BrightnessCommand:
6430     case GammaCommand:
6431     case SpiffCommand:
6432     case DullCommand:
6433     case GrayscaleCommand:
6434     case MapCommand:
6435     case QuantizeCommand:
6436     case DespeckleCommand:
6437     case EmbossCommand:
6438     case ReduceNoiseCommand:
6439     case AddNoiseCommand:
6440     case SharpenCommand:
6441     case BlurCommand:
6442     case ThresholdCommand:
6443     case EdgeDetectCommand:
6444     case SpreadCommand:
6445     case ShadeCommand:
6446     case RaiseCommand:
6447     case SegmentCommand:
6448     case SolarizeCommand:
6449     case SepiaToneCommand:
6450     case SwirlCommand:
6451     case ImplodeCommand:
6452     case VignetteCommand:
6453     case WaveCommand:
6454     case OilPaintCommand:
6455     case CharcoalDrawCommand:
6456     case AnnotateCommand:
6457     case AddBorderCommand:
6458     case AddFrameCommand:
6459     case CompositeCommand:
6460     case CommentCommand:
6461     case LaunchCommand:
6462     case RegionofInterestCommand:
6463     case SaveToUndoBufferCommand:
6464     case RedoCommand:
6465     {
6466       Image
6467         *previous_image;
6468
6469       long
6470         bytes;
6471
6472       bytes=(long) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6473       if (undo_image != (Image *) NULL)
6474         {
6475           /*
6476             Ensure the undo stash.has enough memory available.
6477           */
6478           previous_image=undo_image;
6479           while (previous_image != (Image *) NULL)
6480           {
6481             bytes+=previous_image->list->columns*previous_image->list->rows*
6482               sizeof(PixelPacket);
6483             if (bytes <= (long) (resource_info->undo_cache << 20))
6484               {
6485                 previous_image=GetPreviousImageInList(previous_image);
6486                 continue;
6487               }
6488             bytes-=previous_image->list->columns*previous_image->list->rows*
6489               sizeof(PixelPacket);
6490             if (previous_image == undo_image)
6491               undo_image=NewImageList();
6492             else
6493               previous_image->next->previous=NewImageList();
6494             break;
6495           }
6496           while (previous_image != (Image *) NULL)
6497           {
6498             /*
6499               Delete any excess memory from undo cache.
6500             */
6501             cache_image=previous_image;
6502             previous_image=GetPreviousImageInList(previous_image);
6503             cache_image->list=DestroyImage(cache_image->list);
6504             cache_image=DestroyImage(cache_image);
6505           }
6506         }
6507       if (bytes > (long) (resource_info->undo_cache << 20))
6508         break;
6509       /*
6510         Save image before transformations are applied.
6511       */
6512       cache_image=AcquireImage((ImageInfo *) NULL);
6513       if (cache_image == (Image *) NULL)
6514         break;
6515       XSetCursorState(display,windows,MagickTrue);
6516       XCheckRefreshWindows(display,windows);
6517       cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6518       XSetCursorState(display,windows,MagickFalse);
6519       if (cache_image->list == (Image *) NULL)
6520         {
6521           cache_image=DestroyImage(cache_image);
6522           break;
6523         }
6524       cache_image->columns=(unsigned long) windows->image.ximage->width;
6525       cache_image->rows=(unsigned long) windows->image.ximage->height;
6526       cache_image->geometry=windows->image.crop_geometry;
6527       if (windows->image.crop_geometry != (char *) NULL)
6528         {
6529           cache_image->geometry=AcquireString((char *) NULL);
6530           (void) CopyMagickString(cache_image->geometry,
6531             windows->image.crop_geometry,MaxTextExtent);
6532         }
6533       if (undo_image == (Image *) NULL)
6534         {
6535           undo_image=cache_image;
6536           break;
6537         }
6538       undo_image->next=cache_image;
6539       undo_image->next->previous=undo_image;
6540       undo_image=undo_image->next;
6541       break;
6542     }
6543     default:
6544       break;
6545   }
6546   if (command == RedoCommand)
6547     {
6548       /*
6549         Redo the last image transformation.
6550       */
6551       if (redo_image == (Image *) NULL)
6552         {
6553           (void) XBell(display,0);
6554           return;
6555         }
6556       windows->image.window_changes.width=(int) redo_image->columns;
6557       windows->image.window_changes.height=(int) redo_image->rows;
6558       if (windows->image.crop_geometry != (char *) NULL)
6559         windows->image.crop_geometry=(char *)
6560           RelinquishMagickMemory(windows->image.crop_geometry);
6561       windows->image.crop_geometry=redo_image->geometry;
6562       *image=DestroyImage(*image);
6563       *image=redo_image;
6564       redo_image=NewImageList();
6565       if (windows->image.orphan != MagickFalse)
6566         return;
6567       XConfigureImageColormap(display,resource_info,windows,*image);
6568       (void) XConfigureImage(display,resource_info,windows,*image);
6569       return;
6570     }
6571   if (command != InfoCommand)
6572     return;
6573   /*
6574     Display image info.
6575   */
6576   XSetCursorState(display,windows,MagickTrue);
6577   XCheckRefreshWindows(display,windows);
6578   XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6579   XSetCursorState(display,windows,MagickFalse);
6580 }
6581 \f
6582 /*
6583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6584 %                                                                             %
6585 %                                                                             %
6586 %                                                                             %
6587 +   X I m a g e W i n d o w C o m m a n d                                     %
6588 %                                                                             %
6589 %                                                                             %
6590 %                                                                             %
6591 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6592 %
6593 %  XImageWindowCommand() makes a transform to the image or Image window as
6594 %  specified by a user menu button or keyboard command.
6595 %
6596 %  The format of the XMagickCommand method is:
6597 %
6598 %      CommandType XImageWindowCommand(Display *display,
6599 %        XResourceInfo *resource_info,XWindows *windows,
6600 %        const MagickStatusType state,KeySym key_symbol,Image **image)
6601 %
6602 %  A description of each parameter follows:
6603 %
6604 %    o nexus:  Method XImageWindowCommand returns an image when the
6605 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6606 %      image is returned.
6607 %
6608 %    o display: Specifies a connection to an X server; returned from
6609 %      XOpenDisplay.
6610 %
6611 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6612 %
6613 %    o windows: Specifies a pointer to a XWindows structure.
6614 %
6615 %    o state: key mask.
6616 %
6617 %    o key_symbol: Specifies a command to perform.
6618 %
6619 %    o image: the image;  XImageWIndowCommand
6620 %      may transform the image and return a new image pointer.
6621 %
6622 */
6623 static CommandType XImageWindowCommand(Display *display,
6624   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6625   KeySym key_symbol,Image **image)
6626 {
6627   static char
6628     delta[MaxTextExtent] = "";
6629
6630   static const char
6631     Digits[] = "01234567890";
6632
6633   static KeySym
6634     last_symbol = XK_0;
6635
6636   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6637     {
6638       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6639         {
6640           *delta='\0';
6641           resource_info->quantum=1;
6642         }
6643       last_symbol=key_symbol;
6644       delta[strlen(delta)+1]='\0';
6645       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6646       resource_info->quantum=StringToLong(delta);
6647       return(NullCommand);
6648     }
6649   last_symbol=key_symbol;
6650   if (resource_info->immutable)
6651     {
6652       /*
6653         Virtual image window has a restricted command set.
6654       */
6655       switch (key_symbol)
6656       {
6657         case XK_question:
6658           return(InfoCommand);
6659         case XK_p:
6660         case XK_Print:
6661           return(PrintCommand);
6662         case XK_space:
6663           return(NextCommand);
6664         case XK_q:
6665         case XK_Escape:
6666           return(QuitCommand);
6667         default:
6668           break;
6669       }
6670       return(NullCommand);
6671     }
6672   switch ((int) key_symbol)
6673   {
6674     case XK_o:
6675     {
6676       if ((state & ControlMask) == 0)
6677         break;
6678       return(OpenCommand);
6679     }
6680     case XK_space:
6681       return(NextCommand);
6682     case XK_BackSpace:
6683       return(FormerCommand);
6684     case XK_s:
6685     {
6686       if ((state & Mod1Mask) != 0)
6687         return(SwirlCommand);
6688       if ((state & ControlMask) == 0)
6689         return(ShearCommand);
6690       return(SaveCommand);
6691     }
6692     case XK_p:
6693     case XK_Print:
6694     {
6695       if ((state & Mod1Mask) != 0)
6696         return(OilPaintCommand);
6697       if ((state & Mod4Mask) != 0)
6698         return(ColorCommand);
6699       if ((state & ControlMask) == 0)
6700         return(NullCommand);
6701       return(PrintCommand);
6702     }
6703     case XK_d:
6704     {
6705       if ((state & Mod4Mask) != 0)
6706         return(DrawCommand);
6707       if ((state & ControlMask) == 0)
6708         return(NullCommand);
6709       return(DeleteCommand);
6710     }
6711     case XK_Select:
6712     {
6713       if ((state & ControlMask) == 0)
6714         return(NullCommand);
6715       return(SelectCommand);
6716     }
6717     case XK_n:
6718     {
6719       if ((state & ControlMask) == 0)
6720         return(NullCommand);
6721       return(NewCommand);
6722     }
6723     case XK_q:
6724     case XK_Escape:
6725       return(QuitCommand);
6726     case XK_z:
6727     case XK_Undo:
6728     {
6729       if ((state & ControlMask) == 0)
6730         return(NullCommand);
6731       return(UndoCommand);
6732     }
6733     case XK_r:
6734     case XK_Redo:
6735     {
6736       if ((state & ControlMask) == 0)
6737         return(RollCommand);
6738       return(RedoCommand);
6739     }
6740     case XK_x:
6741     {
6742       if ((state & ControlMask) == 0)
6743         return(NullCommand);
6744       return(CutCommand);
6745     }
6746     case XK_c:
6747     {
6748       if ((state & Mod1Mask) != 0)
6749         return(CharcoalDrawCommand);
6750       if ((state & ControlMask) == 0)
6751         return(CropCommand);
6752       return(CopyCommand);
6753     }
6754     case XK_v:
6755     case XK_Insert:
6756     {
6757       if ((state & Mod4Mask) != 0)
6758         return(CompositeCommand);
6759       if ((state & ControlMask) == 0)
6760         return(FlipCommand);
6761       return(PasteCommand);
6762     }
6763     case XK_less:
6764       return(HalfSizeCommand);
6765     case XK_minus:
6766       return(OriginalSizeCommand);
6767     case XK_greater:
6768       return(DoubleSizeCommand);
6769     case XK_percent:
6770       return(ResizeCommand);
6771     case XK_at:
6772       return(RefreshCommand);
6773     case XK_bracketleft:
6774       return(ChopCommand);
6775     case XK_h:
6776       return(FlopCommand);
6777     case XK_slash:
6778       return(RotateRightCommand);
6779     case XK_backslash:
6780       return(RotateLeftCommand);
6781     case XK_asterisk:
6782       return(RotateCommand);
6783     case XK_t:
6784       return(TrimCommand);
6785     case XK_H:
6786       return(HueCommand);
6787     case XK_S:
6788       return(SaturationCommand);
6789     case XK_L:
6790       return(BrightnessCommand);
6791     case XK_G:
6792       return(GammaCommand);
6793     case XK_C:
6794       return(SpiffCommand);
6795     case XK_Z:
6796       return(DullCommand);
6797     case XK_N:
6798       return(NormalizeCommand);
6799     case XK_equal:
6800       return(EqualizeCommand);
6801     case XK_asciitilde:
6802       return(NegateCommand);
6803     case XK_period:
6804       return(GrayscaleCommand);
6805     case XK_numbersign:
6806       return(QuantizeCommand);
6807     case XK_F2:
6808       return(DespeckleCommand);
6809     case XK_F3:
6810       return(EmbossCommand);
6811     case XK_F4:
6812       return(ReduceNoiseCommand);
6813     case XK_F5:
6814       return(AddNoiseCommand);
6815     case XK_F6:
6816       return(SharpenCommand);
6817     case XK_F7:
6818       return(BlurCommand);
6819     case XK_F8:
6820       return(ThresholdCommand);
6821     case XK_F9:
6822       return(EdgeDetectCommand);
6823     case XK_F10:
6824       return(SpreadCommand);
6825     case XK_F11:
6826       return(ShadeCommand);
6827     case XK_F12:
6828       return(RaiseCommand);
6829     case XK_F13:
6830       return(SegmentCommand);
6831     case XK_i:
6832     {
6833       if ((state & Mod1Mask) == 0)
6834         return(NullCommand);
6835       return(ImplodeCommand);
6836     }
6837     case XK_w:
6838     {
6839       if ((state & Mod1Mask) == 0)
6840         return(NullCommand);
6841       return(WaveCommand);
6842     }
6843     case XK_m:
6844     {
6845       if ((state & Mod4Mask) == 0)
6846         return(NullCommand);
6847       return(MatteCommand);
6848     }
6849     case XK_b:
6850     {
6851       if ((state & Mod4Mask) == 0)
6852         return(NullCommand);
6853       return(AddBorderCommand);
6854     }
6855     case XK_f:
6856     {
6857       if ((state & Mod4Mask) == 0)
6858         return(NullCommand);
6859       return(AddFrameCommand);
6860     }
6861     case XK_exclam:
6862     {
6863       if ((state & Mod4Mask) == 0)
6864         return(NullCommand);
6865       return(CommentCommand);
6866     }
6867     case XK_a:
6868     {
6869       if ((state & Mod1Mask) != 0)
6870         return(ApplyCommand);
6871       if ((state & Mod4Mask) != 0)
6872         return(AnnotateCommand);
6873       if ((state & ControlMask) == 0)
6874         return(NullCommand);
6875       return(RegionofInterestCommand);
6876     }
6877     case XK_question:
6878       return(InfoCommand);
6879     case XK_plus:
6880       return(ZoomCommand);
6881     case XK_P:
6882     {
6883       if ((state & ShiftMask) == 0)
6884         return(NullCommand);
6885       return(ShowPreviewCommand);
6886     }
6887     case XK_Execute:
6888       return(LaunchCommand);
6889     case XK_F1:
6890       return(HelpCommand);
6891     case XK_Find:
6892       return(BrowseDocumentationCommand);
6893     case XK_Menu:
6894     {
6895       (void) XMapRaised(display,windows->command.id);
6896       return(NullCommand);
6897     }
6898     case XK_Next:
6899     case XK_Prior:
6900     case XK_Home:
6901     case XK_KP_Home:
6902     {
6903       XTranslateImage(display,windows,*image,key_symbol);
6904       return(NullCommand);
6905     }
6906     case XK_Up:
6907     case XK_KP_Up:
6908     case XK_Down:
6909     case XK_KP_Down:
6910     case XK_Left:
6911     case XK_KP_Left:
6912     case XK_Right:
6913     case XK_KP_Right:
6914     {
6915       if ((state & Mod1Mask) != 0)
6916         {
6917           RectangleInfo
6918             crop_info;
6919
6920           /*
6921             Trim one pixel from edge of image.
6922           */
6923           crop_info.x=0;
6924           crop_info.y=0;
6925           crop_info.width=(unsigned long) windows->image.ximage->width;
6926           crop_info.height=(unsigned long) windows->image.ximage->height;
6927           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6928             {
6929               if (resource_info->quantum >= (int) crop_info.height)
6930                 resource_info->quantum=(int) crop_info.height-1;
6931               crop_info.height-=resource_info->quantum;
6932             }
6933           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6934             {
6935               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6936                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6937               crop_info.y+=resource_info->quantum;
6938               crop_info.height-=resource_info->quantum;
6939             }
6940           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6941             {
6942               if (resource_info->quantum >= (int) crop_info.width)
6943                 resource_info->quantum=(int) crop_info.width-1;
6944               crop_info.width-=resource_info->quantum;
6945             }
6946           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6947             {
6948               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
6949                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
6950               crop_info.x+=resource_info->quantum;
6951               crop_info.width-=resource_info->quantum;
6952             }
6953           if ((int) (windows->image.x+windows->image.width) >
6954               (int) crop_info.width)
6955             windows->image.x=(int) (crop_info.width-windows->image.width);
6956           if ((int) (windows->image.y+windows->image.height) >
6957               (int) crop_info.height)
6958             windows->image.y=(int) (crop_info.height-windows->image.height);
6959           XSetCropGeometry(display,windows,&crop_info,*image);
6960           windows->image.window_changes.width=(int) crop_info.width;
6961           windows->image.window_changes.height=(int) crop_info.height;
6962           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
6963           (void) XConfigureImage(display,resource_info,windows,*image);
6964           return(NullCommand);
6965         }
6966       XTranslateImage(display,windows,*image,key_symbol);
6967       return(NullCommand);
6968     }
6969     default:
6970       return(NullCommand);
6971   }
6972   return(NullCommand);
6973 }
6974 \f
6975 /*
6976 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6977 %                                                                             %
6978 %                                                                             %
6979 %                                                                             %
6980 +   X M a g i c k C o m m a n d                                               %
6981 %                                                                             %
6982 %                                                                             %
6983 %                                                                             %
6984 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6985 %
6986 %  XMagickCommand() makes a transform to the image or Image window as
6987 %  specified by a user menu button or keyboard command.
6988 %
6989 %  The format of the XMagickCommand method is:
6990 %
6991 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
6992 %        XWindows *windows,const CommandType command,Image **image)
6993 %
6994 %  A description of each parameter follows:
6995 %
6996 %    o nexus:  Method XMagickCommand returns an image when the
6997 %      user chooses 'Load Image' from the command menu.  Otherwise a null
6998 %      image is returned.
6999 %
7000 %    o display: Specifies a connection to an X server; returned from
7001 %      XOpenDisplay.
7002 %
7003 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7004 %
7005 %    o windows: Specifies a pointer to a XWindows structure.
7006 %
7007 %    o command: Specifies a command to perform.
7008 %
7009 %    o image: the image;  XMagickCommand
7010 %      may transform the image and return a new image pointer.
7011 %
7012 */
7013 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7014   XWindows *windows,const CommandType command,Image **image)
7015 {
7016   char
7017     filename[MaxTextExtent],
7018     geometry[MaxTextExtent],
7019     modulate_factors[MaxTextExtent];
7020
7021   GeometryInfo
7022     geometry_info;
7023
7024   Image
7025     *nexus;
7026
7027   ImageInfo
7028     *image_info;
7029
7030   int
7031     x,
7032     y;
7033
7034   MagickStatusType
7035     flags,
7036     status;
7037
7038   QuantizeInfo
7039     quantize_info;
7040
7041   RectangleInfo
7042     page_geometry;
7043
7044   register int
7045     i;
7046
7047   static char
7048     color[MaxTextExtent] = "gray";
7049
7050   unsigned int
7051     height,
7052     width;
7053
7054   /*
7055     Process user command.
7056   */
7057   XCheckRefreshWindows(display,windows);
7058   XImageCache(display,resource_info,windows,command,image);
7059   nexus=NewImageList();
7060   windows->image.window_changes.width=windows->image.ximage->width;
7061   windows->image.window_changes.height=windows->image.ximage->height;
7062   image_info=CloneImageInfo(resource_info->image_info);
7063   SetGeometryInfo(&geometry_info);
7064   GetQuantizeInfo(&quantize_info);
7065   switch (command)
7066   {
7067     case OpenCommand:
7068     {
7069       /*
7070         Load image.
7071       */
7072       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7073       break;
7074     }
7075     case NextCommand:
7076     {
7077       /*
7078         Display next image.
7079       */
7080       for (i=0; i < resource_info->quantum; i++)
7081         XClientMessage(display,windows->image.id,windows->im_protocols,
7082           windows->im_next_image,CurrentTime);
7083       break;
7084     }
7085     case FormerCommand:
7086     {
7087       /*
7088         Display former image.
7089       */
7090       for (i=0; i < resource_info->quantum; i++)
7091         XClientMessage(display,windows->image.id,windows->im_protocols,
7092           windows->im_former_image,CurrentTime);
7093       break;
7094     }
7095     case SelectCommand:
7096     {
7097       int
7098         status;
7099
7100       /*
7101         Select image.
7102       */
7103       status=chdir(resource_info->home_directory);
7104       if (status == -1)
7105         (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7106           FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7107       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7108       break;
7109     }
7110     case SaveCommand:
7111     {
7112       /*
7113         Save image.
7114       */
7115       status=XSaveImage(display,resource_info,windows,*image);
7116       if (status == MagickFalse)
7117         {
7118           XNoticeWidget(display,windows,"Unable to write X image:",
7119             (*image)->filename);
7120           break;
7121         }
7122       break;
7123     }
7124     case PrintCommand:
7125     {
7126       /*
7127         Print image.
7128       */
7129       status=XPrintImage(display,resource_info,windows,*image);
7130       if (status == MagickFalse)
7131         {
7132           XNoticeWidget(display,windows,"Unable to print X image:",
7133             (*image)->filename);
7134           break;
7135         }
7136       break;
7137     }
7138     case DeleteCommand:
7139     {
7140       static char
7141         filename[MaxTextExtent] = "\0";
7142
7143       /*
7144         Delete image file.
7145       */
7146       XFileBrowserWidget(display,windows,"Delete",filename);
7147       if (*filename == '\0')
7148         break;
7149       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
7150       if (status != MagickFalse)
7151         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7152       break;
7153     }
7154     case NewCommand:
7155     {
7156       int
7157         status;
7158
7159       static char
7160         color[MaxTextExtent] = "gray",
7161         geometry[MaxTextExtent] = "640x480";
7162
7163       static const char
7164         *format = "gradient";
7165
7166       /*
7167         Query user for canvas geometry.
7168       */
7169       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7170         geometry);
7171       if (*geometry == '\0')
7172         break;
7173       if (status == 0)
7174         format="xc";
7175       XColorBrowserWidget(display,windows,"Select",color);
7176       if (*color == '\0')
7177         break;
7178       /*
7179         Create canvas.
7180       */
7181       (void) FormatMagickString(image_info->filename,MaxTextExtent,
7182         "%s:%s",format,color);
7183       (void) CloneString(&image_info->size,geometry);
7184       nexus=ReadImage(image_info,&(*image)->exception);
7185       CatchException(&(*image)->exception);
7186       XClientMessage(display,windows->image.id,windows->im_protocols,
7187         windows->im_next_image,CurrentTime);
7188       break;
7189     }
7190     case VisualDirectoryCommand:
7191     {
7192       /*
7193         Visual Image directory.
7194       */
7195       nexus=XVisualDirectoryImage(display,resource_info,windows);
7196       break;
7197     }
7198     case QuitCommand:
7199     {
7200       /*
7201         exit program.
7202       */
7203       if (resource_info->confirm_exit == MagickFalse)
7204         XClientMessage(display,windows->image.id,windows->im_protocols,
7205           windows->im_exit,CurrentTime);
7206       else
7207         {
7208           int
7209             status;
7210
7211           /*
7212             Confirm program exit.
7213           */
7214           status=XConfirmWidget(display,windows,"Do you really want to exit",
7215             resource_info->client_name);
7216           if (status > 0)
7217             XClientMessage(display,windows->image.id,windows->im_protocols,
7218               windows->im_exit,CurrentTime);
7219         }
7220       break;
7221     }
7222     case CutCommand:
7223     {
7224       /*
7225         Cut image.
7226       */
7227       (void) XCropImage(display,resource_info,windows,*image,CutMode);
7228       break;
7229     }
7230     case CopyCommand:
7231     {
7232       /*
7233         Copy image.
7234       */
7235       (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7236       break;
7237     }
7238     case PasteCommand:
7239     {
7240       /*
7241         Paste image.
7242       */
7243       status=XPasteImage(display,resource_info,windows,*image);
7244       if (status == MagickFalse)
7245         {
7246           XNoticeWidget(display,windows,"Unable to paste X image",
7247             (*image)->filename);
7248           break;
7249         }
7250       break;
7251     }
7252     case HalfSizeCommand:
7253     {
7254       /*
7255         Half image size.
7256       */
7257       windows->image.window_changes.width=windows->image.ximage->width/2;
7258       windows->image.window_changes.height=windows->image.ximage->height/2;
7259       (void) XConfigureImage(display,resource_info,windows,*image);
7260       break;
7261     }
7262     case OriginalSizeCommand:
7263     {
7264       /*
7265         Original image size.
7266       */
7267       windows->image.window_changes.width=(int) (*image)->columns;
7268       windows->image.window_changes.height=(int) (*image)->rows;
7269       (void) XConfigureImage(display,resource_info,windows,*image);
7270       break;
7271     }
7272     case DoubleSizeCommand:
7273     {
7274       /*
7275         Double the image size.
7276       */
7277       windows->image.window_changes.width=windows->image.ximage->width << 1;
7278       windows->image.window_changes.height=windows->image.ximage->height << 1;
7279       (void) XConfigureImage(display,resource_info,windows,*image);
7280       break;
7281     }
7282     case ResizeCommand:
7283     {
7284       int
7285         status;
7286
7287       long
7288         x,
7289         y;
7290
7291       unsigned long
7292         height,
7293         width;
7294
7295       /*
7296         Resize image.
7297       */
7298       width=(unsigned long) windows->image.ximage->width;
7299       height=(unsigned long) windows->image.ximage->height;
7300       x=0;
7301       y=0;
7302       (void) FormatMagickString(geometry,MaxTextExtent,"%lux%lu+0+0",
7303         width,height);
7304       status=XDialogWidget(display,windows,"Resize",
7305         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7306       if (*geometry == '\0')
7307         break;
7308       if (status == 0)
7309         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7310       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7311       windows->image.window_changes.width=(int) width;
7312       windows->image.window_changes.height=(int) height;
7313       (void) XConfigureImage(display,resource_info,windows,*image);
7314       break;
7315     }
7316     case ApplyCommand:
7317     {
7318       char
7319         image_geometry[MaxTextExtent];
7320
7321       if ((windows->image.crop_geometry == (char *) NULL) &&
7322           ((int) (*image)->columns == windows->image.ximage->width) &&
7323           ((int) (*image)->rows == windows->image.ximage->height))
7324         break;
7325       /*
7326         Apply size transforms to image.
7327       */
7328       XSetCursorState(display,windows,MagickTrue);
7329       XCheckRefreshWindows(display,windows);
7330       /*
7331         Crop and/or scale displayed image.
7332       */
7333       (void) FormatMagickString(image_geometry,MaxTextExtent,"%dx%d!",
7334         windows->image.ximage->width,windows->image.ximage->height);
7335       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7336       if (windows->image.crop_geometry != (char *) NULL)
7337         windows->image.crop_geometry=(char *)
7338           RelinquishMagickMemory(windows->image.crop_geometry);
7339       windows->image.x=0;
7340       windows->image.y=0;
7341       XConfigureImageColormap(display,resource_info,windows,*image);
7342       (void) XConfigureImage(display,resource_info,windows,*image);
7343       break;
7344     }
7345     case RefreshCommand:
7346     {
7347       (void) XConfigureImage(display,resource_info,windows,*image);
7348       break;
7349     }
7350     case RestoreCommand:
7351     {
7352       /*
7353         Restore Image window to its original size.
7354       */
7355       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7356           (windows->image.height == (unsigned int) (*image)->rows) &&
7357           (windows->image.crop_geometry == (char *) NULL))
7358         {
7359           (void) XBell(display,0);
7360           break;
7361         }
7362       windows->image.window_changes.width=(int) (*image)->columns;
7363       windows->image.window_changes.height=(int) (*image)->rows;
7364       if (windows->image.crop_geometry != (char *) NULL)
7365         {
7366           windows->image.crop_geometry=(char *)
7367             RelinquishMagickMemory(windows->image.crop_geometry);
7368           windows->image.crop_geometry=(char *) NULL;
7369           windows->image.x=0;
7370           windows->image.y=0;
7371         }
7372       XConfigureImageColormap(display,resource_info,windows,*image);
7373       (void) XConfigureImage(display,resource_info,windows,*image);
7374       break;
7375     }
7376     case CropCommand:
7377     {
7378       /*
7379         Crop image.
7380       */
7381       (void) XCropImage(display,resource_info,windows,*image,CropMode);
7382       break;
7383     }
7384     case ChopCommand:
7385     {
7386       /*
7387         Chop image.
7388       */
7389       status=XChopImage(display,resource_info,windows,image);
7390       if (status == MagickFalse)
7391         {
7392           XNoticeWidget(display,windows,"Unable to cut X image",
7393             (*image)->filename);
7394           break;
7395         }
7396       break;
7397     }
7398     case FlopCommand:
7399     {
7400       Image
7401         *flop_image;
7402
7403       /*
7404         Flop image scanlines.
7405       */
7406       XSetCursorState(display,windows,MagickTrue);
7407       XCheckRefreshWindows(display,windows);
7408       flop_image=FlopImage(*image,&(*image)->exception);
7409       if (flop_image != (Image *) NULL)
7410         {
7411           *image=DestroyImage(*image);
7412           *image=flop_image;
7413         }
7414       CatchException(&(*image)->exception);
7415       XSetCursorState(display,windows,MagickFalse);
7416       if (windows->image.crop_geometry != (char *) NULL)
7417         {
7418           /*
7419             Flop crop geometry.
7420           */
7421           width=(unsigned int) (*image)->columns;
7422           height=(unsigned int) (*image)->rows;
7423           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7424             &width,&height);
7425           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
7426             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7427         }
7428       if (windows->image.orphan != MagickFalse)
7429         break;
7430       (void) XConfigureImage(display,resource_info,windows,*image);
7431       break;
7432     }
7433     case FlipCommand:
7434     {
7435       Image
7436         *flip_image;
7437
7438       /*
7439         Flip image scanlines.
7440       */
7441       XSetCursorState(display,windows,MagickTrue);
7442       XCheckRefreshWindows(display,windows);
7443       flip_image=FlipImage(*image,&(*image)->exception);
7444       if (flip_image != (Image *) NULL)
7445         {
7446           *image=DestroyImage(*image);
7447           *image=flip_image;
7448         }
7449       CatchException(&(*image)->exception);
7450       XSetCursorState(display,windows,MagickFalse);
7451       if (windows->image.crop_geometry != (char *) NULL)
7452         {
7453           /*
7454             Flip crop geometry.
7455           */
7456           width=(unsigned int) (*image)->columns;
7457           height=(unsigned int) (*image)->rows;
7458           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7459             &width,&height);
7460           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
7461             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7462         }
7463       if (windows->image.orphan != MagickFalse)
7464         break;
7465       (void) XConfigureImage(display,resource_info,windows,*image);
7466       break;
7467     }
7468     case RotateRightCommand:
7469     {
7470       /*
7471         Rotate image 90 degrees clockwise.
7472       */
7473       status=XRotateImage(display,resource_info,windows,90.0,image);
7474       if (status == MagickFalse)
7475         {
7476           XNoticeWidget(display,windows,"Unable to rotate X image",
7477             (*image)->filename);
7478           break;
7479         }
7480       break;
7481     }
7482     case RotateLeftCommand:
7483     {
7484       /*
7485         Rotate image 90 degrees counter-clockwise.
7486       */
7487       status=XRotateImage(display,resource_info,windows,-90.0,image);
7488       if (status == MagickFalse)
7489         {
7490           XNoticeWidget(display,windows,"Unable to rotate X image",
7491             (*image)->filename);
7492           break;
7493         }
7494       break;
7495     }
7496     case RotateCommand:
7497     {
7498       /*
7499         Rotate image.
7500       */
7501       status=XRotateImage(display,resource_info,windows,0.0,image);
7502       if (status == MagickFalse)
7503         {
7504           XNoticeWidget(display,windows,"Unable to rotate X image",
7505             (*image)->filename);
7506           break;
7507         }
7508       break;
7509     }
7510     case ShearCommand:
7511     {
7512       Image
7513         *shear_image;
7514
7515       static char
7516         geometry[MaxTextExtent] = "45.0x45.0";
7517
7518       /*
7519         Query user for shear color and geometry.
7520       */
7521       XColorBrowserWidget(display,windows,"Select",color);
7522       if (*color == '\0')
7523         break;
7524       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7525         geometry);
7526       if (*geometry == '\0')
7527         break;
7528       /*
7529         Shear image.
7530       */
7531       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7532       XSetCursorState(display,windows,MagickTrue);
7533       XCheckRefreshWindows(display,windows);
7534       (void) QueryColorDatabase(color,&(*image)->background_color,
7535         &(*image)->exception);
7536       flags=ParseGeometry(geometry,&geometry_info);
7537       if ((flags & SigmaValue) == 0)
7538         geometry_info.sigma=geometry_info.rho;
7539       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7540         &(*image)->exception);
7541       if (shear_image != (Image *) NULL)
7542         {
7543           *image=DestroyImage(*image);
7544           *image=shear_image;
7545         }
7546       CatchException(&(*image)->exception);
7547       XSetCursorState(display,windows,MagickFalse);
7548       if (windows->image.orphan != MagickFalse)
7549         break;
7550       windows->image.window_changes.width=(int) (*image)->columns;
7551       windows->image.window_changes.height=(int) (*image)->rows;
7552       XConfigureImageColormap(display,resource_info,windows,*image);
7553       (void) XConfigureImage(display,resource_info,windows,*image);
7554       break;
7555     }
7556     case RollCommand:
7557     {
7558       Image
7559         *roll_image;
7560
7561       static char
7562         geometry[MaxTextExtent] = "+2+2";
7563
7564       /*
7565         Query user for the roll geometry.
7566       */
7567       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7568         geometry);
7569       if (*geometry == '\0')
7570         break;
7571       /*
7572         Roll image.
7573       */
7574       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7575       XSetCursorState(display,windows,MagickTrue);
7576       XCheckRefreshWindows(display,windows);
7577       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7578         &(*image)->exception);
7579       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7580         &(*image)->exception);
7581       if (roll_image != (Image *) NULL)
7582         {
7583           *image=DestroyImage(*image);
7584           *image=roll_image;
7585         }
7586       CatchException(&(*image)->exception);
7587       XSetCursorState(display,windows,MagickFalse);
7588       if (windows->image.orphan != MagickFalse)
7589         break;
7590       windows->image.window_changes.width=(int) (*image)->columns;
7591       windows->image.window_changes.height=(int) (*image)->rows;
7592       XConfigureImageColormap(display,resource_info,windows,*image);
7593       (void) XConfigureImage(display,resource_info,windows,*image);
7594       break;
7595     }
7596     case TrimCommand:
7597     {
7598       static char
7599         fuzz[MaxTextExtent];
7600
7601       /*
7602         Query user for the fuzz factor.
7603       */
7604       (void) FormatMagickString(fuzz,MaxTextExtent,"%g%%",100.0*
7605         (*image)->fuzz/(QuantumRange+1.0));
7606       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7607       if (*fuzz == '\0')
7608         break;
7609       (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7610       /*
7611         Trim image.
7612       */
7613       status=XTrimImage(display,resource_info,windows,*image);
7614       if (status == MagickFalse)
7615         {
7616           XNoticeWidget(display,windows,"Unable to trim X image",
7617             (*image)->filename);
7618           break;
7619         }
7620       break;
7621     }
7622     case HueCommand:
7623     {
7624       static char
7625         hue_percent[MaxTextExtent] = "110";
7626
7627       /*
7628         Query user for percent hue change.
7629       */
7630       (void) XDialogWidget(display,windows,"Apply",
7631         "Enter percent change in image hue (0-200):",hue_percent);
7632       if (*hue_percent == '\0')
7633         break;
7634       /*
7635         Vary the image hue.
7636       */
7637       XSetCursorState(display,windows,MagickTrue);
7638       XCheckRefreshWindows(display,windows);
7639       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7640       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7641         MaxTextExtent);
7642       (void) ModulateImage(*image,modulate_factors);
7643       XSetCursorState(display,windows,MagickFalse);
7644       if (windows->image.orphan != MagickFalse)
7645         break;
7646       XConfigureImageColormap(display,resource_info,windows,*image);
7647       (void) XConfigureImage(display,resource_info,windows,*image);
7648       break;
7649     }
7650     case SaturationCommand:
7651     {
7652       static char
7653         saturation_percent[MaxTextExtent] = "110";
7654
7655       /*
7656         Query user for percent saturation change.
7657       */
7658       (void) XDialogWidget(display,windows,"Apply",
7659         "Enter percent change in color saturation (0-200):",saturation_percent);
7660       if (*saturation_percent == '\0')
7661         break;
7662       /*
7663         Vary color saturation.
7664       */
7665       XSetCursorState(display,windows,MagickTrue);
7666       XCheckRefreshWindows(display,windows);
7667       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7668       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7669         MaxTextExtent);
7670       (void) ModulateImage(*image,modulate_factors);
7671       XSetCursorState(display,windows,MagickFalse);
7672       if (windows->image.orphan != MagickFalse)
7673         break;
7674       XConfigureImageColormap(display,resource_info,windows,*image);
7675       (void) XConfigureImage(display,resource_info,windows,*image);
7676       break;
7677     }
7678     case BrightnessCommand:
7679     {
7680       static char
7681         brightness_percent[MaxTextExtent] = "110";
7682
7683       /*
7684         Query user for percent brightness change.
7685       */
7686       (void) XDialogWidget(display,windows,"Apply",
7687         "Enter percent change in color brightness (0-200):",brightness_percent);
7688       if (*brightness_percent == '\0')
7689         break;
7690       /*
7691         Vary the color brightness.
7692       */
7693       XSetCursorState(display,windows,MagickTrue);
7694       XCheckRefreshWindows(display,windows);
7695       (void) CopyMagickString(modulate_factors,brightness_percent,
7696         MaxTextExtent);
7697       (void) ModulateImage(*image,modulate_factors);
7698       XSetCursorState(display,windows,MagickFalse);
7699       if (windows->image.orphan != MagickFalse)
7700         break;
7701       XConfigureImageColormap(display,resource_info,windows,*image);
7702       (void) XConfigureImage(display,resource_info,windows,*image);
7703       break;
7704     }
7705     case GammaCommand:
7706     {
7707       static char
7708         factor[MaxTextExtent] = "1.6";
7709
7710       /*
7711         Query user for gamma value.
7712       */
7713       (void) XDialogWidget(display,windows,"Gamma",
7714         "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
7715       if (*factor == '\0')
7716         break;
7717       /*
7718         Gamma correct image.
7719       */
7720       XSetCursorState(display,windows,MagickTrue);
7721       XCheckRefreshWindows(display,windows);
7722       (void) GammaImage(*image,factor);
7723       XSetCursorState(display,windows,MagickFalse);
7724       if (windows->image.orphan != MagickFalse)
7725         break;
7726       XConfigureImageColormap(display,resource_info,windows,*image);
7727       (void) XConfigureImage(display,resource_info,windows,*image);
7728       break;
7729     }
7730     case SpiffCommand:
7731     {
7732       /*
7733         Sharpen the image contrast.
7734       */
7735       XSetCursorState(display,windows,MagickTrue);
7736       XCheckRefreshWindows(display,windows);
7737       (void) ContrastImage(*image,MagickTrue);
7738       XSetCursorState(display,windows,MagickFalse);
7739       if (windows->image.orphan != MagickFalse)
7740         break;
7741       XConfigureImageColormap(display,resource_info,windows,*image);
7742       (void) XConfigureImage(display,resource_info,windows,*image);
7743       break;
7744     }
7745     case DullCommand:
7746     {
7747       /*
7748         Dull the image contrast.
7749       */
7750       XSetCursorState(display,windows,MagickTrue);
7751       XCheckRefreshWindows(display,windows);
7752       (void) ContrastImage(*image,MagickFalse);
7753       XSetCursorState(display,windows,MagickFalse);
7754       if (windows->image.orphan != MagickFalse)
7755         break;
7756       XConfigureImageColormap(display,resource_info,windows,*image);
7757       (void) XConfigureImage(display,resource_info,windows,*image);
7758       break;
7759     }
7760     case ContrastStretchCommand:
7761     {
7762       double
7763         black_point,
7764         white_point;
7765
7766       static char
7767         levels[MaxTextExtent] = "1%";
7768
7769       /*
7770         Query user for gamma value.
7771       */
7772       (void) XDialogWidget(display,windows,"Contrast Stretch",
7773         "Enter black and white points:",levels);
7774       if (*levels == '\0')
7775         break;
7776       /*
7777         Contrast stretch image.
7778       */
7779       XSetCursorState(display,windows,MagickTrue);
7780       XCheckRefreshWindows(display,windows);
7781       flags=ParseGeometry(levels,&geometry_info);
7782       black_point=geometry_info.rho;
7783       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7784       if ((flags & PercentValue) != 0)
7785         {
7786           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7787           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7788         }
7789       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7790       (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7791         white_point);
7792       XSetCursorState(display,windows,MagickFalse);
7793       if (windows->image.orphan != MagickFalse)
7794         break;
7795       XConfigureImageColormap(display,resource_info,windows,*image);
7796       (void) XConfigureImage(display,resource_info,windows,*image);
7797       break;
7798     }
7799     case SigmoidalContrastCommand:
7800     {
7801       static char
7802         levels[MaxTextExtent] = "3x50%";
7803
7804       /*
7805         Query user for gamma value.
7806       */
7807       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7808         "Enter contrast and midpoint:",levels);
7809       if (*levels == '\0')
7810         break;
7811       /*
7812         Contrast stretch image.
7813       */
7814       XSetCursorState(display,windows,MagickTrue);
7815       XCheckRefreshWindows(display,windows);
7816       (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7817       XSetCursorState(display,windows,MagickFalse);
7818       if (windows->image.orphan != MagickFalse)
7819         break;
7820       XConfigureImageColormap(display,resource_info,windows,*image);
7821       (void) XConfigureImage(display,resource_info,windows,*image);
7822       break;
7823     }
7824     case NormalizeCommand:
7825     {
7826       /*
7827         Perform histogram normalization on the image.
7828       */
7829       XSetCursorState(display,windows,MagickTrue);
7830       XCheckRefreshWindows(display,windows);
7831       (void) NormalizeImage(*image);
7832       XSetCursorState(display,windows,MagickFalse);
7833       if (windows->image.orphan != MagickFalse)
7834         break;
7835       XConfigureImageColormap(display,resource_info,windows,*image);
7836       (void) XConfigureImage(display,resource_info,windows,*image);
7837       break;
7838     }
7839     case EqualizeCommand:
7840     {
7841       /*
7842         Perform histogram equalization on the image.
7843       */
7844       XSetCursorState(display,windows,MagickTrue);
7845       XCheckRefreshWindows(display,windows);
7846       (void) EqualizeImage(*image);
7847       XSetCursorState(display,windows,MagickFalse);
7848       if (windows->image.orphan != MagickFalse)
7849         break;
7850       XConfigureImageColormap(display,resource_info,windows,*image);
7851       (void) XConfigureImage(display,resource_info,windows,*image);
7852       break;
7853     }
7854     case NegateCommand:
7855     {
7856       /*
7857         Negate colors in image.
7858       */
7859       XSetCursorState(display,windows,MagickTrue);
7860       XCheckRefreshWindows(display,windows);
7861       (void) NegateImage(*image,MagickFalse);
7862       XSetCursorState(display,windows,MagickFalse);
7863       if (windows->image.orphan != MagickFalse)
7864         break;
7865       XConfigureImageColormap(display,resource_info,windows,*image);
7866       (void) XConfigureImage(display,resource_info,windows,*image);
7867       break;
7868     }
7869     case GrayscaleCommand:
7870     {
7871       /*
7872         Convert image to grayscale.
7873       */
7874       XSetCursorState(display,windows,MagickTrue);
7875       XCheckRefreshWindows(display,windows);
7876       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7877         GrayscaleType : GrayscaleMatteType);
7878       XSetCursorState(display,windows,MagickFalse);
7879       if (windows->image.orphan != MagickFalse)
7880         break;
7881       XConfigureImageColormap(display,resource_info,windows,*image);
7882       (void) XConfigureImage(display,resource_info,windows,*image);
7883       break;
7884     }
7885     case MapCommand:
7886     {
7887       Image
7888         *affinity_image;
7889
7890       static char
7891         filename[MaxTextExtent] = "\0";
7892
7893       /*
7894         Request image file name from user.
7895       */
7896       XFileBrowserWidget(display,windows,"Map",filename);
7897       if (*filename == '\0')
7898         break;
7899       /*
7900         Map image.
7901       */
7902       XSetCursorState(display,windows,MagickTrue);
7903       XCheckRefreshWindows(display,windows);
7904       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7905       affinity_image=ReadImage(image_info,&(*image)->exception);
7906       if (affinity_image != (Image *) NULL)
7907         {
7908           (void) RemapImage(&quantize_info,*image,affinity_image);
7909           affinity_image=DestroyImage(affinity_image);
7910         }
7911       CatchException(&(*image)->exception);
7912       XSetCursorState(display,windows,MagickFalse);
7913       if (windows->image.orphan != MagickFalse)
7914         break;
7915       XConfigureImageColormap(display,resource_info,windows,*image);
7916       (void) XConfigureImage(display,resource_info,windows,*image);
7917       break;
7918     }
7919     case QuantizeCommand:
7920     {
7921       int
7922         status;
7923
7924       static char
7925         colors[MaxTextExtent] = "256";
7926
7927       /*
7928         Query user for maximum number of colors.
7929       */
7930       status=XDialogWidget(display,windows,"Quantize",
7931         "Maximum number of colors:",colors);
7932       if (*colors == '\0')
7933         break;
7934       /*
7935         Color reduce the image.
7936       */
7937       XSetCursorState(display,windows,MagickTrue);
7938       XCheckRefreshWindows(display,windows);
7939       quantize_info.number_colors=StringToUnsignedLong(colors);
7940       quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7941       (void) QuantizeImage(&quantize_info,*image);
7942       XSetCursorState(display,windows,MagickFalse);
7943       if (windows->image.orphan != MagickFalse)
7944         break;
7945       XConfigureImageColormap(display,resource_info,windows,*image);
7946       (void) XConfigureImage(display,resource_info,windows,*image);
7947       break;
7948     }
7949     case DespeckleCommand:
7950     {
7951       Image
7952         *despeckle_image;
7953
7954       /*
7955         Despeckle image.
7956       */
7957       XSetCursorState(display,windows,MagickTrue);
7958       XCheckRefreshWindows(display,windows);
7959       despeckle_image=DespeckleImage(*image,&(*image)->exception);
7960       if (despeckle_image != (Image *) NULL)
7961         {
7962           *image=DestroyImage(*image);
7963           *image=despeckle_image;
7964         }
7965       CatchException(&(*image)->exception);
7966       XSetCursorState(display,windows,MagickFalse);
7967       if (windows->image.orphan != MagickFalse)
7968         break;
7969       XConfigureImageColormap(display,resource_info,windows,*image);
7970       (void) XConfigureImage(display,resource_info,windows,*image);
7971       break;
7972     }
7973     case EmbossCommand:
7974     {
7975       Image
7976         *emboss_image;
7977
7978       static char
7979         radius[MaxTextExtent] = "0.0x1.0";
7980
7981       /*
7982         Query user for emboss radius.
7983       */
7984       (void) XDialogWidget(display,windows,"Emboss",
7985         "Enter the emboss radius and standard deviation:",radius);
7986       if (*radius == '\0')
7987         break;
7988       /*
7989         Reduce noise in the image.
7990       */
7991       XSetCursorState(display,windows,MagickTrue);
7992       XCheckRefreshWindows(display,windows);
7993       flags=ParseGeometry(radius,&geometry_info);
7994       if ((flags & SigmaValue) == 0)
7995         geometry_info.sigma=1.0;
7996       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
7997         &(*image)->exception);
7998       if (emboss_image != (Image *) NULL)
7999         {
8000           *image=DestroyImage(*image);
8001           *image=emboss_image;
8002         }
8003       CatchException(&(*image)->exception);
8004       XSetCursorState(display,windows,MagickFalse);
8005       if (windows->image.orphan != MagickFalse)
8006         break;
8007       XConfigureImageColormap(display,resource_info,windows,*image);
8008       (void) XConfigureImage(display,resource_info,windows,*image);
8009       break;
8010     }
8011     case ReduceNoiseCommand:
8012     {
8013       Image
8014         *noise_image;
8015
8016       static char
8017         radius[MaxTextExtent] = "0";
8018
8019       /*
8020         Query user for noise radius.
8021       */
8022       (void) XDialogWidget(display,windows,"Reduce Noise",
8023         "Enter the noise radius:",radius);
8024       if (*radius == '\0')
8025         break;
8026       /*
8027         Reduce noise in the image.
8028       */
8029       XSetCursorState(display,windows,MagickTrue);
8030       XCheckRefreshWindows(display,windows);
8031       flags=ParseGeometry(radius,&geometry_info);
8032       noise_image=ReduceNoiseImage(*image,geometry_info.rho,
8033         &(*image)->exception);
8034       if (noise_image != (Image *) NULL)
8035         {
8036           *image=DestroyImage(*image);
8037           *image=noise_image;
8038         }
8039       CatchException(&(*image)->exception);
8040       XSetCursorState(display,windows,MagickFalse);
8041       if (windows->image.orphan != MagickFalse)
8042         break;
8043       XConfigureImageColormap(display,resource_info,windows,*image);
8044       (void) XConfigureImage(display,resource_info,windows,*image);
8045       break;
8046     }
8047     case AddNoiseCommand:
8048     {
8049       char
8050         **noises;
8051
8052       Image
8053         *noise_image;
8054
8055       static char
8056         noise_type[MaxTextExtent] = "Gaussian";
8057
8058       /*
8059         Add noise to the image.
8060       */
8061       noises=GetMagickOptions(MagickNoiseOptions);
8062       if (noises == (char **) NULL)
8063         break;
8064       XListBrowserWidget(display,windows,&windows->widget,
8065         (const char **) noises,"Add Noise",
8066         "Select a type of noise to add to your image:",noise_type);
8067       noises=DestroyStringList(noises);
8068       if (*noise_type == '\0')
8069         break;
8070       XSetCursorState(display,windows,MagickTrue);
8071       XCheckRefreshWindows(display,windows);
8072       noise_image=AddNoiseImage(*image,(NoiseType) ParseMagickOption(
8073         MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8074       if (noise_image != (Image *) NULL)
8075         {
8076           *image=DestroyImage(*image);
8077           *image=noise_image;
8078         }
8079       CatchException(&(*image)->exception);
8080       XSetCursorState(display,windows,MagickFalse);
8081       if (windows->image.orphan != MagickFalse)
8082         break;
8083       XConfigureImageColormap(display,resource_info,windows,*image);
8084       (void) XConfigureImage(display,resource_info,windows,*image);
8085       break;
8086     }
8087     case SharpenCommand:
8088     {
8089       Image
8090         *sharp_image;
8091
8092       static char
8093         radius[MaxTextExtent] = "0.0x1.0";
8094
8095       /*
8096         Query user for sharpen radius.
8097       */
8098       (void) XDialogWidget(display,windows,"Sharpen",
8099         "Enter the sharpen radius and standard deviation:",radius);
8100       if (*radius == '\0')
8101         break;
8102       /*
8103         Sharpen image scanlines.
8104       */
8105       XSetCursorState(display,windows,MagickTrue);
8106       XCheckRefreshWindows(display,windows);
8107       flags=ParseGeometry(radius,&geometry_info);
8108       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8109         &(*image)->exception);
8110       if (sharp_image != (Image *) NULL)
8111         {
8112           *image=DestroyImage(*image);
8113           *image=sharp_image;
8114         }
8115       CatchException(&(*image)->exception);
8116       XSetCursorState(display,windows,MagickFalse);
8117       if (windows->image.orphan != MagickFalse)
8118         break;
8119       XConfigureImageColormap(display,resource_info,windows,*image);
8120       (void) XConfigureImage(display,resource_info,windows,*image);
8121       break;
8122     }
8123     case BlurCommand:
8124     {
8125       Image
8126         *blur_image;
8127
8128       static char
8129         radius[MaxTextExtent] = "0.0x1.0";
8130
8131       /*
8132         Query user for blur radius.
8133       */
8134       (void) XDialogWidget(display,windows,"Blur",
8135         "Enter the blur radius and standard deviation:",radius);
8136       if (*radius == '\0')
8137         break;
8138       /*
8139         Blur an image.
8140       */
8141       XSetCursorState(display,windows,MagickTrue);
8142       XCheckRefreshWindows(display,windows);
8143       flags=ParseGeometry(radius,&geometry_info);
8144       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8145         &(*image)->exception);
8146       if (blur_image != (Image *) NULL)
8147         {
8148           *image=DestroyImage(*image);
8149           *image=blur_image;
8150         }
8151       CatchException(&(*image)->exception);
8152       XSetCursorState(display,windows,MagickFalse);
8153       if (windows->image.orphan != MagickFalse)
8154         break;
8155       XConfigureImageColormap(display,resource_info,windows,*image);
8156       (void) XConfigureImage(display,resource_info,windows,*image);
8157       break;
8158     }
8159     case ThresholdCommand:
8160     {
8161       double
8162         threshold;
8163
8164       static char
8165         factor[MaxTextExtent] = "128";
8166
8167       /*
8168         Query user for threshold value.
8169       */
8170       (void) XDialogWidget(display,windows,"Threshold",
8171         "Enter threshold value:",factor);
8172       if (*factor == '\0')
8173         break;
8174       /*
8175         Gamma correct image.
8176       */
8177       XSetCursorState(display,windows,MagickTrue);
8178       XCheckRefreshWindows(display,windows);
8179       threshold=SiPrefixToDouble(factor,QuantumRange);
8180       (void) BilevelImage(*image,threshold);
8181       XSetCursorState(display,windows,MagickFalse);
8182       if (windows->image.orphan != MagickFalse)
8183         break;
8184       XConfigureImageColormap(display,resource_info,windows,*image);
8185       (void) XConfigureImage(display,resource_info,windows,*image);
8186       break;
8187     }
8188     case EdgeDetectCommand:
8189     {
8190       Image
8191         *edge_image;
8192
8193       static char
8194         radius[MaxTextExtent] = "0";
8195
8196       /*
8197         Query user for edge factor.
8198       */
8199       (void) XDialogWidget(display,windows,"Detect Edges",
8200         "Enter the edge detect radius:",radius);
8201       if (*radius == '\0')
8202         break;
8203       /*
8204         Detect edge in image.
8205       */
8206       XSetCursorState(display,windows,MagickTrue);
8207       XCheckRefreshWindows(display,windows);
8208       flags=ParseGeometry(radius,&geometry_info);
8209       edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8210       if (edge_image != (Image *) NULL)
8211         {
8212           *image=DestroyImage(*image);
8213           *image=edge_image;
8214         }
8215       CatchException(&(*image)->exception);
8216       XSetCursorState(display,windows,MagickFalse);
8217       if (windows->image.orphan != MagickFalse)
8218         break;
8219       XConfigureImageColormap(display,resource_info,windows,*image);
8220       (void) XConfigureImage(display,resource_info,windows,*image);
8221       break;
8222     }
8223     case SpreadCommand:
8224     {
8225       Image
8226         *spread_image;
8227
8228       static char
8229         amount[MaxTextExtent] = "2";
8230
8231       /*
8232         Query user for spread amount.
8233       */
8234       (void) XDialogWidget(display,windows,"Spread",
8235         "Enter the displacement amount:",amount);
8236       if (*amount == '\0')
8237         break;
8238       /*
8239         Displace image pixels by a random amount.
8240       */
8241       XSetCursorState(display,windows,MagickTrue);
8242       XCheckRefreshWindows(display,windows);
8243       flags=ParseGeometry(amount,&geometry_info);
8244       spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8245       if (spread_image != (Image *) NULL)
8246         {
8247           *image=DestroyImage(*image);
8248           *image=spread_image;
8249         }
8250       CatchException(&(*image)->exception);
8251       XSetCursorState(display,windows,MagickFalse);
8252       if (windows->image.orphan != MagickFalse)
8253         break;
8254       XConfigureImageColormap(display,resource_info,windows,*image);
8255       (void) XConfigureImage(display,resource_info,windows,*image);
8256       break;
8257     }
8258     case ShadeCommand:
8259     {
8260       Image
8261         *shade_image;
8262
8263       int
8264         status;
8265
8266       static char
8267         geometry[MaxTextExtent] = "30x30";
8268
8269       /*
8270         Query user for the shade geometry.
8271       */
8272       status=XDialogWidget(display,windows,"Shade",
8273         "Enter the azimuth and elevation of the light source:",geometry);
8274       if (*geometry == '\0')
8275         break;
8276       /*
8277         Shade image pixels.
8278       */
8279       XSetCursorState(display,windows,MagickTrue);
8280       XCheckRefreshWindows(display,windows);
8281       flags=ParseGeometry(geometry,&geometry_info);
8282       if ((flags & SigmaValue) == 0)
8283         geometry_info.sigma=1.0;
8284       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8285         geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8286       if (shade_image != (Image *) NULL)
8287         {
8288           *image=DestroyImage(*image);
8289           *image=shade_image;
8290         }
8291       CatchException(&(*image)->exception);
8292       XSetCursorState(display,windows,MagickFalse);
8293       if (windows->image.orphan != MagickFalse)
8294         break;
8295       XConfigureImageColormap(display,resource_info,windows,*image);
8296       (void) XConfigureImage(display,resource_info,windows,*image);
8297       break;
8298     }
8299     case RaiseCommand:
8300     {
8301       static char
8302         bevel_width[MaxTextExtent] = "10";
8303
8304       /*
8305         Query user for bevel width.
8306       */
8307       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8308       if (*bevel_width == '\0')
8309         break;
8310       /*
8311         Raise an image.
8312       */
8313       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8314       XSetCursorState(display,windows,MagickTrue);
8315       XCheckRefreshWindows(display,windows);
8316       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8317         &(*image)->exception);
8318       (void) RaiseImage(*image,&page_geometry,MagickTrue);
8319       XSetCursorState(display,windows,MagickFalse);
8320       if (windows->image.orphan != MagickFalse)
8321         break;
8322       XConfigureImageColormap(display,resource_info,windows,*image);
8323       (void) XConfigureImage(display,resource_info,windows,*image);
8324       break;
8325     }
8326     case SegmentCommand:
8327     {
8328       static char
8329         threshold[MaxTextExtent] = "1.0x1.5";
8330
8331       /*
8332         Query user for smoothing threshold.
8333       */
8334       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8335         threshold);
8336       if (*threshold == '\0')
8337         break;
8338       /*
8339         Segment an image.
8340       */
8341       XSetCursorState(display,windows,MagickTrue);
8342       XCheckRefreshWindows(display,windows);
8343       flags=ParseGeometry(threshold,&geometry_info);
8344       if ((flags & SigmaValue) == 0)
8345         geometry_info.sigma=1.0;
8346       (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8347         geometry_info.sigma);
8348       XSetCursorState(display,windows,MagickFalse);
8349       if (windows->image.orphan != MagickFalse)
8350         break;
8351       XConfigureImageColormap(display,resource_info,windows,*image);
8352       (void) XConfigureImage(display,resource_info,windows,*image);
8353       break;
8354     }
8355     case SepiaToneCommand:
8356     {
8357       double
8358         threshold;
8359
8360       Image
8361         *sepia_image;
8362
8363       static char
8364         factor[MaxTextExtent] = "80%";
8365
8366       /*
8367         Query user for sepia-tone factor.
8368       */
8369       (void) XDialogWidget(display,windows,"Sepia Tone",
8370         "Enter the sepia tone factor (0 - 99.9%):",factor);
8371       if (*factor == '\0')
8372         break;
8373       /*
8374         Sepia tone image pixels.
8375       */
8376       XSetCursorState(display,windows,MagickTrue);
8377       XCheckRefreshWindows(display,windows);
8378       threshold=SiPrefixToDouble(factor,QuantumRange);
8379       sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8380       if (sepia_image != (Image *) NULL)
8381         {
8382           *image=DestroyImage(*image);
8383           *image=sepia_image;
8384         }
8385       CatchException(&(*image)->exception);
8386       XSetCursorState(display,windows,MagickFalse);
8387       if (windows->image.orphan != MagickFalse)
8388         break;
8389       XConfigureImageColormap(display,resource_info,windows,*image);
8390       (void) XConfigureImage(display,resource_info,windows,*image);
8391       break;
8392     }
8393     case SolarizeCommand:
8394     {
8395       double
8396         threshold;
8397
8398       static char
8399         factor[MaxTextExtent] = "60%";
8400
8401       /*
8402         Query user for solarize factor.
8403       */
8404       (void) XDialogWidget(display,windows,"Solarize",
8405         "Enter the solarize factor (0 - 99.9%):",factor);
8406       if (*factor == '\0')
8407         break;
8408       /*
8409         Solarize image pixels.
8410       */
8411       XSetCursorState(display,windows,MagickTrue);
8412       XCheckRefreshWindows(display,windows);
8413       threshold=SiPrefixToDouble(factor,QuantumRange);
8414       (void) SolarizeImage(*image,threshold);
8415       XSetCursorState(display,windows,MagickFalse);
8416       if (windows->image.orphan != MagickFalse)
8417         break;
8418       XConfigureImageColormap(display,resource_info,windows,*image);
8419       (void) XConfigureImage(display,resource_info,windows,*image);
8420       break;
8421     }
8422     case SwirlCommand:
8423     {
8424       Image
8425         *swirl_image;
8426
8427       static char
8428         degrees[MaxTextExtent] = "60";
8429
8430       /*
8431         Query user for swirl angle.
8432       */
8433       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8434         degrees);
8435       if (*degrees == '\0')
8436         break;
8437       /*
8438         Swirl image pixels about the center.
8439       */
8440       XSetCursorState(display,windows,MagickTrue);
8441       XCheckRefreshWindows(display,windows);
8442       flags=ParseGeometry(degrees,&geometry_info);
8443       swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8444       if (swirl_image != (Image *) NULL)
8445         {
8446           *image=DestroyImage(*image);
8447           *image=swirl_image;
8448         }
8449       CatchException(&(*image)->exception);
8450       XSetCursorState(display,windows,MagickFalse);
8451       if (windows->image.orphan != MagickFalse)
8452         break;
8453       XConfigureImageColormap(display,resource_info,windows,*image);
8454       (void) XConfigureImage(display,resource_info,windows,*image);
8455       break;
8456     }
8457     case ImplodeCommand:
8458     {
8459       Image
8460         *implode_image;
8461
8462       static char
8463         factor[MaxTextExtent] = "0.3";
8464
8465       /*
8466         Query user for implode factor.
8467       */
8468       (void) XDialogWidget(display,windows,"Implode",
8469         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8470       if (*factor == '\0')
8471         break;
8472       /*
8473         Implode image pixels about the center.
8474       */
8475       XSetCursorState(display,windows,MagickTrue);
8476       XCheckRefreshWindows(display,windows);
8477       flags=ParseGeometry(factor,&geometry_info);
8478       implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8479       if (implode_image != (Image *) NULL)
8480         {
8481           *image=DestroyImage(*image);
8482           *image=implode_image;
8483         }
8484       CatchException(&(*image)->exception);
8485       XSetCursorState(display,windows,MagickFalse);
8486       if (windows->image.orphan != MagickFalse)
8487         break;
8488       XConfigureImageColormap(display,resource_info,windows,*image);
8489       (void) XConfigureImage(display,resource_info,windows,*image);
8490       break;
8491     }
8492     case VignetteCommand:
8493     {
8494       Image
8495         *vignette_image;
8496
8497       static char
8498         geometry[MaxTextExtent] = "0x20";
8499
8500       /*
8501         Query user for the vignette geometry.
8502       */
8503       (void) XDialogWidget(display,windows,"Vignette",
8504         "Enter the radius, sigma, and x and y offsets:",geometry);
8505       if (*geometry == '\0')
8506         break;
8507       /*
8508         Soften the edges of the image in vignette style
8509       */
8510       XSetCursorState(display,windows,MagickTrue);
8511       XCheckRefreshWindows(display,windows);
8512       flags=ParseGeometry(geometry,&geometry_info);
8513       if ((flags & SigmaValue) == 0)
8514         geometry_info.sigma=1.0;
8515       if ((flags & XiValue) == 0)
8516         geometry_info.xi=0.1*(*image)->columns;
8517       if ((flags & PsiValue) == 0)
8518         geometry_info.psi=0.1*(*image)->rows;
8519       vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8520         (long) floor(geometry_info.xi+0.5),(long) floor(geometry_info.psi+0.5),
8521         &(*image)->exception);
8522       if (vignette_image != (Image *) NULL)
8523         {
8524           *image=DestroyImage(*image);
8525           *image=vignette_image;
8526         }
8527       CatchException(&(*image)->exception);
8528       XSetCursorState(display,windows,MagickFalse);
8529       if (windows->image.orphan != MagickFalse)
8530         break;
8531       XConfigureImageColormap(display,resource_info,windows,*image);
8532       (void) XConfigureImage(display,resource_info,windows,*image);
8533       break;
8534     }
8535     case WaveCommand:
8536     {
8537       Image
8538         *wave_image;
8539
8540       static char
8541         geometry[MaxTextExtent] = "25x150";
8542
8543       /*
8544         Query user for the wave geometry.
8545       */
8546       (void) XDialogWidget(display,windows,"Wave",
8547         "Enter the amplitude and length of the wave:",geometry);
8548       if (*geometry == '\0')
8549         break;
8550       /*
8551         Alter an image along a sine wave.
8552       */
8553       XSetCursorState(display,windows,MagickTrue);
8554       XCheckRefreshWindows(display,windows);
8555       flags=ParseGeometry(geometry,&geometry_info);
8556       if ((flags & SigmaValue) == 0)
8557         geometry_info.sigma=1.0;
8558       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8559         &(*image)->exception);
8560       if (wave_image != (Image *) NULL)
8561         {
8562           *image=DestroyImage(*image);
8563           *image=wave_image;
8564         }
8565       CatchException(&(*image)->exception);
8566       XSetCursorState(display,windows,MagickFalse);
8567       if (windows->image.orphan != MagickFalse)
8568         break;
8569       XConfigureImageColormap(display,resource_info,windows,*image);
8570       (void) XConfigureImage(display,resource_info,windows,*image);
8571       break;
8572     }
8573     case OilPaintCommand:
8574     {
8575       Image
8576         *paint_image;
8577
8578       static char
8579         radius[MaxTextExtent] = "0";
8580
8581       /*
8582         Query user for circular neighborhood radius.
8583       */
8584       (void) XDialogWidget(display,windows,"Oil Paint",
8585         "Enter the mask radius:",radius);
8586       if (*radius == '\0')
8587         break;
8588       /*
8589         OilPaint image scanlines.
8590       */
8591       XSetCursorState(display,windows,MagickTrue);
8592       XCheckRefreshWindows(display,windows);
8593       flags=ParseGeometry(radius,&geometry_info);
8594       paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8595       if (paint_image != (Image *) NULL)
8596         {
8597           *image=DestroyImage(*image);
8598           *image=paint_image;
8599         }
8600       CatchException(&(*image)->exception);
8601       XSetCursorState(display,windows,MagickFalse);
8602       if (windows->image.orphan != MagickFalse)
8603         break;
8604       XConfigureImageColormap(display,resource_info,windows,*image);
8605       (void) XConfigureImage(display,resource_info,windows,*image);
8606       break;
8607     }
8608     case CharcoalDrawCommand:
8609     {
8610       Image
8611         *charcoal_image;
8612
8613       static char
8614         radius[MaxTextExtent] = "0x1";
8615
8616       /*
8617         Query user for charcoal radius.
8618       */
8619       (void) XDialogWidget(display,windows,"Charcoal Draw",
8620         "Enter the charcoal radius and sigma:",radius);
8621       if (*radius == '\0')
8622         break;
8623       /*
8624         Charcoal the image.
8625       */
8626       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8627       XSetCursorState(display,windows,MagickTrue);
8628       XCheckRefreshWindows(display,windows);
8629       flags=ParseGeometry(radius,&geometry_info);
8630       if ((flags & SigmaValue) == 0)
8631         geometry_info.sigma=geometry_info.rho;
8632       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8633         &(*image)->exception);
8634       if (charcoal_image != (Image *) NULL)
8635         {
8636           *image=DestroyImage(*image);
8637           *image=charcoal_image;
8638         }
8639       CatchException(&(*image)->exception);
8640       XSetCursorState(display,windows,MagickFalse);
8641       if (windows->image.orphan != MagickFalse)
8642         break;
8643       XConfigureImageColormap(display,resource_info,windows,*image);
8644       (void) XConfigureImage(display,resource_info,windows,*image);
8645       break;
8646     }
8647     case AnnotateCommand:
8648     {
8649       /*
8650         Annotate the image with text.
8651       */
8652       status=XAnnotateEditImage(display,resource_info,windows,*image);
8653       if (status == MagickFalse)
8654         {
8655           XNoticeWidget(display,windows,"Unable to annotate X image",
8656             (*image)->filename);
8657           break;
8658         }
8659       break;
8660     }
8661     case DrawCommand:
8662     {
8663       /*
8664         Draw image.
8665       */
8666       status=XDrawEditImage(display,resource_info,windows,image);
8667       if (status == MagickFalse)
8668         {
8669           XNoticeWidget(display,windows,"Unable to draw on the X image",
8670             (*image)->filename);
8671           break;
8672         }
8673       break;
8674     }
8675     case ColorCommand:
8676     {
8677       /*
8678         Color edit.
8679       */
8680       status=XColorEditImage(display,resource_info,windows,image);
8681       if (status == MagickFalse)
8682         {
8683           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8684             (*image)->filename);
8685           break;
8686         }
8687       break;
8688     }
8689     case MatteCommand:
8690     {
8691       /*
8692         Matte edit.
8693       */
8694       status=XMatteEditImage(display,resource_info,windows,image);
8695       if (status == MagickFalse)
8696         {
8697           XNoticeWidget(display,windows,"Unable to matte edit X image",
8698             (*image)->filename);
8699           break;
8700         }
8701       break;
8702     }
8703     case CompositeCommand:
8704     {
8705       /*
8706         Composite image.
8707       */
8708       status=XCompositeImage(display,resource_info,windows,*image);
8709       if (status == MagickFalse)
8710         {
8711           XNoticeWidget(display,windows,"Unable to composite X image",
8712             (*image)->filename);
8713           break;
8714         }
8715       break;
8716     }
8717     case AddBorderCommand:
8718     {
8719       Image
8720         *border_image;
8721
8722       static char
8723         geometry[MaxTextExtent] = "6x6";
8724
8725       /*
8726         Query user for border color and geometry.
8727       */
8728       XColorBrowserWidget(display,windows,"Select",color);
8729       if (*color == '\0')
8730         break;
8731       (void) XDialogWidget(display,windows,"Add Border",
8732         "Enter border geometry:",geometry);
8733       if (*geometry == '\0')
8734         break;
8735       /*
8736         Add a border to the image.
8737       */
8738       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8739       XSetCursorState(display,windows,MagickTrue);
8740       XCheckRefreshWindows(display,windows);
8741       (void) QueryColorDatabase(color,&(*image)->border_color,
8742         &(*image)->exception);
8743       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8744         &(*image)->exception);
8745       border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8746       if (border_image != (Image *) NULL)
8747         {
8748           *image=DestroyImage(*image);
8749           *image=border_image;
8750         }
8751       CatchException(&(*image)->exception);
8752       XSetCursorState(display,windows,MagickFalse);
8753       if (windows->image.orphan != MagickFalse)
8754         break;
8755       windows->image.window_changes.width=(int) (*image)->columns;
8756       windows->image.window_changes.height=(int) (*image)->rows;
8757       XConfigureImageColormap(display,resource_info,windows,*image);
8758       (void) XConfigureImage(display,resource_info,windows,*image);
8759       break;
8760     }
8761     case AddFrameCommand:
8762     {
8763       FrameInfo
8764         frame_info;
8765
8766       Image
8767         *frame_image;
8768
8769       static char
8770         geometry[MaxTextExtent] = "6x6";
8771
8772       /*
8773         Query user for frame color and geometry.
8774       */
8775       XColorBrowserWidget(display,windows,"Select",color);
8776       if (*color == '\0')
8777         break;
8778       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8779         geometry);
8780       if (*geometry == '\0')
8781         break;
8782       /*
8783         Surround image with an ornamental border.
8784       */
8785       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8786       XSetCursorState(display,windows,MagickTrue);
8787       XCheckRefreshWindows(display,windows);
8788       (void) QueryColorDatabase(color,&(*image)->matte_color,
8789         &(*image)->exception);
8790       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8791         &(*image)->exception);
8792       frame_info.width=page_geometry.width;
8793       frame_info.height=page_geometry.height;
8794       frame_info.outer_bevel=page_geometry.x;
8795       frame_info.inner_bevel=page_geometry.y;
8796       frame_info.x=(long) frame_info.width;
8797       frame_info.y=(long) frame_info.height;
8798       frame_info.width=(*image)->columns+2*frame_info.width;
8799       frame_info.height=(*image)->rows+2*frame_info.height;
8800       frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8801       if (frame_image != (Image *) NULL)
8802         {
8803           *image=DestroyImage(*image);
8804           *image=frame_image;
8805         }
8806       CatchException(&(*image)->exception);
8807       XSetCursorState(display,windows,MagickFalse);
8808       if (windows->image.orphan != MagickFalse)
8809         break;
8810       windows->image.window_changes.width=(int) (*image)->columns;
8811       windows->image.window_changes.height=(int) (*image)->rows;
8812       XConfigureImageColormap(display,resource_info,windows,*image);
8813       (void) XConfigureImage(display,resource_info,windows,*image);
8814       break;
8815     }
8816     case CommentCommand:
8817     {
8818       const char
8819         *value;
8820
8821       FILE
8822         *file;
8823
8824       int
8825         unique_file;
8826
8827       /*
8828         Edit image comment.
8829       */
8830       unique_file=AcquireUniqueFileResource(image_info->filename);
8831       if (unique_file == -1)
8832         XNoticeWidget(display,windows,"Unable to edit image comment",
8833           image_info->filename);
8834       value=GetImageProperty(*image,"comment");
8835       if (value == (char *) NULL)
8836         unique_file=close(unique_file)-1;
8837       else
8838         {
8839           register const char
8840             *p;
8841
8842           file=fdopen(unique_file,"w");
8843           if (file == (FILE *) NULL)
8844             {
8845               XNoticeWidget(display,windows,"Unable to edit image comment",
8846                 image_info->filename);
8847               break;
8848             }
8849           for (p=value; *p != '\0'; p++)
8850             (void) fputc((int) *p,file);
8851           (void) fputc('\n',file);
8852           (void) fclose(file);
8853         }
8854       XSetCursorState(display,windows,MagickTrue);
8855       XCheckRefreshWindows(display,windows);
8856       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8857         &(*image)->exception);
8858       if (status == MagickFalse)
8859         XNoticeWidget(display,windows,"Unable to edit image comment",
8860           (char *) NULL);
8861       else
8862         {
8863           char
8864             *comment;
8865
8866           comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8867           if (comment != (char *) NULL)
8868             {
8869               (void) SetImageProperty(*image,"comment",comment);
8870               (*image)->taint=MagickTrue;
8871             }
8872         }
8873       (void) RelinquishUniqueFileResource(image_info->filename);
8874       XSetCursorState(display,windows,MagickFalse);
8875       break;
8876     }
8877     case LaunchCommand:
8878     {
8879       /*
8880         Launch program.
8881       */
8882       XSetCursorState(display,windows,MagickTrue);
8883       XCheckRefreshWindows(display,windows);
8884       (void) AcquireUniqueFilename(filename);
8885       (void) FormatMagickString((*image)->filename,MaxTextExtent,"launch:%s",
8886         filename);
8887       status=WriteImage(image_info,*image);
8888       if (status == MagickFalse)
8889         XNoticeWidget(display,windows,"Unable to launch image editor",
8890           (char *) NULL);
8891       else
8892         {
8893           nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8894           CatchException(&(*image)->exception);
8895           XClientMessage(display,windows->image.id,windows->im_protocols,
8896             windows->im_next_image,CurrentTime);
8897         }
8898       (void) RelinquishUniqueFileResource(filename);
8899       XSetCursorState(display,windows,MagickFalse);
8900       break;
8901     }
8902     case RegionofInterestCommand:
8903     {
8904       /*
8905         Apply an image processing technique to a region of interest.
8906       */
8907       (void) XROIImage(display,resource_info,windows,image);
8908       break;
8909     }
8910     case InfoCommand:
8911       break;
8912     case ZoomCommand:
8913     {
8914       /*
8915         Zoom image.
8916       */
8917       if (windows->magnify.mapped != MagickFalse)
8918         (void) XRaiseWindow(display,windows->magnify.id);
8919       else
8920         {
8921           /*
8922             Make magnify image.
8923           */
8924           XSetCursorState(display,windows,MagickTrue);
8925           (void) XMapRaised(display,windows->magnify.id);
8926           XSetCursorState(display,windows,MagickFalse);
8927         }
8928       break;
8929     }
8930     case ShowPreviewCommand:
8931     {
8932       char
8933         **previews;
8934
8935       Image
8936         *preview_image;
8937
8938       static char
8939         preview_type[MaxTextExtent] = "Gamma";
8940
8941       /*
8942         Select preview type from menu.
8943       */
8944       previews=GetMagickOptions(MagickPreviewOptions);
8945       if (previews == (char **) NULL)
8946         break;
8947       XListBrowserWidget(display,windows,&windows->widget,
8948         (const char **) previews,"Preview",
8949         "Select an enhancement, effect, or F/X:",preview_type);
8950       previews=DestroyStringList(previews);
8951       if (*preview_type == '\0')
8952         break;
8953       /*
8954         Show image preview.
8955       */
8956       XSetCursorState(display,windows,MagickTrue);
8957       XCheckRefreshWindows(display,windows);
8958       image_info->preview_type=(PreviewType)
8959         ParseMagickOption(MagickPreviewOptions,MagickFalse,preview_type);
8960       image_info->group=(long) windows->image.id;
8961       (void) DeleteImageProperty(*image,"label");
8962       (void) SetImageProperty(*image,"label","Preview");
8963       (void) AcquireUniqueFilename(filename);
8964       (void) FormatMagickString((*image)->filename,MaxTextExtent,"preview:%s",
8965         filename);
8966       status=WriteImage(image_info,*image);
8967       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8968       preview_image=ReadImage(image_info,&(*image)->exception);
8969       (void) RelinquishUniqueFileResource(filename);
8970       if (preview_image == (Image *) NULL)
8971         break;
8972       (void) FormatMagickString(preview_image->filename,MaxTextExtent,"show:%s",
8973         filename);
8974       status=WriteImage(image_info,preview_image);
8975       preview_image=DestroyImage(preview_image);
8976       if (status == MagickFalse)
8977         XNoticeWidget(display,windows,"Unable to show image preview",
8978           (*image)->filename);
8979       XDelay(display,1500);
8980       XSetCursorState(display,windows,MagickFalse);
8981       break;
8982     }
8983     case ShowHistogramCommand:
8984     {
8985       Image
8986         *histogram_image;
8987
8988       /*
8989         Show image histogram.
8990       */
8991       XSetCursorState(display,windows,MagickTrue);
8992       XCheckRefreshWindows(display,windows);
8993       image_info->group=(long) windows->image.id;
8994       (void) DeleteImageProperty(*image,"label");
8995       (void) SetImageProperty(*image,"label","Histogram");
8996       (void) AcquireUniqueFilename(filename);
8997       (void) FormatMagickString((*image)->filename,MaxTextExtent,"histogram:%s",
8998         filename);
8999       status=WriteImage(image_info,*image);
9000       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9001       histogram_image=ReadImage(image_info,&(*image)->exception);
9002       (void) RelinquishUniqueFileResource(filename);
9003       if (histogram_image == (Image *) NULL)
9004         break;
9005       (void) FormatMagickString(histogram_image->filename,MaxTextExtent,
9006         "show:%s",filename);
9007       status=WriteImage(image_info,histogram_image);
9008       histogram_image=DestroyImage(histogram_image);
9009       if (status == MagickFalse)
9010         XNoticeWidget(display,windows,"Unable to show histogram",
9011           (*image)->filename);
9012       XDelay(display,1500);
9013       XSetCursorState(display,windows,MagickFalse);
9014       break;
9015     }
9016     case ShowMatteCommand:
9017     {
9018       Image
9019         *matte_image;
9020
9021       if ((*image)->matte == MagickFalse)
9022         {
9023           XNoticeWidget(display,windows,
9024             "Image does not have any matte information",(*image)->filename);
9025           break;
9026         }
9027       /*
9028         Show image matte.
9029       */
9030       XSetCursorState(display,windows,MagickTrue);
9031       XCheckRefreshWindows(display,windows);
9032       image_info->group=(long) windows->image.id;
9033       (void) DeleteImageProperty(*image,"label");
9034       (void) SetImageProperty(*image,"label","Matte");
9035       (void) AcquireUniqueFilename(filename);
9036       (void) FormatMagickString((*image)->filename,MaxTextExtent,"matte:%s",
9037         filename);
9038       status=WriteImage(image_info,*image);
9039       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9040       matte_image=ReadImage(image_info,&(*image)->exception);
9041       (void) RelinquishUniqueFileResource(filename);
9042       if (matte_image == (Image *) NULL)
9043         break;
9044       (void) FormatMagickString(matte_image->filename,MaxTextExtent,"show:%s",
9045         filename);
9046       status=WriteImage(image_info,matte_image);
9047       matte_image=DestroyImage(matte_image);
9048       if (status == MagickFalse)
9049         XNoticeWidget(display,windows,"Unable to show matte",
9050           (*image)->filename);
9051       XDelay(display,1500);
9052       XSetCursorState(display,windows,MagickFalse);
9053       break;
9054     }
9055     case BackgroundCommand:
9056     {
9057       /*
9058         Background image.
9059       */
9060       status=XBackgroundImage(display,resource_info,windows,image);
9061       if (status == MagickFalse)
9062         break;
9063       nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9064       if (nexus != (Image *) NULL)
9065         XClientMessage(display,windows->image.id,windows->im_protocols,
9066           windows->im_next_image,CurrentTime);
9067       break;
9068     }
9069     case SlideShowCommand:
9070     {
9071       static char
9072         delay[MaxTextExtent] = "5";
9073
9074       /*
9075         Display next image after pausing.
9076       */
9077       (void) XDialogWidget(display,windows,"Slide Show",
9078         "Pause how many 1/100ths of a second between images:",delay);
9079       if (*delay == '\0')
9080         break;
9081       resource_info->delay=StringToUnsignedLong(delay);
9082       XClientMessage(display,windows->image.id,windows->im_protocols,
9083         windows->im_next_image,CurrentTime);
9084       break;
9085     }
9086     case PreferencesCommand:
9087     {
9088       /*
9089         Set user preferences.
9090       */
9091       status=XPreferencesWidget(display,resource_info,windows);
9092       if (status == MagickFalse)
9093         break;
9094       nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9095       if (nexus != (Image *) NULL)
9096         XClientMessage(display,windows->image.id,windows->im_protocols,
9097           windows->im_next_image,CurrentTime);
9098       break;
9099     }
9100     case HelpCommand:
9101     {
9102       /*
9103         User requested help.
9104       */
9105       XTextViewWidget(display,resource_info,windows,MagickFalse,
9106         "Help Viewer - Display",DisplayHelp);
9107       break;
9108     }
9109     case BrowseDocumentationCommand:
9110     {
9111       Atom
9112         mozilla_atom;
9113
9114       Window
9115         mozilla_window,
9116         root_window;
9117
9118       /*
9119         Browse the ImageMagick documentation.
9120       */
9121       root_window=XRootWindow(display,XDefaultScreen(display));
9122       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9123       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9124       if (mozilla_window != (Window) NULL)
9125         {
9126           char
9127             command[MaxTextExtent],
9128             *url;
9129
9130           /*
9131             Display documentation using Netscape remote control.
9132           */
9133           url=GetMagickHomeURL();
9134           (void) FormatMagickString(command,MaxTextExtent,
9135             "openurl(%s,new-tab)",url);
9136           url=DestroyString(url);
9137           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9138           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9139             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9140           XSetCursorState(display,windows,MagickFalse);
9141           break;
9142         }
9143       XSetCursorState(display,windows,MagickTrue);
9144       XCheckRefreshWindows(display,windows);
9145       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9146         &(*image)->exception);
9147       if (status == MagickFalse)
9148         XNoticeWidget(display,windows,"Unable to browse documentation",
9149           (char *) NULL);
9150       XDelay(display,1500);
9151       XSetCursorState(display,windows,MagickFalse);
9152       break;
9153     }
9154     case VersionCommand:
9155     {
9156       XNoticeWidget(display,windows,GetMagickVersion((unsigned long *) NULL),
9157         GetMagickCopyright());
9158       break;
9159     }
9160     case SaveToUndoBufferCommand:
9161       break;
9162     default:
9163     {
9164       (void) XBell(display,0);
9165       break;
9166     }
9167   }
9168   image_info=DestroyImageInfo(image_info);
9169   return(nexus);
9170 }
9171 \f
9172 /*
9173 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9174 %                                                                             %
9175 %                                                                             %
9176 %                                                                             %
9177 +   X M a g n i f y I m a g e                                                 %
9178 %                                                                             %
9179 %                                                                             %
9180 %                                                                             %
9181 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9182 %
9183 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9184 %  The magnified portion is displayed in a separate window.
9185 %
9186 %  The format of the XMagnifyImage method is:
9187 %
9188 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9189 %
9190 %  A description of each parameter follows:
9191 %
9192 %    o display: Specifies a connection to an X server;  returned from
9193 %      XOpenDisplay.
9194 %
9195 %    o windows: Specifies a pointer to a XWindows structure.
9196 %
9197 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9198 %      the entire image is refreshed.
9199 %
9200 */
9201 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9202 {
9203   char
9204     text[MaxTextExtent];
9205
9206   register int
9207     x,
9208     y;
9209
9210   unsigned long
9211     state;
9212
9213   /*
9214     Update magnified image until the mouse button is released.
9215   */
9216   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9217   state=DefaultState;
9218   x=event->xbutton.x;
9219   y=event->xbutton.y;
9220   windows->magnify.x=windows->image.x+x;
9221   windows->magnify.y=windows->image.y+y;
9222   do
9223   {
9224     /*
9225       Map and unmap Info widget as text cursor crosses its boundaries.
9226     */
9227     if (windows->info.mapped != MagickFalse)
9228       {
9229         if ((x < (int) (windows->info.x+windows->info.width)) &&
9230             (y < (int) (windows->info.y+windows->info.height)))
9231           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9232       }
9233     else
9234       if ((x > (int) (windows->info.x+windows->info.width)) ||
9235           (y > (int) (windows->info.y+windows->info.height)))
9236         (void) XMapWindow(display,windows->info.id);
9237     if (windows->info.mapped != MagickFalse)
9238       {
9239         /*
9240           Display pointer position.
9241         */
9242         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
9243           windows->magnify.x,windows->magnify.y);
9244         XInfoWidget(display,windows,text);
9245       }
9246     /*
9247       Wait for next event.
9248     */
9249     XScreenEvent(display,windows,event);
9250     switch (event->type)
9251     {
9252       case ButtonPress:
9253         break;
9254       case ButtonRelease:
9255       {
9256         /*
9257           User has finished magnifying image.
9258         */
9259         x=event->xbutton.x;
9260         y=event->xbutton.y;
9261         state|=ExitState;
9262         break;
9263       }
9264       case Expose:
9265         break;
9266       case MotionNotify:
9267       {
9268         x=event->xmotion.x;
9269         y=event->xmotion.y;
9270         break;
9271       }
9272       default:
9273         break;
9274     }
9275     /*
9276       Check boundary conditions.
9277     */
9278     if (x < 0)
9279       x=0;
9280     else
9281       if (x >= (int) windows->image.width)
9282         x=(int) windows->image.width-1;
9283     if (y < 0)
9284       y=0;
9285     else
9286      if (y >= (int) windows->image.height)
9287        y=(int) windows->image.height-1;
9288   } while ((state & ExitState) == 0);
9289   /*
9290     Display magnified image.
9291   */
9292   XSetCursorState(display,windows,MagickFalse);
9293 }
9294 \f
9295 /*
9296 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9297 %                                                                             %
9298 %                                                                             %
9299 %                                                                             %
9300 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9301 %                                                                             %
9302 %                                                                             %
9303 %                                                                             %
9304 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9305 %
9306 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9307 %  pixel as specified by the key symbol.
9308 %
9309 %  The format of the XMagnifyWindowCommand method is:
9310 %
9311 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9312 %        const MagickStatusType state,const KeySym key_symbol)
9313 %
9314 %  A description of each parameter follows:
9315 %
9316 %    o display: Specifies a connection to an X server; returned from
9317 %      XOpenDisplay.
9318 %
9319 %    o windows: Specifies a pointer to a XWindows structure.
9320 %
9321 %    o state: key mask.
9322 %
9323 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9324 %      to trim.
9325 %
9326 */
9327 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9328   const MagickStatusType state,const KeySym key_symbol)
9329 {
9330   unsigned int
9331     quantum;
9332
9333   /*
9334     User specified a magnify factor or position.
9335   */
9336   quantum=1;
9337   if ((state & Mod1Mask) != 0)
9338     quantum=10;
9339   switch ((int) key_symbol)
9340   {
9341     case QuitCommand:
9342     {
9343       (void) XWithdrawWindow(display,windows->magnify.id,
9344         windows->magnify.screen);
9345       break;
9346     }
9347     case XK_Home:
9348     case XK_KP_Home:
9349     {
9350       windows->magnify.x=(int) windows->image.width/2;
9351       windows->magnify.y=(int) windows->image.height/2;
9352       break;
9353     }
9354     case XK_Left:
9355     case XK_KP_Left:
9356     {
9357       if (windows->magnify.x > 0)
9358         windows->magnify.x-=quantum;
9359       break;
9360     }
9361     case XK_Up:
9362     case XK_KP_Up:
9363     {
9364       if (windows->magnify.y > 0)
9365         windows->magnify.y-=quantum;
9366       break;
9367     }
9368     case XK_Right:
9369     case XK_KP_Right:
9370     {
9371       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9372         windows->magnify.x+=quantum;
9373       break;
9374     }
9375     case XK_Down:
9376     case XK_KP_Down:
9377     {
9378       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9379         windows->magnify.y+=quantum;
9380       break;
9381     }
9382     case XK_0:
9383     case XK_1:
9384     case XK_2:
9385     case XK_3:
9386     case XK_4:
9387     case XK_5:
9388     case XK_6:
9389     case XK_7:
9390     case XK_8:
9391     case XK_9:
9392     {
9393       windows->magnify.data=(key_symbol-XK_0);
9394       break;
9395     }
9396     case XK_KP_0:
9397     case XK_KP_1:
9398     case XK_KP_2:
9399     case XK_KP_3:
9400     case XK_KP_4:
9401     case XK_KP_5:
9402     case XK_KP_6:
9403     case XK_KP_7:
9404     case XK_KP_8:
9405     case XK_KP_9:
9406     {
9407       windows->magnify.data=(key_symbol-XK_KP_0);
9408       break;
9409     }
9410     default:
9411       break;
9412   }
9413   XMakeMagnifyImage(display,windows);
9414 }
9415 \f
9416 /*
9417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9418 %                                                                             %
9419 %                                                                             %
9420 %                                                                             %
9421 +   X M a k e P a n I m a g e                                                 %
9422 %                                                                             %
9423 %                                                                             %
9424 %                                                                             %
9425 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9426 %
9427 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9428 %  icon window.
9429 %
9430 %  The format of the XMakePanImage method is:
9431 %
9432 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9433 %          XWindows *windows,Image *image)
9434 %
9435 %  A description of each parameter follows:
9436 %
9437 %    o display: Specifies a connection to an X server;  returned from
9438 %      XOpenDisplay.
9439 %
9440 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9441 %
9442 %    o windows: Specifies a pointer to a XWindows structure.
9443 %
9444 %    o image: the image.
9445 %
9446 */
9447 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9448   XWindows *windows,Image *image)
9449 {
9450   MagickStatusType
9451     status;
9452
9453   /*
9454     Create and display image for panning icon.
9455   */
9456   XSetCursorState(display,windows,MagickTrue);
9457   XCheckRefreshWindows(display,windows);
9458   windows->pan.x=windows->image.x;
9459   windows->pan.y=windows->image.y;
9460   status=XMakeImage(display,resource_info,&windows->pan,image,
9461     windows->pan.width,windows->pan.height);
9462   if (status == MagickFalse)
9463     ThrowXWindowFatalException(XServerError,image->exception.reason,
9464       image->exception.description);
9465   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9466     windows->pan.pixmap);
9467   (void) XClearWindow(display,windows->pan.id);
9468   XDrawPanRectangle(display,windows);
9469   XSetCursorState(display,windows,MagickFalse);
9470 }
9471 \f
9472 /*
9473 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9474 %                                                                             %
9475 %                                                                             %
9476 %                                                                             %
9477 +   X M a t t a E d i t I m a g e                                             %
9478 %                                                                             %
9479 %                                                                             %
9480 %                                                                             %
9481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9482 %
9483 %  XMatteEditImage() allows the user to interactively change the Matte channel
9484 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9485 %  before the matte information is stored.
9486 %
9487 %  The format of the XMatteEditImage method is:
9488 %
9489 %      MagickBooleanType XMatteEditImage(Display *display,
9490 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
9491 %
9492 %  A description of each parameter follows:
9493 %
9494 %    o display: Specifies a connection to an X server;  returned from
9495 %      XOpenDisplay.
9496 %
9497 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9498 %
9499 %    o windows: Specifies a pointer to a XWindows structure.
9500 %
9501 %    o image: the image; returned from ReadImage.
9502 %
9503 */
9504 static MagickBooleanType XMatteEditImage(Display *display,
9505   XResourceInfo *resource_info,XWindows *windows,Image **image)
9506 {
9507   static char
9508     matte[MaxTextExtent] = "0";
9509
9510   static const char
9511     *MatteEditMenu[] =
9512     {
9513       "Method",
9514       "Border Color",
9515       "Fuzz",
9516       "Matte Value",
9517       "Undo",
9518       "Help",
9519       "Dismiss",
9520       (char *) NULL
9521     };
9522
9523   static const ModeType
9524     MatteEditCommands[] =
9525     {
9526       MatteEditMethod,
9527       MatteEditBorderCommand,
9528       MatteEditFuzzCommand,
9529       MatteEditValueCommand,
9530       MatteEditUndoCommand,
9531       MatteEditHelpCommand,
9532       MatteEditDismissCommand
9533     };
9534
9535   static PaintMethod
9536     method = PointMethod;
9537
9538   static XColor
9539     border_color = { 0, 0, 0, 0, 0, 0 };
9540
9541   char
9542     command[MaxTextExtent],
9543     text[MaxTextExtent];
9544
9545   Cursor
9546     cursor;
9547
9548   int
9549     entry,
9550     id,
9551     x,
9552     x_offset,
9553     y,
9554     y_offset;
9555
9556   register int
9557     i;
9558
9559   register PixelPacket
9560     *q;
9561
9562   unsigned int
9563     height,
9564     width;
9565
9566   unsigned long
9567     state;
9568
9569   XEvent
9570     event;
9571
9572   /*
9573     Map Command widget.
9574   */
9575   (void) CloneString(&windows->command.name,"Matte Edit");
9576   windows->command.data=4;
9577   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9578   (void) XMapRaised(display,windows->command.id);
9579   XClientMessage(display,windows->image.id,windows->im_protocols,
9580     windows->im_update_widget,CurrentTime);
9581   /*
9582     Make cursor.
9583   */
9584   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9585     resource_info->background_color,resource_info->foreground_color);
9586   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9587   /*
9588     Track pointer until button 1 is pressed.
9589   */
9590   XQueryPosition(display,windows->image.id,&x,&y);
9591   (void) XSelectInput(display,windows->image.id,
9592     windows->image.attributes.event_mask | PointerMotionMask);
9593   state=DefaultState;
9594   do
9595   {
9596     if (windows->info.mapped != MagickFalse)
9597       {
9598         /*
9599           Display pointer position.
9600         */
9601         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
9602           x+windows->image.x,y+windows->image.y);
9603         XInfoWidget(display,windows,text);
9604       }
9605     /*
9606       Wait for next event.
9607     */
9608     XScreenEvent(display,windows,&event);
9609     if (event.xany.window == windows->command.id)
9610       {
9611         /*
9612           Select a command from the Command widget.
9613         */
9614         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9615         if (id < 0)
9616           {
9617             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9618             continue;
9619           }
9620         switch (MatteEditCommands[id])
9621         {
9622           case MatteEditMethod:
9623           {
9624             char
9625               **methods;
9626
9627             /*
9628               Select a method from the pop-up menu.
9629             */
9630             methods=GetMagickOptions(MagickMethodOptions);
9631             if (methods == (char **) NULL)
9632               break;
9633             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9634               (const char **) methods,command);
9635             if (entry >= 0)
9636               method=(PaintMethod) ParseMagickOption(MagickMethodOptions,
9637                 MagickFalse,methods[entry]);
9638             methods=DestroyStringList(methods);
9639             break;
9640           }
9641           case MatteEditBorderCommand:
9642           {
9643             const char
9644               *ColorMenu[MaxNumberPens];
9645
9646             int
9647               pen_number;
9648
9649             /*
9650               Initialize menu selections.
9651             */
9652             for (i=0; i < (int) (MaxNumberPens-2); i++)
9653               ColorMenu[i]=resource_info->pen_colors[i];
9654             ColorMenu[MaxNumberPens-2]="Browser...";
9655             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9656             /*
9657               Select a pen color from the pop-up menu.
9658             */
9659             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9660               (const char **) ColorMenu,command);
9661             if (pen_number < 0)
9662               break;
9663             if (pen_number == (MaxNumberPens-2))
9664               {
9665                 static char
9666                   color_name[MaxTextExtent] = "gray";
9667
9668                 /*
9669                   Select a pen color from a dialog.
9670                 */
9671                 resource_info->pen_colors[pen_number]=color_name;
9672                 XColorBrowserWidget(display,windows,"Select",color_name);
9673                 if (*color_name == '\0')
9674                   break;
9675               }
9676             /*
9677               Set border color.
9678             */
9679             (void) XParseColor(display,windows->map_info->colormap,
9680               resource_info->pen_colors[pen_number],&border_color);
9681             break;
9682           }
9683           case MatteEditFuzzCommand:
9684           {
9685             static char
9686               fuzz[MaxTextExtent];
9687
9688             static const char
9689               *FuzzMenu[] =
9690               {
9691                 "0%",
9692                 "2%",
9693                 "5%",
9694                 "10%",
9695                 "15%",
9696                 "Dialog...",
9697                 (char *) NULL,
9698               };
9699
9700             /*
9701               Select a command from the pop-up menu.
9702             */
9703             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9704               command);
9705             if (entry < 0)
9706               break;
9707             if (entry != 5)
9708               {
9709                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*QuantumRange+
9710                   1.0);
9711                 break;
9712               }
9713             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9714             (void) XDialogWidget(display,windows,"Ok",
9715               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9716             if (*fuzz == '\0')
9717               break;
9718             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9719             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9720             break;
9721           }
9722           case MatteEditValueCommand:
9723           {
9724             static char
9725               message[MaxTextExtent];
9726
9727             static const char
9728               *MatteMenu[] =
9729               {
9730                 "Opaque",
9731                 "Transparent",
9732                 "Dialog...",
9733                 (char *) NULL,
9734               };
9735
9736             /*
9737               Select a command from the pop-up menu.
9738             */
9739             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9740               command);
9741             if (entry < 0)
9742               break;
9743             if (entry != 2)
9744               {
9745                 (void) FormatMagickString(matte,MaxTextExtent,QuantumFormat,
9746                   OpaqueOpacity);
9747                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9748                   (void) FormatMagickString(matte,MaxTextExtent,QuantumFormat,
9749                     (Quantum) TransparentOpacity);
9750                 break;
9751               }
9752             (void) FormatMagickString(message,MaxTextExtent,
9753               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9754               QuantumRange);
9755             (void) XDialogWidget(display,windows,"Matte",message,matte);
9756             if (*matte == '\0')
9757               break;
9758             break;
9759           }
9760           case MatteEditUndoCommand:
9761           {
9762             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9763               image);
9764             break;
9765           }
9766           case MatteEditHelpCommand:
9767           {
9768             XTextViewWidget(display,resource_info,windows,MagickFalse,
9769               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9770             break;
9771           }
9772           case MatteEditDismissCommand:
9773           {
9774             /*
9775               Prematurely exit.
9776             */
9777             state|=EscapeState;
9778             state|=ExitState;
9779             break;
9780           }
9781           default:
9782             break;
9783         }
9784         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9785         continue;
9786       }
9787     switch (event.type)
9788     {
9789       case ButtonPress:
9790       {
9791         if (event.xbutton.button != Button1)
9792           break;
9793         if ((event.xbutton.window != windows->image.id) &&
9794             (event.xbutton.window != windows->magnify.id))
9795           break;
9796         /*
9797           Update matte data.
9798         */
9799         x=event.xbutton.x;
9800         y=event.xbutton.y;
9801         (void) XMagickCommand(display,resource_info,windows,
9802           SaveToUndoBufferCommand,image);
9803         state|=UpdateConfigurationState;
9804         break;
9805       }
9806       case ButtonRelease:
9807       {
9808         if (event.xbutton.button != Button1)
9809           break;
9810         if ((event.xbutton.window != windows->image.id) &&
9811             (event.xbutton.window != windows->magnify.id))
9812           break;
9813         /*
9814           Update colormap information.
9815         */
9816         x=event.xbutton.x;
9817         y=event.xbutton.y;
9818         XConfigureImageColormap(display,resource_info,windows,*image);
9819         (void) XConfigureImage(display,resource_info,windows,*image);
9820         XInfoWidget(display,windows,text);
9821         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9822         state&=(~UpdateConfigurationState);
9823         break;
9824       }
9825       case Expose:
9826         break;
9827       case KeyPress:
9828       {
9829         char
9830           command[MaxTextExtent];
9831
9832         KeySym
9833           key_symbol;
9834
9835         if (event.xkey.window == windows->magnify.id)
9836           {
9837             Window
9838               window;
9839
9840             window=windows->magnify.id;
9841             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9842           }
9843         if (event.xkey.window != windows->image.id)
9844           break;
9845         /*
9846           Respond to a user key press.
9847         */
9848         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9849           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9850         switch ((int) key_symbol)
9851         {
9852           case XK_Escape:
9853           case XK_F20:
9854           {
9855             /*
9856               Prematurely exit.
9857             */
9858             state|=ExitState;
9859             break;
9860           }
9861           case XK_F1:
9862           case XK_Help:
9863           {
9864             XTextViewWidget(display,resource_info,windows,MagickFalse,
9865               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9866             break;
9867           }
9868           default:
9869           {
9870             (void) XBell(display,0);
9871             break;
9872           }
9873         }
9874         break;
9875       }
9876       case MotionNotify:
9877       {
9878         /*
9879           Map and unmap Info widget as cursor crosses its boundaries.
9880         */
9881         x=event.xmotion.x;
9882         y=event.xmotion.y;
9883         if (windows->info.mapped != MagickFalse)
9884           {
9885             if ((x < (int) (windows->info.x+windows->info.width)) &&
9886                 (y < (int) (windows->info.y+windows->info.height)))
9887               (void) XWithdrawWindow(display,windows->info.id,
9888                 windows->info.screen);
9889           }
9890         else
9891           if ((x > (int) (windows->info.x+windows->info.width)) ||
9892               (y > (int) (windows->info.y+windows->info.height)))
9893             (void) XMapWindow(display,windows->info.id);
9894         break;
9895       }
9896       default:
9897         break;
9898     }
9899     if (event.xany.window == windows->magnify.id)
9900       {
9901         x=windows->magnify.x-windows->image.x;
9902         y=windows->magnify.y-windows->image.y;
9903       }
9904     x_offset=x;
9905     y_offset=y;
9906     if ((state & UpdateConfigurationState) != 0)
9907       {
9908         ExceptionInfo
9909           *exception;
9910
9911         int
9912           x,
9913           y;
9914
9915         /*
9916           Matte edit is relative to image configuration.
9917         */
9918         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9919           MagickTrue);
9920         XPutPixel(windows->image.ximage,x_offset,y_offset,
9921           windows->pixel_info->background_color.pixel);
9922         width=(unsigned int) (*image)->columns;
9923         height=(unsigned int) (*image)->rows;
9924         x=0;
9925         y=0;
9926         if (windows->image.crop_geometry != (char *) NULL)
9927           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
9928             &width,&height);
9929         x_offset=(int)
9930           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
9931         y_offset=(int)
9932           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
9933         if ((x_offset < 0) || (y_offset < 0))
9934           continue;
9935         if ((x_offset >= (int) (*image)->columns) ||
9936             (y_offset >= (int) (*image)->rows))
9937           continue;
9938         if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9939           return(MagickFalse);
9940         (*image)->matte=MagickTrue;
9941         exception=(&(*image)->exception);
9942         switch (method)
9943         {
9944           case PointMethod:
9945           default:
9946           {
9947             /*
9948               Update matte information using point algorithm.
9949             */
9950             q=GetAuthenticPixels(*image,x_offset,y_offset,1,1,exception);
9951             if (q == (PixelPacket *) NULL)
9952               break;
9953             q->opacity=(Quantum) StringToLong(matte);
9954             (void) SyncAuthenticPixels(*image,exception);
9955             break;
9956           }
9957           case ReplaceMethod:
9958           {
9959             PixelPacket
9960               target;
9961
9962             /*
9963               Update matte information using replace algorithm.
9964             */
9965             (void) GetOneVirtualPixel(*image,x_offset,y_offset,&target,
9966               exception);
9967             for (y=0; y < (long) (*image)->rows; y++)
9968             {
9969               q=GetAuthenticPixels(*image,0,y,(*image)->columns,1,
9970                 &(*image)->exception);
9971               if (q == (PixelPacket *) NULL)
9972                 break;
9973               for (x=0; x < (int) (*image)->columns; x++)
9974               {
9975                 if (IsColorSimilar(*image,q,&target))
9976                   q->opacity=(Quantum) StringToLong(matte);
9977                 q++;
9978               }
9979               if (SyncAuthenticPixels(*image,exception) == MagickFalse)
9980                 break;
9981             }
9982             break;
9983           }
9984           case FloodfillMethod:
9985           case FillToBorderMethod:
9986           {
9987             DrawInfo
9988               *draw_info;
9989
9990             MagickPixelPacket
9991               target;
9992
9993             /*
9994               Update matte information using floodfill algorithm.
9995             */
9996             (void) GetOneVirtualMagickPixel(*image,x_offset,y_offset,&target,
9997               exception);
9998             if (method == FillToBorderMethod)
9999               {
10000                 target.red=(MagickRealType)
10001                   ScaleShortToQuantum(border_color.red);
10002                 target.green=(MagickRealType)
10003                   ScaleShortToQuantum(border_color.green);
10004                 target.blue=(MagickRealType)
10005                   ScaleShortToQuantum(border_color.blue);
10006               }
10007             draw_info=CloneDrawInfo(resource_info->image_info,
10008               (DrawInfo *) NULL);
10009             draw_info->fill.opacity=ClampToQuantum(StringToDouble(matte));
10010             (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
10011               x_offset,y_offset,method == FloodfillMethod ? MagickFalse :
10012               MagickTrue);
10013             draw_info=DestroyDrawInfo(draw_info);
10014             break;
10015           }
10016           case ResetMethod:
10017           {
10018             /*
10019               Update matte information using reset algorithm.
10020             */
10021             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10022               return(MagickFalse);
10023             for (y=0; y < (long) (*image)->rows; y++)
10024             {
10025               q=QueueAuthenticPixels(*image,0,y,(*image)->columns,1,exception);
10026               if (q == (PixelPacket *) NULL)
10027                 break;
10028               for (x=0; x < (int) (*image)->columns; x++)
10029               {
10030                 q->opacity=(Quantum) StringToLong(matte);
10031                 q++;
10032               }
10033               if (SyncAuthenticPixels(*image,exception) == MagickFalse)
10034                 break;
10035             }
10036             if (StringToLong(matte) == OpaqueOpacity)
10037               (*image)->matte=MagickFalse;
10038             break;
10039           }
10040         }
10041         state&=(~UpdateConfigurationState);
10042       }
10043   } while ((state & ExitState) == 0);
10044   (void) XSelectInput(display,windows->image.id,
10045     windows->image.attributes.event_mask);
10046   XSetCursorState(display,windows,MagickFalse);
10047   (void) XFreeCursor(display,cursor);
10048   return(MagickTrue);
10049 }
10050 \f
10051 /*
10052 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10053 %                                                                             %
10054 %                                                                             %
10055 %                                                                             %
10056 +   X O p e n I m a g e                                                       %
10057 %                                                                             %
10058 %                                                                             %
10059 %                                                                             %
10060 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10061 %
10062 %  XOpenImage() loads an image from a file.
10063 %
10064 %  The format of the XOpenImage method is:
10065 %
10066 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10067 %       XWindows *windows,const unsigned int command)
10068 %
10069 %  A description of each parameter follows:
10070 %
10071 %    o display: Specifies a connection to an X server; returned from
10072 %      XOpenDisplay.
10073 %
10074 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10075 %
10076 %    o windows: Specifies a pointer to a XWindows structure.
10077 %
10078 %    o command: A value other than zero indicates that the file is selected
10079 %      from the command line argument list.
10080 %
10081 */
10082 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10083   XWindows *windows,const MagickBooleanType command)
10084 {
10085   const MagickInfo
10086     *magick_info;
10087
10088   ExceptionInfo
10089     *exception;
10090
10091   Image
10092     *nexus;
10093
10094   ImageInfo
10095     *image_info;
10096
10097   static char
10098     filename[MaxTextExtent] = "\0";
10099
10100   /*
10101     Request file name from user.
10102   */
10103   if (command == MagickFalse)
10104     XFileBrowserWidget(display,windows,"Open",filename);
10105   else
10106     {
10107       char
10108         **filelist,
10109         **files;
10110
10111       int
10112         count,
10113         status;
10114
10115       register int
10116         i,
10117         j;
10118
10119       /*
10120         Select next image from the command line.
10121       */
10122       status=XGetCommand(display,windows->image.id,&files,&count);
10123       if (status == 0)
10124         {
10125           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10126           return((Image *) NULL);
10127         }
10128       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10129       if (filelist == (char **) NULL)
10130         {
10131           ThrowXWindowFatalException(ResourceLimitError,
10132             "MemoryAllocationFailed","...");
10133           (void) XFreeStringList(files);
10134           return((Image *) NULL);
10135         }
10136       j=0;
10137       for (i=1; i < count; i++)
10138         if (*files[i] != '-')
10139           filelist[j++]=files[i];
10140       filelist[j]=(char *) NULL;
10141       XListBrowserWidget(display,windows,&windows->widget,
10142         (const char **) filelist,"Load","Select Image to Load:",filename);
10143       filelist=(char **) RelinquishMagickMemory(filelist);
10144       (void) XFreeStringList(files);
10145     }
10146   if (*filename == '\0')
10147     return((Image *) NULL);
10148   image_info=CloneImageInfo(resource_info->image_info);
10149   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10150     (void *) NULL);
10151   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10152   exception=AcquireExceptionInfo();
10153   (void) SetImageInfo(image_info,0,exception);
10154   if (LocaleCompare(image_info->magick,"X") == 0)
10155     {
10156       char
10157         seconds[MaxTextExtent];
10158
10159       /*
10160         User may want to delay the X server screen grab.
10161       */
10162       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10163       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10164         seconds);
10165       if (*seconds == '\0')
10166         return((Image *) NULL);
10167       XDelay(display,(unsigned long) (1000*StringToLong(seconds)));
10168     }
10169   magick_info=GetMagickInfo(image_info->magick,exception);
10170   if ((magick_info != (const MagickInfo *) NULL) &&
10171       (magick_info->raw != MagickFalse))
10172     {
10173       char
10174         geometry[MaxTextExtent];
10175
10176       /*
10177         Request image size from the user.
10178       */
10179       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10180       if (image_info->size != (char *) NULL)
10181         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10182       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10183         geometry);
10184       (void) CloneString(&image_info->size,geometry);
10185     }
10186   /*
10187     Load the image.
10188   */
10189   XSetCursorState(display,windows,MagickTrue);
10190   XCheckRefreshWindows(display,windows);
10191   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10192   nexus=ReadImage(image_info,exception);
10193   CatchException(exception);
10194   XSetCursorState(display,windows,MagickFalse);
10195   if (nexus != (Image *) NULL)
10196     XClientMessage(display,windows->image.id,windows->im_protocols,
10197       windows->im_next_image,CurrentTime);
10198   else
10199     {
10200       char
10201         *text,
10202         **textlist;
10203
10204       /*
10205         Unknown image format.
10206       */
10207       text=FileToString(filename,~0,exception);
10208       if (text == (char *) NULL)
10209         return((Image *) NULL);
10210       textlist=StringToList(text);
10211       if (textlist != (char **) NULL)
10212         {
10213           char
10214             title[MaxTextExtent];
10215
10216           register int
10217             i;
10218
10219           (void) FormatMagickString(title,MaxTextExtent,
10220             "Unknown format: %s",filename);
10221           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10222             (const char **) textlist);
10223           for (i=0; textlist[i] != (char *) NULL; i++)
10224             textlist[i]=DestroyString(textlist[i]);
10225           textlist=(char **) RelinquishMagickMemory(textlist);
10226         }
10227       text=DestroyString(text);
10228     }
10229   exception=DestroyExceptionInfo(exception);
10230   image_info=DestroyImageInfo(image_info);
10231   return(nexus);
10232 }
10233 \f
10234 /*
10235 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10236 %                                                                             %
10237 %                                                                             %
10238 %                                                                             %
10239 +   X P a n I m a g e                                                         %
10240 %                                                                             %
10241 %                                                                             %
10242 %                                                                             %
10243 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10244 %
10245 %  XPanImage() pans the image until the mouse button is released.
10246 %
10247 %  The format of the XPanImage method is:
10248 %
10249 %      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10250 %
10251 %  A description of each parameter follows:
10252 %
10253 %    o display: Specifies a connection to an X server;  returned from
10254 %      XOpenDisplay.
10255 %
10256 %    o windows: Specifies a pointer to a XWindows structure.
10257 %
10258 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10259 %      the entire image is refreshed.
10260 %
10261 */
10262 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10263 {
10264   char
10265     text[MaxTextExtent];
10266
10267   Cursor
10268     cursor;
10269
10270   MagickRealType
10271     x_factor,
10272     y_factor;
10273
10274   RectangleInfo
10275     pan_info;
10276
10277   unsigned long
10278     state;
10279
10280   /*
10281     Define cursor.
10282   */
10283   if ((windows->image.ximage->width > (int) windows->image.width) &&
10284       (windows->image.ximage->height > (int) windows->image.height))
10285     cursor=XCreateFontCursor(display,XC_fleur);
10286   else
10287     if (windows->image.ximage->width > (int) windows->image.width)
10288       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10289     else
10290       if (windows->image.ximage->height > (int) windows->image.height)
10291         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10292       else
10293         cursor=XCreateFontCursor(display,XC_arrow);
10294   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10295   /*
10296     Pan image as pointer moves until the mouse button is released.
10297   */
10298   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10299   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10300   pan_info.width=windows->pan.width*windows->image.width/
10301     windows->image.ximage->width;
10302   pan_info.height=windows->pan.height*windows->image.height/
10303     windows->image.ximage->height;
10304   pan_info.x=0;
10305   pan_info.y=0;
10306   state=UpdateConfigurationState;
10307   do
10308   {
10309     switch (event->type)
10310     {
10311       case ButtonPress:
10312       {
10313         /*
10314           User choose an initial pan location.
10315         */
10316         pan_info.x=event->xbutton.x;
10317         pan_info.y=event->xbutton.y;
10318         state|=UpdateConfigurationState;
10319         break;
10320       }
10321       case ButtonRelease:
10322       {
10323         /*
10324           User has finished panning the image.
10325         */
10326         pan_info.x=event->xbutton.x;
10327         pan_info.y=event->xbutton.y;
10328         state|=UpdateConfigurationState | ExitState;
10329         break;
10330       }
10331       case MotionNotify:
10332       {
10333         pan_info.x=event->xmotion.x;
10334         pan_info.y=event->xmotion.y;
10335         state|=UpdateConfigurationState;
10336       }
10337       default:
10338         break;
10339     }
10340     if ((state & UpdateConfigurationState) != 0)
10341       {
10342         /*
10343           Check boundary conditions.
10344         */
10345         if (pan_info.x < (int) (pan_info.width/2))
10346           pan_info.x=0;
10347         else
10348           pan_info.x=(int) (x_factor*(pan_info.x-(pan_info.width/2)));
10349         if (pan_info.x < 0)
10350           pan_info.x=0;
10351         else
10352           if ((int) (pan_info.x+windows->image.width) >
10353               windows->image.ximage->width)
10354             pan_info.x=(long)
10355               (windows->image.ximage->width-windows->image.width);
10356         if (pan_info.y < (long) (pan_info.height/2))
10357           pan_info.y=0;
10358         else
10359           pan_info.y=(long) (y_factor*(pan_info.y-(pan_info.height/2)));
10360         if (pan_info.y < 0)
10361           pan_info.y=0;
10362         else
10363           if ((int) (pan_info.y+windows->image.height) >
10364               windows->image.ximage->height)
10365             pan_info.y=(long)
10366               (windows->image.ximage->height-windows->image.height);
10367         if ((windows->image.x != (int) pan_info.x) ||
10368             (windows->image.y != (int) pan_info.y))
10369           {
10370             /*
10371               Display image pan offset.
10372             */
10373             windows->image.x=(int) pan_info.x;
10374             windows->image.y=(int) pan_info.y;
10375             (void) FormatMagickString(text,MaxTextExtent," %ux%u%+d%+d ",
10376               windows->image.width,windows->image.height,windows->image.x,
10377               windows->image.y);
10378             XInfoWidget(display,windows,text);
10379             /*
10380               Refresh Image window.
10381             */
10382             XDrawPanRectangle(display,windows);
10383             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10384           }
10385         state&=(~UpdateConfigurationState);
10386       }
10387     /*
10388       Wait for next event.
10389     */
10390     if ((state & ExitState) == 0)
10391       XScreenEvent(display,windows,event);
10392   } while ((state & ExitState) == 0);
10393   /*
10394     Restore cursor.
10395   */
10396   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10397   (void) XFreeCursor(display,cursor);
10398   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10399 }
10400 \f
10401 /*
10402 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10403 %                                                                             %
10404 %                                                                             %
10405 %                                                                             %
10406 +   X P a s t e I m a g e                                                     %
10407 %                                                                             %
10408 %                                                                             %
10409 %                                                                             %
10410 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10411 %
10412 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10413 %  window image at a location the user chooses with the pointer.
10414 %
10415 %  The format of the XPasteImage method is:
10416 %
10417 %      MagickBooleanType XPasteImage(Display *display,
10418 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
10419 %
10420 %  A description of each parameter follows:
10421 %
10422 %    o display: Specifies a connection to an X server;  returned from
10423 %      XOpenDisplay.
10424 %
10425 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10426 %
10427 %    o windows: Specifies a pointer to a XWindows structure.
10428 %
10429 %    o image: the image; returned from ReadImage.
10430 %
10431 */
10432 static MagickBooleanType XPasteImage(Display *display,
10433   XResourceInfo *resource_info,XWindows *windows,Image *image)
10434 {
10435   static const char
10436     *PasteMenu[] =
10437     {
10438       "Operator",
10439       "Help",
10440       "Dismiss",
10441       (char *) NULL
10442     };
10443
10444   static const ModeType
10445     PasteCommands[] =
10446     {
10447       PasteOperatorsCommand,
10448       PasteHelpCommand,
10449       PasteDismissCommand
10450     };
10451
10452   static CompositeOperator
10453     compose = CopyCompositeOp;
10454
10455   char
10456     text[MaxTextExtent];
10457
10458   Cursor
10459     cursor;
10460
10461   Image
10462     *paste_image;
10463
10464   int
10465     entry,
10466     id,
10467     x,
10468     y;
10469
10470   MagickRealType
10471     scale_factor;
10472
10473   RectangleInfo
10474     highlight_info,
10475     paste_info;
10476
10477   unsigned int
10478     height,
10479     width;
10480
10481   unsigned long
10482     state;
10483
10484   XEvent
10485     event;
10486
10487   /*
10488     Copy image.
10489   */
10490   if (resource_info->copy_image == (Image *) NULL)
10491     return(MagickFalse);
10492   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10493     &image->exception);
10494   /*
10495     Map Command widget.
10496   */
10497   (void) CloneString(&windows->command.name,"Paste");
10498   windows->command.data=1;
10499   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10500   (void) XMapRaised(display,windows->command.id);
10501   XClientMessage(display,windows->image.id,windows->im_protocols,
10502     windows->im_update_widget,CurrentTime);
10503   /*
10504     Track pointer until button 1 is pressed.
10505   */
10506   XSetCursorState(display,windows,MagickFalse);
10507   XQueryPosition(display,windows->image.id,&x,&y);
10508   (void) XSelectInput(display,windows->image.id,
10509     windows->image.attributes.event_mask | PointerMotionMask);
10510   paste_info.x=windows->image.x+x;
10511   paste_info.y=windows->image.y+y;
10512   paste_info.width=0;
10513   paste_info.height=0;
10514   cursor=XCreateFontCursor(display,XC_ul_angle);
10515   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10516   state=DefaultState;
10517   do
10518   {
10519     if (windows->info.mapped != MagickFalse)
10520       {
10521         /*
10522           Display pointer position.
10523         */
10524         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
10525           paste_info.x,paste_info.y);
10526         XInfoWidget(display,windows,text);
10527       }
10528     highlight_info=paste_info;
10529     highlight_info.x=paste_info.x-windows->image.x;
10530     highlight_info.y=paste_info.y-windows->image.y;
10531     XHighlightRectangle(display,windows->image.id,
10532       windows->image.highlight_context,&highlight_info);
10533     /*
10534       Wait for next event.
10535     */
10536     XScreenEvent(display,windows,&event);
10537     XHighlightRectangle(display,windows->image.id,
10538       windows->image.highlight_context,&highlight_info);
10539     if (event.xany.window == windows->command.id)
10540       {
10541         /*
10542           Select a command from the Command widget.
10543         */
10544         id=XCommandWidget(display,windows,PasteMenu,&event);
10545         if (id < 0)
10546           continue;
10547         switch (PasteCommands[id])
10548         {
10549           case PasteOperatorsCommand:
10550           {
10551             char
10552               command[MaxTextExtent],
10553               **operators;
10554
10555             /*
10556               Select a command from the pop-up menu.
10557             */
10558             operators=GetMagickOptions(MagickComposeOptions);
10559             if (operators == (char **) NULL)
10560               break;
10561             entry=XMenuWidget(display,windows,PasteMenu[id],
10562               (const char **) operators,command);
10563             if (entry >= 0)
10564               compose=(CompositeOperator) ParseMagickOption(
10565                 MagickComposeOptions,MagickFalse,operators[entry]);
10566             operators=DestroyStringList(operators);
10567             break;
10568           }
10569           case PasteHelpCommand:
10570           {
10571             XTextViewWidget(display,resource_info,windows,MagickFalse,
10572               "Help Viewer - Image Composite",ImagePasteHelp);
10573             break;
10574           }
10575           case PasteDismissCommand:
10576           {
10577             /*
10578               Prematurely exit.
10579             */
10580             state|=EscapeState;
10581             state|=ExitState;
10582             break;
10583           }
10584           default:
10585             break;
10586         }
10587         continue;
10588       }
10589     switch (event.type)
10590     {
10591       case ButtonPress:
10592       {
10593         if (image->debug != MagickFalse)
10594           (void) LogMagickEvent(X11Event,GetMagickModule(),
10595             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10596             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10597         if (event.xbutton.button != Button1)
10598           break;
10599         if (event.xbutton.window != windows->image.id)
10600           break;
10601         /*
10602           Paste rectangle is relative to image configuration.
10603         */
10604         width=(unsigned int) image->columns;
10605         height=(unsigned int) image->rows;
10606         x=0;
10607         y=0;
10608         if (windows->image.crop_geometry != (char *) NULL)
10609           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10610             &width,&height);
10611         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10612         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10613         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10614         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10615         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10616         paste_info.x=windows->image.x+event.xbutton.x;
10617         paste_info.y=windows->image.y+event.xbutton.y;
10618         break;
10619       }
10620       case ButtonRelease:
10621       {
10622         if (image->debug != MagickFalse)
10623           (void) LogMagickEvent(X11Event,GetMagickModule(),
10624             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10625             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10626         if (event.xbutton.button != Button1)
10627           break;
10628         if (event.xbutton.window != windows->image.id)
10629           break;
10630         if ((paste_info.width != 0) && (paste_info.height != 0))
10631           {
10632             /*
10633               User has selected the location of the paste image.
10634             */
10635             paste_info.x=windows->image.x+event.xbutton.x;
10636             paste_info.y=windows->image.y+event.xbutton.y;
10637             state|=ExitState;
10638           }
10639         break;
10640       }
10641       case Expose:
10642         break;
10643       case KeyPress:
10644       {
10645         char
10646           command[MaxTextExtent];
10647
10648         KeySym
10649           key_symbol;
10650
10651         int
10652           length;
10653
10654         if (event.xkey.window != windows->image.id)
10655           break;
10656         /*
10657           Respond to a user key press.
10658         */
10659         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10660           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10661         *(command+length)='\0';
10662         if (image->debug != MagickFalse)
10663           (void) LogMagickEvent(X11Event,GetMagickModule(),
10664             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10665         switch ((int) key_symbol)
10666         {
10667           case XK_Escape:
10668           case XK_F20:
10669           {
10670             /*
10671               Prematurely exit.
10672             */
10673             paste_image=DestroyImage(paste_image);
10674             state|=EscapeState;
10675             state|=ExitState;
10676             break;
10677           }
10678           case XK_F1:
10679           case XK_Help:
10680           {
10681             (void) XSetFunction(display,windows->image.highlight_context,
10682               GXcopy);
10683             XTextViewWidget(display,resource_info,windows,MagickFalse,
10684               "Help Viewer - Image Composite",ImagePasteHelp);
10685             (void) XSetFunction(display,windows->image.highlight_context,
10686               GXinvert);
10687             break;
10688           }
10689           default:
10690           {
10691             (void) XBell(display,0);
10692             break;
10693           }
10694         }
10695         break;
10696       }
10697       case MotionNotify:
10698       {
10699         /*
10700           Map and unmap Info widget as text cursor crosses its boundaries.
10701         */
10702         x=event.xmotion.x;
10703         y=event.xmotion.y;
10704         if (windows->info.mapped != MagickFalse)
10705           {
10706             if ((x < (int) (windows->info.x+windows->info.width)) &&
10707                 (y < (int) (windows->info.y+windows->info.height)))
10708               (void) XWithdrawWindow(display,windows->info.id,
10709                 windows->info.screen);
10710           }
10711         else
10712           if ((x > (int) (windows->info.x+windows->info.width)) ||
10713               (y > (int) (windows->info.y+windows->info.height)))
10714             (void) XMapWindow(display,windows->info.id);
10715         paste_info.x=windows->image.x+x;
10716         paste_info.y=windows->image.y+y;
10717         break;
10718       }
10719       default:
10720       {
10721         if (image->debug != MagickFalse)
10722           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10723             event.type);
10724         break;
10725       }
10726     }
10727   } while ((state & ExitState) == 0);
10728   (void) XSelectInput(display,windows->image.id,
10729     windows->image.attributes.event_mask);
10730   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10731   XSetCursorState(display,windows,MagickFalse);
10732   (void) XFreeCursor(display,cursor);
10733   if ((state & EscapeState) != 0)
10734     return(MagickTrue);
10735   /*
10736     Image pasting is relative to image configuration.
10737   */
10738   XSetCursorState(display,windows,MagickTrue);
10739   XCheckRefreshWindows(display,windows);
10740   width=(unsigned int) image->columns;
10741   height=(unsigned int) image->rows;
10742   x=0;
10743   y=0;
10744   if (windows->image.crop_geometry != (char *) NULL)
10745     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10746   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10747   paste_info.x+=x;
10748   paste_info.x=(int) (scale_factor*paste_info.x+0.5);
10749   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10750   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10751   paste_info.y+=y;
10752   paste_info.y=(int) (scale_factor*paste_info.y*scale_factor+0.5);
10753   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10754   /*
10755     Paste image with X Image window.
10756   */
10757   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10758   paste_image=DestroyImage(paste_image);
10759   XSetCursorState(display,windows,MagickFalse);
10760   /*
10761     Update image colormap.
10762   */
10763   XConfigureImageColormap(display,resource_info,windows,image);
10764   (void) XConfigureImage(display,resource_info,windows,image);
10765   return(MagickTrue);
10766 }
10767 \f
10768 /*
10769 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10770 %                                                                             %
10771 %                                                                             %
10772 %                                                                             %
10773 +   X P r i n t I m a g e                                                     %
10774 %                                                                             %
10775 %                                                                             %
10776 %                                                                             %
10777 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10778 %
10779 %  XPrintImage() prints an image to a Postscript printer.
10780 %
10781 %  The format of the XPrintImage method is:
10782 %
10783 %      MagickBooleanType XPrintImage(Display *display,
10784 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
10785 %
10786 %  A description of each parameter follows:
10787 %
10788 %    o display: Specifies a connection to an X server; returned from
10789 %      XOpenDisplay.
10790 %
10791 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10792 %
10793 %    o windows: Specifies a pointer to a XWindows structure.
10794 %
10795 %    o image: the image.
10796 %
10797 */
10798 static MagickBooleanType XPrintImage(Display *display,
10799   XResourceInfo *resource_info,XWindows *windows,Image *image)
10800 {
10801   char
10802     filename[MaxTextExtent],
10803     geometry[MaxTextExtent];
10804
10805   Image
10806     *print_image;
10807
10808   ImageInfo
10809     *image_info;
10810
10811   MagickStatusType
10812     status;
10813
10814   /*
10815     Request Postscript page geometry from user.
10816   */
10817   image_info=CloneImageInfo(resource_info->image_info);
10818   (void) FormatMagickString(geometry,MaxTextExtent,"Letter");
10819   if (image_info->page != (char *) NULL)
10820     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10821   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10822     "Select Postscript Page Geometry:",geometry);
10823   if (*geometry == '\0')
10824     return(MagickTrue);
10825   image_info->page=GetPageGeometry(geometry);
10826   /*
10827     Apply image transforms.
10828   */
10829   XSetCursorState(display,windows,MagickTrue);
10830   XCheckRefreshWindows(display,windows);
10831   print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10832   if (print_image == (Image *) NULL)
10833     return(MagickFalse);
10834   (void) FormatMagickString(geometry,MaxTextExtent,"%dx%d!",
10835     windows->image.ximage->width,windows->image.ximage->height);
10836   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10837   /*
10838     Print image.
10839   */
10840   (void) AcquireUniqueFilename(filename);
10841   (void) FormatMagickString(print_image->filename,MaxTextExtent,"print:%s",
10842     filename);
10843   status=WriteImage(image_info,print_image);
10844   (void) RelinquishUniqueFileResource(filename);
10845   print_image=DestroyImage(print_image);
10846   image_info=DestroyImageInfo(image_info);
10847   XSetCursorState(display,windows,MagickFalse);
10848   return(status != 0 ? MagickTrue : MagickFalse);
10849 }
10850 \f
10851 /*
10852 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10853 %                                                                             %
10854 %                                                                             %
10855 %                                                                             %
10856 +   X R O I I m a g e                                                         %
10857 %                                                                             %
10858 %                                                                             %
10859 %                                                                             %
10860 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10861 %
10862 %  XROIImage() applies an image processing technique to a region of interest.
10863 %
10864 %  The format of the XROIImage method is:
10865 %
10866 %      MagickBooleanType XROIImage(Display *display,
10867 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
10868 %
10869 %  A description of each parameter follows:
10870 %
10871 %    o display: Specifies a connection to an X server; returned from
10872 %      XOpenDisplay.
10873 %
10874 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10875 %
10876 %    o windows: Specifies a pointer to a XWindows structure.
10877 %
10878 %    o image: the image; returned from ReadImage.
10879 %
10880 */
10881 static MagickBooleanType XROIImage(Display *display,
10882   XResourceInfo *resource_info,XWindows *windows,Image **image)
10883 {
10884 #define ApplyMenus  7
10885
10886   static const char
10887     *ROIMenu[] =
10888     {
10889       "Help",
10890       "Dismiss",
10891       (char *) NULL
10892     },
10893     *ApplyMenu[] =
10894     {
10895       "File",
10896       "Edit",
10897       "Transform",
10898       "Enhance",
10899       "Effects",
10900       "F/X",
10901       "Miscellany",
10902       "Help",
10903       "Dismiss",
10904       (char *) NULL
10905     },
10906     *FileMenu[] =
10907     {
10908       "Save...",
10909       "Print...",
10910       (char *) NULL
10911     },
10912     *EditMenu[] =
10913     {
10914       "Undo",
10915       "Redo",
10916       (char *) NULL
10917     },
10918     *TransformMenu[] =
10919     {
10920       "Flop",
10921       "Flip",
10922       "Rotate Right",
10923       "Rotate Left",
10924       (char *) NULL
10925     },
10926     *EnhanceMenu[] =
10927     {
10928       "Hue...",
10929       "Saturation...",
10930       "Brightness...",
10931       "Gamma...",
10932       "Spiff",
10933       "Dull",
10934       "Contrast Stretch...",
10935       "Sigmoidal Contrast...",
10936       "Normalize",
10937       "Equalize",
10938       "Negate",
10939       "Grayscale",
10940       "Map...",
10941       "Quantize...",
10942       (char *) NULL
10943     },
10944     *EffectsMenu[] =
10945     {
10946       "Despeckle",
10947       "Emboss",
10948       "Reduce Noise",
10949       "Add Noise",
10950       "Sharpen...",
10951       "Blur...",
10952       "Threshold...",
10953       "Edge Detect...",
10954       "Spread...",
10955       "Shade...",
10956       "Raise...",
10957       "Segment...",
10958       (char *) NULL
10959     },
10960     *FXMenu[] =
10961     {
10962       "Solarize...",
10963       "Sepia Tone...",
10964       "Swirl...",
10965       "Implode...",
10966       "Vignette...",
10967       "Wave...",
10968       "Oil Paint...",
10969       "Charcoal Draw...",
10970       (char *) NULL
10971     },
10972     *MiscellanyMenu[] =
10973     {
10974       "Image Info",
10975       "Zoom Image",
10976       "Show Preview...",
10977       "Show Histogram",
10978       "Show Matte",
10979       (char *) NULL
10980     };
10981
10982   static const char
10983     **Menus[ApplyMenus] =
10984     {
10985       FileMenu,
10986       EditMenu,
10987       TransformMenu,
10988       EnhanceMenu,
10989       EffectsMenu,
10990       FXMenu,
10991       MiscellanyMenu
10992     };
10993
10994   static const CommandType
10995     ApplyCommands[] =
10996     {
10997       NullCommand,
10998       NullCommand,
10999       NullCommand,
11000       NullCommand,
11001       NullCommand,
11002       NullCommand,
11003       NullCommand,
11004       HelpCommand,
11005       QuitCommand
11006     },
11007     FileCommands[] =
11008     {
11009       SaveCommand,
11010       PrintCommand
11011     },
11012     EditCommands[] =
11013     {
11014       UndoCommand,
11015       RedoCommand
11016     },
11017     TransformCommands[] =
11018     {
11019       FlopCommand,
11020       FlipCommand,
11021       RotateRightCommand,
11022       RotateLeftCommand
11023     },
11024     EnhanceCommands[] =
11025     {
11026       HueCommand,
11027       SaturationCommand,
11028       BrightnessCommand,
11029       GammaCommand,
11030       SpiffCommand,
11031       DullCommand,
11032       ContrastStretchCommand,
11033       SigmoidalContrastCommand,
11034       NormalizeCommand,
11035       EqualizeCommand,
11036       NegateCommand,
11037       GrayscaleCommand,
11038       MapCommand,
11039       QuantizeCommand
11040     },
11041     EffectsCommands[] =
11042     {
11043       DespeckleCommand,
11044       EmbossCommand,
11045       ReduceNoiseCommand,
11046       AddNoiseCommand,
11047       SharpenCommand,
11048       BlurCommand,
11049       EdgeDetectCommand,
11050       SpreadCommand,
11051       ShadeCommand,
11052       RaiseCommand,
11053       SegmentCommand
11054     },
11055     FXCommands[] =
11056     {
11057       SolarizeCommand,
11058       SepiaToneCommand,
11059       SwirlCommand,
11060       ImplodeCommand,
11061       VignetteCommand,
11062       WaveCommand,
11063       OilPaintCommand,
11064       CharcoalDrawCommand
11065     },
11066     MiscellanyCommands[] =
11067     {
11068       InfoCommand,
11069       ZoomCommand,
11070       ShowPreviewCommand,
11071       ShowHistogramCommand,
11072       ShowMatteCommand
11073     },
11074     ROICommands[] =
11075     {
11076       ROIHelpCommand,
11077       ROIDismissCommand
11078     };
11079
11080   static const CommandType
11081     *Commands[ApplyMenus] =
11082     {
11083       FileCommands,
11084       EditCommands,
11085       TransformCommands,
11086       EnhanceCommands,
11087       EffectsCommands,
11088       FXCommands,
11089       MiscellanyCommands
11090     };
11091
11092   char
11093     command[MaxTextExtent],
11094     text[MaxTextExtent];
11095
11096   CommandType
11097     command_type;
11098
11099   Cursor
11100     cursor;
11101
11102   Image
11103     *roi_image;
11104
11105   int
11106     entry,
11107     id,
11108     x,
11109     y;
11110
11111   MagickRealType
11112     scale_factor;
11113
11114   MagickProgressMonitor
11115     progress_monitor;
11116
11117   RectangleInfo
11118     crop_info,
11119     highlight_info,
11120     roi_info;
11121
11122   unsigned int
11123     height,
11124     width;
11125
11126   unsigned long
11127     state;
11128
11129   XEvent
11130     event;
11131
11132   /*
11133     Map Command widget.
11134   */
11135   (void) CloneString(&windows->command.name,"ROI");
11136   windows->command.data=0;
11137   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11138   (void) XMapRaised(display,windows->command.id);
11139   XClientMessage(display,windows->image.id,windows->im_protocols,
11140     windows->im_update_widget,CurrentTime);
11141   /*
11142     Track pointer until button 1 is pressed.
11143   */
11144   XQueryPosition(display,windows->image.id,&x,&y);
11145   (void) XSelectInput(display,windows->image.id,
11146     windows->image.attributes.event_mask | PointerMotionMask);
11147   roi_info.x=windows->image.x+x;
11148   roi_info.y=windows->image.y+y;
11149   roi_info.width=0;
11150   roi_info.height=0;
11151   cursor=XCreateFontCursor(display,XC_fleur);
11152   state=DefaultState;
11153   do
11154   {
11155     if (windows->info.mapped != MagickFalse)
11156       {
11157         /*
11158           Display pointer position.
11159         */
11160         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
11161           roi_info.x,roi_info.y);
11162         XInfoWidget(display,windows,text);
11163       }
11164     /*
11165       Wait for next event.
11166     */
11167     XScreenEvent(display,windows,&event);
11168     if (event.xany.window == windows->command.id)
11169       {
11170         /*
11171           Select a command from the Command widget.
11172         */
11173         id=XCommandWidget(display,windows,ROIMenu,&event);
11174         if (id < 0)
11175           continue;
11176         switch (ROICommands[id])
11177         {
11178           case ROIHelpCommand:
11179           {
11180             XTextViewWidget(display,resource_info,windows,MagickFalse,
11181               "Help Viewer - Region of Interest",ImageROIHelp);
11182             break;
11183           }
11184           case ROIDismissCommand:
11185           {
11186             /*
11187               Prematurely exit.
11188             */
11189             state|=EscapeState;
11190             state|=ExitState;
11191             break;
11192           }
11193           default:
11194             break;
11195         }
11196         continue;
11197       }
11198     switch (event.type)
11199     {
11200       case ButtonPress:
11201       {
11202         if (event.xbutton.button != Button1)
11203           break;
11204         if (event.xbutton.window != windows->image.id)
11205           break;
11206         /*
11207           Note first corner of region of interest rectangle-- exit loop.
11208         */
11209         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11210         roi_info.x=windows->image.x+event.xbutton.x;
11211         roi_info.y=windows->image.y+event.xbutton.y;
11212         state|=ExitState;
11213         break;
11214       }
11215       case ButtonRelease:
11216         break;
11217       case Expose:
11218         break;
11219       case KeyPress:
11220       {
11221         KeySym
11222           key_symbol;
11223
11224         if (event.xkey.window != windows->image.id)
11225           break;
11226         /*
11227           Respond to a user key press.
11228         */
11229         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11230           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11231         switch ((int) key_symbol)
11232         {
11233           case XK_Escape:
11234           case XK_F20:
11235           {
11236             /*
11237               Prematurely exit.
11238             */
11239             state|=EscapeState;
11240             state|=ExitState;
11241             break;
11242           }
11243           case XK_F1:
11244           case XK_Help:
11245           {
11246             XTextViewWidget(display,resource_info,windows,MagickFalse,
11247               "Help Viewer - Region of Interest",ImageROIHelp);
11248             break;
11249           }
11250           default:
11251           {
11252             (void) XBell(display,0);
11253             break;
11254           }
11255         }
11256         break;
11257       }
11258       case MotionNotify:
11259       {
11260         /*
11261           Map and unmap Info widget as text cursor crosses its boundaries.
11262         */
11263         x=event.xmotion.x;
11264         y=event.xmotion.y;
11265         if (windows->info.mapped != MagickFalse)
11266           {
11267             if ((x < (int) (windows->info.x+windows->info.width)) &&
11268                 (y < (int) (windows->info.y+windows->info.height)))
11269               (void) XWithdrawWindow(display,windows->info.id,
11270                 windows->info.screen);
11271           }
11272         else
11273           if ((x > (int) (windows->info.x+windows->info.width)) ||
11274               (y > (int) (windows->info.y+windows->info.height)))
11275             (void) XMapWindow(display,windows->info.id);
11276         roi_info.x=windows->image.x+x;
11277         roi_info.y=windows->image.y+y;
11278         break;
11279       }
11280       default:
11281         break;
11282     }
11283   } while ((state & ExitState) == 0);
11284   (void) XSelectInput(display,windows->image.id,
11285     windows->image.attributes.event_mask);
11286   if ((state & EscapeState) != 0)
11287     {
11288       /*
11289         User want to exit without region of interest.
11290       */
11291       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11292       (void) XFreeCursor(display,cursor);
11293       return(MagickTrue);
11294     }
11295   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11296   do
11297   {
11298     /*
11299       Size rectangle as pointer moves until the mouse button is released.
11300     */
11301     x=(int) roi_info.x;
11302     y=(int) roi_info.y;
11303     roi_info.width=0;
11304     roi_info.height=0;
11305     state=DefaultState;
11306     do
11307     {
11308       highlight_info=roi_info;
11309       highlight_info.x=roi_info.x-windows->image.x;
11310       highlight_info.y=roi_info.y-windows->image.y;
11311       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11312         {
11313           /*
11314             Display info and draw region of interest rectangle.
11315           */
11316           if (windows->info.mapped == MagickFalse)
11317             (void) XMapWindow(display,windows->info.id);
11318           (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
11319             roi_info.width,roi_info.height,roi_info.x,roi_info.y);
11320           XInfoWidget(display,windows,text);
11321           XHighlightRectangle(display,windows->image.id,
11322             windows->image.highlight_context,&highlight_info);
11323         }
11324       else
11325         if (windows->info.mapped != MagickFalse)
11326           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11327       /*
11328         Wait for next event.
11329       */
11330       XScreenEvent(display,windows,&event);
11331       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11332         XHighlightRectangle(display,windows->image.id,
11333           windows->image.highlight_context,&highlight_info);
11334       switch (event.type)
11335       {
11336         case ButtonPress:
11337         {
11338           roi_info.x=windows->image.x+event.xbutton.x;
11339           roi_info.y=windows->image.y+event.xbutton.y;
11340           break;
11341         }
11342         case ButtonRelease:
11343         {
11344           /*
11345             User has committed to region of interest rectangle.
11346           */
11347           roi_info.x=windows->image.x+event.xbutton.x;
11348           roi_info.y=windows->image.y+event.xbutton.y;
11349           XSetCursorState(display,windows,MagickFalse);
11350           state|=ExitState;
11351           if (LocaleCompare(windows->command.name,"Apply") == 0)
11352             break;
11353           (void) CloneString(&windows->command.name,"Apply");
11354           windows->command.data=ApplyMenus;
11355           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11356           break;
11357         }
11358         case Expose:
11359           break;
11360         case MotionNotify:
11361         {
11362           roi_info.x=windows->image.x+event.xmotion.x;
11363           roi_info.y=windows->image.y+event.xmotion.y;
11364         }
11365         default:
11366           break;
11367       }
11368       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11369           ((state & ExitState) != 0))
11370         {
11371           /*
11372             Check boundary conditions.
11373           */
11374           if (roi_info.x < 0)
11375             roi_info.x=0;
11376           else
11377             if (roi_info.x > (int) windows->image.ximage->width)
11378               roi_info.x=windows->image.ximage->width;
11379           if ((int) roi_info.x < x)
11380             roi_info.width=(unsigned int) (x-roi_info.x);
11381           else
11382             {
11383               roi_info.width=(unsigned int) (roi_info.x-x);
11384               roi_info.x=x;
11385             }
11386           if (roi_info.y < 0)
11387             roi_info.y=0;
11388           else
11389             if (roi_info.y > (int) windows->image.ximage->height)
11390               roi_info.y=windows->image.ximage->height;
11391           if ((int) roi_info.y < y)
11392             roi_info.height=(unsigned int) (y-roi_info.y);
11393           else
11394             {
11395               roi_info.height=(unsigned int) (roi_info.y-y);
11396               roi_info.y=y;
11397             }
11398         }
11399     } while ((state & ExitState) == 0);
11400     /*
11401       Wait for user to grab a corner of the rectangle or press return.
11402     */
11403     state=DefaultState;
11404     command_type=NullCommand;
11405     (void) XMapWindow(display,windows->info.id);
11406     do
11407     {
11408       if (windows->info.mapped != MagickFalse)
11409         {
11410           /*
11411             Display pointer position.
11412           */
11413           (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
11414             roi_info.width,roi_info.height,roi_info.x,roi_info.y);
11415           XInfoWidget(display,windows,text);
11416         }
11417       highlight_info=roi_info;
11418       highlight_info.x=roi_info.x-windows->image.x;
11419       highlight_info.y=roi_info.y-windows->image.y;
11420       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11421         {
11422           state|=EscapeState;
11423           state|=ExitState;
11424           break;
11425         }
11426       if ((state & UpdateRegionState) != 0)
11427         {
11428           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11429           switch (command_type)
11430           {
11431             case UndoCommand:
11432             case RedoCommand:
11433             {
11434               (void) XMagickCommand(display,resource_info,windows,command_type,
11435                 image);
11436               break;
11437             }
11438             default:
11439             {
11440               /*
11441                 Region of interest is relative to image configuration.
11442               */
11443               progress_monitor=SetImageProgressMonitor(*image,
11444                 (MagickProgressMonitor) NULL,(*image)->client_data);
11445               crop_info=roi_info;
11446               width=(unsigned int) (*image)->columns;
11447               height=(unsigned int) (*image)->rows;
11448               x=0;
11449               y=0;
11450               if (windows->image.crop_geometry != (char *) NULL)
11451                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11452                   &width,&height);
11453               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11454               crop_info.x+=x;
11455               crop_info.x=(int) (scale_factor*crop_info.x+0.5);
11456               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11457               scale_factor=(MagickRealType)
11458                 height/windows->image.ximage->height;
11459               crop_info.y+=y;
11460               crop_info.y=(int) (scale_factor*crop_info.y+0.5);
11461               crop_info.height=(unsigned int)
11462                 (scale_factor*crop_info.height+0.5);
11463               roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11464               (void) SetImageProgressMonitor(*image,progress_monitor,
11465                 (*image)->client_data);
11466               if (roi_image == (Image *) NULL)
11467                 continue;
11468               /*
11469                 Apply image processing technique to the region of interest.
11470               */
11471               windows->image.orphan=MagickTrue;
11472               (void) XMagickCommand(display,resource_info,windows,command_type,
11473                 &roi_image);
11474               progress_monitor=SetImageProgressMonitor(*image,
11475                 (MagickProgressMonitor) NULL,(*image)->client_data);
11476               (void) XMagickCommand(display,resource_info,windows,
11477                 SaveToUndoBufferCommand,image);
11478               windows->image.orphan=MagickFalse;
11479               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11480                 crop_info.x,crop_info.y);
11481               roi_image=DestroyImage(roi_image);
11482               (void) SetImageProgressMonitor(*image,progress_monitor,
11483                 (*image)->client_data);
11484               break;
11485             }
11486           }
11487           if (command_type != InfoCommand)
11488             {
11489               XConfigureImageColormap(display,resource_info,windows,*image);
11490               (void) XConfigureImage(display,resource_info,windows,*image);
11491             }
11492           XCheckRefreshWindows(display,windows);
11493           XInfoWidget(display,windows,text);
11494           (void) XSetFunction(display,windows->image.highlight_context,
11495             GXinvert);
11496           state&=(~UpdateRegionState);
11497         }
11498       XHighlightRectangle(display,windows->image.id,
11499         windows->image.highlight_context,&highlight_info);
11500       XScreenEvent(display,windows,&event);
11501       if (event.xany.window == windows->command.id)
11502         {
11503           /*
11504             Select a command from the Command widget.
11505           */
11506           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11507           command_type=NullCommand;
11508           id=XCommandWidget(display,windows,ApplyMenu,&event);
11509           if (id >= 0)
11510             {
11511               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11512               command_type=ApplyCommands[id];
11513               if (id < ApplyMenus)
11514                 {
11515                   /*
11516                     Select a command from a pop-up menu.
11517                   */
11518                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11519                     (const char **) Menus[id],command);
11520                   if (entry >= 0)
11521                     {
11522                       (void) CopyMagickString(command,Menus[id][entry],
11523                         MaxTextExtent);
11524                       command_type=Commands[id][entry];
11525                     }
11526                 }
11527             }
11528           (void) XSetFunction(display,windows->image.highlight_context,
11529             GXinvert);
11530           XHighlightRectangle(display,windows->image.id,
11531             windows->image.highlight_context,&highlight_info);
11532           if (command_type == HelpCommand)
11533             {
11534               (void) XSetFunction(display,windows->image.highlight_context,
11535                 GXcopy);
11536               XTextViewWidget(display,resource_info,windows,MagickFalse,
11537                 "Help Viewer - Region of Interest",ImageROIHelp);
11538               (void) XSetFunction(display,windows->image.highlight_context,
11539                 GXinvert);
11540               continue;
11541             }
11542           if (command_type == QuitCommand)
11543             {
11544               /*
11545                 exit.
11546               */
11547               state|=EscapeState;
11548               state|=ExitState;
11549               continue;
11550             }
11551           if (command_type != NullCommand)
11552             state|=UpdateRegionState;
11553           continue;
11554         }
11555       XHighlightRectangle(display,windows->image.id,
11556         windows->image.highlight_context,&highlight_info);
11557       switch (event.type)
11558       {
11559         case ButtonPress:
11560         {
11561           x=windows->image.x;
11562           y=windows->image.y;
11563           if (event.xbutton.button != Button1)
11564             break;
11565           if (event.xbutton.window != windows->image.id)
11566             break;
11567           x=windows->image.x+event.xbutton.x;
11568           y=windows->image.y+event.xbutton.y;
11569           if ((x < (int) (roi_info.x+RoiDelta)) &&
11570               (x > (int) (roi_info.x-RoiDelta)) &&
11571               (y < (int) (roi_info.y+RoiDelta)) &&
11572               (y > (int) (roi_info.y-RoiDelta)))
11573             {
11574               roi_info.x=(long) (roi_info.x+roi_info.width);
11575               roi_info.y=(long) (roi_info.y+roi_info.height);
11576               state|=UpdateConfigurationState;
11577               break;
11578             }
11579           if ((x < (int) (roi_info.x+RoiDelta)) &&
11580               (x > (int) (roi_info.x-RoiDelta)) &&
11581               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11582               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11583             {
11584               roi_info.x=(long) (roi_info.x+roi_info.width);
11585               state|=UpdateConfigurationState;
11586               break;
11587             }
11588           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11589               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11590               (y < (int) (roi_info.y+RoiDelta)) &&
11591               (y > (int) (roi_info.y-RoiDelta)))
11592             {
11593               roi_info.y=(long) (roi_info.y+roi_info.height);
11594               state|=UpdateConfigurationState;
11595               break;
11596             }
11597           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11598               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11599               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11600               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11601             {
11602               state|=UpdateConfigurationState;
11603               break;
11604             }
11605         }
11606         case ButtonRelease:
11607         {
11608           if (event.xbutton.window == windows->pan.id)
11609             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11610                 (highlight_info.y != crop_info.y-windows->image.y))
11611               XHighlightRectangle(display,windows->image.id,
11612                 windows->image.highlight_context,&highlight_info);
11613           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11614             event.xbutton.time);
11615           break;
11616         }
11617         case Expose:
11618         {
11619           if (event.xexpose.window == windows->image.id)
11620             if (event.xexpose.count == 0)
11621               {
11622                 event.xexpose.x=(int) highlight_info.x;
11623                 event.xexpose.y=(int) highlight_info.y;
11624                 event.xexpose.width=(int) highlight_info.width;
11625                 event.xexpose.height=(int) highlight_info.height;
11626                 XRefreshWindow(display,&windows->image,&event);
11627               }
11628           if (event.xexpose.window == windows->info.id)
11629             if (event.xexpose.count == 0)
11630               XInfoWidget(display,windows,text);
11631           break;
11632         }
11633         case KeyPress:
11634         {
11635           KeySym
11636             key_symbol;
11637
11638           if (event.xkey.window != windows->image.id)
11639             break;
11640           /*
11641             Respond to a user key press.
11642           */
11643           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11644             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11645           switch ((int) key_symbol)
11646           {
11647             case XK_Shift_L:
11648             case XK_Shift_R:
11649               break;
11650             case XK_Escape:
11651             case XK_F20:
11652               state|=EscapeState;
11653             case XK_Return:
11654             {
11655               state|=ExitState;
11656               break;
11657             }
11658             case XK_Home:
11659             case XK_KP_Home:
11660             {
11661               roi_info.x=(long) (windows->image.width/2L-roi_info.width/2L);
11662               roi_info.y=(long) (windows->image.height/2L-roi_info.height/2L);
11663               break;
11664             }
11665             case XK_Left:
11666             case XK_KP_Left:
11667             {
11668               roi_info.x--;
11669               break;
11670             }
11671             case XK_Up:
11672             case XK_KP_Up:
11673             case XK_Next:
11674             {
11675               roi_info.y--;
11676               break;
11677             }
11678             case XK_Right:
11679             case XK_KP_Right:
11680             {
11681               roi_info.x++;
11682               break;
11683             }
11684             case XK_Prior:
11685             case XK_Down:
11686             case XK_KP_Down:
11687             {
11688               roi_info.y++;
11689               break;
11690             }
11691             case XK_F1:
11692             case XK_Help:
11693             {
11694               (void) XSetFunction(display,windows->image.highlight_context,
11695                 GXcopy);
11696               XTextViewWidget(display,resource_info,windows,MagickFalse,
11697                 "Help Viewer - Region of Interest",ImageROIHelp);
11698               (void) XSetFunction(display,windows->image.highlight_context,
11699                 GXinvert);
11700               break;
11701             }
11702             default:
11703             {
11704               command_type=XImageWindowCommand(display,resource_info,windows,
11705                 event.xkey.state,key_symbol,image);
11706               if (command_type != NullCommand)
11707                 state|=UpdateRegionState;
11708               break;
11709             }
11710           }
11711           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11712             event.xkey.time);
11713           break;
11714         }
11715         case KeyRelease:
11716           break;
11717         case MotionNotify:
11718         {
11719           if (event.xbutton.window != windows->image.id)
11720             break;
11721           /*
11722             Map and unmap Info widget as text cursor crosses its boundaries.
11723           */
11724           x=event.xmotion.x;
11725           y=event.xmotion.y;
11726           if (windows->info.mapped != MagickFalse)
11727             {
11728               if ((x < (int) (windows->info.x+windows->info.width)) &&
11729                   (y < (int) (windows->info.y+windows->info.height)))
11730                 (void) XWithdrawWindow(display,windows->info.id,
11731                   windows->info.screen);
11732             }
11733           else
11734             if ((x > (int) (windows->info.x+windows->info.width)) ||
11735                 (y > (int) (windows->info.y+windows->info.height)))
11736               (void) XMapWindow(display,windows->info.id);
11737           roi_info.x=windows->image.x+event.xmotion.x;
11738           roi_info.y=windows->image.y+event.xmotion.y;
11739           break;
11740         }
11741         case SelectionRequest:
11742         {
11743           XSelectionEvent
11744             notify;
11745
11746           XSelectionRequestEvent
11747             *request;
11748
11749           /*
11750             Set primary selection.
11751           */
11752           (void) FormatMagickString(text,MaxTextExtent,"%lux%lu%+ld%+ld",
11753             roi_info.width,roi_info.height,roi_info.x,roi_info.y);
11754           request=(&(event.xselectionrequest));
11755           (void) XChangeProperty(request->display,request->requestor,
11756             request->property,request->target,8,PropModeReplace,
11757             (unsigned char *) text,(int) strlen(text));
11758           notify.type=SelectionNotify;
11759           notify.display=request->display;
11760           notify.requestor=request->requestor;
11761           notify.selection=request->selection;
11762           notify.target=request->target;
11763           notify.time=request->time;
11764           if (request->property == None)
11765             notify.property=request->target;
11766           else
11767             notify.property=request->property;
11768           (void) XSendEvent(request->display,request->requestor,False,0,
11769             (XEvent *) &notify);
11770         }
11771         default:
11772           break;
11773       }
11774       if ((state & UpdateConfigurationState) != 0)
11775         {
11776           (void) XPutBackEvent(display,&event);
11777           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11778           break;
11779         }
11780     } while ((state & ExitState) == 0);
11781   } while ((state & ExitState) == 0);
11782   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11783   XSetCursorState(display,windows,MagickFalse);
11784   if ((state & EscapeState) != 0)
11785     return(MagickTrue);
11786   return(MagickTrue);
11787 }
11788 \f
11789 /*
11790 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11791 %                                                                             %
11792 %                                                                             %
11793 %                                                                             %
11794 +   X R o t a t e I m a g e                                                   %
11795 %                                                                             %
11796 %                                                                             %
11797 %                                                                             %
11798 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11799 %
11800 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11801 %  rotation angle is computed from the slope of a line drawn by the user.
11802 %
11803 %  The format of the XRotateImage method is:
11804 %
11805 %      MagickBooleanType XRotateImage(Display *display,
11806 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11807 %        Image **image)
11808 %
11809 %  A description of each parameter follows:
11810 %
11811 %    o display: Specifies a connection to an X server; returned from
11812 %      XOpenDisplay.
11813 %
11814 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11815 %
11816 %    o windows: Specifies a pointer to a XWindows structure.
11817 %
11818 %    o degrees: Specifies the number of degrees to rotate the image.
11819 %
11820 %    o image: the image.
11821 %
11822 */
11823 static MagickBooleanType XRotateImage(Display *display,
11824   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11825 {
11826   static const char
11827     *RotateMenu[] =
11828     {
11829       "Pixel Color",
11830       "Direction",
11831       "Help",
11832       "Dismiss",
11833       (char *) NULL
11834     };
11835
11836   static ModeType
11837     direction = HorizontalRotateCommand;
11838
11839   static const ModeType
11840     DirectionCommands[] =
11841     {
11842       HorizontalRotateCommand,
11843       VerticalRotateCommand
11844     },
11845     RotateCommands[] =
11846     {
11847       RotateColorCommand,
11848       RotateDirectionCommand,
11849       RotateHelpCommand,
11850       RotateDismissCommand
11851     };
11852
11853   static unsigned int
11854     pen_id = 0;
11855
11856   char
11857     command[MaxTextExtent],
11858     text[MaxTextExtent];
11859
11860   Image
11861     *rotate_image;
11862
11863   int
11864     id,
11865     x,
11866     y;
11867
11868   MagickRealType
11869     normalized_degrees;
11870
11871   register int
11872     i;
11873
11874   unsigned int
11875     height,
11876     rotations,
11877     width;
11878
11879   if (degrees == 0.0)
11880     {
11881       unsigned int
11882         distance;
11883
11884       unsigned long
11885         state;
11886
11887       XEvent
11888         event;
11889
11890       XSegment
11891         rotate_info;
11892
11893       /*
11894         Map Command widget.
11895       */
11896       (void) CloneString(&windows->command.name,"Rotate");
11897       windows->command.data=2;
11898       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11899       (void) XMapRaised(display,windows->command.id);
11900       XClientMessage(display,windows->image.id,windows->im_protocols,
11901         windows->im_update_widget,CurrentTime);
11902       /*
11903         Wait for first button press.
11904       */
11905       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11906       XQueryPosition(display,windows->image.id,&x,&y);
11907       rotate_info.x1=x;
11908       rotate_info.y1=y;
11909       rotate_info.x2=x;
11910       rotate_info.y2=y;
11911       state=DefaultState;
11912       do
11913       {
11914         XHighlightLine(display,windows->image.id,
11915           windows->image.highlight_context,&rotate_info);
11916         /*
11917           Wait for next event.
11918         */
11919         XScreenEvent(display,windows,&event);
11920         XHighlightLine(display,windows->image.id,
11921           windows->image.highlight_context,&rotate_info);
11922         if (event.xany.window == windows->command.id)
11923           {
11924             /*
11925               Select a command from the Command widget.
11926             */
11927             id=XCommandWidget(display,windows,RotateMenu,&event);
11928             if (id < 0)
11929               continue;
11930             (void) XSetFunction(display,windows->image.highlight_context,
11931               GXcopy);
11932             switch (RotateCommands[id])
11933             {
11934               case RotateColorCommand:
11935               {
11936                 const char
11937                   *ColorMenu[MaxNumberPens];
11938
11939                 int
11940                   pen_number;
11941
11942                 XColor
11943                   color;
11944
11945                 /*
11946                   Initialize menu selections.
11947                 */
11948                 for (i=0; i < (int) (MaxNumberPens-2); i++)
11949                   ColorMenu[i]=resource_info->pen_colors[i];
11950                 ColorMenu[MaxNumberPens-2]="Browser...";
11951                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
11952                 /*
11953                   Select a pen color from the pop-up menu.
11954                 */
11955                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
11956                   (const char **) ColorMenu,command);
11957                 if (pen_number < 0)
11958                   break;
11959                 if (pen_number == (MaxNumberPens-2))
11960                   {
11961                     static char
11962                       color_name[MaxTextExtent] = "gray";
11963
11964                     /*
11965                       Select a pen color from a dialog.
11966                     */
11967                     resource_info->pen_colors[pen_number]=color_name;
11968                     XColorBrowserWidget(display,windows,"Select",color_name);
11969                     if (*color_name == '\0')
11970                       break;
11971                   }
11972                 /*
11973                   Set pen color.
11974                 */
11975                 (void) XParseColor(display,windows->map_info->colormap,
11976                   resource_info->pen_colors[pen_number],&color);
11977                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
11978                   (unsigned int) MaxColors,&color);
11979                 windows->pixel_info->pen_colors[pen_number]=color;
11980                 pen_id=(unsigned int) pen_number;
11981                 break;
11982               }
11983               case RotateDirectionCommand:
11984               {
11985                 static const char
11986                   *Directions[] =
11987                   {
11988                     "horizontal",
11989                     "vertical",
11990                     (char *) NULL,
11991                   };
11992
11993                 /*
11994                   Select a command from the pop-up menu.
11995                 */
11996                 id=XMenuWidget(display,windows,RotateMenu[id],
11997                   Directions,command);
11998                 if (id >= 0)
11999                   direction=DirectionCommands[id];
12000                 break;
12001               }
12002               case RotateHelpCommand:
12003               {
12004                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12005                   "Help Viewer - Image Rotation",ImageRotateHelp);
12006                 break;
12007               }
12008               case RotateDismissCommand:
12009               {
12010                 /*
12011                   Prematurely exit.
12012                 */
12013                 state|=EscapeState;
12014                 state|=ExitState;
12015                 break;
12016               }
12017               default:
12018                 break;
12019             }
12020             (void) XSetFunction(display,windows->image.highlight_context,
12021               GXinvert);
12022             continue;
12023           }
12024         switch (event.type)
12025         {
12026           case ButtonPress:
12027           {
12028             if (event.xbutton.button != Button1)
12029               break;
12030             if (event.xbutton.window != windows->image.id)
12031               break;
12032             /*
12033               exit loop.
12034             */
12035             (void) XSetFunction(display,windows->image.highlight_context,
12036               GXcopy);
12037             rotate_info.x1=event.xbutton.x;
12038             rotate_info.y1=event.xbutton.y;
12039             state|=ExitState;
12040             break;
12041           }
12042           case ButtonRelease:
12043             break;
12044           case Expose:
12045             break;
12046           case KeyPress:
12047           {
12048             char
12049               command[MaxTextExtent];
12050
12051             KeySym
12052               key_symbol;
12053
12054             if (event.xkey.window != windows->image.id)
12055               break;
12056             /*
12057               Respond to a user key press.
12058             */
12059             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12060               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12061             switch ((int) key_symbol)
12062             {
12063               case XK_Escape:
12064               case XK_F20:
12065               {
12066                 /*
12067                   Prematurely exit.
12068                 */
12069                 state|=EscapeState;
12070                 state|=ExitState;
12071                 break;
12072               }
12073               case XK_F1:
12074               case XK_Help:
12075               {
12076                 (void) XSetFunction(display,windows->image.highlight_context,
12077                   GXcopy);
12078                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12079                   "Help Viewer - Image Rotation",ImageRotateHelp);
12080                 (void) XSetFunction(display,windows->image.highlight_context,
12081                   GXinvert);
12082                 break;
12083               }
12084               default:
12085               {
12086                 (void) XBell(display,0);
12087                 break;
12088               }
12089             }
12090             break;
12091           }
12092           case MotionNotify:
12093           {
12094             rotate_info.x1=event.xmotion.x;
12095             rotate_info.y1=event.xmotion.y;
12096           }
12097         }
12098         rotate_info.x2=rotate_info.x1;
12099         rotate_info.y2=rotate_info.y1;
12100         if (direction == HorizontalRotateCommand)
12101           rotate_info.x2+=32;
12102         else
12103           rotate_info.y2-=32;
12104       } while ((state & ExitState) == 0);
12105       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12106       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12107       if ((state & EscapeState) != 0)
12108         return(MagickTrue);
12109       /*
12110         Draw line as pointer moves until the mouse button is released.
12111       */
12112       distance=0;
12113       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12114       state=DefaultState;
12115       do
12116       {
12117         if (distance > 9)
12118           {
12119             /*
12120               Display info and draw rotation line.
12121             */
12122             if (windows->info.mapped == MagickFalse)
12123               (void) XMapWindow(display,windows->info.id);
12124             (void) FormatMagickString(text,MaxTextExtent," %g",
12125               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12126             XInfoWidget(display,windows,text);
12127             XHighlightLine(display,windows->image.id,
12128               windows->image.highlight_context,&rotate_info);
12129           }
12130         else
12131           if (windows->info.mapped != MagickFalse)
12132             (void) XWithdrawWindow(display,windows->info.id,
12133               windows->info.screen);
12134         /*
12135           Wait for next event.
12136         */
12137         XScreenEvent(display,windows,&event);
12138         if (distance > 9)
12139           XHighlightLine(display,windows->image.id,
12140             windows->image.highlight_context,&rotate_info);
12141         switch (event.type)
12142         {
12143           case ButtonPress:
12144             break;
12145           case ButtonRelease:
12146           {
12147             /*
12148               User has committed to rotation line.
12149             */
12150             rotate_info.x2=event.xbutton.x;
12151             rotate_info.y2=event.xbutton.y;
12152             state|=ExitState;
12153             break;
12154           }
12155           case Expose:
12156             break;
12157           case MotionNotify:
12158           {
12159             rotate_info.x2=event.xmotion.x;
12160             rotate_info.y2=event.xmotion.y;
12161           }
12162           default:
12163             break;
12164         }
12165         /*
12166           Check boundary conditions.
12167         */
12168         if (rotate_info.x2 < 0)
12169           rotate_info.x2=0;
12170         else
12171           if (rotate_info.x2 > (int) windows->image.width)
12172             rotate_info.x2=(short) windows->image.width;
12173         if (rotate_info.y2 < 0)
12174           rotate_info.y2=0;
12175         else
12176           if (rotate_info.y2 > (int) windows->image.height)
12177             rotate_info.y2=(short) windows->image.height;
12178         /*
12179           Compute rotation angle from the slope of the line.
12180         */
12181         degrees=0.0;
12182         distance=(unsigned int)
12183           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12184           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12185         if (distance > 9)
12186           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12187             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12188       } while ((state & ExitState) == 0);
12189       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12190       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12191       if (distance <= 9)
12192         return(MagickTrue);
12193     }
12194   if (direction == VerticalRotateCommand)
12195     degrees-=90.0;
12196   if (degrees == 0.0)
12197     return(MagickTrue);
12198   /*
12199     Rotate image.
12200   */
12201   normalized_degrees=degrees;
12202   while (normalized_degrees < -45.0)
12203     normalized_degrees+=360.0;
12204   for (rotations=0; normalized_degrees > 45.0; rotations++)
12205     normalized_degrees-=90.0;
12206   if (normalized_degrees != 0.0)
12207     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12208   XSetCursorState(display,windows,MagickTrue);
12209   XCheckRefreshWindows(display,windows);
12210   (*image)->background_color.red=ScaleShortToQuantum(
12211     windows->pixel_info->pen_colors[pen_id].red);
12212   (*image)->background_color.green=ScaleShortToQuantum(
12213     windows->pixel_info->pen_colors[pen_id].green);
12214   (*image)->background_color.blue=ScaleShortToQuantum(
12215     windows->pixel_info->pen_colors[pen_id].blue);
12216   rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12217   XSetCursorState(display,windows,MagickFalse);
12218   if (rotate_image == (Image *) NULL)
12219     return(MagickFalse);
12220   *image=DestroyImage(*image);
12221   *image=rotate_image;
12222   if (windows->image.crop_geometry != (char *) NULL)
12223     {
12224       /*
12225         Rotate crop geometry.
12226       */
12227       width=(unsigned int) (*image)->columns;
12228       height=(unsigned int) (*image)->rows;
12229       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12230       switch (rotations % 4)
12231       {
12232         default:
12233         case 0:
12234           break;
12235         case 1:
12236         {
12237           /*
12238             Rotate 90 degrees.
12239           */
12240           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12241             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12242             (int) height-y,x);
12243           break;
12244         }
12245         case 2:
12246         {
12247           /*
12248             Rotate 180 degrees.
12249           */
12250           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12251             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12252           break;
12253         }
12254         case 3:
12255         {
12256           /*
12257             Rotate 270 degrees.
12258           */
12259           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12260             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12261           break;
12262         }
12263       }
12264     }
12265   if (windows->image.orphan != MagickFalse)
12266     return(MagickTrue);
12267   if (normalized_degrees != 0.0)
12268     {
12269       /*
12270         Update image colormap.
12271       */
12272       windows->image.window_changes.width=(int) (*image)->columns;
12273       windows->image.window_changes.height=(int) (*image)->rows;
12274       if (windows->image.crop_geometry != (char *) NULL)
12275         {
12276           /*
12277             Obtain dimensions of image from crop geometry.
12278           */
12279           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12280             &width,&height);
12281           windows->image.window_changes.width=(int) width;
12282           windows->image.window_changes.height=(int) height;
12283         }
12284       XConfigureImageColormap(display,resource_info,windows,*image);
12285     }
12286   else
12287     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12288       {
12289         windows->image.window_changes.width=windows->image.ximage->height;
12290         windows->image.window_changes.height=windows->image.ximage->width;
12291       }
12292   /*
12293     Update image configuration.
12294   */
12295   (void) XConfigureImage(display,resource_info,windows,*image);
12296   return(MagickTrue);
12297 }
12298 \f
12299 /*
12300 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12301 %                                                                             %
12302 %                                                                             %
12303 %                                                                             %
12304 +   X S a v e I m a g e                                                       %
12305 %                                                                             %
12306 %                                                                             %
12307 %                                                                             %
12308 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12309 %
12310 %  XSaveImage() saves an image to a file.
12311 %
12312 %  The format of the XSaveImage method is:
12313 %
12314 %      MagickBooleanType XSaveImage(Display *display,
12315 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
12316 %
12317 %  A description of each parameter follows:
12318 %
12319 %    o display: Specifies a connection to an X server; returned from
12320 %      XOpenDisplay.
12321 %
12322 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12323 %
12324 %    o windows: Specifies a pointer to a XWindows structure.
12325 %
12326 %    o image: the image.
12327 %
12328 */
12329 static MagickBooleanType XSaveImage(Display *display,
12330   XResourceInfo *resource_info,XWindows *windows,Image *image)
12331 {
12332   char
12333     filename[MaxTextExtent],
12334     geometry[MaxTextExtent];
12335
12336   Image
12337     *save_image;
12338
12339   ImageInfo
12340     *image_info;
12341
12342   MagickStatusType
12343     status;
12344
12345   /*
12346     Request file name from user.
12347   */
12348   if (resource_info->write_filename != (char *) NULL)
12349     (void) CopyMagickString(filename,resource_info->write_filename,
12350       MaxTextExtent);
12351   else
12352     {
12353       char
12354         path[MaxTextExtent];
12355
12356       int
12357         status;
12358
12359       GetPathComponent(image->filename,HeadPath,path);
12360       GetPathComponent(image->filename,TailPath,filename);
12361       status=chdir(path);
12362       if (status == -1)
12363         (void) ThrowMagickException(&image->exception,GetMagickModule(),
12364           FileOpenError,"UnableToOpenFile","%s",path);
12365     }
12366   XFileBrowserWidget(display,windows,"Save",filename);
12367   if (*filename == '\0')
12368     return(MagickTrue);
12369   if (IsPathAccessible(filename) != MagickFalse)
12370     {
12371       int
12372         status;
12373
12374       /*
12375         File exists-- seek user's permission before overwriting.
12376       */
12377       status=XConfirmWidget(display,windows,"Overwrite",filename);
12378       if (status <= 0)
12379         return(MagickTrue);
12380     }
12381   image_info=CloneImageInfo(resource_info->image_info);
12382   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12383   (void) SetImageInfo(image_info,1,&image->exception);
12384   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12385       (LocaleCompare(image_info->magick,"JPG") == 0))
12386     {
12387       char
12388         quality[MaxTextExtent];
12389
12390       int
12391         status;
12392
12393       /*
12394         Request JPEG quality from user.
12395       */
12396       (void) FormatMagickString(quality,MaxTextExtent,"%lu",image->quality);
12397       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12398         quality);
12399       if (*quality == '\0')
12400         return(MagickTrue);
12401       image->quality=StringToUnsignedLong(quality);
12402       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12403     }
12404   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12405       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12406       (LocaleCompare(image_info->magick,"PS") == 0) ||
12407       (LocaleCompare(image_info->magick,"PS2") == 0))
12408     {
12409       char
12410         geometry[MaxTextExtent];
12411
12412       /*
12413         Request page geometry from user.
12414       */
12415       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12416       if (LocaleCompare(image_info->magick,"PDF") == 0)
12417         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12418       if (image_info->page != (char *) NULL)
12419         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12420       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12421         "Select page geometry:",geometry);
12422       if (*geometry != '\0')
12423         image_info->page=GetPageGeometry(geometry);
12424     }
12425   /*
12426     Apply image transforms.
12427   */
12428   XSetCursorState(display,windows,MagickTrue);
12429   XCheckRefreshWindows(display,windows);
12430   save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12431   if (save_image == (Image *) NULL)
12432     return(MagickFalse);
12433   (void) FormatMagickString(geometry,MaxTextExtent,"%dx%d!",
12434     windows->image.ximage->width,windows->image.ximage->height);
12435   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12436   /*
12437     Write image.
12438   */
12439   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12440   status=WriteImage(image_info,save_image);
12441   if (status != MagickFalse)
12442     image->taint=MagickFalse;
12443   save_image=DestroyImage(save_image);
12444   image_info=DestroyImageInfo(image_info);
12445   XSetCursorState(display,windows,MagickFalse);
12446   return(status != 0 ? MagickTrue : MagickFalse);
12447 }
12448 \f
12449 /*
12450 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12451 %                                                                             %
12452 %                                                                             %
12453 %                                                                             %
12454 +   X S c r e e n E v e n t                                                   %
12455 %                                                                             %
12456 %                                                                             %
12457 %                                                                             %
12458 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12459 %
12460 %  XScreenEvent() handles global events associated with the Pan and Magnify
12461 %  windows.
12462 %
12463 %  The format of the XScreenEvent function is:
12464 %
12465 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12466 %
12467 %  A description of each parameter follows:
12468 %
12469 %    o display: Specifies a pointer to the Display structure;  returned from
12470 %      XOpenDisplay.
12471 %
12472 %    o windows: Specifies a pointer to a XWindows structure.
12473 %
12474 %    o event: Specifies a pointer to a X11 XEvent structure.
12475 %
12476 %
12477 */
12478
12479 #if defined(__cplusplus) || defined(c_plusplus)
12480 extern "C" {
12481 #endif
12482
12483 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12484 {
12485   register XWindows
12486     *windows;
12487
12488   windows=(XWindows *) data;
12489   if ((event->type == ClientMessage) &&
12490       (event->xclient.window == windows->image.id))
12491     return(MagickFalse);
12492   return(MagickTrue);
12493 }
12494
12495 #if defined(__cplusplus) || defined(c_plusplus)
12496 }
12497 #endif
12498
12499 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12500 {
12501   register int
12502     x,
12503     y;
12504
12505   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12506   if (event->xany.window == windows->command.id)
12507     return;
12508   switch (event->type)
12509   {
12510     case ButtonPress:
12511     case ButtonRelease:
12512     {
12513       if ((event->xbutton.button == Button3) &&
12514           (event->xbutton.state & Mod1Mask))
12515         {
12516           /*
12517             Convert Alt-Button3 to Button2.
12518           */
12519           event->xbutton.button=Button2;
12520           event->xbutton.state&=(~Mod1Mask);
12521         }
12522       if (event->xbutton.window == windows->backdrop.id)
12523         {
12524           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12525             event->xbutton.time);
12526           break;
12527         }
12528       if (event->xbutton.window == windows->pan.id)
12529         {
12530           XPanImage(display,windows,event);
12531           break;
12532         }
12533       if (event->xbutton.window == windows->image.id)
12534         if (event->xbutton.button == Button2)
12535           {
12536             /*
12537               Update magnified image.
12538             */
12539             x=event->xbutton.x;
12540             y=event->xbutton.y;
12541             if (x < 0)
12542               x=0;
12543             else
12544               if (x >= (int) windows->image.width)
12545                 x=(int) (windows->image.width-1);
12546             windows->magnify.x=windows->image.x+x;
12547             if (y < 0)
12548               y=0;
12549             else
12550              if (y >= (int) windows->image.height)
12551                y=(int) (windows->image.height-1);
12552             windows->magnify.y=windows->image.y+y;
12553             if (windows->magnify.mapped == MagickFalse)
12554               (void) XMapRaised(display,windows->magnify.id);
12555             XMakeMagnifyImage(display,windows);
12556             if (event->type == ButtonRelease)
12557               (void) XWithdrawWindow(display,windows->info.id,
12558                 windows->info.screen);
12559             break;
12560           }
12561       break;
12562     }
12563     case ClientMessage:
12564     {
12565       /*
12566         If client window delete message, exit.
12567       */
12568       if (event->xclient.message_type != windows->wm_protocols)
12569         break;
12570       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12571         break;
12572       if (event->xclient.window == windows->magnify.id)
12573         {
12574           (void) XWithdrawWindow(display,windows->magnify.id,
12575             windows->magnify.screen);
12576           break;
12577         }
12578       break;
12579     }
12580     case ConfigureNotify:
12581     {
12582       if (event->xconfigure.window == windows->magnify.id)
12583         {
12584           unsigned int
12585             magnify;
12586
12587           /*
12588             Magnify window has a new configuration.
12589           */
12590           windows->magnify.width=(unsigned int) event->xconfigure.width;
12591           windows->magnify.height=(unsigned int) event->xconfigure.height;
12592           if (windows->magnify.mapped == MagickFalse)
12593             break;
12594           magnify=1;
12595           while ((int) magnify <= event->xconfigure.width)
12596             magnify<<=1;
12597           while ((int) magnify <= event->xconfigure.height)
12598             magnify<<=1;
12599           magnify>>=1;
12600           if (((int) magnify != event->xconfigure.width) ||
12601               ((int) magnify != event->xconfigure.height))
12602             {
12603               XWindowChanges
12604                 window_changes;
12605
12606               window_changes.width=(int) magnify;
12607               window_changes.height=(int) magnify;
12608               (void) XReconfigureWMWindow(display,windows->magnify.id,
12609                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12610                 &window_changes);
12611               break;
12612             }
12613           XMakeMagnifyImage(display,windows);
12614           break;
12615         }
12616       break;
12617     }
12618     case Expose:
12619     {
12620       if (event->xexpose.window == windows->image.id)
12621         {
12622           XRefreshWindow(display,&windows->image,event);
12623           break;
12624         }
12625       if (event->xexpose.window == windows->pan.id)
12626         if (event->xexpose.count == 0)
12627           {
12628             XDrawPanRectangle(display,windows);
12629             break;
12630           }
12631       if (event->xexpose.window == windows->magnify.id)
12632         if (event->xexpose.count == 0)
12633           {
12634             XMakeMagnifyImage(display,windows);
12635             break;
12636           }
12637       break;
12638     }
12639     case KeyPress:
12640     {
12641       char
12642         command[MaxTextExtent];
12643
12644       KeySym
12645         key_symbol;
12646
12647       if (event->xkey.window != windows->magnify.id)
12648         break;
12649       /*
12650         Respond to a user key press.
12651       */
12652       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12653         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12654       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12655       break;
12656     }
12657     case MapNotify:
12658     {
12659       if (event->xmap.window == windows->magnify.id)
12660         {
12661           windows->magnify.mapped=MagickTrue;
12662           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12663           break;
12664         }
12665       if (event->xmap.window == windows->info.id)
12666         {
12667           windows->info.mapped=MagickTrue;
12668           break;
12669         }
12670       break;
12671     }
12672     case MotionNotify:
12673     {
12674       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12675       if (event->xmotion.window == windows->image.id)
12676         if (windows->magnify.mapped != MagickFalse)
12677           {
12678             /*
12679               Update magnified image.
12680             */
12681             x=event->xmotion.x;
12682             y=event->xmotion.y;
12683             if (x < 0)
12684               x=0;
12685             else
12686               if (x >= (int) windows->image.width)
12687                 x=(int) (windows->image.width-1);
12688             windows->magnify.x=windows->image.x+x;
12689             if (y < 0)
12690               y=0;
12691             else
12692              if (y >= (int) windows->image.height)
12693                y=(int) (windows->image.height-1);
12694             windows->magnify.y=windows->image.y+y;
12695             XMakeMagnifyImage(display,windows);
12696           }
12697       break;
12698     }
12699     case UnmapNotify:
12700     {
12701       if (event->xunmap.window == windows->magnify.id)
12702         {
12703           windows->magnify.mapped=MagickFalse;
12704           break;
12705         }
12706       if (event->xunmap.window == windows->info.id)
12707         {
12708           windows->info.mapped=MagickFalse;
12709           break;
12710         }
12711       break;
12712     }
12713     default:
12714       break;
12715   }
12716 }
12717 \f
12718 /*
12719 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12720 %                                                                             %
12721 %                                                                             %
12722 %                                                                             %
12723 +   X S e t C r o p G e o m e t r y                                           %
12724 %                                                                             %
12725 %                                                                             %
12726 %                                                                             %
12727 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12728 %
12729 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12730 %  and translates it to a cropping geometry relative to the image.
12731 %
12732 %  The format of the XSetCropGeometry method is:
12733 %
12734 %      void XSetCropGeometry(Display *display,XWindows *windows,
12735 %        RectangleInfo *crop_info,Image *image)
12736 %
12737 %  A description of each parameter follows:
12738 %
12739 %    o display: Specifies a connection to an X server; returned from
12740 %      XOpenDisplay.
12741 %
12742 %    o windows: Specifies a pointer to a XWindows structure.
12743 %
12744 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12745 %      Image window to crop.
12746 %
12747 %    o image: the image.
12748 %
12749 */
12750 static void XSetCropGeometry(Display *display,XWindows *windows,
12751   RectangleInfo *crop_info,Image *image)
12752 {
12753   char
12754     text[MaxTextExtent];
12755
12756   int
12757     x,
12758     y;
12759
12760   MagickRealType
12761     scale_factor;
12762
12763   unsigned int
12764     height,
12765     width;
12766
12767   if (windows->info.mapped != MagickFalse)
12768     {
12769       /*
12770         Display info on cropping rectangle.
12771       */
12772       (void) FormatMagickString(text,MaxTextExtent," %lux%lu%+ld%+ld",
12773         crop_info->width,crop_info->height,crop_info->x,crop_info->y);
12774       XInfoWidget(display,windows,text);
12775     }
12776   /*
12777     Cropping geometry is relative to any previous crop geometry.
12778   */
12779   x=0;
12780   y=0;
12781   width=(unsigned int) image->columns;
12782   height=(unsigned int) image->rows;
12783   if (windows->image.crop_geometry != (char *) NULL)
12784     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12785   else
12786     windows->image.crop_geometry=AcquireString((char *) NULL);
12787   /*
12788     Define the crop geometry string from the cropping rectangle.
12789   */
12790   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12791   if (crop_info->x > 0)
12792     x+=(int) (scale_factor*crop_info->x+0.5);
12793   width=(unsigned int) (scale_factor*crop_info->width+0.5);
12794   if (width == 0)
12795     width=1;
12796   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12797   if (crop_info->y > 0)
12798     y+=(int) (scale_factor*crop_info->y+0.5);
12799   height=(unsigned int) (scale_factor*crop_info->height+0.5);
12800   if (height == 0)
12801     height=1;
12802   (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12803     "%ux%u%+d%+d",width,height,x,y);
12804 }
12805 \f
12806 /*
12807 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12808 %                                                                             %
12809 %                                                                             %
12810 %                                                                             %
12811 +   X T i l e I m a g e                                                       %
12812 %                                                                             %
12813 %                                                                             %
12814 %                                                                             %
12815 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12816 %
12817 %  XTileImage() loads or deletes a selected tile from a visual image directory.
12818 %  The load or delete command is chosen from a menu.
12819 %
12820 %  The format of the XTileImage method is:
12821 %
12822 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
12823 %        XWindows *windows,Image *image,XEvent *event)
12824 %
12825 %  A description of each parameter follows:
12826 %
12827 %    o tile_image:  XTileImage reads or deletes the tile image
12828 %      and returns it.  A null image is returned if an error occurs.
12829 %
12830 %    o display: Specifies a connection to an X server;  returned from
12831 %      XOpenDisplay.
12832 %
12833 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12834 %
12835 %    o windows: Specifies a pointer to a XWindows structure.
12836 %
12837 %    o image: the image; returned from ReadImage.
12838 %
12839 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
12840 %      the entire image is refreshed.
12841 %
12842 */
12843 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12844   XWindows *windows,Image *image,XEvent *event)
12845 {
12846   static const char
12847     *VerbMenu[] =
12848     {
12849       "Load",
12850       "Next",
12851       "Former",
12852       "Delete",
12853       "Update",
12854       (char *) NULL,
12855     };
12856
12857   static const ModeType
12858     TileCommands[] =
12859     {
12860       TileLoadCommand,
12861       TileNextCommand,
12862       TileFormerCommand,
12863       TileDeleteCommand,
12864       TileUpdateCommand
12865     };
12866
12867   char
12868     command[MaxTextExtent],
12869     filename[MaxTextExtent];
12870
12871   Image
12872     *tile_image;
12873
12874   int
12875     id,
12876     status,
12877     tile,
12878     x,
12879     y;
12880
12881   MagickRealType
12882     scale_factor;
12883
12884   register char
12885     *p,
12886     *q;
12887
12888   register int
12889     i;
12890
12891   unsigned int
12892     height,
12893     width;
12894
12895   /*
12896     Tile image is relative to montage image configuration.
12897   */
12898   x=0;
12899   y=0;
12900   width=(unsigned int) image->columns;
12901   height=(unsigned int) image->rows;
12902   if (windows->image.crop_geometry != (char *) NULL)
12903     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12904   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12905   event->xbutton.x+=windows->image.x;
12906   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
12907   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12908   event->xbutton.y+=windows->image.y;
12909   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
12910   /*
12911     Determine size and location of each tile in the visual image directory.
12912   */
12913   width=(unsigned int) image->columns;
12914   height=(unsigned int) image->rows;
12915   x=0;
12916   y=0;
12917   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
12918   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
12919     (event->xbutton.x-x)/width;
12920   if (tile < 0)
12921     {
12922       /*
12923         Button press is outside any tile.
12924       */
12925       (void) XBell(display,0);
12926       return((Image *) NULL);
12927     }
12928   /*
12929     Determine file name from the tile directory.
12930   */
12931   p=image->directory;
12932   for (i=tile; (i != 0) && (*p != '\0'); )
12933   {
12934     if (*p == '\n')
12935       i--;
12936     p++;
12937   }
12938   if (*p == '\0')
12939     {
12940       /*
12941         Button press is outside any tile.
12942       */
12943       (void) XBell(display,0);
12944       return((Image *) NULL);
12945     }
12946   /*
12947     Select a command from the pop-up menu.
12948   */
12949   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
12950   if (id < 0)
12951     return((Image *) NULL);
12952   q=p;
12953   while ((*q != '\n') && (*q != '\0'))
12954     q++;
12955   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
12956   /*
12957     Perform command for the selected tile.
12958   */
12959   XSetCursorState(display,windows,MagickTrue);
12960   XCheckRefreshWindows(display,windows);
12961   tile_image=NewImageList();
12962   switch (TileCommands[id])
12963   {
12964     case TileLoadCommand:
12965     {
12966       /*
12967         Load tile image.
12968       */
12969       XCheckRefreshWindows(display,windows);
12970       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
12971         MaxTextExtent);
12972       (void) CopyMagickString(resource_info->image_info->filename,filename,
12973         MaxTextExtent);
12974       tile_image=ReadImage(resource_info->image_info,&image->exception);
12975       CatchException(&image->exception);
12976       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12977       break;
12978     }
12979     case TileNextCommand:
12980     {
12981       /*
12982         Display next image.
12983       */
12984       XClientMessage(display,windows->image.id,windows->im_protocols,
12985         windows->im_next_image,CurrentTime);
12986       break;
12987     }
12988     case TileFormerCommand:
12989     {
12990       /*
12991         Display former image.
12992       */
12993       XClientMessage(display,windows->image.id,windows->im_protocols,
12994         windows->im_former_image,CurrentTime);
12995       break;
12996     }
12997     case TileDeleteCommand:
12998     {
12999       /*
13000         Delete tile image.
13001       */
13002       if (IsPathAccessible(filename) == MagickFalse)
13003         {
13004           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13005           break;
13006         }
13007       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13008       if (status <= 0)
13009         break;
13010       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13011       if (status != MagickFalse)
13012         {
13013           XNoticeWidget(display,windows,"Unable to delete image file:",
13014             filename);
13015           break;
13016         }
13017     }
13018     case TileUpdateCommand:
13019     {
13020       ExceptionInfo
13021         *exception;
13022
13023       int
13024         x_offset,
13025         y_offset;
13026
13027       PixelPacket
13028         pixel;
13029
13030       register int
13031         j;
13032
13033       register PixelPacket
13034         *s;
13035
13036       /*
13037         Ensure all the images exist.
13038       */
13039       tile=0;
13040       for (p=image->directory; *p != '\0'; p++)
13041       {
13042         q=p;
13043         while ((*q != '\n') && (*q != '\0'))
13044           q++;
13045         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13046         p=q;
13047         if (IsPathAccessible(filename) != MagickFalse)
13048           {
13049             tile++;
13050             continue;
13051           }
13052         /*
13053           Overwrite tile with background color.
13054         */
13055         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13056         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13057         exception=(&image->exception);
13058         (void) GetOneVirtualPixel(image,0,0,&pixel,exception);
13059         for (i=0; i < (int) height; i++)
13060         {
13061           s=GetAuthenticPixels(image,x_offset,y_offset+i,width,1,exception);
13062           if (s == (PixelPacket *) NULL)
13063             break;
13064           for (j=0; j < (int) width; j++)
13065             *s++=pixel;
13066           if (SyncAuthenticPixels(image,exception) == MagickFalse)
13067             break;
13068         }
13069         tile++;
13070       }
13071       windows->image.window_changes.width=(int) image->columns;
13072       windows->image.window_changes.height=(int) image->rows;
13073       XConfigureImageColormap(display,resource_info,windows,image);
13074       (void) XConfigureImage(display,resource_info,windows,image);
13075       break;
13076     }
13077     default:
13078       break;
13079   }
13080   XSetCursorState(display,windows,MagickFalse);
13081   return(tile_image);
13082 }
13083 \f
13084 /*
13085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13086 %                                                                             %
13087 %                                                                             %
13088 %                                                                             %
13089 +   X T r a n s l a t e I m a g e                                             %
13090 %                                                                             %
13091 %                                                                             %
13092 %                                                                             %
13093 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13094 %
13095 %  XTranslateImage() translates the image within an Image window by one pixel
13096 %  as specified by the key symbol.  If the image has a `montage string the
13097 %  translation is respect to the width and height contained within the string.
13098 %
13099 %  The format of the XTranslateImage method is:
13100 %
13101 %      void XTranslateImage(Display *display,XWindows *windows,
13102 %        Image *image,const KeySym key_symbol)
13103 %
13104 %  A description of each parameter follows:
13105 %
13106 %    o display: Specifies a connection to an X server; returned from
13107 %      XOpenDisplay.
13108 %
13109 %    o windows: Specifies a pointer to a XWindows structure.
13110 %
13111 %    o image: the image.
13112 %
13113 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13114 %      to trim.
13115 %
13116 */
13117 static void XTranslateImage(Display *display,XWindows *windows,
13118   Image *image,const KeySym key_symbol)
13119 {
13120   char
13121     text[MaxTextExtent];
13122
13123   int
13124     x,
13125     y;
13126
13127   unsigned int
13128     x_offset,
13129     y_offset;
13130
13131   /*
13132     User specified a pan position offset.
13133   */
13134   x_offset=windows->image.width;
13135   y_offset=windows->image.height;
13136   if (image->montage != (char *) NULL)
13137     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13138   switch ((int) key_symbol)
13139   {
13140     case XK_Home:
13141     case XK_KP_Home:
13142     {
13143       windows->image.x=(int) windows->image.width/2;
13144       windows->image.y=(int) windows->image.height/2;
13145       break;
13146     }
13147     case XK_Left:
13148     case XK_KP_Left:
13149     {
13150       windows->image.x-=x_offset;
13151       break;
13152     }
13153     case XK_Next:
13154     case XK_Up:
13155     case XK_KP_Up:
13156     {
13157       windows->image.y-=y_offset;
13158       break;
13159     }
13160     case XK_Right:
13161     case XK_KP_Right:
13162     {
13163       windows->image.x+=x_offset;
13164       break;
13165     }
13166     case XK_Prior:
13167     case XK_Down:
13168     case XK_KP_Down:
13169     {
13170       windows->image.y+=y_offset;
13171       break;
13172     }
13173     default:
13174       return;
13175   }
13176   /*
13177     Check boundary conditions.
13178   */
13179   if (windows->image.x < 0)
13180     windows->image.x=0;
13181   else
13182     if ((int) (windows->image.x+windows->image.width) >
13183         windows->image.ximage->width)
13184       windows->image.x=windows->image.ximage->width-windows->image.width;
13185   if (windows->image.y < 0)
13186     windows->image.y=0;
13187   else
13188     if ((int) (windows->image.y+windows->image.height) >
13189         windows->image.ximage->height)
13190       windows->image.y=windows->image.ximage->height-windows->image.height;
13191   /*
13192     Refresh Image window.
13193   */
13194   (void) FormatMagickString(text,MaxTextExtent," %ux%u%+d%+d ",
13195     windows->image.width,windows->image.height,windows->image.x,
13196     windows->image.y);
13197   XInfoWidget(display,windows,text);
13198   XCheckRefreshWindows(display,windows);
13199   XDrawPanRectangle(display,windows);
13200   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13201   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13202 }
13203 \f
13204 /*
13205 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13206 %                                                                             %
13207 %                                                                             %
13208 %                                                                             %
13209 +   X T r i m I m a g e                                                       %
13210 %                                                                             %
13211 %                                                                             %
13212 %                                                                             %
13213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13214 %
13215 %  XTrimImage() trims the edges from the Image window.
13216 %
13217 %  The format of the XTrimImage method is:
13218 %
13219 %      MagickBooleanType XTrimImage(Display *display,
13220 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
13221 %
13222 %  A description of each parameter follows:
13223 %
13224 %    o display: Specifies a connection to an X server; returned from
13225 %      XOpenDisplay.
13226 %
13227 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13228 %
13229 %    o windows: Specifies a pointer to a XWindows structure.
13230 %
13231 %    o image: the image.
13232 %
13233 */
13234 static MagickBooleanType XTrimImage(Display *display,
13235   XResourceInfo *resource_info,XWindows *windows,Image *image)
13236 {
13237   RectangleInfo
13238     trim_info;
13239
13240   register int
13241     x,
13242     y;
13243
13244   unsigned long
13245     background,
13246     pixel;
13247
13248   /*
13249     Trim edges from image.
13250   */
13251   XSetCursorState(display,windows,MagickTrue);
13252   XCheckRefreshWindows(display,windows);
13253   /*
13254     Crop the left edge.
13255   */
13256   background=XGetPixel(windows->image.ximage,0,0);
13257   trim_info.width=(unsigned long) windows->image.ximage->width;
13258   for (x=0; x < windows->image.ximage->width; x++)
13259   {
13260     for (y=0; y < windows->image.ximage->height; y++)
13261     {
13262       pixel=XGetPixel(windows->image.ximage,x,y);
13263       if (pixel != background)
13264         break;
13265     }
13266     if (y < windows->image.ximage->height)
13267       break;
13268   }
13269   trim_info.x=x;
13270   if (trim_info.x == (int) windows->image.ximage->width)
13271     {
13272       XSetCursorState(display,windows,MagickFalse);
13273       return(MagickFalse);
13274     }
13275   /*
13276     Crop the right edge.
13277   */
13278   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13279   for (x=windows->image.ximage->width-1; x != 0; x--)
13280   {
13281     for (y=0; y < windows->image.ximage->height; y++)
13282     {
13283       pixel=XGetPixel(windows->image.ximage,x,y);
13284       if (pixel != background)
13285         break;
13286     }
13287     if (y < windows->image.ximage->height)
13288       break;
13289   }
13290   trim_info.width=(unsigned long) (x-trim_info.x+1);
13291   /*
13292     Crop the top edge.
13293   */
13294   background=XGetPixel(windows->image.ximage,0,0);
13295   trim_info.height=(unsigned long) windows->image.ximage->height;
13296   for (y=0; y < windows->image.ximage->height; y++)
13297   {
13298     for (x=0; x < windows->image.ximage->width; x++)
13299     {
13300       pixel=XGetPixel(windows->image.ximage,x,y);
13301       if (pixel != background)
13302         break;
13303     }
13304     if (x < windows->image.ximage->width)
13305       break;
13306   }
13307   trim_info.y=y;
13308   /*
13309     Crop the bottom edge.
13310   */
13311   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13312   for (y=windows->image.ximage->height-1; y != 0; y--)
13313   {
13314     for (x=0; x < windows->image.ximage->width; x++)
13315     {
13316       pixel=XGetPixel(windows->image.ximage,x,y);
13317       if (pixel != background)
13318         break;
13319     }
13320     if (x < windows->image.ximage->width)
13321       break;
13322   }
13323   trim_info.height=(unsigned long) y-trim_info.y+1;
13324   if (((unsigned int) trim_info.width != windows->image.width) ||
13325       ((unsigned int) trim_info.height != windows->image.height))
13326     {
13327       /*
13328         Reconfigure Image window as defined by the trimming rectangle.
13329       */
13330       XSetCropGeometry(display,windows,&trim_info,image);
13331       windows->image.window_changes.width=(int) trim_info.width;
13332       windows->image.window_changes.height=(int) trim_info.height;
13333       (void) XConfigureImage(display,resource_info,windows,image);
13334     }
13335   XSetCursorState(display,windows,MagickFalse);
13336   return(MagickTrue);
13337 }
13338 \f
13339 /*
13340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13341 %                                                                             %
13342 %                                                                             %
13343 %                                                                             %
13344 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13345 %                                                                             %
13346 %                                                                             %
13347 %                                                                             %
13348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13349 %
13350 %  XVisualDirectoryImage() creates a Visual Image Directory.
13351 %
13352 %  The format of the XVisualDirectoryImage method is:
13353 %
13354 %      Image *XVisualDirectoryImage(Display *display,
13355 %        XResourceInfo *resource_info,XWindows *windows)
13356 %
13357 %  A description of each parameter follows:
13358 %
13359 %    o nexus: Method XVisualDirectoryImage returns a visual image
13360 %      directory if it can be created successfully.  Otherwise a null image
13361 %      is returned.
13362 %
13363 %    o display: Specifies a connection to an X server; returned from
13364 %      XOpenDisplay.
13365 %
13366 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13367 %
13368 %    o windows: Specifies a pointer to a XWindows structure.
13369 %
13370 */
13371 static Image *XVisualDirectoryImage(Display *display,
13372   XResourceInfo *resource_info,XWindows *windows)
13373 {
13374 #define TileImageTag  "Scale/Image"
13375 #define XClientName  "montage"
13376
13377   char
13378     **filelist;
13379
13380   ExceptionInfo
13381     *exception;
13382
13383   Image
13384     *images,
13385     *montage_image,
13386     *next_image,
13387     *thumbnail_image;
13388
13389   ImageInfo
13390     *read_info;
13391
13392   int
13393     number_files;
13394
13395   MagickBooleanType
13396     backdrop;
13397
13398   MagickStatusType
13399     status;
13400
13401   MontageInfo
13402     *montage_info;
13403
13404   RectangleInfo
13405     geometry;
13406
13407   register int
13408     i;
13409
13410   static char
13411     filename[MaxTextExtent] = "\0",
13412     filenames[MaxTextExtent] = "*";
13413
13414   XResourceInfo
13415     background_resources;
13416
13417   /*
13418     Request file name from user.
13419   */
13420   XFileBrowserWidget(display,windows,"Directory",filenames);
13421   if (*filenames == '\0')
13422     return((Image *) NULL);
13423   /*
13424     Expand the filenames.
13425   */
13426   filelist=(char **) AcquireAlignedMemory(1,sizeof(*filelist));
13427   if (filelist == (char **) NULL)
13428     {
13429       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13430         filenames);
13431       return((Image *) NULL);
13432     }
13433   number_files=1;
13434   filelist[0]=filenames;
13435   status=ExpandFilenames(&number_files,&filelist);
13436   if ((status == MagickFalse) || (number_files == 0))
13437     {
13438       if (number_files == 0)
13439         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13440       else
13441         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13442           filenames);
13443       return((Image *) NULL);
13444     }
13445   /*
13446     Set image background resources.
13447   */
13448   background_resources=(*resource_info);
13449   background_resources.window_id=AcquireString("");
13450   (void) FormatMagickString(background_resources.window_id,MaxTextExtent,
13451     "0x%lx",windows->image.id);
13452   background_resources.backdrop=MagickTrue;
13453   /*
13454     Read each image and convert them to a tile.
13455   */
13456   backdrop=(windows->visual_info->klass == TrueColor) ||
13457     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13458   read_info=CloneImageInfo(resource_info->image_info);
13459   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13460     (void *) NULL);
13461   images=NewImageList();
13462   exception=AcquireExceptionInfo();
13463   XSetCursorState(display,windows,MagickTrue);
13464   XCheckRefreshWindows(display,windows);
13465   for (i=0; i < (long) number_files; i++)
13466   {
13467     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13468     filelist[i]=DestroyString(filelist[i]);
13469     *read_info->magick='\0';
13470     (void) CloneString(&read_info->size,DefaultTileGeometry);
13471     next_image=ReadImage(read_info,exception);
13472     CatchException(exception);
13473     if (next_image != (Image *) NULL)
13474       {
13475         (void) DeleteImageProperty(next_image,"label");
13476         (void) SetImageProperty(next_image,"label",DefaultTileLabel);
13477         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13478           exception);
13479         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13480           geometry.height,exception);
13481         if (thumbnail_image != (Image *) NULL)
13482           {
13483             next_image=DestroyImage(next_image);
13484             next_image=thumbnail_image;
13485           }
13486         if (backdrop)
13487           {
13488             (void) XDisplayBackgroundImage(display,&background_resources,
13489               next_image);
13490             XSetCursorState(display,windows,MagickTrue);
13491           }
13492         AppendImageToList(&images,next_image);
13493         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13494           {
13495             MagickBooleanType
13496               proceed;
13497
13498             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13499               (MagickSizeType) number_files);
13500             if (proceed == MagickFalse)
13501               break;
13502           }
13503       }
13504   }
13505   exception=DestroyExceptionInfo(exception);
13506   filelist=(char **) RelinquishMagickMemory(filelist);
13507   read_info=DestroyImageInfo(read_info);
13508   if (images == (Image *) NULL)
13509     {
13510       XSetCursorState(display,windows,MagickFalse);
13511       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13512       return((Image *) NULL);
13513     }
13514   /*
13515     Create the Visual Image Directory.
13516   */
13517   montage_info=CloneMontageInfo(resource_info->image_info,(MontageInfo *) NULL);
13518   if (resource_info->font != (char *) NULL)
13519     (void) CloneString(&montage_info->font,resource_info->font);
13520   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13521   montage_image=MontageImageList(resource_info->image_info,montage_info,
13522     GetFirstImageInList(images),&images->exception);
13523   montage_info=DestroyMontageInfo(montage_info);
13524   images=DestroyImageList(images);
13525   XSetCursorState(display,windows,MagickFalse);
13526   if (montage_image == (Image *) NULL)
13527     return(montage_image);
13528   XClientMessage(display,windows->image.id,windows->im_protocols,
13529     windows->im_next_image,CurrentTime);
13530   return(montage_image);
13531 }
13532 \f
13533 /*
13534 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13535 %                                                                             %
13536 %                                                                             %
13537 %                                                                             %
13538 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13539 %                                                                             %
13540 %                                                                             %
13541 %                                                                             %
13542 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13543 %
13544 %  XDisplayBackgroundImage() displays an image in the background of a window.
13545 %
13546 %  The format of the XDisplayBackgroundImage method is:
13547 %
13548 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13549 %        XResourceInfo *resource_info,Image *image)
13550 %
13551 %  A description of each parameter follows:
13552 %
13553 %    o display: Specifies a connection to an X server;  returned from
13554 %      XOpenDisplay.
13555 %
13556 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13557 %
13558 %    o image: the image.
13559 %
13560 */
13561 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13562   XResourceInfo *resource_info,Image *image)
13563 {
13564   char
13565     geometry[MaxTextExtent],
13566     visual_type[MaxTextExtent];
13567
13568   int
13569     height,
13570     status,
13571     width;
13572
13573   RectangleInfo
13574     geometry_info;
13575
13576   static XPixelInfo
13577     pixel;
13578
13579   static XStandardColormap
13580     *map_info;
13581
13582   static XVisualInfo
13583     *visual_info = (XVisualInfo *) NULL;
13584
13585   static XWindowInfo
13586     window_info;
13587
13588   unsigned long
13589     delay;
13590
13591   Window
13592     root_window;
13593
13594   XGCValues
13595     context_values;
13596
13597   XResourceInfo
13598     resources;
13599
13600   XWindowAttributes
13601     window_attributes;
13602
13603   /*
13604     Determine target window.
13605   */
13606   assert(image != (Image *) NULL);
13607   assert(image->signature == MagickSignature);
13608   if (image->debug != MagickFalse)
13609     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13610   resources=(*resource_info);
13611   window_info.id=(Window) NULL;
13612   root_window=XRootWindow(display,XDefaultScreen(display));
13613   if (LocaleCompare(resources.window_id,"root") == 0)
13614     window_info.id=root_window;
13615   else
13616     {
13617       if (isdigit((unsigned char) *resources.window_id) != 0)
13618         window_info.id=XWindowByID(display,root_window,
13619           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13620       if (window_info.id == (Window) NULL)
13621         window_info.id=XWindowByName(display,root_window,resources.window_id);
13622     }
13623   if (window_info.id == (Window) NULL)
13624     {
13625       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13626         resources.window_id);
13627       return(MagickFalse);
13628     }
13629   /*
13630     Determine window visual id.
13631   */
13632   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13633   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13634   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13635   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13636   if (status != 0)
13637     (void) FormatMagickString(visual_type,MaxTextExtent,"0x%lx",
13638       XVisualIDFromVisual(window_attributes.visual));
13639   if (visual_info == (XVisualInfo *) NULL)
13640     {
13641       /*
13642         Allocate standard colormap.
13643       */
13644       map_info=XAllocStandardColormap();
13645       if (map_info == (XStandardColormap *) NULL)
13646         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13647           image->filename);
13648       map_info->colormap=(Colormap) NULL;
13649       pixel.pixels=(unsigned long *) NULL;
13650       /*
13651         Initialize visual info.
13652       */
13653       resources.map_type=(char *) NULL;
13654       resources.visual_type=visual_type;
13655       visual_info=XBestVisualInfo(display,map_info,&resources);
13656       if (visual_info == (XVisualInfo *) NULL)
13657         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13658           resources.visual_type);
13659       /*
13660         Initialize window info.
13661       */
13662       window_info.ximage=(XImage *) NULL;
13663       window_info.matte_image=(XImage *) NULL;
13664       window_info.pixmap=(Pixmap) NULL;
13665       window_info.matte_pixmap=(Pixmap) NULL;
13666     }
13667   /*
13668     Free previous root colors.
13669   */
13670   if (window_info.id == root_window)
13671     (void) XDestroyWindowColors(display,root_window);
13672   /*
13673     Initialize Standard Colormap.
13674   */
13675   resources.colormap=SharedColormap;
13676   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13677   /*
13678     Graphic context superclass.
13679   */
13680   context_values.background=pixel.background_color.pixel;
13681   context_values.foreground=pixel.foreground_color.pixel;
13682   pixel.annotate_context=XCreateGC(display,window_info.id,
13683     (unsigned long) (GCBackground | GCForeground),&context_values);
13684   if (pixel.annotate_context == (GC) NULL)
13685     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13686       image->filename);
13687   /*
13688     Initialize Image window attributes.
13689   */
13690   window_info.name=AcquireString("\0");
13691   window_info.icon_name=AcquireString("\0");
13692   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13693     &resources,&window_info);
13694   /*
13695     Create the X image.
13696   */
13697   window_info.width=(unsigned int) image->columns;
13698   window_info.height=(unsigned int) image->rows;
13699   if ((image->columns != window_info.width) ||
13700       (image->rows != window_info.height))
13701     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13702       image->filename);
13703   (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>",
13704     window_attributes.width,window_attributes.height);
13705   geometry_info.width=window_info.width;
13706   geometry_info.height=window_info.height;
13707   geometry_info.x=window_info.x;
13708   geometry_info.y=window_info.y;
13709   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13710     &geometry_info.width,&geometry_info.height);
13711   window_info.width=(unsigned int) geometry_info.width;
13712   window_info.height=(unsigned int) geometry_info.height;
13713   window_info.x=(int) geometry_info.x;
13714   window_info.y=(int) geometry_info.y;
13715   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13716     window_info.height);
13717   if (status == MagickFalse)
13718     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13719       image->filename);
13720   window_info.x=0;
13721   window_info.y=0;
13722   if (image->debug != MagickFalse)
13723     {
13724       (void) LogMagickEvent(X11Event,GetMagickModule(),
13725         "Image: %s[%lu] %lux%lu ",image->filename,image->scene,
13726         image->columns,image->rows);
13727       if (image->colors != 0)
13728         (void) LogMagickEvent(X11Event,GetMagickModule(),"%luc ",image->colors);
13729       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13730     }
13731   /*
13732     Adjust image dimensions as specified by backdrop or geometry options.
13733   */
13734   width=(int) window_info.width;
13735   height=(int) window_info.height;
13736   if (resources.backdrop != MagickFalse)
13737     {
13738       /*
13739         Center image on window.
13740       */
13741       window_info.x=(window_attributes.width/2)-
13742         (window_info.ximage->width/2);
13743       window_info.y=(window_attributes.height/2)-
13744         (window_info.ximage->height/2);
13745       width=window_attributes.width;
13746       height=window_attributes.height;
13747     }
13748   if ((resources.image_geometry != (char *) NULL) &&
13749       (*resources.image_geometry != '\0'))
13750     {
13751       char
13752         default_geometry[MaxTextExtent];
13753
13754       int
13755         flags,
13756         gravity;
13757
13758       XSizeHints
13759         *size_hints;
13760
13761       /*
13762         User specified geometry.
13763       */
13764       size_hints=XAllocSizeHints();
13765       if (size_hints == (XSizeHints *) NULL)
13766         ThrowXWindowFatalException(ResourceLimitFatalError,
13767           "MemoryAllocationFailed",image->filename);
13768       size_hints->flags=0L;
13769       (void) FormatMagickString(default_geometry,MaxTextExtent,"%dx%d",
13770         width,height);
13771       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13772         default_geometry,window_info.border_width,size_hints,&window_info.x,
13773         &window_info.y,&width,&height,&gravity);
13774       if (flags & (XValue | YValue))
13775         {
13776           width=window_attributes.width;
13777           height=window_attributes.height;
13778         }
13779       (void) XFree((void *) size_hints);
13780     }
13781   /*
13782     Create the X pixmap.
13783   */
13784   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13785     (unsigned int) height,window_info.depth);
13786   if (window_info.pixmap == (Pixmap) NULL)
13787     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13788       image->filename);
13789   /*
13790     Display pixmap on the window.
13791   */
13792   if (((unsigned int) width > window_info.width) ||
13793       ((unsigned int) height > window_info.height))
13794     (void) XFillRectangle(display,window_info.pixmap,
13795       window_info.annotate_context,0,0,(unsigned int) width,
13796       (unsigned int) height);
13797   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13798     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13799     window_info.width,(unsigned int) window_info.height);
13800   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13801   (void) XClearWindow(display,window_info.id);
13802   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13803   XDelay(display,delay == 0UL ? 10UL : delay);
13804   (void) XSync(display,MagickFalse);
13805   return(window_info.id == root_window ? MagickTrue : MagickFalse);
13806 }
13807 \f
13808 /*
13809 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13810 %                                                                             %
13811 %                                                                             %
13812 %                                                                             %
13813 +   X D i s p l a y I m a g e                                                 %
13814 %                                                                             %
13815 %                                                                             %
13816 %                                                                             %
13817 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13818 %
13819 %  XDisplayImage() displays an image via X11.  A new image is created and
13820 %  returned if the user interactively transforms the displayed image.
13821 %
13822 %  The format of the XDisplayImage method is:
13823 %
13824 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13825 %        char **argv,int argc,Image **image,unsigned long *state)
13826 %
13827 %  A description of each parameter follows:
13828 %
13829 %    o nexus:  Method XDisplayImage returns an image when the
13830 %      user chooses 'Open Image' from the command menu or picks a tile
13831 %      from the image directory.  Otherwise a null image is returned.
13832 %
13833 %    o display: Specifies a connection to an X server;  returned from
13834 %      XOpenDisplay.
13835 %
13836 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13837 %
13838 %    o argv: Specifies the application's argument list.
13839 %
13840 %    o argc: Specifies the number of arguments.
13841 %
13842 %    o image: Specifies an address to an address of an Image structure;
13843 %
13844 */
13845 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13846   char **argv,int argc,Image **image,unsigned long *state)
13847 {
13848 #define MagnifySize  256  /* must be a power of 2 */
13849 #define MagickMenus  10
13850 #define MagickTitle  "Commands"
13851
13852   static const char
13853     *CommandMenu[] =
13854     {
13855       "File",
13856       "Edit",
13857       "View",
13858       "Transform",
13859       "Enhance",
13860       "Effects",
13861       "F/X",
13862       "Image Edit",
13863       "Miscellany",
13864       "Help",
13865       (char *) NULL
13866     },
13867     *FileMenu[] =
13868     {
13869       "Open...",
13870       "Next",
13871       "Former",
13872       "Select...",
13873       "Save...",
13874       "Print...",
13875       "Delete...",
13876       "New...",
13877       "Visual Directory...",
13878       "Quit",
13879       (char *) NULL
13880     },
13881     *EditMenu[] =
13882     {
13883       "Undo",
13884       "Redo",
13885       "Cut",
13886       "Copy",
13887       "Paste",
13888       (char *) NULL
13889     },
13890     *ViewMenu[] =
13891     {
13892       "Half Size",
13893       "Original Size",
13894       "Double Size",
13895       "Resize...",
13896       "Apply",
13897       "Refresh",
13898       "Restore",
13899       (char *) NULL
13900     },
13901     *TransformMenu[] =
13902     {
13903       "Crop",
13904       "Chop",
13905       "Flop",
13906       "Flip",
13907       "Rotate Right",
13908       "Rotate Left",
13909       "Rotate...",
13910       "Shear...",
13911       "Roll...",
13912       "Trim Edges",
13913       (char *) NULL
13914     },
13915     *EnhanceMenu[] =
13916     {
13917       "Hue...",
13918       "Saturation...",
13919       "Brightness...",
13920       "Gamma...",
13921       "Spiff",
13922       "Dull",
13923       "Contrast Stretch...",
13924       "Sigmoidal Contrast...",
13925       "Normalize",
13926       "Equalize",
13927       "Negate",
13928       "Grayscale",
13929       "Map...",
13930       "Quantize...",
13931       (char *) NULL
13932     },
13933     *EffectsMenu[] =
13934     {
13935       "Despeckle",
13936       "Emboss",
13937       "Reduce Noise",
13938       "Add Noise...",
13939       "Sharpen...",
13940       "Blur...",
13941       "Threshold...",
13942       "Edge Detect...",
13943       "Spread...",
13944       "Shade...",
13945       "Raise...",
13946       "Segment...",
13947       (char *) NULL
13948     },
13949     *FXMenu[] =
13950     {
13951       "Solarize...",
13952       "Sepia Tone...",
13953       "Swirl...",
13954       "Implode...",
13955       "Vignette...",
13956       "Wave...",
13957       "Oil Paint...",
13958       "Charcoal Draw...",
13959       (char *) NULL
13960     },
13961     *ImageEditMenu[] =
13962     {
13963       "Annotate...",
13964       "Draw...",
13965       "Color...",
13966       "Matte...",
13967       "Composite...",
13968       "Add Border...",
13969       "Add Frame...",
13970       "Comment...",
13971       "Launch...",
13972       "Region of Interest...",
13973       (char *) NULL
13974     },
13975     *MiscellanyMenu[] =
13976     {
13977       "Image Info",
13978       "Zoom Image",
13979       "Show Preview...",
13980       "Show Histogram",
13981       "Show Matte",
13982       "Background...",
13983       "Slide Show...",
13984       "Preferences...",
13985       (char *) NULL
13986     },
13987     *HelpMenu[] =
13988     {
13989       "Overview",
13990       "Browse Documentation",
13991       "About Display",
13992       (char *) NULL
13993     },
13994     *ShortCutsMenu[] =
13995     {
13996       "Next",
13997       "Former",
13998       "Open...",
13999       "Save...",
14000       "Print...",
14001       "Undo",
14002       "Restore",
14003       "Image Info",
14004       "Quit",
14005       (char *) NULL
14006     },
14007     *VirtualMenu[] =
14008     {
14009       "Image Info",
14010       "Print",
14011       "Next",
14012       "Quit",
14013       (char *) NULL
14014     };
14015
14016   static const char
14017     **Menus[MagickMenus] =
14018     {
14019       FileMenu,
14020       EditMenu,
14021       ViewMenu,
14022       TransformMenu,
14023       EnhanceMenu,
14024       EffectsMenu,
14025       FXMenu,
14026       ImageEditMenu,
14027       MiscellanyMenu,
14028       HelpMenu
14029     };
14030
14031   static CommandType
14032     CommandMenus[] =
14033     {
14034       NullCommand,
14035       NullCommand,
14036       NullCommand,
14037       NullCommand,
14038       NullCommand,
14039       NullCommand,
14040       NullCommand,
14041       NullCommand,
14042       NullCommand,
14043       NullCommand,
14044     },
14045     FileCommands[] =
14046     {
14047       OpenCommand,
14048       NextCommand,
14049       FormerCommand,
14050       SelectCommand,
14051       SaveCommand,
14052       PrintCommand,
14053       DeleteCommand,
14054       NewCommand,
14055       VisualDirectoryCommand,
14056       QuitCommand
14057     },
14058     EditCommands[] =
14059     {
14060       UndoCommand,
14061       RedoCommand,
14062       CutCommand,
14063       CopyCommand,
14064       PasteCommand
14065     },
14066     ViewCommands[] =
14067     {
14068       HalfSizeCommand,
14069       OriginalSizeCommand,
14070       DoubleSizeCommand,
14071       ResizeCommand,
14072       ApplyCommand,
14073       RefreshCommand,
14074       RestoreCommand
14075     },
14076     TransformCommands[] =
14077     {
14078       CropCommand,
14079       ChopCommand,
14080       FlopCommand,
14081       FlipCommand,
14082       RotateRightCommand,
14083       RotateLeftCommand,
14084       RotateCommand,
14085       ShearCommand,
14086       RollCommand,
14087       TrimCommand
14088     },
14089     EnhanceCommands[] =
14090     {
14091       HueCommand,
14092       SaturationCommand,
14093       BrightnessCommand,
14094       GammaCommand,
14095       SpiffCommand,
14096       DullCommand,
14097       ContrastStretchCommand,
14098       SigmoidalContrastCommand,
14099       NormalizeCommand,
14100       EqualizeCommand,
14101       NegateCommand,
14102       GrayscaleCommand,
14103       MapCommand,
14104       QuantizeCommand
14105     },
14106     EffectsCommands[] =
14107     {
14108       DespeckleCommand,
14109       EmbossCommand,
14110       ReduceNoiseCommand,
14111       AddNoiseCommand,
14112       SharpenCommand,
14113       BlurCommand,
14114       ThresholdCommand,
14115       EdgeDetectCommand,
14116       SpreadCommand,
14117       ShadeCommand,
14118       RaiseCommand,
14119       SegmentCommand
14120     },
14121     FXCommands[] =
14122     {
14123       SolarizeCommand,
14124       SepiaToneCommand,
14125       SwirlCommand,
14126       ImplodeCommand,
14127       VignetteCommand,
14128       WaveCommand,
14129       OilPaintCommand,
14130       CharcoalDrawCommand
14131     },
14132     ImageEditCommands[] =
14133     {
14134       AnnotateCommand,
14135       DrawCommand,
14136       ColorCommand,
14137       MatteCommand,
14138       CompositeCommand,
14139       AddBorderCommand,
14140       AddFrameCommand,
14141       CommentCommand,
14142       LaunchCommand,
14143       RegionofInterestCommand
14144     },
14145     MiscellanyCommands[] =
14146     {
14147       InfoCommand,
14148       ZoomCommand,
14149       ShowPreviewCommand,
14150       ShowHistogramCommand,
14151       ShowMatteCommand,
14152       BackgroundCommand,
14153       SlideShowCommand,
14154       PreferencesCommand
14155     },
14156     HelpCommands[] =
14157     {
14158       HelpCommand,
14159       BrowseDocumentationCommand,
14160       VersionCommand
14161     },
14162     ShortCutsCommands[] =
14163     {
14164       NextCommand,
14165       FormerCommand,
14166       OpenCommand,
14167       SaveCommand,
14168       PrintCommand,
14169       UndoCommand,
14170       RestoreCommand,
14171       InfoCommand,
14172       QuitCommand
14173     },
14174     VirtualCommands[] =
14175     {
14176       InfoCommand,
14177       PrintCommand,
14178       NextCommand,
14179       QuitCommand
14180     };
14181
14182   static CommandType
14183     *Commands[MagickMenus] =
14184     {
14185       FileCommands,
14186       EditCommands,
14187       ViewCommands,
14188       TransformCommands,
14189       EnhanceCommands,
14190       EffectsCommands,
14191       FXCommands,
14192       ImageEditCommands,
14193       MiscellanyCommands,
14194       HelpCommands
14195     };
14196
14197   char
14198     command[MaxTextExtent],
14199     *cwd,
14200     geometry[MaxTextExtent],
14201     resource_name[MaxTextExtent];
14202
14203   CommandType
14204     command_type;
14205
14206   Image
14207     *display_image,
14208     *nexus;
14209
14210   int
14211     entry,
14212     id;
14213
14214   KeySym
14215     key_symbol;
14216
14217   MagickStatusType
14218     context_mask,
14219     status;
14220
14221   RectangleInfo
14222     geometry_info;
14223
14224   register int
14225     i;
14226
14227   static char
14228     working_directory[MaxTextExtent];
14229
14230   static XPoint
14231     vid_info;
14232
14233   static XWindowInfo
14234     *magick_windows[MaxXWindows];
14235
14236   static unsigned int
14237     number_windows;
14238
14239   struct stat
14240     attributes;
14241
14242   time_t
14243     timer,
14244     timestamp,
14245     update_time;
14246
14247   unsigned int
14248     height,
14249     width;
14250
14251   unsigned long
14252     delay;
14253
14254   WarningHandler
14255     warning_handler;
14256
14257   Window
14258     root_window;
14259
14260   XClassHint
14261     *class_hints;
14262
14263   XEvent
14264     event;
14265
14266   XFontStruct
14267     *font_info;
14268
14269   XGCValues
14270     context_values;
14271
14272   XPixelInfo
14273     *icon_pixel,
14274     *pixel;
14275
14276   XResourceInfo
14277     *icon_resources;
14278
14279   XStandardColormap
14280     *icon_map,
14281     *map_info;
14282
14283   XVisualInfo
14284     *icon_visual,
14285     *visual_info;
14286
14287   XWindowChanges
14288     window_changes;
14289
14290   XWindows
14291     *windows;
14292
14293   XWMHints
14294     *manager_hints;
14295
14296   assert(image != (Image **) NULL);
14297   assert((*image)->signature == MagickSignature);
14298   if ((*image)->debug != MagickFalse)
14299     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14300   display_image=(*image);
14301   warning_handler=(WarningHandler) NULL;
14302   windows=XSetWindows((XWindows *) ~0);
14303   if (windows != (XWindows *) NULL)
14304     {
14305       int
14306         status;
14307
14308       status=chdir(working_directory);
14309       if (status == -1)
14310         (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14311           FileOpenError,"UnableToOpenFile","%s",working_directory);
14312       warning_handler=resource_info->display_warnings ?
14313         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14314       warning_handler=resource_info->display_warnings ?
14315         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14316     }
14317   else
14318     {
14319       /*
14320         Allocate windows structure.
14321       */
14322       resource_info->colors=display_image->colors;
14323       windows=XSetWindows(XInitializeWindows(display,resource_info));
14324       if (windows == (XWindows *) NULL)
14325         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14326           (*image)->filename);
14327       /*
14328         Initialize window id's.
14329       */
14330       number_windows=0;
14331       magick_windows[number_windows++]=(&windows->icon);
14332       magick_windows[number_windows++]=(&windows->backdrop);
14333       magick_windows[number_windows++]=(&windows->image);
14334       magick_windows[number_windows++]=(&windows->info);
14335       magick_windows[number_windows++]=(&windows->command);
14336       magick_windows[number_windows++]=(&windows->widget);
14337       magick_windows[number_windows++]=(&windows->popup);
14338       magick_windows[number_windows++]=(&windows->magnify);
14339       magick_windows[number_windows++]=(&windows->pan);
14340       for (i=0; i < (int) number_windows; i++)
14341         magick_windows[i]->id=(Window) NULL;
14342       vid_info.x=0;
14343       vid_info.y=0;
14344     }
14345   /*
14346     Initialize font info.
14347   */
14348   if (windows->font_info != (XFontStruct *) NULL)
14349     (void) XFreeFont(display,windows->font_info);
14350   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14351   if (windows->font_info == (XFontStruct *) NULL)
14352     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14353       resource_info->font);
14354   /*
14355     Initialize Standard Colormap.
14356   */
14357   map_info=windows->map_info;
14358   icon_map=windows->icon_map;
14359   visual_info=windows->visual_info;
14360   icon_visual=windows->icon_visual;
14361   pixel=windows->pixel_info;
14362   icon_pixel=windows->icon_pixel;
14363   font_info=windows->font_info;
14364   icon_resources=windows->icon_resources;
14365   class_hints=windows->class_hints;
14366   manager_hints=windows->manager_hints;
14367   root_window=XRootWindow(display,visual_info->screen);
14368   nexus=NewImageList();
14369   if (display_image->debug != MagickFalse)
14370     {
14371       (void) LogMagickEvent(X11Event,GetMagickModule(),
14372         "Image: %s[%lu] %lux%lu ",display_image->filename,
14373         display_image->scene,display_image->columns,display_image->rows);
14374       if (display_image->colors != 0)
14375         (void) LogMagickEvent(X11Event,GetMagickModule(),"%luc ",
14376           display_image->colors);
14377       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14378         display_image->magick);
14379     }
14380   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14381     map_info,pixel);
14382   display_image->taint=MagickFalse;
14383   /*
14384     Initialize graphic context.
14385   */
14386   windows->context.id=(Window) NULL;
14387   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14388     resource_info,&windows->context);
14389   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14390   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14391   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14392   manager_hints->flags=InputHint | StateHint;
14393   manager_hints->input=MagickFalse;
14394   manager_hints->initial_state=WithdrawnState;
14395   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14396     &windows->context);
14397   if (display_image->debug != MagickFalse)
14398     (void) LogMagickEvent(X11Event,GetMagickModule(),
14399       "Window id: 0x%lx (context)",windows->context.id);
14400   context_values.background=pixel->background_color.pixel;
14401   context_values.font=font_info->fid;
14402   context_values.foreground=pixel->foreground_color.pixel;
14403   context_values.graphics_exposures=MagickFalse;
14404   context_mask=(MagickStatusType)
14405     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14406   if (pixel->annotate_context != (GC) NULL)
14407     (void) XFreeGC(display,pixel->annotate_context);
14408   pixel->annotate_context=XCreateGC(display,windows->context.id,
14409     context_mask,&context_values);
14410   if (pixel->annotate_context == (GC) NULL)
14411     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14412       display_image->filename);
14413   context_values.background=pixel->depth_color.pixel;
14414   if (pixel->widget_context != (GC) NULL)
14415     (void) XFreeGC(display,pixel->widget_context);
14416   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14417     &context_values);
14418   if (pixel->widget_context == (GC) NULL)
14419     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14420       display_image->filename);
14421   context_values.background=pixel->foreground_color.pixel;
14422   context_values.foreground=pixel->background_color.pixel;
14423   context_values.plane_mask=context_values.background ^
14424     context_values.foreground;
14425   if (pixel->highlight_context != (GC) NULL)
14426     (void) XFreeGC(display,pixel->highlight_context);
14427   pixel->highlight_context=XCreateGC(display,windows->context.id,
14428     (unsigned long) (context_mask | GCPlaneMask),&context_values);
14429   if (pixel->highlight_context == (GC) NULL)
14430     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14431       display_image->filename);
14432   (void) XDestroyWindow(display,windows->context.id);
14433   /*
14434     Initialize icon window.
14435   */
14436   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14437     icon_resources,&windows->icon);
14438   windows->icon.geometry=resource_info->icon_geometry;
14439   XBestIconSize(display,&windows->icon,display_image);
14440   windows->icon.attributes.colormap=XDefaultColormap(display,
14441     icon_visual->screen);
14442   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14443   manager_hints->flags=InputHint | StateHint;
14444   manager_hints->input=MagickFalse;
14445   manager_hints->initial_state=IconicState;
14446   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14447     &windows->icon);
14448   if (display_image->debug != MagickFalse)
14449     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14450       windows->icon.id);
14451   /*
14452     Initialize graphic context for icon window.
14453   */
14454   if (icon_pixel->annotate_context != (GC) NULL)
14455     (void) XFreeGC(display,icon_pixel->annotate_context);
14456   context_values.background=icon_pixel->background_color.pixel;
14457   context_values.foreground=icon_pixel->foreground_color.pixel;
14458   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14459     (unsigned long) (GCBackground | GCForeground),&context_values);
14460   if (icon_pixel->annotate_context == (GC) NULL)
14461     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14462       display_image->filename);
14463   windows->icon.annotate_context=icon_pixel->annotate_context;
14464   /*
14465     Initialize Image window.
14466   */
14467   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14468     &windows->image);
14469   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14470   if (resource_info->use_shared_memory == MagickFalse)
14471     windows->image.shared_memory=MagickFalse;
14472   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14473     {
14474       char
14475         *title;
14476
14477       title=InterpretImageProperties(resource_info->image_info,display_image,
14478         resource_info->title);
14479       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14480       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14481       title=DestroyString(title);
14482     }
14483   else
14484     {
14485       char
14486         filename[MaxTextExtent];
14487
14488       /*
14489         Window name is the base of the filename.
14490       */
14491       GetPathComponent(display_image->magick_filename,TailPath,filename);
14492       if (GetImageListLength(display_image) == 1)
14493         (void) FormatMagickString(windows->image.name,MaxTextExtent,
14494           "%s: %s",MagickPackageName,filename);
14495       else
14496         (void) FormatMagickString(windows->image.name,MaxTextExtent,
14497           "%s: %s[%lu of %lu]",MagickPackageName,filename,display_image->scene,
14498           GetImageListLength(display_image));
14499       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14500     }
14501   if (resource_info->immutable)
14502     windows->image.immutable=MagickTrue;
14503   windows->image.use_pixmap=resource_info->use_pixmap;
14504   windows->image.geometry=resource_info->image_geometry;
14505   (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14506     XDisplayWidth(display,visual_info->screen),
14507     XDisplayHeight(display,visual_info->screen));
14508   geometry_info.width=display_image->columns;
14509   geometry_info.height=display_image->rows;
14510   geometry_info.x=0;
14511   geometry_info.y=0;
14512   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14513     &geometry_info.width,&geometry_info.height);
14514   windows->image.width=(unsigned int) geometry_info.width;
14515   windows->image.height=(unsigned int) geometry_info.height;
14516   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14517     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14518     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14519     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14520   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14521     resource_info,&windows->backdrop);
14522   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14523     {
14524       /*
14525         Initialize backdrop window.
14526       */
14527       windows->backdrop.x=0;
14528       windows->backdrop.y=0;
14529       (void) CloneString(&windows->backdrop.name,"Backdrop");
14530       windows->backdrop.flags=(unsigned long) (USSize | USPosition);
14531       windows->backdrop.width=(unsigned int)
14532         XDisplayWidth(display,visual_info->screen);
14533       windows->backdrop.height=(unsigned int)
14534         XDisplayHeight(display,visual_info->screen);
14535       windows->backdrop.border_width=0;
14536       windows->backdrop.immutable=MagickTrue;
14537       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14538         ButtonReleaseMask;
14539       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14540         StructureNotifyMask;
14541       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14542       manager_hints->icon_window=windows->icon.id;
14543       manager_hints->input=MagickTrue;
14544       manager_hints->initial_state=resource_info->iconic ? IconicState :
14545         NormalState;
14546       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14547         &windows->backdrop);
14548       if (display_image->debug != MagickFalse)
14549         (void) LogMagickEvent(X11Event,GetMagickModule(),
14550           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14551       (void) XMapWindow(display,windows->backdrop.id);
14552       (void) XClearWindow(display,windows->backdrop.id);
14553       if (windows->image.id != (Window) NULL)
14554         {
14555           (void) XDestroyWindow(display,windows->image.id);
14556           windows->image.id=(Window) NULL;
14557         }
14558       /*
14559         Position image in the center the backdrop.
14560       */
14561       windows->image.flags|=USPosition;
14562       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14563         (windows->image.width/2);
14564       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14565         (windows->image.height/2);
14566     }
14567   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14568   manager_hints->icon_window=windows->icon.id;
14569   manager_hints->input=MagickTrue;
14570   manager_hints->initial_state=resource_info->iconic ? IconicState :
14571     NormalState;
14572   if (windows->group_leader.id != (Window) NULL)
14573     {
14574       /*
14575         Follow the leader.
14576       */
14577       manager_hints->flags|=WindowGroupHint;
14578       manager_hints->window_group=windows->group_leader.id;
14579       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14580       if (display_image->debug != MagickFalse)
14581         (void) LogMagickEvent(X11Event,GetMagickModule(),
14582           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14583     }
14584   XMakeWindow(display,
14585     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14586     argv,argc,class_hints,manager_hints,&windows->image);
14587   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14588     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14589   if (windows->group_leader.id != (Window) NULL)
14590     (void) XSetTransientForHint(display,windows->image.id,
14591       windows->group_leader.id);
14592   if (display_image->debug != MagickFalse)
14593     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14594       windows->image.id);
14595   /*
14596     Initialize Info widget.
14597   */
14598   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14599     &windows->info);
14600   (void) CloneString(&windows->info.name,"Info");
14601   (void) CloneString(&windows->info.icon_name,"Info");
14602   windows->info.border_width=1;
14603   windows->info.x=2;
14604   windows->info.y=2;
14605   windows->info.flags|=PPosition;
14606   windows->info.attributes.win_gravity=UnmapGravity;
14607   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14608     StructureNotifyMask;
14609   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14610   manager_hints->input=MagickFalse;
14611   manager_hints->initial_state=NormalState;
14612   manager_hints->window_group=windows->image.id;
14613   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14614     &windows->info);
14615   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14616     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14617   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14618     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14619   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14620   if (windows->image.mapped != MagickFalse)
14621     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14622   if (display_image->debug != MagickFalse)
14623     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14624       windows->info.id);
14625   /*
14626     Initialize Command widget.
14627   */
14628   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14629     resource_info,&windows->command);
14630   windows->command.data=MagickMenus;
14631   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14632   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.command",
14633     resource_info->client_name);
14634   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14635     resource_name,"geometry",(char *) NULL);
14636   (void) CloneString(&windows->command.name,MagickTitle);
14637   windows->command.border_width=0;
14638   windows->command.flags|=PPosition;
14639   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14640     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14641     OwnerGrabButtonMask | StructureNotifyMask;
14642   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14643   manager_hints->input=MagickTrue;
14644   manager_hints->initial_state=NormalState;
14645   manager_hints->window_group=windows->image.id;
14646   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14647     &windows->command);
14648   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14649     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14650     HighlightHeight);
14651   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14652     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14653   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14654   if (windows->command.mapped != MagickFalse)
14655     (void) XMapRaised(display,windows->command.id);
14656   if (display_image->debug != MagickFalse)
14657     (void) LogMagickEvent(X11Event,GetMagickModule(),
14658       "Window id: 0x%lx (command)",windows->command.id);
14659   /*
14660     Initialize Widget window.
14661   */
14662   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14663     resource_info,&windows->widget);
14664   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.widget",
14665     resource_info->client_name);
14666   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14667     resource_name,"geometry",(char *) NULL);
14668   windows->widget.border_width=0;
14669   windows->widget.flags|=PPosition;
14670   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14671     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14672     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14673     StructureNotifyMask;
14674   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14675   manager_hints->input=MagickTrue;
14676   manager_hints->initial_state=NormalState;
14677   manager_hints->window_group=windows->image.id;
14678   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14679     &windows->widget);
14680   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14681     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14682   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14683     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14684   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14685   if (display_image->debug != MagickFalse)
14686     (void) LogMagickEvent(X11Event,GetMagickModule(),
14687       "Window id: 0x%lx (widget)",windows->widget.id);
14688   /*
14689     Initialize popup window.
14690   */
14691   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14692     resource_info,&windows->popup);
14693   windows->popup.border_width=0;
14694   windows->popup.flags|=PPosition;
14695   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14696     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14697     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14698   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14699   manager_hints->input=MagickTrue;
14700   manager_hints->initial_state=NormalState;
14701   manager_hints->window_group=windows->image.id;
14702   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14703     &windows->popup);
14704   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14705     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14706   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14707     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14708   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14709   if (display_image->debug != MagickFalse)
14710     (void) LogMagickEvent(X11Event,GetMagickModule(),
14711       "Window id: 0x%lx (pop up)",windows->popup.id);
14712   /*
14713     Initialize Magnify window and cursor.
14714   */
14715   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14716     resource_info,&windows->magnify);
14717   if (resource_info->use_shared_memory == MagickFalse)
14718     windows->magnify.shared_memory=MagickFalse;
14719   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.magnify",
14720     resource_info->client_name);
14721   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14722     resource_name,"geometry",(char *) NULL);
14723   (void) FormatMagickString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14724     resource_info->magnify);
14725   if (windows->magnify.cursor != (Cursor) NULL)
14726     (void) XFreeCursor(display,windows->magnify.cursor);
14727   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14728     map_info->colormap,resource_info->background_color,
14729     resource_info->foreground_color);
14730   if (windows->magnify.cursor == (Cursor) NULL)
14731     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14732       display_image->filename);
14733   windows->magnify.width=MagnifySize;
14734   windows->magnify.height=MagnifySize;
14735   windows->magnify.flags|=PPosition;
14736   windows->magnify.min_width=MagnifySize;
14737   windows->magnify.min_height=MagnifySize;
14738   windows->magnify.width_inc=MagnifySize;
14739   windows->magnify.height_inc=MagnifySize;
14740   windows->magnify.data=resource_info->magnify;
14741   windows->magnify.attributes.cursor=windows->magnify.cursor;
14742   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14743     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14744     StructureNotifyMask;
14745   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14746   manager_hints->input=MagickTrue;
14747   manager_hints->initial_state=NormalState;
14748   manager_hints->window_group=windows->image.id;
14749   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14750     &windows->magnify);
14751   if (display_image->debug != MagickFalse)
14752     (void) LogMagickEvent(X11Event,GetMagickModule(),
14753       "Window id: 0x%lx (magnify)",windows->magnify.id);
14754   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14755   /*
14756     Initialize panning window.
14757   */
14758   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14759     resource_info,&windows->pan);
14760   (void) CloneString(&windows->pan.name,"Pan Icon");
14761   windows->pan.width=windows->icon.width;
14762   windows->pan.height=windows->icon.height;
14763   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.pan",
14764     resource_info->client_name);
14765   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14766     resource_name,"geometry",(char *) NULL);
14767   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14768     &windows->pan.width,&windows->pan.height);
14769   windows->pan.flags|=PPosition;
14770   windows->pan.immutable=MagickTrue;
14771   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14772     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14773     StructureNotifyMask;
14774   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14775   manager_hints->input=MagickFalse;
14776   manager_hints->initial_state=NormalState;
14777   manager_hints->window_group=windows->image.id;
14778   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14779     &windows->pan);
14780   if (display_image->debug != MagickFalse)
14781     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14782       windows->pan.id);
14783   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14784   if (windows->info.mapped != MagickFalse)
14785     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14786   if ((windows->image.mapped == MagickFalse) ||
14787       (windows->backdrop.id != (Window) NULL))
14788     (void) XMapWindow(display,windows->image.id);
14789   /*
14790     Set our progress monitor and warning handlers.
14791   */
14792   if (warning_handler == (WarningHandler) NULL)
14793     {
14794       warning_handler=resource_info->display_warnings ?
14795         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14796       warning_handler=resource_info->display_warnings ?
14797         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14798     }
14799   /*
14800     Initialize Image and Magnify X images.
14801   */
14802   windows->image.x=0;
14803   windows->image.y=0;
14804   windows->magnify.shape=MagickFalse;
14805   width=(unsigned int) display_image->columns;
14806   height=(unsigned int) display_image->rows;
14807   if ((display_image->columns != width) || (display_image->rows != height))
14808     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14809       display_image->filename);
14810   status=XMakeImage(display,resource_info,&windows->image,display_image,
14811     width,height);
14812   if (status == MagickFalse)
14813     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14814       display_image->filename);
14815   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14816     windows->magnify.width,windows->magnify.height);
14817   if (status == MagickFalse)
14818     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14819       display_image->filename);
14820   if (windows->magnify.mapped != MagickFalse)
14821     (void) XMapRaised(display,windows->magnify.id);
14822   if (windows->pan.mapped != MagickFalse)
14823     (void) XMapRaised(display,windows->pan.id);
14824   windows->image.window_changes.width=(int) display_image->columns;
14825   windows->image.window_changes.height=(int) display_image->rows;
14826   (void) XConfigureImage(display,resource_info,windows,display_image);
14827   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14828   (void) XSync(display,MagickFalse);
14829   /*
14830     Respond to events.
14831   */
14832   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14833   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14834   update_time=0;
14835   if (resource_info->update != MagickFalse)
14836     {
14837       MagickBooleanType
14838         status;
14839
14840       /*
14841         Determine when file data was last modified.
14842       */
14843       status=GetPathAttributes(display_image->filename,&attributes);
14844       if (status != MagickFalse)
14845         update_time=attributes.st_mtime;
14846     }
14847   *state&=(~FormerImageState);
14848   *state&=(~MontageImageState);
14849   *state&=(~NextImageState);
14850   do
14851   {
14852     /*
14853       Handle a window event.
14854     */
14855     if (windows->image.mapped != MagickFalse)
14856       if ((display_image->delay != 0) || (resource_info->update != 0))
14857         {
14858           if (timer < time((time_t *) NULL))
14859             {
14860               if (resource_info->update == MagickFalse)
14861                 *state|=NextImageState | ExitState;
14862               else
14863                 {
14864                   MagickBooleanType
14865                     status;
14866
14867                   /*
14868                     Determine if image file was modified.
14869                   */
14870                   status=GetPathAttributes(display_image->filename,&attributes);
14871                   if (status != MagickFalse)
14872                     if (update_time != attributes.st_mtime)
14873                       {
14874                         /*
14875                           Redisplay image.
14876                         */
14877                         (void) FormatMagickString(
14878                           resource_info->image_info->filename,MaxTextExtent,
14879                           "%s:%s",display_image->magick,
14880                           display_image->filename);
14881                         nexus=ReadImage(resource_info->image_info,
14882                           &display_image->exception);
14883                         if (nexus != (Image *) NULL)
14884                           {
14885                             nexus=DestroyImage(nexus);
14886                             *state|=NextImageState | ExitState;
14887                           }
14888                       }
14889                   delay=display_image->delay/MagickMax(
14890                     display_image->ticks_per_second,1L);
14891                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14892                 }
14893             }
14894           if (XEventsQueued(display,QueuedAfterFlush) == 0)
14895             {
14896               /*
14897                 Do not block if delay > 0.
14898               */
14899               XDelay(display,SuspendTime << 2);
14900               continue;
14901             }
14902         }
14903     timestamp=time((time_t *) NULL);
14904     (void) XNextEvent(display,&event);
14905     if (windows->image.stasis == MagickFalse)
14906       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14907         MagickTrue : MagickFalse;
14908     if (windows->magnify.stasis == MagickFalse)
14909       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14910         MagickTrue : MagickFalse;
14911     if (event.xany.window == windows->command.id)
14912       {
14913         /*
14914           Select a command from the Command widget.
14915         */
14916         id=XCommandWidget(display,windows,CommandMenu,&event);
14917         if (id < 0)
14918           continue;
14919         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
14920         command_type=CommandMenus[id];
14921         if (id < MagickMenus)
14922           {
14923             /*
14924               Select a command from a pop-up menu.
14925             */
14926             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
14927               command);
14928             if (entry < 0)
14929               continue;
14930             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
14931             command_type=Commands[id][entry];
14932           }
14933         if (command_type != NullCommand)
14934           nexus=XMagickCommand(display,resource_info,windows,command_type,
14935             &display_image);
14936         continue;
14937       }
14938     switch (event.type)
14939     {
14940       case ButtonPress:
14941       {
14942         if (display_image->debug != MagickFalse)
14943           (void) LogMagickEvent(X11Event,GetMagickModule(),
14944             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
14945             event.xbutton.button,event.xbutton.x,event.xbutton.y);
14946         if ((event.xbutton.button == Button3) &&
14947             (event.xbutton.state & Mod1Mask))
14948           {
14949             /*
14950               Convert Alt-Button3 to Button2.
14951             */
14952             event.xbutton.button=Button2;
14953             event.xbutton.state&=(~Mod1Mask);
14954           }
14955         if (event.xbutton.window == windows->backdrop.id)
14956           {
14957             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
14958               event.xbutton.time);
14959             break;
14960           }
14961         if (event.xbutton.window == windows->image.id)
14962           {
14963             switch (event.xbutton.button)
14964             {
14965               case Button1:
14966               {
14967                 if (resource_info->immutable)
14968                   {
14969                     /*
14970                       Select a command from the Virtual menu.
14971                     */
14972                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
14973                       command);
14974                     if (entry >= 0)
14975                       nexus=XMagickCommand(display,resource_info,windows,
14976                         VirtualCommands[entry],&display_image);
14977                     break;
14978                   }
14979                 /*
14980                   Map/unmap Command widget.
14981                 */
14982                 if (windows->command.mapped != MagickFalse)
14983                   (void) XWithdrawWindow(display,windows->command.id,
14984                     windows->command.screen);
14985                 else
14986                   {
14987                     (void) XCommandWidget(display,windows,CommandMenu,
14988                       (XEvent *) NULL);
14989                     (void) XMapRaised(display,windows->command.id);
14990                   }
14991                 break;
14992               }
14993               case Button2:
14994               {
14995                 /*
14996                   User pressed the image magnify button.
14997                 */
14998                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
14999                   &display_image);
15000                 XMagnifyImage(display,windows,&event);
15001                 break;
15002               }
15003               case Button3:
15004               {
15005                 if (resource_info->immutable)
15006                   {
15007                     /*
15008                       Select a command from the Virtual menu.
15009                     */
15010                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15011                       command);
15012                     if (entry >= 0)
15013                       nexus=XMagickCommand(display,resource_info,windows,
15014                         VirtualCommands[entry],&display_image);
15015                     break;
15016                   }
15017                 if (display_image->montage != (char *) NULL)
15018                   {
15019                     /*
15020                       Open or delete a tile from a visual image directory.
15021                     */
15022                     nexus=XTileImage(display,resource_info,windows,
15023                       display_image,&event);
15024                     if (nexus != (Image *) NULL)
15025                       *state|=MontageImageState | NextImageState | ExitState;
15026                     vid_info.x=windows->image.x;
15027                     vid_info.y=windows->image.y;
15028                     break;
15029                   }
15030                 /*
15031                   Select a command from the Short Cuts menu.
15032                 */
15033                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15034                   command);
15035                 if (entry >= 0)
15036                   nexus=XMagickCommand(display,resource_info,windows,
15037                     ShortCutsCommands[entry],&display_image);
15038                 break;
15039               }
15040               case Button4:
15041               {
15042                 /*
15043                   Wheel up.
15044                 */
15045                 XTranslateImage(display,windows,*image,XK_Up);
15046                 break;
15047               }
15048               case Button5:
15049               {
15050                 /*
15051                   Wheel down.
15052                 */
15053                 XTranslateImage(display,windows,*image,XK_Down);
15054                 break;
15055               }
15056               default:
15057                 break;
15058             }
15059             break;
15060           }
15061         if (event.xbutton.window == windows->magnify.id)
15062           {
15063             int
15064               factor;
15065
15066             static const char
15067               *MagnifyMenu[] =
15068               {
15069                 "2",
15070                 "4",
15071                 "5",
15072                 "6",
15073                 "7",
15074                 "8",
15075                 "9",
15076                 "3",
15077                 (char *) NULL,
15078               };
15079
15080             static KeySym
15081               MagnifyCommands[] =
15082               {
15083                 XK_2,
15084                 XK_4,
15085                 XK_5,
15086                 XK_6,
15087                 XK_7,
15088                 XK_8,
15089                 XK_9,
15090                 XK_3
15091               };
15092
15093             /*
15094               Select a magnify factor from the pop-up menu.
15095             */
15096             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15097             if (factor >= 0)
15098               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15099             break;
15100           }
15101         if (event.xbutton.window == windows->pan.id)
15102           {
15103             switch (event.xbutton.button)
15104             {
15105               case Button4:
15106               {
15107                 /*
15108                   Wheel up.
15109                 */
15110                 XTranslateImage(display,windows,*image,XK_Up);
15111                 break;
15112               }
15113               case Button5:
15114               {
15115                 /*
15116                   Wheel down.
15117                 */
15118                 XTranslateImage(display,windows,*image,XK_Down);
15119                 break;
15120               }
15121               default:
15122               {
15123                 XPanImage(display,windows,&event);
15124                 break;
15125               }
15126             }
15127             break;
15128           }
15129         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15130           1L);
15131         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15132         break;
15133       }
15134       case ButtonRelease:
15135       {
15136         if (display_image->debug != MagickFalse)
15137           (void) LogMagickEvent(X11Event,GetMagickModule(),
15138             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15139             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15140         break;
15141       }
15142       case ClientMessage:
15143       {
15144         if (display_image->debug != MagickFalse)
15145           (void) LogMagickEvent(X11Event,GetMagickModule(),
15146             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15147             event.xclient.message_type,event.xclient.format,(unsigned long)
15148             event.xclient.data.l[0]);
15149         if (event.xclient.message_type == windows->im_protocols)
15150           {
15151             if (*event.xclient.data.l == (long) windows->im_update_widget)
15152               {
15153                 (void) CloneString(&windows->command.name,MagickTitle);
15154                 windows->command.data=MagickMenus;
15155                 (void) XCommandWidget(display,windows,CommandMenu,
15156                   (XEvent *) NULL);
15157                 break;
15158               }
15159             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15160               {
15161                 /*
15162                   Update graphic context and window colormap.
15163                 */
15164                 for (i=0; i < (int) number_windows; i++)
15165                 {
15166                   if (magick_windows[i]->id == windows->icon.id)
15167                     continue;
15168                   context_values.background=pixel->background_color.pixel;
15169                   context_values.foreground=pixel->foreground_color.pixel;
15170                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15171                     context_mask,&context_values);
15172                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15173                     context_mask,&context_values);
15174                   context_values.background=pixel->foreground_color.pixel;
15175                   context_values.foreground=pixel->background_color.pixel;
15176                   context_values.plane_mask=context_values.background ^
15177                     context_values.foreground;
15178                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15179                     (unsigned long) (context_mask | GCPlaneMask),
15180                     &context_values);
15181                   magick_windows[i]->attributes.background_pixel=
15182                     pixel->background_color.pixel;
15183                   magick_windows[i]->attributes.border_pixel=
15184                     pixel->border_color.pixel;
15185                   magick_windows[i]->attributes.colormap=map_info->colormap;
15186                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15187                     magick_windows[i]->mask,&magick_windows[i]->attributes);
15188                 }
15189                 if (windows->pan.mapped != MagickFalse)
15190                   {
15191                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15192                       windows->pan.pixmap);
15193                     (void) XClearWindow(display,windows->pan.id);
15194                     XDrawPanRectangle(display,windows);
15195                   }
15196                 if (windows->backdrop.id != (Window) NULL)
15197                   (void) XInstallColormap(display,map_info->colormap);
15198                 break;
15199               }
15200             if (*event.xclient.data.l == (long) windows->im_former_image)
15201               {
15202                 *state|=FormerImageState | ExitState;
15203                 break;
15204               }
15205             if (*event.xclient.data.l == (long) windows->im_next_image)
15206               {
15207                 *state|=NextImageState | ExitState;
15208                 break;
15209               }
15210             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15211               {
15212                 *state|=RetainColorsState;
15213                 break;
15214               }
15215             if (*event.xclient.data.l == (long) windows->im_exit)
15216               {
15217                 *state|=ExitState;
15218                 break;
15219               }
15220             break;
15221           }
15222         if (event.xclient.message_type == windows->dnd_protocols)
15223           {
15224             Atom
15225               selection,
15226               type;
15227
15228             int
15229               format,
15230               status;
15231
15232             unsigned char
15233               *data;
15234
15235             unsigned long
15236               after,
15237               length;
15238
15239             /*
15240               Display image named by the Drag-and-Drop selection.
15241             */
15242             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15243               break;
15244             selection=XInternAtom(display,"DndSelection",MagickFalse);
15245             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15246               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15247               &length,&after,&data);
15248             if ((status != Success) || (length == 0))
15249               break;
15250             if (*event.xclient.data.l == 2)
15251               {
15252                 /*
15253                   Offix DND.
15254                 */
15255                 (void) CopyMagickString(resource_info->image_info->filename,
15256                   (char *) data,MaxTextExtent);
15257               }
15258             else
15259               {
15260                 /*
15261                   XDND.
15262                 */
15263                 if (strncmp((char *) data, "file:", 5) != 0)
15264                   {
15265                     (void) XFree((void *) data);
15266                     break;
15267                   }
15268                 (void) CopyMagickString(resource_info->image_info->filename,
15269                   ((char *) data)+5,MaxTextExtent);
15270               }
15271             nexus=ReadImage(resource_info->image_info,
15272               &display_image->exception);
15273             CatchException(&display_image->exception);
15274             if (nexus != (Image *) NULL)
15275               *state|=NextImageState | ExitState;
15276             (void) XFree((void *) data);
15277             break;
15278           }
15279         /*
15280           If client window delete message, exit.
15281         */
15282         if (event.xclient.message_type != windows->wm_protocols)
15283           break;
15284         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15285           break;
15286         (void) XWithdrawWindow(display,event.xclient.window,
15287           visual_info->screen);
15288         if (event.xclient.window == windows->image.id)
15289           {
15290             *state|=ExitState;
15291             break;
15292           }
15293         if (event.xclient.window == windows->pan.id)
15294           {
15295             /*
15296               Restore original image size when pan window is deleted.
15297             */
15298             windows->image.window_changes.width=windows->image.ximage->width;
15299             windows->image.window_changes.height=windows->image.ximage->height;
15300             (void) XConfigureImage(display,resource_info,windows,
15301               display_image);
15302           }
15303         break;
15304       }
15305       case ConfigureNotify:
15306       {
15307         if (display_image->debug != MagickFalse)
15308           (void) LogMagickEvent(X11Event,GetMagickModule(),
15309             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15310             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15311             event.xconfigure.y,event.xconfigure.send_event);
15312         if (event.xconfigure.window == windows->image.id)
15313           {
15314             /*
15315               Image window has a new configuration.
15316             */
15317             if (event.xconfigure.send_event != 0)
15318               {
15319                 XWindowChanges
15320                   window_changes;
15321
15322                 /*
15323                   Position the transient windows relative of the Image window.
15324                 */
15325                 if (windows->command.geometry == (char *) NULL)
15326                   if (windows->command.mapped == MagickFalse)
15327                     {
15328                       windows->command.x=event.xconfigure.x-
15329                         windows->command.width-25;
15330                       windows->command.y=event.xconfigure.y;
15331                       XConstrainWindowPosition(display,&windows->command);
15332                       window_changes.x=windows->command.x;
15333                       window_changes.y=windows->command.y;
15334                       (void) XReconfigureWMWindow(display,windows->command.id,
15335                         windows->command.screen,(unsigned int) (CWX | CWY),
15336                         &window_changes);
15337                     }
15338                 if (windows->widget.geometry == (char *) NULL)
15339                   if (windows->widget.mapped == MagickFalse)
15340                     {
15341                       windows->widget.x=event.xconfigure.x+
15342                         event.xconfigure.width/10;
15343                       windows->widget.y=event.xconfigure.y+
15344                         event.xconfigure.height/10;
15345                       XConstrainWindowPosition(display,&windows->widget);
15346                       window_changes.x=windows->widget.x;
15347                       window_changes.y=windows->widget.y;
15348                       (void) XReconfigureWMWindow(display,windows->widget.id,
15349                         windows->widget.screen,(unsigned int) (CWX | CWY),
15350                         &window_changes);
15351                     }
15352                 if (windows->magnify.geometry == (char *) NULL)
15353                   if (windows->magnify.mapped == MagickFalse)
15354                     {
15355                       windows->magnify.x=event.xconfigure.x+
15356                         event.xconfigure.width+25;
15357                       windows->magnify.y=event.xconfigure.y;
15358                       XConstrainWindowPosition(display,&windows->magnify);
15359                       window_changes.x=windows->magnify.x;
15360                       window_changes.y=windows->magnify.y;
15361                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15362                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15363                         &window_changes);
15364                     }
15365                 if (windows->pan.geometry == (char *) NULL)
15366                   if (windows->pan.mapped == MagickFalse)
15367                     {
15368                       windows->pan.x=event.xconfigure.x+
15369                         event.xconfigure.width+25;
15370                       windows->pan.y=event.xconfigure.y+
15371                         windows->magnify.height+50;
15372                       XConstrainWindowPosition(display,&windows->pan);
15373                       window_changes.x=windows->pan.x;
15374                       window_changes.y=windows->pan.y;
15375                       (void) XReconfigureWMWindow(display,windows->pan.id,
15376                         windows->pan.screen,(unsigned int) (CWX | CWY),
15377                         &window_changes);
15378                     }
15379               }
15380             if ((event.xconfigure.width == (long) windows->image.width) &&
15381                 (event.xconfigure.height == (long) windows->image.height))
15382               break;
15383             windows->image.width=(unsigned int) event.xconfigure.width;
15384             windows->image.height=(unsigned int) event.xconfigure.height;
15385             windows->image.x=0;
15386             windows->image.y=0;
15387             if (display_image->montage != (char *) NULL)
15388               {
15389                 windows->image.x=vid_info.x;
15390                 windows->image.y=vid_info.y;
15391               }
15392             if ((windows->image.mapped != MagickFalse) &&
15393                 (windows->image.stasis != MagickFalse))
15394               {
15395                 /*
15396                   Update image window configuration.
15397                 */
15398                 windows->image.window_changes.width=event.xconfigure.width;
15399                 windows->image.window_changes.height=event.xconfigure.height;
15400                 (void) XConfigureImage(display,resource_info,windows,
15401                   display_image);
15402               }
15403             /*
15404               Update pan window configuration.
15405             */
15406             if ((event.xconfigure.width < windows->image.ximage->width) ||
15407                 (event.xconfigure.height < windows->image.ximage->height))
15408               {
15409                 (void) XMapRaised(display,windows->pan.id);
15410                 XDrawPanRectangle(display,windows);
15411               }
15412             else
15413               if (windows->pan.mapped != MagickFalse)
15414                 (void) XWithdrawWindow(display,windows->pan.id,
15415                   windows->pan.screen);
15416             break;
15417           }
15418         if (event.xconfigure.window == windows->magnify.id)
15419           {
15420             unsigned int
15421               magnify;
15422
15423             /*
15424               Magnify window has a new configuration.
15425             */
15426             windows->magnify.width=(unsigned int) event.xconfigure.width;
15427             windows->magnify.height=(unsigned int) event.xconfigure.height;
15428             if (windows->magnify.mapped == MagickFalse)
15429               break;
15430             magnify=1;
15431             while ((int) magnify <= event.xconfigure.width)
15432               magnify<<=1;
15433             while ((int) magnify <= event.xconfigure.height)
15434               magnify<<=1;
15435             magnify>>=1;
15436             if (((int) magnify != event.xconfigure.width) ||
15437                 ((int) magnify != event.xconfigure.height))
15438               {
15439                 window_changes.width=(int) magnify;
15440                 window_changes.height=(int) magnify;
15441                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15442                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15443                   &window_changes);
15444                 break;
15445               }
15446             if ((windows->magnify.mapped != MagickFalse) &&
15447                 (windows->magnify.stasis != MagickFalse))
15448               {
15449                 status=XMakeImage(display,resource_info,&windows->magnify,
15450                   display_image,windows->magnify.width,windows->magnify.height);
15451                 XMakeMagnifyImage(display,windows);
15452               }
15453             break;
15454           }
15455         if ((windows->magnify.mapped != MagickFalse) &&
15456             (event.xconfigure.window == windows->pan.id))
15457           {
15458             /*
15459               Pan icon window has a new configuration.
15460             */
15461             if (event.xconfigure.send_event != 0)
15462               {
15463                 windows->pan.x=event.xconfigure.x;
15464                 windows->pan.y=event.xconfigure.y;
15465               }
15466             windows->pan.width=(unsigned int) event.xconfigure.width;
15467             windows->pan.height=(unsigned int) event.xconfigure.height;
15468             break;
15469           }
15470         if (event.xconfigure.window == windows->icon.id)
15471           {
15472             /*
15473               Icon window has a new configuration.
15474             */
15475             windows->icon.width=(unsigned int) event.xconfigure.width;
15476             windows->icon.height=(unsigned int) event.xconfigure.height;
15477             break;
15478           }
15479         break;
15480       }
15481       case DestroyNotify:
15482       {
15483         /*
15484           Group leader has exited.
15485         */
15486         if (display_image->debug != MagickFalse)
15487           (void) LogMagickEvent(X11Event,GetMagickModule(),
15488             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15489         if (event.xdestroywindow.window == windows->group_leader.id)
15490           {
15491             *state|=ExitState;
15492             break;
15493           }
15494         break;
15495       }
15496       case EnterNotify:
15497       {
15498         /*
15499           Selectively install colormap.
15500         */
15501         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15502           if (event.xcrossing.mode != NotifyUngrab)
15503             XInstallColormap(display,map_info->colormap);
15504         break;
15505       }
15506       case Expose:
15507       {
15508         if (display_image->debug != MagickFalse)
15509           (void) LogMagickEvent(X11Event,GetMagickModule(),
15510             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15511             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15512             event.xexpose.y);
15513         /*
15514           Refresh windows that are now exposed.
15515         */
15516         if ((event.xexpose.window == windows->image.id) &&
15517             (windows->image.mapped != MagickFalse))
15518           {
15519             XRefreshWindow(display,&windows->image,&event);
15520             delay=display_image->delay/MagickMax(
15521               display_image->ticks_per_second,1L);
15522             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15523             break;
15524           }
15525         if ((event.xexpose.window == windows->magnify.id) &&
15526             (windows->magnify.mapped != MagickFalse))
15527           {
15528             XMakeMagnifyImage(display,windows);
15529             break;
15530           }
15531         if (event.xexpose.window == windows->pan.id)
15532           {
15533             XDrawPanRectangle(display,windows);
15534             break;
15535           }
15536         if (event.xexpose.window == windows->icon.id)
15537           {
15538             XRefreshWindow(display,&windows->icon,&event);
15539             break;
15540           }
15541         break;
15542       }
15543       case KeyPress:
15544       {
15545         int
15546           length;
15547
15548         /*
15549           Respond to a user key press.
15550         */
15551         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15552           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15553         *(command+length)='\0';
15554         if (display_image->debug != MagickFalse)
15555           (void) LogMagickEvent(X11Event,GetMagickModule(),
15556             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15557             key_symbol,command);
15558         if (event.xkey.window == windows->image.id)
15559           {
15560             command_type=XImageWindowCommand(display,resource_info,windows,
15561               event.xkey.state,key_symbol,&display_image);
15562             if (command_type != NullCommand)
15563               nexus=XMagickCommand(display,resource_info,windows,command_type,
15564                 &display_image);
15565           }
15566         if (event.xkey.window == windows->magnify.id)
15567           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15568         if (event.xkey.window == windows->pan.id)
15569           {
15570             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15571               (void) XWithdrawWindow(display,windows->pan.id,
15572                 windows->pan.screen);
15573             else
15574               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15575                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15576                   "Help Viewer - Image Pan",ImagePanHelp);
15577               else
15578                 XTranslateImage(display,windows,*image,key_symbol);
15579           }
15580         delay=display_image->delay/MagickMax(
15581           display_image->ticks_per_second,1L);
15582         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15583         break;
15584       }
15585       case KeyRelease:
15586       {
15587         /*
15588           Respond to a user key release.
15589         */
15590         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15591           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15592         if (display_image->debug != MagickFalse)
15593           (void) LogMagickEvent(X11Event,GetMagickModule(),
15594             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15595         break;
15596       }
15597       case LeaveNotify:
15598       {
15599         /*
15600           Selectively uninstall colormap.
15601         */
15602         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15603           if (event.xcrossing.mode != NotifyUngrab)
15604             XUninstallColormap(display,map_info->colormap);
15605         break;
15606       }
15607       case MapNotify:
15608       {
15609         if (display_image->debug != MagickFalse)
15610           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15611             event.xmap.window);
15612         if (event.xmap.window == windows->backdrop.id)
15613           {
15614             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15615               CurrentTime);
15616             windows->backdrop.mapped=MagickTrue;
15617             break;
15618           }
15619         if (event.xmap.window == windows->image.id)
15620           {
15621             if (windows->backdrop.id != (Window) NULL)
15622               (void) XInstallColormap(display,map_info->colormap);
15623             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15624               {
15625                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15626                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15627               }
15628             if (((int) windows->image.width < windows->image.ximage->width) ||
15629                 ((int) windows->image.height < windows->image.ximage->height))
15630               (void) XMapRaised(display,windows->pan.id);
15631             windows->image.mapped=MagickTrue;
15632             break;
15633           }
15634         if (event.xmap.window == windows->magnify.id)
15635           {
15636             XMakeMagnifyImage(display,windows);
15637             windows->magnify.mapped=MagickTrue;
15638             (void) XWithdrawWindow(display,windows->info.id,
15639               windows->info.screen);
15640             break;
15641           }
15642         if (event.xmap.window == windows->pan.id)
15643           {
15644             XMakePanImage(display,resource_info,windows,display_image);
15645             windows->pan.mapped=MagickTrue;
15646             break;
15647           }
15648         if (event.xmap.window == windows->info.id)
15649           {
15650             windows->info.mapped=MagickTrue;
15651             break;
15652           }
15653         if (event.xmap.window == windows->icon.id)
15654           {
15655             MagickBooleanType
15656               taint;
15657
15658             /*
15659               Create an icon image.
15660             */
15661             taint=display_image->taint;
15662             XMakeStandardColormap(display,icon_visual,icon_resources,
15663               display_image,icon_map,icon_pixel);
15664             (void) XMakeImage(display,icon_resources,&windows->icon,
15665               display_image,windows->icon.width,windows->icon.height);
15666             display_image->taint=taint;
15667             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15668               windows->icon.pixmap);
15669             (void) XClearWindow(display,windows->icon.id);
15670             (void) XWithdrawWindow(display,windows->info.id,
15671               windows->info.screen);
15672             windows->icon.mapped=MagickTrue;
15673             break;
15674           }
15675         if (event.xmap.window == windows->command.id)
15676           {
15677             windows->command.mapped=MagickTrue;
15678             break;
15679           }
15680         if (event.xmap.window == windows->popup.id)
15681           {
15682             windows->popup.mapped=MagickTrue;
15683             break;
15684           }
15685         if (event.xmap.window == windows->widget.id)
15686           {
15687             windows->widget.mapped=MagickTrue;
15688             break;
15689           }
15690         break;
15691       }
15692       case MappingNotify:
15693       {
15694         (void) XRefreshKeyboardMapping(&event.xmapping);
15695         break;
15696       }
15697       case NoExpose:
15698         break;
15699       case PropertyNotify:
15700       {
15701         Atom
15702           type;
15703
15704         int
15705           format,
15706           status;
15707
15708         unsigned char
15709           *data;
15710
15711         unsigned long
15712           after,
15713           length;
15714
15715         if (display_image->debug != MagickFalse)
15716           (void) LogMagickEvent(X11Event,GetMagickModule(),
15717             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15718             event.xproperty.atom,event.xproperty.state);
15719         if (event.xproperty.atom != windows->im_remote_command)
15720           break;
15721         /*
15722           Display image named by the remote command protocol.
15723         */
15724         status=XGetWindowProperty(display,event.xproperty.window,
15725           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15726           AnyPropertyType,&type,&format,&length,&after,&data);
15727         if ((status != Success) || (length == 0))
15728           break;
15729         if (LocaleCompare((char *) data,"-quit") == 0)
15730           {
15731             XClientMessage(display,windows->image.id,windows->im_protocols,
15732               windows->im_exit,CurrentTime);
15733             (void) XFree((void *) data);
15734             break;
15735           }
15736         (void) CopyMagickString(resource_info->image_info->filename,
15737           (char *) data,MaxTextExtent);
15738         (void) XFree((void *) data);
15739         nexus=ReadImage(resource_info->image_info,&display_image->exception);
15740         CatchException(&display_image->exception);
15741         if (nexus != (Image *) NULL)
15742           *state|=NextImageState | ExitState;
15743         break;
15744       }
15745       case ReparentNotify:
15746       {
15747         if (display_image->debug != MagickFalse)
15748           (void) LogMagickEvent(X11Event,GetMagickModule(),
15749             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15750             event.xreparent.window);
15751         break;
15752       }
15753       case UnmapNotify:
15754       {
15755         if (display_image->debug != MagickFalse)
15756           (void) LogMagickEvent(X11Event,GetMagickModule(),
15757             "Unmap Notify: 0x%lx",event.xunmap.window);
15758         if (event.xunmap.window == windows->backdrop.id)
15759           {
15760             windows->backdrop.mapped=MagickFalse;
15761             break;
15762           }
15763         if (event.xunmap.window == windows->image.id)
15764           {
15765             windows->image.mapped=MagickFalse;
15766             break;
15767           }
15768         if (event.xunmap.window == windows->magnify.id)
15769           {
15770             windows->magnify.mapped=MagickFalse;
15771             break;
15772           }
15773         if (event.xunmap.window == windows->pan.id)
15774           {
15775             windows->pan.mapped=MagickFalse;
15776             break;
15777           }
15778         if (event.xunmap.window == windows->info.id)
15779           {
15780             windows->info.mapped=MagickFalse;
15781             break;
15782           }
15783         if (event.xunmap.window == windows->icon.id)
15784           {
15785             if (map_info->colormap == icon_map->colormap)
15786               XConfigureImageColormap(display,resource_info,windows,
15787                 display_image);
15788             (void) XFreeStandardColormap(display,icon_visual,icon_map,
15789               icon_pixel);
15790             windows->icon.mapped=MagickFalse;
15791             break;
15792           }
15793         if (event.xunmap.window == windows->command.id)
15794           {
15795             windows->command.mapped=MagickFalse;
15796             break;
15797           }
15798         if (event.xunmap.window == windows->popup.id)
15799           {
15800             if (windows->backdrop.id != (Window) NULL)
15801               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15802                 CurrentTime);
15803             windows->popup.mapped=MagickFalse;
15804             break;
15805           }
15806         if (event.xunmap.window == windows->widget.id)
15807           {
15808             if (windows->backdrop.id != (Window) NULL)
15809               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15810                 CurrentTime);
15811             windows->widget.mapped=MagickFalse;
15812             break;
15813           }
15814         break;
15815       }
15816       default:
15817       {
15818         if (display_image->debug != MagickFalse)
15819           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15820             event.type);
15821         break;
15822       }
15823     }
15824   } while (!(*state & ExitState));
15825   if ((*state & ExitState) == 0)
15826     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15827       &display_image);
15828   else
15829     if (resource_info->confirm_edit != MagickFalse)
15830       {
15831         /*
15832           Query user if image has changed.
15833         */
15834         if ((resource_info->immutable == MagickFalse) &&
15835             (display_image->taint != MagickFalse))
15836           {
15837             int
15838               status;
15839
15840             status=XConfirmWidget(display,windows,"Your image changed.",
15841               "Do you want to save it");
15842             if (status == 0)
15843               *state&=(~ExitState);
15844             else
15845               if (status > 0)
15846                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15847                   &display_image);
15848           }
15849       }
15850   if ((windows->visual_info->klass == GrayScale) ||
15851       (windows->visual_info->klass == PseudoColor) ||
15852       (windows->visual_info->klass == DirectColor))
15853     {
15854       /*
15855         Withdraw pan and Magnify window.
15856       */
15857       if (windows->info.mapped != MagickFalse)
15858         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15859       if (windows->magnify.mapped != MagickFalse)
15860         (void) XWithdrawWindow(display,windows->magnify.id,
15861           windows->magnify.screen);
15862       if (windows->command.mapped != MagickFalse)
15863         (void) XWithdrawWindow(display,windows->command.id,
15864           windows->command.screen);
15865     }
15866   if (windows->pan.mapped != MagickFalse)
15867     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15868   if (resource_info->backdrop == MagickFalse)
15869     if (windows->backdrop.mapped)
15870       {
15871         (void) XWithdrawWindow(display,windows->backdrop.id,
15872           windows->backdrop.screen);
15873         (void) XDestroyWindow(display,windows->backdrop.id);
15874         windows->backdrop.id=(Window) NULL;
15875         (void) XWithdrawWindow(display,windows->image.id,
15876           windows->image.screen);
15877         (void) XDestroyWindow(display,windows->image.id);
15878         windows->image.id=(Window) NULL;
15879       }
15880   XSetCursorState(display,windows,MagickTrue);
15881   XCheckRefreshWindows(display,windows);
15882   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15883     *state&=(~ExitState);
15884   if (*state & ExitState)
15885     {
15886       /*
15887         Free Standard Colormap.
15888       */
15889       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
15890       if (resource_info->map_type == (char *) NULL)
15891         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
15892       /*
15893         Free X resources.
15894       */
15895       if (resource_info->copy_image != (Image *) NULL)
15896         {
15897           resource_info->copy_image=DestroyImage(resource_info->copy_image);
15898           resource_info->copy_image=NewImageList();
15899         }
15900       DestroyXResources();
15901     }
15902   (void) XSync(display,MagickFalse);
15903   /*
15904     Restore our progress monitor and warning handlers.
15905   */
15906   (void) SetErrorHandler(warning_handler);
15907   (void) SetWarningHandler(warning_handler);
15908   /*
15909     Change to home directory.
15910   */
15911   cwd=getcwd(working_directory,MaxTextExtent);
15912   {
15913     int
15914       status;
15915
15916     status=chdir(resource_info->home_directory);
15917     if (status == -1)
15918       (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
15919         FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
15920   }
15921   *image=display_image;
15922   return(nexus);
15923 }
15924 #else
15925 \f
15926 /*
15927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15928 %                                                                             %
15929 %                                                                             %
15930 %                                                                             %
15931 +   D i s p l a y I m a g e s                                                 %
15932 %                                                                             %
15933 %                                                                             %
15934 %                                                                             %
15935 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15936 %
15937 %  DisplayImages() displays an image sequence to any X window screen.  It
15938 %  returns a value other than 0 if successful.  Check the exception member
15939 %  of image to determine the reason for any failure.
15940 %
15941 %  The format of the DisplayImages method is:
15942 %
15943 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
15944 %        Image *images)
15945 %
15946 %  A description of each parameter follows:
15947 %
15948 %    o image_info: the image info.
15949 %
15950 %    o image: the image.
15951 %
15952 */
15953 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
15954   Image *image)
15955 {
15956   assert(image_info != (const ImageInfo *) NULL);
15957   assert(image_info->signature == MagickSignature);
15958   assert(image != (Image *) NULL);
15959   assert(image->signature == MagickSignature);
15960   if (image->debug != MagickFalse)
15961     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
15962   (void) ThrowMagickException(&image->exception,GetMagickModule(),
15963     MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
15964     image->filename);
15965   return(MagickFalse);
15966 }
15967 \f
15968 /*
15969 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15970 %                                                                             %
15971 %                                                                             %
15972 %                                                                             %
15973 +   R e m o t e D i s p l a y C o m m a n d                                   %
15974 %                                                                             %
15975 %                                                                             %
15976 %                                                                             %
15977 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15978 %
15979 %  RemoteDisplayCommand() encourages a remote display program to display the
15980 %  specified image filename.
15981 %
15982 %  The format of the RemoteDisplayCommand method is:
15983 %
15984 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
15985 %        const char *window,const char *filename,ExceptionInfo *exception)
15986 %
15987 %  A description of each parameter follows:
15988 %
15989 %    o image_info: the image info.
15990 %
15991 %    o window: Specifies the name or id of an X window.
15992 %
15993 %    o filename: the name of the image filename to display.
15994 %
15995 %    o exception: return any errors or warnings in this structure.
15996 %
15997 */
15998 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
15999   const char *window,const char *filename,ExceptionInfo *exception)
16000 {
16001   assert(image_info != (const ImageInfo *) NULL);
16002   assert(image_info->signature == MagickSignature);
16003   assert(filename != (char *) NULL);
16004   (void) window;
16005   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16006   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16007     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16008   return(MagickFalse);
16009 }
16010 #endif