]> granicus.if.org Git - imagemagick/blob - MagickCore/display.c
(no commit message)
[imagemagick] / MagickCore / display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7 %               D   D    I    SS     P   P  L      A   A   Y Y                %
8 %               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9 %               D   D    I       SS  P      L      A   A    Y                 %
10 %               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11 %                                                                             %
12 %                                                                             %
13 %        MagickCore Methods to Interactively Display and Edit an Image        %
14 %                                                                             %
15 %                             Software Design                                 %
16 %                               John Cristy                                   %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/client.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/colorspace.h"
49 #include "MagickCore/composite.h"
50 #include "MagickCore/constitute.h"
51 #include "MagickCore/decorate.h"
52 #include "MagickCore/delegate.h"
53 #include "MagickCore/display.h"
54 #include "MagickCore/display-private.h"
55 #include "MagickCore/draw.h"
56 #include "MagickCore/effect.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/fx.h"
61 #include "MagickCore/geometry.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/magick.h"
67 #include "MagickCore/memory_.h"
68 #include "MagickCore/monitor.h"
69 #include "MagickCore/monitor-private.h"
70 #include "MagickCore/montage.h"
71 #include "MagickCore/option.h"
72 #include "MagickCore/paint.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/PreRvIcccm.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum.h"
78 #include "MagickCore/quantum-private.h"
79 #include "MagickCore/resize.h"
80 #include "MagickCore/resource_.h"
81 #include "MagickCore/shear.h"
82 #include "MagickCore/segment.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/threshold.h"
87 #include "MagickCore/utility.h"
88 #include "MagickCore/utility-private.h"
89 #include "MagickCore/version.h"
90 #include "MagickCore/widget.h"
91 #include "MagickCore/widget-private.h"
92 #include "MagickCore/xwindow.h"
93 #include "MagickCore/xwindow-private.h"
94 \f
95 #if defined(MAGICKCORE_X11_DELEGATE)
96 /*
97   Define declarations.
98 */
99 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
100 \f
101 /*
102   Constant declarations.
103 */
104 static const unsigned char
105   HighlightBitmap[8] =
106   {
107     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
108   },
109   OpaqueBitmap[8] =
110   {
111     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
112   },
113   ShadowBitmap[8] =
114   {
115     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
116   };
117
118 static const char
119   *PageSizes[] =
120   {
121     "Letter",
122     "Tabloid",
123     "Ledger",
124     "Legal",
125     "Statement",
126     "Executive",
127     "A3",
128     "A4",
129     "A5",
130     "B4",
131     "B5",
132     "Folio",
133     "Quarto",
134     "10x14",
135     (char *) NULL
136   };
137 \f
138 /*
139   Help widget declarations.
140 */
141 static const char
142   *ImageAnnotateHelp[] =
143   {
144     "In annotate mode, the Command widget has these options:",
145     "",
146     "    Font Name",
147     "      fixed",
148     "      variable",
149     "      5x8",
150     "      6x10",
151     "      7x13bold",
152     "      8x13bold",
153     "      9x15bold",
154     "      10x20",
155     "      12x24",
156     "      Browser...",
157     "    Font Color",
158     "      black",
159     "      blue",
160     "      cyan",
161     "      green",
162     "      gray",
163     "      red",
164     "      magenta",
165     "      yellow",
166     "      white",
167     "      transparent",
168     "      Browser...",
169     "    Font Color",
170     "      black",
171     "      blue",
172     "      cyan",
173     "      green",
174     "      gray",
175     "      red",
176     "      magenta",
177     "      yellow",
178     "      white",
179     "      transparent",
180     "      Browser...",
181     "    Rotate Text",
182     "      -90",
183     "      -45",
184     "      -30",
185     "      0",
186     "      30",
187     "      45",
188     "      90",
189     "      180",
190     "      Dialog...",
191     "    Help",
192     "    Dismiss",
193     "",
194     "Choose a font name from the Font Name sub-menu.  Additional",
195     "font names can be specified with the font browser.  You can",
196     "change the menu names by setting the X resources font1",
197     "through font9.",
198     "",
199     "Choose a font color from the Font Color sub-menu.",
200     "Additional font colors can be specified with the color",
201     "browser.  You can change the menu colors by setting the X",
202     "resources pen1 through pen9.",
203     "",
204     "If you select the color browser and press Grab, you can",
205     "choose the font color by moving the pointer to the desired",
206     "color on the screen and press any button.",
207     "",
208     "If you choose to rotate the text, choose Rotate Text from the",
209     "menu and select an angle.  Typically you will only want to",
210     "rotate one line of text at a time.  Depending on the angle you",
211     "choose, subsequent lines may end up overwriting each other.",
212     "",
213     "Choosing a font and its color is optional.  The default font",
214     "is fixed and the default color is black.  However, you must",
215     "choose a location to begin entering text and press button 1.",
216     "An underscore character will appear at the location of the",
217     "pointer.  The cursor changes to a pencil to indicate you are",
218     "in text mode.  To exit immediately, press Dismiss.",
219     "",
220     "In text mode, any key presses will display the character at",
221     "the location of the underscore and advance the underscore",
222     "cursor.  Enter your text and once completed press Apply to",
223     "finish your image annotation.  To correct errors press BACK",
224     "SPACE.  To delete an entire line of text, press DELETE.  Any",
225     "text that exceeds the boundaries of the image window is",
226     "automagically continued onto the next line.",
227     "",
228     "The actual color you request for the font is saved in the",
229     "image.  However, the color that appears in your image window",
230     "may be different.  For example, on a monochrome screen the",
231     "text will appear black or white even if you choose the color",
232     "red as the font color.  However, the image saved to a file",
233     "with -write is written with red lettering.  To assure the",
234     "correct color text in the final image, any PseudoClass image",
235     "is promoted to DirectClass (see miff(5)).  To force a",
236     "PseudoClass image to remain PseudoClass, use -colors.",
237     (char *) NULL,
238   },
239   *ImageChopHelp[] =
240   {
241     "In chop mode, the Command widget has these options:",
242     "",
243     "    Direction",
244     "      horizontal",
245     "      vertical",
246     "    Help",
247     "    Dismiss",
248     "",
249     "If the you choose the horizontal direction (this the",
250     "default), the area of the image between the two horizontal",
251     "endpoints of the chop line is removed.  Otherwise, the area",
252     "of the image between the two vertical endpoints of the chop",
253     "line is removed.",
254     "",
255     "Select a location within the image window to begin your chop,",
256     "press and hold any button.  Next, move the pointer to",
257     "another location in the image.  As you move a line will",
258     "connect the initial location and the pointer.  When you",
259     "release the button, the area within the image to chop is",
260     "determined by which direction you choose from the Command",
261     "widget.",
262     "",
263     "To cancel the image chopping, move the pointer back to the",
264     "starting point of the line and release the button.",
265     (char *) NULL,
266   },
267   *ImageColorEditHelp[] =
268   {
269     "In color edit mode, the Command widget has these options:",
270     "",
271     "    Method",
272     "      point",
273     "      replace",
274     "      floodfill",
275     "      filltoborder",
276     "      reset",
277     "    Pixel Color",
278     "      black",
279     "      blue",
280     "      cyan",
281     "      green",
282     "      gray",
283     "      red",
284     "      magenta",
285     "      yellow",
286     "      white",
287     "      Browser...",
288     "    Border Color",
289     "      black",
290     "      blue",
291     "      cyan",
292     "      green",
293     "      gray",
294     "      red",
295     "      magenta",
296     "      yellow",
297     "      white",
298     "      Browser...",
299     "    Fuzz",
300     "      0%",
301     "      2%",
302     "      5%",
303     "      10%",
304     "      15%",
305     "      Dialog...",
306     "    Undo",
307     "    Help",
308     "    Dismiss",
309     "",
310     "Choose a color editing method from the Method sub-menu",
311     "of the Command widget.  The point method recolors any pixel",
312     "selected with the pointer until the button is released.  The",
313     "replace method recolors any pixel that matches the color of",
314     "the pixel you select with a button press.  Floodfill recolors",
315     "any pixel that matches the color of the pixel you select with",
316     "a button press and is a neighbor.  Whereas filltoborder recolors",
317     "any neighbor pixel that is not the border color.  Finally reset",
318     "changes the entire image to the designated color.",
319     "",
320     "Next, choose a pixel color from the Pixel Color sub-menu.",
321     "Additional pixel colors can be specified with the color",
322     "browser.  You can change the menu colors by setting the X",
323     "resources pen1 through pen9.",
324     "",
325     "Now press button 1 to select a pixel within the image window",
326     "to change its color.  Additional pixels may be recolored as",
327     "prescribed by the method you choose.",
328     "",
329     "If the Magnify widget is mapped, it can be helpful in positioning",
330     "your pointer within the image (refer to button 2).",
331     "",
332     "The actual color you request for the pixels is saved in the",
333     "image.  However, the color that appears in your image window",
334     "may be different.  For example, on a monochrome screen the",
335     "pixel will appear black or white even if you choose the",
336     "color red as the pixel color.  However, the image saved to a",
337     "file with -write is written with red pixels.  To assure the",
338     "correct color text in the final image, any PseudoClass image",
339     "is promoted to DirectClass (see miff(5)).  To force a",
340     "PseudoClass image to remain PseudoClass, use -colors.",
341     (char *) NULL,
342   },
343   *ImageCompositeHelp[] =
344   {
345     "First a widget window is displayed requesting you to enter an",
346     "image name. Press Composite, Grab or type a file name.",
347     "Press Cancel if you choose not to create a composite image.",
348     "When you choose Grab, move the pointer to the desired window",
349     "and press any button.",
350     "",
351     "If the Composite image does not have any matte information,",
352     "you are informed and the file browser is displayed again.",
353     "Enter the name of a mask image.  The image is typically",
354     "grayscale and the same size as the composite image.  If the",
355     "image is not grayscale, it is converted to grayscale and the",
356     "resulting intensities are used as matte information.",
357     "",
358     "A small window appears showing the location of the cursor in",
359     "the image window. You are now in composite mode.  To exit",
360     "immediately, press Dismiss.  In composite mode, the Command",
361     "widget has these options:",
362     "",
363     "    Operators",
364     "      Over",
365     "      In",
366     "      Out",
367     "      Atop",
368     "      Xor",
369     "      Plus",
370     "      Minus",
371     "      Add",
372     "      Subtract",
373     "      Difference",
374     "      Multiply",
375     "      Bumpmap",
376     "      Copy",
377     "      CopyRed",
378     "      CopyGreen",
379     "      CopyBlue",
380     "      CopyOpacity",
381     "      Clear",
382     "    Dissolve",
383     "    Displace",
384     "    Help",
385     "    Dismiss",
386     "",
387     "Choose a composite operation from the Operators sub-menu of",
388     "the Command widget.  How each operator behaves is described",
389     "below.  Image window is the image currently displayed on",
390     "your X server and image is the image obtained with the File",
391     "Browser widget.",
392     "",
393     "Over     The result is the union of the two image shapes,",
394     "         with image obscuring image window in the region of",
395     "         overlap.",
396     "",
397     "In       The result is simply image cut by the shape of",
398     "         image window.  None of the image data of image",
399     "         window is in the result.",
400     "",
401     "Out      The resulting image is image with the shape of",
402     "         image window cut out.",
403     "",
404     "Atop     The result is the same shape as image image window,",
405     "         with image obscuring image window where the image",
406     "         shapes overlap.  Note this differs from over",
407     "         because the portion of image outside image window's",
408     "         shape does not appear in the result.",
409     "",
410     "Xor      The result is the image data from both image and",
411     "         image window that is outside the overlap region.",
412     "         The overlap region is blank.",
413     "",
414     "Plus     The result is just the sum of the image data.",
415     "         Output values are cropped to QuantumRange (no overflow).",
416     "",
417     "Minus    The result of image - image window, with underflow",
418     "         cropped to zero.",
419     "",
420     "Add      The result of image + image window, with overflow",
421     "         wrapping around (mod 256).",
422     "",
423     "Subtract The result of image - image window, with underflow",
424     "         wrapping around (mod 256).  The add and subtract",
425     "         operators can be used to perform reversible",
426     "         transformations.",
427     "",
428     "Difference",
429     "         The result of abs(image - image window).  This",
430     "         useful for comparing two very similar images.",
431     "",
432     "Multiply",
433     "         The result of image * image window.  This",
434     "         useful for the creation of drop-shadows.",
435     "",
436     "Bumpmap  The result of surface normals from image * image",
437     "         window.",
438     "",
439     "Copy     The resulting image is image window replaced with",
440     "         image.  Here the matte information is ignored.",
441     "",
442     "CopyRed  The red layer of the image window is replace with",
443     "         the red layer of the image.  The other layers are",
444     "         untouched.",
445     "",
446     "CopyGreen",
447     "         The green layer of the image window is replace with",
448     "         the green layer of the image.  The other layers are",
449     "         untouched.",
450     "",
451     "CopyBlue The blue layer of the image window is replace with",
452     "         the blue layer of the image.  The other layers are",
453     "         untouched.",
454     "",
455     "CopyOpacity",
456     "         The matte layer of the image window is replace with",
457     "         the matte layer of the image.  The other layers are",
458     "         untouched.",
459     "",
460     "The image compositor requires a matte, or alpha channel in",
461     "the image for some operations.  This extra channel usually",
462     "defines a mask which represents a sort of a cookie-cutter",
463     "for the image.  This the case when matte is opaque (full",
464     "coverage) for pixels inside the shape, zero outside, and",
465     "between 0 and QuantumRange on the boundary.  If image does not",
466     "have a matte channel, it is initialized with 0 for any pixel",
467     "matching in color to pixel location (0,0), otherwise QuantumRange.",
468     "",
469     "If you choose Dissolve, the composite operator becomes Over.  The",
470     "image matte channel percent transparency is initialized to factor.",
471     "The image window is initialized to (100-factor). Where factor is the",
472     "value you specify in the Dialog widget.",
473     "",
474     "Displace shifts the image pixels as defined by a displacement",
475     "map.  With this option, image is used as a displacement map.",
476     "Black, within the displacement map, is a maximum positive",
477     "displacement.  White is a maximum negative displacement and",
478     "middle gray is neutral.  The displacement is scaled to determine",
479     "the pixel shift.  By default, the displacement applies in both the",
480     "horizontal and vertical directions.  However, if you specify a mask,",
481     "image is the horizontal X displacement and mask the vertical Y",
482     "displacement.",
483     "",
484     "Note that matte information for image window is not retained",
485     "for colormapped X server visuals (e.g. StaticColor,",
486     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
487     "behavior may require a TrueColor or DirectColor visual or a",
488     "Standard Colormap.",
489     "",
490     "Choosing a composite operator is optional.  The default",
491     "operator is replace.  However, you must choose a location to",
492     "composite your image and press button 1.  Press and hold the",
493     "button before releasing and an outline of the image will",
494     "appear to help you identify your location.",
495     "",
496     "The actual colors of the composite image is saved.  However,",
497     "the color that appears in image window may be different.",
498     "For example, on a monochrome screen image window will appear",
499     "black or white even though your composited image may have",
500     "many colors.  If the image is saved to a file it is written",
501     "with the correct colors.  To assure the correct colors are",
502     "saved in the final image, any PseudoClass image is promoted",
503     "to DirectClass (see miff(5)).  To force a PseudoClass image",
504     "to remain PseudoClass, use -colors.",
505     (char *) NULL,
506   },
507   *ImageCutHelp[] =
508   {
509     "In cut mode, the Command widget has these options:",
510     "",
511     "    Help",
512     "    Dismiss",
513     "",
514     "To define a cut region, press button 1 and drag.  The",
515     "cut region is defined by a highlighted rectangle that",
516     "expands or contracts as it follows the pointer.  Once you",
517     "are satisfied with the cut region, release the button.",
518     "You are now in rectify mode.  In rectify mode, the Command",
519     "widget has these options:",
520     "",
521     "    Cut",
522     "    Help",
523     "    Dismiss",
524     "",
525     "You can make adjustments by moving the pointer to one of the",
526     "cut rectangle corners, pressing a button, and dragging.",
527     "Finally, press Cut to commit your copy region.  To",
528     "exit without cutting the image, press Dismiss.",
529     (char *) NULL,
530   },
531   *ImageCopyHelp[] =
532   {
533     "In copy mode, the Command widget has these options:",
534     "",
535     "    Help",
536     "    Dismiss",
537     "",
538     "To define a copy region, press button 1 and drag.  The",
539     "copy region is defined by a highlighted rectangle that",
540     "expands or contracts as it follows the pointer.  Once you",
541     "are satisfied with the copy region, release the button.",
542     "You are now in rectify mode.  In rectify mode, the Command",
543     "widget has these options:",
544     "",
545     "    Copy",
546     "    Help",
547     "    Dismiss",
548     "",
549     "You can make adjustments by moving the pointer to one of the",
550     "copy rectangle corners, pressing a button, and dragging.",
551     "Finally, press Copy to commit your copy region.  To",
552     "exit without copying the image, press Dismiss.",
553     (char *) NULL,
554   },
555   *ImageCropHelp[] =
556   {
557     "In crop mode, the Command widget has these options:",
558     "",
559     "    Help",
560     "    Dismiss",
561     "",
562     "To define a cropping region, press button 1 and drag.  The",
563     "cropping region is defined by a highlighted rectangle that",
564     "expands or contracts as it follows the pointer.  Once you",
565     "are satisfied with the cropping region, release the button.",
566     "You are now in rectify mode.  In rectify mode, the Command",
567     "widget has these options:",
568     "",
569     "    Crop",
570     "    Help",
571     "    Dismiss",
572     "",
573     "You can make adjustments by moving the pointer to one of the",
574     "cropping rectangle corners, pressing a button, and dragging.",
575     "Finally, press Crop to commit your cropping region.  To",
576     "exit without cropping the image, press Dismiss.",
577     (char *) NULL,
578   },
579   *ImageDrawHelp[] =
580   {
581     "The cursor changes to a crosshair to indicate you are in",
582     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
583     "the Command widget has these options:",
584     "",
585     "    Element",
586     "      point",
587     "      line",
588     "      rectangle",
589     "      fill rectangle",
590     "      circle",
591     "      fill circle",
592     "      ellipse",
593     "      fill ellipse",
594     "      polygon",
595     "      fill polygon",
596     "    Color",
597     "      black",
598     "      blue",
599     "      cyan",
600     "      green",
601     "      gray",
602     "      red",
603     "      magenta",
604     "      yellow",
605     "      white",
606     "      transparent",
607     "      Browser...",
608     "    Stipple",
609     "      Brick",
610     "      Diagonal",
611     "      Scales",
612     "      Vertical",
613     "      Wavy",
614     "      Translucent",
615     "      Opaque",
616     "      Open...",
617     "    Width",
618     "      1",
619     "      2",
620     "      4",
621     "      8",
622     "      16",
623     "      Dialog...",
624     "    Undo",
625     "    Help",
626     "    Dismiss",
627     "",
628     "Choose a drawing primitive from the Element sub-menu.",
629     "",
630     "Choose a color from the Color sub-menu.  Additional",
631     "colors can be specified with the color browser.",
632     "",
633     "If you choose the color browser and press Grab, you can",
634     "select the color by moving the pointer to the desired",
635     "color on the screen and press any button.  The transparent",
636     "color updates the image matte channel and is useful for",
637     "image compositing.",
638     "",
639     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
640     "Additional stipples can be specified with the file browser.",
641     "Stipples obtained from the file browser must be on disk in the",
642     "X11 bitmap format.",
643     "",
644     "Choose a width, if appropriate, from the Width sub-menu.  To",
645     "choose a specific width select the Dialog widget.",
646     "",
647     "Choose a point in the Image window and press button 1 and",
648     "hold.  Next, move the pointer to another location in the",
649     "image.  As you move, a line connects the initial location and",
650     "the pointer.  When you release the button, the image is",
651     "updated with the primitive you just drew.  For polygons, the",
652     "image is updated when you press and release the button without",
653     "moving the pointer.",
654     "",
655     "To cancel image drawing, move the pointer back to the",
656     "starting point of the line and release the button.",
657     (char *) NULL,
658   },
659   *DisplayHelp[] =
660   {
661     "BUTTONS",
662     "  The effects of each button press is described below.  Three",
663     "  buttons are required.  If you have a two button mouse,",
664     "  button 1 and 3 are returned.  Press ALT and button 3 to",
665     "  simulate button 2.",
666     "",
667     "  1    Press this button to map or unmap the Command widget.",
668     "",
669     "  2    Press and drag to define a region of the image to",
670     "       magnify.",
671     "",
672     "  3    Press and drag to choose from a select set of commands.",
673     "       This button behaves differently if the image being",
674     "       displayed is a visual image directory.  Here, choose a",
675     "       particular tile of the directory and press this button and",
676     "       drag to select a command from a pop-up menu.  Choose from",
677     "       these menu items:",
678     "",
679     "           Open",
680     "           Next",
681     "           Former",
682     "           Delete",
683     "           Update",
684     "",
685     "       If you choose Open, the image represented by the tile is",
686     "       displayed.  To return to the visual image directory, choose",
687     "       Next from the Command widget.  Next and Former moves to the",
688     "       next or former image respectively.  Choose Delete to delete",
689     "       a particular image tile.  Finally, choose Update to",
690     "       synchronize all the image tiles with their respective",
691     "       images.",
692     "",
693     "COMMAND WIDGET",
694     "  The Command widget lists a number of sub-menus and commands.",
695     "  They are",
696     "",
697     "      File",
698     "        Open...",
699     "        Next",
700     "        Former",
701     "        Select...",
702     "        Save...",
703     "        Print...",
704     "        Delete...",
705     "        New...",
706     "        Visual Directory...",
707     "        Quit",
708     "      Edit",
709     "        Undo",
710     "        Redo",
711     "        Cut",
712     "        Copy",
713     "        Paste",
714     "      View",
715     "        Half Size",
716     "        Original Size",
717     "        Double Size",
718     "        Resize...",
719     "        Apply",
720     "        Refresh",
721     "        Restore",
722     "      Transform",
723     "        Crop",
724     "        Chop",
725     "        Flop",
726     "        Flip",
727     "        Rotate Right",
728     "        Rotate Left",
729     "        Rotate...",
730     "        Shear...",
731     "        Roll...",
732     "        Trim Edges",
733     "      Enhance",
734     "        Brightness...",
735     "        Saturation...",
736     "        Hue...",
737     "        Gamma...",
738     "        Sharpen...",
739     "        Dull",
740     "        Contrast Stretch...",
741     "        Sigmoidal Contrast...",
742     "        Normalize",
743     "        Equalize",
744     "        Negate",
745     "        Grayscale",
746     "        Map...",
747     "        Quantize...",
748     "      Effects",
749     "        Despeckle",
750     "        Emboss",
751     "        Reduce Noise",
752     "        Add Noise",
753     "        Sharpen...",
754     "        Blur...",
755     "        Threshold...",
756     "        Edge Detect...",
757     "        Spread...",
758     "        Shade...",
759     "        Painting...",
760     "        Segment...",
761     "      F/X",
762     "        Solarize...",
763     "        Sepia Tone...",
764     "        Swirl...",
765     "        Implode...",
766     "        Vignette...",
767     "        Wave...",
768     "        Oil Painting...",
769     "        Charcoal Drawing...",
770     "      Image Edit",
771     "        Annotate...",
772     "        Draw...",
773     "        Color...",
774     "        Matte...",
775     "        Composite...",
776     "        Add Border...",
777     "        Add Frame...",
778     "        Comment...",
779     "        Launch...",
780     "        Region of Interest...",
781     "      Miscellany",
782     "        Image Info",
783     "        Zoom Image",
784     "        Show Preview...",
785     "        Show Histogram",
786     "        Show Matte",
787     "        Background...",
788     "        Slide Show",
789     "        Preferences...",
790     "      Help",
791     "        Overview",
792     "        Browse Documentation",
793     "        About Display",
794     "",
795     "  Menu items with a indented triangle have a sub-menu.  They",
796     "  are represented above as the indented items.  To access a",
797     "  sub-menu item, move the pointer to the appropriate menu and",
798     "  press a button and drag.  When you find the desired sub-menu",
799     "  item, release the button and the command is executed.  Move",
800     "  the pointer away from the sub-menu if you decide not to",
801     "  execute a particular command.",
802     "",
803     "KEYBOARD ACCELERATORS",
804     "  Accelerators are one or two key presses that effect a",
805     "  particular command.  The keyboard accelerators that",
806     "  display(1) understands is:",
807     "",
808     "  Ctl+O     Press to open an image from a file.",
809     "",
810     "  space     Press to display the next image.",
811     "",
812     "            If the image is a multi-paged document such as a Postscript",
813     "            document, you can skip ahead several pages by preceding",
814     "            this command with a number.  For example to display the",
815     "            third page beyond the current page, press 3<space>.",
816     "",
817     "  backspace Press to display the former image.",
818     "",
819     "            If the image is a multi-paged document such as a Postscript",
820     "            document, you can skip behind several pages by preceding",
821     "            this command with a number.  For example to display the",
822     "            third page preceding the current page, press 3<backspace>.",
823     "",
824     "  Ctl+S     Press to write the image to a file.",
825     "",
826     "  Ctl+P     Press to print the image to a Postscript printer.",
827     "",
828     "  Ctl+D     Press to delete an image file.",
829     "",
830     "  Ctl+N     Press to create a blank canvas.",
831     "",
832     "  Ctl+Q     Press to discard all images and exit program.",
833     "",
834     "  Ctl+Z     Press to undo last image transformation.",
835     "",
836     "  Ctl+R     Press to redo last image transformation.",
837     "",
838     "  Ctl+X     Press to cut a region of the image.",
839     "",
840     "  Ctl+C     Press to copy a region of the image.",
841     "",
842     "  Ctl+V     Press to paste a region to the image.",
843     "",
844     "  <         Press to half the image size.",
845     "",
846     "  -         Press to return to the original image size.",
847     "",
848     "  >         Press to double the image size.",
849     "",
850     "  %         Press to resize the image to a width and height you",
851     "            specify.",
852     "",
853     "Cmd-A       Press to make any image transformations permanent."
854     "",
855     "            By default, any image size transformations are applied",
856     "            to the original image to create the image displayed on",
857     "            the X server.  However, the transformations are not",
858     "            permanent (i.e. the original image does not change",
859     "            size only the X image does).  For example, if you",
860     "            press > the X image will appear to double in size,",
861     "            but the original image will in fact remain the same size.",
862     "            To force the original image to double in size, press >",
863     "            followed by Cmd-A.",
864     "",
865     "  @         Press to refresh the image window.",
866     "",
867     "  C         Press to cut out a rectangular region of the image.",
868     "",
869     "  [         Press to chop the image.",
870     "",
871     "  H         Press to flop image in the horizontal direction.",
872     "",
873     "  V         Press to flip image in the vertical direction.",
874     "",
875     "  /         Press to rotate the image 90 degrees clockwise.",
876     "",
877     " \\         Press to rotate the image 90 degrees counter-clockwise.",
878     "",
879     "  *         Press to rotate the image the number of degrees you",
880     "            specify.",
881     "",
882     "  S         Press to shear the image the number of degrees you",
883     "            specify.",
884     "",
885     "  R         Press to roll the image.",
886     "",
887     "  T         Press to trim the image edges.",
888     "",
889     "  Shft-H    Press to vary the image hue.",
890     "",
891     "  Shft-S    Press to vary the color saturation.",
892     "",
893     "  Shft-L    Press to vary the color brightness.",
894     "",
895     "  Shft-G    Press to gamma correct the image.",
896     "",
897     "  Shft-C    Press to sharpen the image contrast.",
898     "",
899     "  Shft-Z    Press to dull the image contrast.",
900     "",
901     "  =         Press to perform histogram equalization on the image.",
902     "",
903     "  Shft-N    Press to perform histogram normalization on the image.",
904     "",
905     "  Shft-~    Press to negate the colors of the image.",
906     "",
907     "  .         Press to convert the image colors to gray.",
908     "",
909     "  Shft-#    Press to set the maximum number of unique colors in the",
910     "            image.",
911     "",
912     "  F2        Press to reduce the speckles in an image.",
913     "",
914     "  F3        Press to eliminate peak noise from an image.",
915     "",
916     "  F4        Press to add noise to an image.",
917     "",
918     "  F5        Press to sharpen an image.",
919     "",
920     "  F6        Press to delete an image file.",
921     "",
922     "  F7        Press to threshold the image.",
923     "",
924     "  F8        Press to detect edges within an image.",
925     "",
926     "  F9        Press to emboss an image.",
927     "",
928     "  F10       Press to displace pixels by a random amount.",
929     "",
930     "  F11       Press to negate all pixels above the threshold level.",
931     "",
932     "  F12       Press to shade the image using a distant light source.",
933     "",
934     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
935     "",
936     "  F14       Press to segment the image by color.",
937     "",
938     "  Meta-S    Press to swirl image pixels about the center.",
939     "",
940     "  Meta-I    Press to implode image pixels about the center.",
941     "",
942     "  Meta-W    Press to alter an image along a sine wave.",
943     "",
944     "  Meta-P    Press to simulate an oil painting.",
945     "",
946     "  Meta-C    Press to simulate a charcoal drawing.",
947     "",
948     "  Alt-A     Press to annotate the image with text.",
949     "",
950     "  Alt-D     Press to draw on an image.",
951     "",
952     "  Alt-P     Press to edit an image pixel color.",
953     "",
954     "  Alt-M     Press to edit the image matte information.",
955     "",
956     "  Alt-V     Press to composite the image with another.",
957     "",
958     "  Alt-B     Press to add a border to the image.",
959     "",
960     "  Alt-F     Press to add an ornamental border to the image.",
961     "",
962     "  Alt-Shft-!",
963     "            Press to add an image comment.",
964     "",
965     "  Ctl-A     Press to apply image processing techniques to a region",
966     "            of interest.",
967     "",
968     "  Shft-?    Press to display information about the image.",
969     "",
970     "  Shft-+    Press to map the zoom image window.",
971     "",
972     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
973     "",
974     "  F1        Press to display helpful information about display(1).",
975     "",
976     "  Find      Press to browse documentation about ImageMagick.",
977     "",
978     "  1-9       Press to change the level of magnification.",
979     "",
980     "  Use the arrow keys to move the image one pixel up, down,",
981     "  left, or right within the magnify window.  Be sure to first",
982     "  map the magnify window by pressing button 2.",
983     "",
984     "  Press ALT and one of the arrow keys to trim off one pixel",
985     "  from any side of the image.",
986     (char *) NULL,
987   },
988   *ImageMatteEditHelp[] =
989   {
990     "Matte information within an image is useful for some",
991     "operations such as image compositing (See IMAGE",
992     "COMPOSITING).  This extra channel usually defines a mask",
993     "which represents a sort of a cookie-cutter for the image.",
994     "This the case when matte is opaque (full coverage) for",
995     "pixels inside the shape, zero outside, and between 0 and",
996     "QuantumRange on the boundary.",
997     "",
998     "A small window appears showing the location of the cursor in",
999     "the image window. You are now in matte edit mode.  To exit",
1000     "immediately, press Dismiss.  In matte edit mode, the Command",
1001     "widget has these options:",
1002     "",
1003     "    Method",
1004     "      point",
1005     "      replace",
1006     "      floodfill",
1007     "      filltoborder",
1008     "      reset",
1009     "    Border Color",
1010     "      black",
1011     "      blue",
1012     "      cyan",
1013     "      green",
1014     "      gray",
1015     "      red",
1016     "      magenta",
1017     "      yellow",
1018     "      white",
1019     "      Browser...",
1020     "    Fuzz",
1021     "      0%",
1022     "      2%",
1023     "      5%",
1024     "      10%",
1025     "      15%",
1026     "      Dialog...",
1027     "    Matte",
1028     "      Opaque",
1029     "      Transparent",
1030     "      Dialog...",
1031     "    Undo",
1032     "    Help",
1033     "    Dismiss",
1034     "",
1035     "Choose a matte editing method from the Method sub-menu of",
1036     "the Command widget.  The point method changes the matte value",
1037     "of any pixel selected with the pointer until the button is",
1038     "is released.  The replace method changes the matte value of",
1039     "any pixel that matches the color of the pixel you select with",
1040     "a button press.  Floodfill changes the matte value of any pixel",
1041     "that matches the color of the pixel you select with a button",
1042     "press and is a neighbor.  Whereas filltoborder changes the matte",
1043     "value any neighbor pixel that is not the border color.  Finally",
1044     "reset changes the entire image to the designated matte value.",
1045     "",
1046     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1047     "select the Dialog entry.  Here a dialog appears requesting a matte",
1048     "value.  The value you select is assigned as the opacity value of the",
1049     "selected pixel or pixels.",
1050     "",
1051     "Now, press any button to select a pixel within the image",
1052     "window to change its matte value.",
1053     "",
1054     "If the Magnify widget is mapped, it can be helpful in positioning",
1055     "your pointer within the image (refer to button 2).",
1056     "",
1057     "Matte information is only valid in a DirectClass image.",
1058     "Therefore, any PseudoClass image is promoted to DirectClass",
1059     "(see miff(5)).  Note that matte information for PseudoClass",
1060     "is not retained for colormapped X server visuals (e.g.",
1061     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1062     "immediately save your image to a file (refer to Write).",
1063     "Correct matte editing behavior may require a TrueColor or",
1064     "DirectColor visual or a Standard Colormap.",
1065     (char *) NULL,
1066   },
1067   *ImagePanHelp[] =
1068   {
1069     "When an image exceeds the width or height of the X server",
1070     "screen, display maps a small panning icon.  The rectangle",
1071     "within the panning icon shows the area that is currently",
1072     "displayed in the image window.  To pan about the image,",
1073     "press any button and drag the pointer within the panning",
1074     "icon.  The pan rectangle moves with the pointer and the",
1075     "image window is updated to reflect the location of the",
1076     "rectangle within the panning icon.  When you have selected",
1077     "the area of the image you wish to view, release the button.",
1078     "",
1079     "Use the arrow keys to pan the image one pixel up, down,",
1080     "left, or right within the image window.",
1081     "",
1082     "The panning icon is withdrawn if the image becomes smaller",
1083     "than the dimensions of the X server screen.",
1084     (char *) NULL,
1085   },
1086   *ImagePasteHelp[] =
1087   {
1088     "A small window appears showing the location of the cursor in",
1089     "the image window. You are now in paste mode.  To exit",
1090     "immediately, press Dismiss.  In paste mode, the Command",
1091     "widget has these options:",
1092     "",
1093     "    Operators",
1094     "      over",
1095     "      in",
1096     "      out",
1097     "      atop",
1098     "      xor",
1099     "      plus",
1100     "      minus",
1101     "      add",
1102     "      subtract",
1103     "      difference",
1104     "      replace",
1105     "    Help",
1106     "    Dismiss",
1107     "",
1108     "Choose a composite operation from the Operators sub-menu of",
1109     "the Command widget.  How each operator behaves is described",
1110     "below.  Image window is the image currently displayed on",
1111     "your X server and image is the image obtained with the File",
1112     "Browser widget.",
1113     "",
1114     "Over     The result is the union of the two image shapes,",
1115     "         with image obscuring image window in the region of",
1116     "         overlap.",
1117     "",
1118     "In       The result is simply image cut by the shape of",
1119     "         image window.  None of the image data of image",
1120     "         window is in the result.",
1121     "",
1122     "Out      The resulting image is image with the shape of",
1123     "         image window cut out.",
1124     "",
1125     "Atop     The result is the same shape as image image window,",
1126     "         with image obscuring image window where the image",
1127     "         shapes overlap.  Note this differs from over",
1128     "         because the portion of image outside image window's",
1129     "         shape does not appear in the result.",
1130     "",
1131     "Xor      The result is the image data from both image and",
1132     "         image window that is outside the overlap region.",
1133     "         The overlap region is blank.",
1134     "",
1135     "Plus     The result is just the sum of the image data.",
1136     "         Output values are cropped to QuantumRange (no overflow).",
1137     "         This operation is independent of the matte",
1138     "         channels.",
1139     "",
1140     "Minus    The result of image - image window, with underflow",
1141     "         cropped to zero.",
1142     "",
1143     "Add      The result of image + image window, with overflow",
1144     "         wrapping around (mod 256).",
1145     "",
1146     "Subtract The result of image - image window, with underflow",
1147     "         wrapping around (mod 256).  The add and subtract",
1148     "         operators can be used to perform reversible",
1149     "         transformations.",
1150     "",
1151     "Difference",
1152     "         The result of abs(image - image window).  This",
1153     "         useful for comparing two very similar images.",
1154     "",
1155     "Copy     The resulting image is image window replaced with",
1156     "         image.  Here the matte information is ignored.",
1157     "",
1158     "CopyRed  The red layer of the image window is replace with",
1159     "         the red layer of the image.  The other layers are",
1160     "         untouched.",
1161     "",
1162     "CopyGreen",
1163     "         The green layer of the image window is replace with",
1164     "         the green layer of the image.  The other layers are",
1165     "         untouched.",
1166     "",
1167     "CopyBlue The blue layer of the image window is replace with",
1168     "         the blue layer of the image.  The other layers are",
1169     "         untouched.",
1170     "",
1171     "CopyOpacity",
1172     "         The matte layer of the image window is replace with",
1173     "         the matte layer of the image.  The other layers are",
1174     "         untouched.",
1175     "",
1176     "The image compositor requires a matte, or alpha channel in",
1177     "the image for some operations.  This extra channel usually",
1178     "defines a mask which represents a sort of a cookie-cutter",
1179     "for the image.  This the case when matte is opaque (full",
1180     "coverage) for pixels inside the shape, zero outside, and",
1181     "between 0 and QuantumRange on the boundary.  If image does not",
1182     "have a matte channel, it is initialized with 0 for any pixel",
1183     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1184     "",
1185     "Note that matte information for image window is not retained",
1186     "for colormapped X server visuals (e.g. StaticColor,",
1187     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1188     "behavior may require a TrueColor or DirectColor visual or a",
1189     "Standard Colormap.",
1190     "",
1191     "Choosing a composite operator is optional.  The default",
1192     "operator is replace.  However, you must choose a location to",
1193     "paste your image and press button 1.  Press and hold the",
1194     "button before releasing and an outline of the image will",
1195     "appear to help you identify your location.",
1196     "",
1197     "The actual colors of the pasted image is saved.  However,",
1198     "the color that appears in image window may be different.",
1199     "For example, on a monochrome screen image window will appear",
1200     "black or white even though your pasted image may have",
1201     "many colors.  If the image is saved to a file it is written",
1202     "with the correct colors.  To assure the correct colors are",
1203     "saved in the final image, any PseudoClass image is promoted",
1204     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1205     "to remain PseudoClass, use -colors.",
1206     (char *) NULL,
1207   },
1208   *ImageROIHelp[] =
1209   {
1210     "In region of interest mode, the Command widget has these",
1211     "options:",
1212     "",
1213     "    Help",
1214     "    Dismiss",
1215     "",
1216     "To define a region of interest, press button 1 and drag.",
1217     "The region of interest is defined by a highlighted rectangle",
1218     "that expands or contracts as it follows the pointer.  Once",
1219     "you are satisfied with the region of interest, release the",
1220     "button.  You are now in apply mode.  In apply mode the",
1221     "Command widget has these options:",
1222     "",
1223     "      File",
1224     "        Save...",
1225     "        Print...",
1226     "      Edit",
1227     "        Undo",
1228     "        Redo",
1229     "      Transform",
1230     "        Flop",
1231     "        Flip",
1232     "        Rotate Right",
1233     "        Rotate Left",
1234     "      Enhance",
1235     "        Hue...",
1236     "        Saturation...",
1237     "        Brightness...",
1238     "        Gamma...",
1239     "        Spiff",
1240     "        Dull",
1241     "        Contrast Stretch",
1242     "        Sigmoidal Contrast...",
1243     "        Normalize",
1244     "        Equalize",
1245     "        Negate",
1246     "        Grayscale",
1247     "        Map...",
1248     "        Quantize...",
1249     "      Effects",
1250     "        Despeckle",
1251     "        Emboss",
1252     "        Reduce Noise",
1253     "        Sharpen...",
1254     "        Blur...",
1255     "        Threshold...",
1256     "        Edge Detect...",
1257     "        Spread...",
1258     "        Shade...",
1259     "        Raise...",
1260     "        Segment...",
1261     "      F/X",
1262     "        Solarize...",
1263     "        Sepia Tone...",
1264     "        Swirl...",
1265     "        Implode...",
1266     "        Vignette...",
1267     "        Wave...",
1268     "        Oil Painting...",
1269     "        Charcoal Drawing...",
1270     "      Miscellany",
1271     "        Image Info",
1272     "        Zoom Image",
1273     "        Show Preview...",
1274     "        Show Histogram",
1275     "        Show Matte",
1276     "      Help",
1277     "      Dismiss",
1278     "",
1279     "You can make adjustments to the region of interest by moving",
1280     "the pointer to one of the rectangle corners, pressing a",
1281     "button, and dragging.  Finally, choose an image processing",
1282     "technique from the Command widget.  You can choose more than",
1283     "one image processing technique to apply to an area.",
1284     "Alternatively, you can move the region of interest before",
1285     "applying another image processing technique.  To exit, press",
1286     "Dismiss.",
1287     (char *) NULL,
1288   },
1289   *ImageRotateHelp[] =
1290   {
1291     "In rotate mode, the Command widget has these options:",
1292     "",
1293     "    Pixel Color",
1294     "      black",
1295     "      blue",
1296     "      cyan",
1297     "      green",
1298     "      gray",
1299     "      red",
1300     "      magenta",
1301     "      yellow",
1302     "      white",
1303     "      Browser...",
1304     "    Direction",
1305     "      horizontal",
1306     "      vertical",
1307     "    Help",
1308     "    Dismiss",
1309     "",
1310     "Choose a background color from the Pixel Color sub-menu.",
1311     "Additional background colors can be specified with the color",
1312     "browser.  You can change the menu colors by setting the X",
1313     "resources pen1 through pen9.",
1314     "",
1315     "If you choose the color browser and press Grab, you can",
1316     "select the background color by moving the pointer to the",
1317     "desired color on the screen and press any button.",
1318     "",
1319     "Choose a point in the image window and press this button and",
1320     "hold.  Next, move the pointer to another location in the",
1321     "image.  As you move a line connects the initial location and",
1322     "the pointer.  When you release the button, the degree of",
1323     "image rotation is determined by the slope of the line you",
1324     "just drew.  The slope is relative to the direction you",
1325     "choose from the Direction sub-menu of the Command widget.",
1326     "",
1327     "To cancel the image rotation, move the pointer back to the",
1328     "starting point of the line and release the button.",
1329     (char *) NULL,
1330   };
1331 \f
1332 /*
1333   Enumeration declarations.
1334 */
1335 typedef enum
1336 {
1337   CopyMode,
1338   CropMode,
1339   CutMode
1340 } ClipboardMode;
1341
1342 typedef enum
1343 {
1344   OpenCommand,
1345   NextCommand,
1346   FormerCommand,
1347   SelectCommand,
1348   SaveCommand,
1349   PrintCommand,
1350   DeleteCommand,
1351   NewCommand,
1352   VisualDirectoryCommand,
1353   QuitCommand,
1354   UndoCommand,
1355   RedoCommand,
1356   CutCommand,
1357   CopyCommand,
1358   PasteCommand,
1359   HalfSizeCommand,
1360   OriginalSizeCommand,
1361   DoubleSizeCommand,
1362   ResizeCommand,
1363   ApplyCommand,
1364   RefreshCommand,
1365   RestoreCommand,
1366   CropCommand,
1367   ChopCommand,
1368   FlopCommand,
1369   FlipCommand,
1370   RotateRightCommand,
1371   RotateLeftCommand,
1372   RotateCommand,
1373   ShearCommand,
1374   RollCommand,
1375   TrimCommand,
1376   HueCommand,
1377   SaturationCommand,
1378   BrightnessCommand,
1379   GammaCommand,
1380   SpiffCommand,
1381   DullCommand,
1382   ContrastStretchCommand,
1383   SigmoidalContrastCommand,
1384   NormalizeCommand,
1385   EqualizeCommand,
1386   NegateCommand,
1387   GrayscaleCommand,
1388   MapCommand,
1389   QuantizeCommand,
1390   DespeckleCommand,
1391   EmbossCommand,
1392   ReduceNoiseCommand,
1393   AddNoiseCommand,
1394   SharpenCommand,
1395   BlurCommand,
1396   ThresholdCommand,
1397   EdgeDetectCommand,
1398   SpreadCommand,
1399   ShadeCommand,
1400   RaiseCommand,
1401   SegmentCommand,
1402   SolarizeCommand,
1403   SepiaToneCommand,
1404   SwirlCommand,
1405   ImplodeCommand,
1406   VignetteCommand,
1407   WaveCommand,
1408   OilPaintCommand,
1409   CharcoalDrawCommand,
1410   AnnotateCommand,
1411   DrawCommand,
1412   ColorCommand,
1413   MatteCommand,
1414   CompositeCommand,
1415   AddBorderCommand,
1416   AddFrameCommand,
1417   CommentCommand,
1418   LaunchCommand,
1419   RegionofInterestCommand,
1420   ROIHelpCommand,
1421   ROIDismissCommand,
1422   InfoCommand,
1423   ZoomCommand,
1424   ShowPreviewCommand,
1425   ShowHistogramCommand,
1426   ShowMatteCommand,
1427   BackgroundCommand,
1428   SlideShowCommand,
1429   PreferencesCommand,
1430   HelpCommand,
1431   BrowseDocumentationCommand,
1432   VersionCommand,
1433   SaveToUndoBufferCommand,
1434   FreeBuffersCommand,
1435   NullCommand
1436 } CommandType;
1437
1438 typedef enum
1439 {
1440   AnnotateNameCommand,
1441   AnnotateFontColorCommand,
1442   AnnotateBackgroundColorCommand,
1443   AnnotateRotateCommand,
1444   AnnotateHelpCommand,
1445   AnnotateDismissCommand,
1446   TextHelpCommand,
1447   TextApplyCommand,
1448   ChopDirectionCommand,
1449   ChopHelpCommand,
1450   ChopDismissCommand,
1451   HorizontalChopCommand,
1452   VerticalChopCommand,
1453   ColorEditMethodCommand,
1454   ColorEditColorCommand,
1455   ColorEditBorderCommand,
1456   ColorEditFuzzCommand,
1457   ColorEditUndoCommand,
1458   ColorEditHelpCommand,
1459   ColorEditDismissCommand,
1460   CompositeOperatorsCommand,
1461   CompositeDissolveCommand,
1462   CompositeDisplaceCommand,
1463   CompositeHelpCommand,
1464   CompositeDismissCommand,
1465   CropHelpCommand,
1466   CropDismissCommand,
1467   RectifyCopyCommand,
1468   RectifyHelpCommand,
1469   RectifyDismissCommand,
1470   DrawElementCommand,
1471   DrawColorCommand,
1472   DrawStippleCommand,
1473   DrawWidthCommand,
1474   DrawUndoCommand,
1475   DrawHelpCommand,
1476   DrawDismissCommand,
1477   MatteEditMethod,
1478   MatteEditBorderCommand,
1479   MatteEditFuzzCommand,
1480   MatteEditValueCommand,
1481   MatteEditUndoCommand,
1482   MatteEditHelpCommand,
1483   MatteEditDismissCommand,
1484   PasteOperatorsCommand,
1485   PasteHelpCommand,
1486   PasteDismissCommand,
1487   RotateColorCommand,
1488   RotateDirectionCommand,
1489   RotateCropCommand,
1490   RotateSharpenCommand,
1491   RotateHelpCommand,
1492   RotateDismissCommand,
1493   HorizontalRotateCommand,
1494   VerticalRotateCommand,
1495   TileLoadCommand,
1496   TileNextCommand,
1497   TileFormerCommand,
1498   TileDeleteCommand,
1499   TileUpdateCommand
1500 } ModeType;
1501 \f
1502 /*
1503   Stipples.
1504 */
1505 #define BricksWidth  20
1506 #define BricksHeight  20
1507 #define DiagonalWidth  16
1508 #define DiagonalHeight  16
1509 #define HighlightWidth  8
1510 #define HighlightHeight  8
1511 #define OpaqueWidth  8
1512 #define OpaqueHeight  8
1513 #define ScalesWidth  16
1514 #define ScalesHeight  16
1515 #define ShadowWidth  8
1516 #define ShadowHeight  8
1517 #define VerticalWidth  16
1518 #define VerticalHeight  16
1519 #define WavyWidth  16
1520 #define WavyHeight  16
1521 \f
1522 /*
1523   Constant declaration.
1524 */
1525 static const int
1526   RoiDelta = 8;
1527
1528 static const unsigned char
1529   BricksBitmap[] =
1530   {
1531     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1532     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1533     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1534     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1535     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1536   },
1537   DiagonalBitmap[] =
1538   {
1539     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1540     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1541     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1542   },
1543   ScalesBitmap[] =
1544   {
1545     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1546     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1547     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1548   },
1549   VerticalBitmap[] =
1550   {
1551     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1552     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1553     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1554   },
1555   WavyBitmap[] =
1556   {
1557     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1558     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1559     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1560   };
1561 \f
1562 /*
1563   Function prototypes.
1564 */
1565 static CommandType
1566   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1567     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1568
1569 static Image
1570   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1571     Image **,ExceptionInfo *),
1572   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1573   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1574     ExceptionInfo *),
1575   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1576     ExceptionInfo *);
1577
1578 static MagickBooleanType
1579   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1580     ExceptionInfo *),
1581   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1582     ExceptionInfo *),
1583   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1584     ExceptionInfo *),
1585   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1586     ExceptionInfo *),
1587   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1588     ExceptionInfo *),
1589   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1590     ExceptionInfo *),
1591   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1592   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1593     ExceptionInfo *),
1594   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1595     ExceptionInfo *),
1596   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1597   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1599     ExceptionInfo *),
1600   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1601   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1603
1604 static void
1605   XDrawPanRectangle(Display *,XWindows *),
1606   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1607     ExceptionInfo *),
1608   XMagnifyImage(Display *,XWindows *,XEvent *),
1609   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1610   XPanImage(Display *,XWindows *,XEvent *),
1611   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1612     const KeySym),
1613   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1614   XScreenEvent(Display *,XWindows *,XEvent *),
1615   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1616 \f
1617 /*
1618 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1619 %                                                                             %
1620 %                                                                             %
1621 %                                                                             %
1622 %   D i s p l a y I m a g e s                                                 %
1623 %                                                                             %
1624 %                                                                             %
1625 %                                                                             %
1626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1627 %
1628 %  DisplayImages() displays an image sequence to any X window screen.  It
1629 %  returns a value other than 0 if successful.  Check the exception member
1630 %  of image to determine the reason for any failure.
1631 %
1632 %  The format of the DisplayImages method is:
1633 %
1634 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1635 %        Image *images,ExceptionInfo *exception)
1636 %
1637 %  A description of each parameter follows:
1638 %
1639 %    o image_info: the image info.
1640 %
1641 %    o image: the image.
1642 %
1643 %    o exception: return any errors or warnings in this structure.
1644 %
1645 */
1646 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1647   Image *images,ExceptionInfo *exception)
1648 {
1649   char
1650     *argv[1];
1651
1652   Display
1653     *display;
1654
1655   Image
1656     *image;
1657
1658   register ssize_t
1659     i;
1660
1661   size_t
1662     state;
1663
1664   XrmDatabase
1665     resource_database;
1666
1667   XResourceInfo
1668     resource_info;
1669
1670   assert(image_info != (const ImageInfo *) NULL);
1671   assert(image_info->signature == MagickSignature);
1672   assert(images != (Image *) NULL);
1673   assert(images->signature == MagickSignature);
1674   if (images->debug != MagickFalse)
1675     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1676   display=XOpenDisplay(image_info->server_name);
1677   if (display == (Display *) NULL)
1678     {
1679       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1680         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1681       return(MagickFalse);
1682     }
1683   if (exception->severity != UndefinedException)
1684     CatchException(exception);
1685   (void) XSetErrorHandler(XError);
1686   resource_database=XGetResourceDatabase(display,GetClientName());
1687   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1688   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1689   if (image_info->page != (char *) NULL)
1690     resource_info.image_geometry=AcquireString(image_info->page);
1691   resource_info.immutable=MagickTrue;
1692   argv[0]=AcquireString(GetClientName());
1693   state=DefaultState;
1694   for (i=0; (state & ExitState) == 0; i++)
1695   {
1696     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1697       break;
1698     image=GetImageFromList(images,i % GetImageListLength(images));
1699     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1700   }
1701   SetErrorHandler((ErrorHandler) NULL);
1702   SetWarningHandler((WarningHandler) NULL);
1703   argv[0]=DestroyString(argv[0]);
1704   (void) XCloseDisplay(display);
1705   XDestroyResourceInfo(&resource_info);
1706   if (exception->severity != UndefinedException)
1707     return(MagickFalse);
1708   return(MagickTrue);
1709 }
1710 \f
1711 /*
1712 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1713 %                                                                             %
1714 %                                                                             %
1715 %                                                                             %
1716 %   R e m o t e D i s p l a y C o m m a n d                                   %
1717 %                                                                             %
1718 %                                                                             %
1719 %                                                                             %
1720 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1721 %
1722 %  RemoteDisplayCommand() encourages a remote display program to display the
1723 %  specified image filename.
1724 %
1725 %  The format of the RemoteDisplayCommand method is:
1726 %
1727 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1728 %        const char *window,const char *filename,ExceptionInfo *exception)
1729 %
1730 %  A description of each parameter follows:
1731 %
1732 %    o image_info: the image info.
1733 %
1734 %    o window: Specifies the name or id of an X window.
1735 %
1736 %    o filename: the name of the image filename to display.
1737 %
1738 %    o exception: return any errors or warnings in this structure.
1739 %
1740 */
1741 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1742   const char *window,const char *filename,ExceptionInfo *exception)
1743 {
1744   Display
1745     *display;
1746
1747   MagickStatusType
1748     status;
1749
1750   assert(image_info != (const ImageInfo *) NULL);
1751   assert(image_info->signature == MagickSignature);
1752   assert(filename != (char *) NULL);
1753   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1754   display=XOpenDisplay(image_info->server_name);
1755   if (display == (Display *) NULL)
1756     {
1757       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1758         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1759       return(MagickFalse);
1760     }
1761   (void) XSetErrorHandler(XError);
1762   status=XRemoteCommand(display,window,filename);
1763   (void) XCloseDisplay(display);
1764   return(status != 0 ? MagickTrue : MagickFalse);
1765 }
1766 \f
1767 /*
1768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1769 %                                                                             %
1770 %                                                                             %
1771 %                                                                             %
1772 +   X A n n o t a t e E d i t I m a g e                                       %
1773 %                                                                             %
1774 %                                                                             %
1775 %                                                                             %
1776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1777 %
1778 %  XAnnotateEditImage() annotates the image with text.
1779 %
1780 %  The format of the XAnnotateEditImage method is:
1781 %
1782 %      MagickBooleanType XAnnotateEditImage(Display *display,
1783 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1784 %        ExceptionInfo *exception)
1785 %
1786 %  A description of each parameter follows:
1787 %
1788 %    o display: Specifies a connection to an X server;  returned from
1789 %      XOpenDisplay.
1790 %
1791 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1792 %
1793 %    o windows: Specifies a pointer to a XWindows structure.
1794 %
1795 %    o image: the image; returned from ReadImage.
1796 %
1797 */
1798
1799 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1800 {
1801   if (x > y)
1802     return(x);
1803   return(y);
1804 }
1805
1806 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1807 {
1808   if (x < y)
1809     return(x);
1810   return(y);
1811 }
1812
1813 static MagickBooleanType XAnnotateEditImage(Display *display,
1814   XResourceInfo *resource_info,XWindows *windows,Image *image,
1815   ExceptionInfo *exception)
1816 {
1817   static const char
1818     *AnnotateMenu[] =
1819     {
1820       "Font Name",
1821       "Font Color",
1822       "Box Color",
1823       "Rotate Text",
1824       "Help",
1825       "Dismiss",
1826       (char *) NULL
1827     },
1828     *TextMenu[] =
1829     {
1830       "Help",
1831       "Apply",
1832       (char *) NULL
1833     };
1834
1835   static const ModeType
1836     AnnotateCommands[] =
1837     {
1838       AnnotateNameCommand,
1839       AnnotateFontColorCommand,
1840       AnnotateBackgroundColorCommand,
1841       AnnotateRotateCommand,
1842       AnnotateHelpCommand,
1843       AnnotateDismissCommand
1844     },
1845     TextCommands[] =
1846     {
1847       TextHelpCommand,
1848       TextApplyCommand
1849     };
1850
1851   static MagickBooleanType
1852     transparent_box = MagickTrue,
1853     transparent_pen = MagickFalse;
1854
1855   static MagickRealType
1856     degrees = 0.0;
1857
1858   static unsigned int
1859     box_id = MaxNumberPens-2,
1860     font_id = 0,
1861     pen_id = 0;
1862
1863   char
1864     command[MaxTextExtent],
1865     text[MaxTextExtent];
1866
1867   const char
1868     *ColorMenu[MaxNumberPens+1];
1869
1870   Cursor
1871     cursor;
1872
1873   GC
1874     annotate_context;
1875
1876   int
1877     id,
1878     pen_number,
1879     status,
1880     x,
1881     y;
1882
1883   KeySym
1884     key_symbol;
1885
1886   register char
1887     *p;
1888
1889   register ssize_t
1890     i;
1891
1892   unsigned int
1893     height,
1894     width;
1895
1896   size_t
1897     state;
1898
1899   XAnnotateInfo
1900     *annotate_info,
1901     *previous_info;
1902
1903   XColor
1904     color;
1905
1906   XFontStruct
1907     *font_info;
1908
1909   XEvent
1910     event,
1911     text_event;
1912
1913   /*
1914     Map Command widget.
1915   */
1916   (void) CloneString(&windows->command.name,"Annotate");
1917   windows->command.data=4;
1918   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1919   (void) XMapRaised(display,windows->command.id);
1920   XClientMessage(display,windows->image.id,windows->im_protocols,
1921     windows->im_update_widget,CurrentTime);
1922   /*
1923     Track pointer until button 1 is pressed.
1924   */
1925   XQueryPosition(display,windows->image.id,&x,&y);
1926   (void) XSelectInput(display,windows->image.id,
1927     windows->image.attributes.event_mask | PointerMotionMask);
1928   cursor=XCreateFontCursor(display,XC_left_side);
1929   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1930   state=DefaultState;
1931   do
1932   {
1933     if (windows->info.mapped != MagickFalse)
1934       {
1935         /*
1936           Display pointer position.
1937         */
1938         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1939           x+windows->image.x,y+windows->image.y);
1940         XInfoWidget(display,windows,text);
1941       }
1942     /*
1943       Wait for next event.
1944     */
1945     XScreenEvent(display,windows,&event);
1946     if (event.xany.window == windows->command.id)
1947       {
1948         /*
1949           Select a command from the Command widget.
1950         */
1951         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1952         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1953         if (id < 0)
1954           continue;
1955         switch (AnnotateCommands[id])
1956         {
1957           case AnnotateNameCommand:
1958           {
1959             const char
1960               *FontMenu[MaxNumberFonts];
1961
1962             int
1963               font_number;
1964
1965             /*
1966               Initialize menu selections.
1967             */
1968             for (i=0; i < MaxNumberFonts; i++)
1969               FontMenu[i]=resource_info->font_name[i];
1970             FontMenu[MaxNumberFonts-2]="Browser...";
1971             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1972             /*
1973               Select a font name from the pop-up menu.
1974             */
1975             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1976               (const char **) FontMenu,command);
1977             if (font_number < 0)
1978               break;
1979             if (font_number == (MaxNumberFonts-2))
1980               {
1981                 static char
1982                   font_name[MaxTextExtent] = "fixed";
1983
1984                 /*
1985                   Select a font name from a browser.
1986                 */
1987                 resource_info->font_name[font_number]=font_name;
1988                 XFontBrowserWidget(display,windows,"Select",font_name);
1989                 if (*font_name == '\0')
1990                   break;
1991               }
1992             /*
1993               Initialize font info.
1994             */
1995             font_info=XLoadQueryFont(display,resource_info->font_name[
1996               font_number]);
1997             if (font_info == (XFontStruct *) NULL)
1998               {
1999                 XNoticeWidget(display,windows,"Unable to load font:",
2000                   resource_info->font_name[font_number]);
2001                 break;
2002               }
2003             font_id=(unsigned int) font_number;
2004             (void) XFreeFont(display,font_info);
2005             break;
2006           }
2007           case AnnotateFontColorCommand:
2008           {
2009             /*
2010               Initialize menu selections.
2011             */
2012             for (i=0; i < (int) (MaxNumberPens-2); i++)
2013               ColorMenu[i]=resource_info->pen_colors[i];
2014             ColorMenu[MaxNumberPens-2]="transparent";
2015             ColorMenu[MaxNumberPens-1]="Browser...";
2016             ColorMenu[MaxNumberPens]=(const char *) NULL;
2017             /*
2018               Select a pen color from the pop-up menu.
2019             */
2020             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2021               (const char **) ColorMenu,command);
2022             if (pen_number < 0)
2023               break;
2024             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2025               MagickFalse;
2026             if (transparent_pen != MagickFalse)
2027               break;
2028             if (pen_number == (MaxNumberPens-1))
2029               {
2030                 static char
2031                   color_name[MaxTextExtent] = "gray";
2032
2033                 /*
2034                   Select a pen color from a dialog.
2035                 */
2036                 resource_info->pen_colors[pen_number]=color_name;
2037                 XColorBrowserWidget(display,windows,"Select",color_name);
2038                 if (*color_name == '\0')
2039                   break;
2040               }
2041             /*
2042               Set pen color.
2043             */
2044             (void) XParseColor(display,windows->map_info->colormap,
2045               resource_info->pen_colors[pen_number],&color);
2046             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2047               (unsigned int) MaxColors,&color);
2048             windows->pixel_info->pen_colors[pen_number]=color;
2049             pen_id=(unsigned int) pen_number;
2050             break;
2051           }
2052           case AnnotateBackgroundColorCommand:
2053           {
2054             /*
2055               Initialize menu selections.
2056             */
2057             for (i=0; i < (int) (MaxNumberPens-2); i++)
2058               ColorMenu[i]=resource_info->pen_colors[i];
2059             ColorMenu[MaxNumberPens-2]="transparent";
2060             ColorMenu[MaxNumberPens-1]="Browser...";
2061             ColorMenu[MaxNumberPens]=(const char *) NULL;
2062             /*
2063               Select a pen color from the pop-up menu.
2064             */
2065             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2066               (const char **) ColorMenu,command);
2067             if (pen_number < 0)
2068               break;
2069             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2070               MagickFalse;
2071             if (transparent_box != MagickFalse)
2072               break;
2073             if (pen_number == (MaxNumberPens-1))
2074               {
2075                 static char
2076                   color_name[MaxTextExtent] = "gray";
2077
2078                 /*
2079                   Select a pen color from a dialog.
2080                 */
2081                 resource_info->pen_colors[pen_number]=color_name;
2082                 XColorBrowserWidget(display,windows,"Select",color_name);
2083                 if (*color_name == '\0')
2084                   break;
2085               }
2086             /*
2087               Set pen color.
2088             */
2089             (void) XParseColor(display,windows->map_info->colormap,
2090               resource_info->pen_colors[pen_number],&color);
2091             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2092               (unsigned int) MaxColors,&color);
2093             windows->pixel_info->pen_colors[pen_number]=color;
2094             box_id=(unsigned int) pen_number;
2095             break;
2096           }
2097           case AnnotateRotateCommand:
2098           {
2099             int
2100               entry;
2101
2102             static char
2103               angle[MaxTextExtent] = "30.0";
2104
2105             static const char
2106               *RotateMenu[] =
2107               {
2108                 "-90",
2109                 "-45",
2110                 "-30",
2111                 "0",
2112                 "30",
2113                 "45",
2114                 "90",
2115                 "180",
2116                 "Dialog...",
2117                 (char *) NULL,
2118               };
2119
2120             /*
2121               Select a command from the pop-up menu.
2122             */
2123             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2124               command);
2125             if (entry < 0)
2126               break;
2127             if (entry != 8)
2128               {
2129                 degrees=InterpretLocaleValue(RotateMenu[entry],(char **) NULL);
2130                 break;
2131               }
2132             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2133               angle);
2134             if (*angle == '\0')
2135               break;
2136             degrees=InterpretLocaleValue(angle,(char **) NULL);
2137             break;
2138           }
2139           case AnnotateHelpCommand:
2140           {
2141             XTextViewWidget(display,resource_info,windows,MagickFalse,
2142               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2143             break;
2144           }
2145           case AnnotateDismissCommand:
2146           {
2147             /*
2148               Prematurely exit.
2149             */
2150             state|=EscapeState;
2151             state|=ExitState;
2152             break;
2153           }
2154           default:
2155             break;
2156         }
2157         continue;
2158       }
2159     switch (event.type)
2160     {
2161       case ButtonPress:
2162       {
2163         if (event.xbutton.button != Button1)
2164           break;
2165         if (event.xbutton.window != windows->image.id)
2166           break;
2167         /*
2168           Change to text entering mode.
2169         */
2170         x=event.xbutton.x;
2171         y=event.xbutton.y;
2172         state|=ExitState;
2173         break;
2174       }
2175       case ButtonRelease:
2176         break;
2177       case Expose:
2178         break;
2179       case KeyPress:
2180       {
2181         if (event.xkey.window != windows->image.id)
2182           break;
2183         /*
2184           Respond to a user key press.
2185         */
2186         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2187           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2188         switch ((int) key_symbol)
2189         {
2190           case XK_Escape:
2191           case XK_F20:
2192           {
2193             /*
2194               Prematurely exit.
2195             */
2196             state|=EscapeState;
2197             state|=ExitState;
2198             break;
2199           }
2200           case XK_F1:
2201           case XK_Help:
2202           {
2203             XTextViewWidget(display,resource_info,windows,MagickFalse,
2204               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2205             break;
2206           }
2207           default:
2208           {
2209             (void) XBell(display,0);
2210             break;
2211           }
2212         }
2213         break;
2214       }
2215       case MotionNotify:
2216       {
2217         /*
2218           Map and unmap Info widget as cursor crosses its boundaries.
2219         */
2220         x=event.xmotion.x;
2221         y=event.xmotion.y;
2222         if (windows->info.mapped != MagickFalse)
2223           {
2224             if ((x < (int) (windows->info.x+windows->info.width)) &&
2225                 (y < (int) (windows->info.y+windows->info.height)))
2226               (void) XWithdrawWindow(display,windows->info.id,
2227                 windows->info.screen);
2228           }
2229         else
2230           if ((x > (int) (windows->info.x+windows->info.width)) ||
2231               (y > (int) (windows->info.y+windows->info.height)))
2232             (void) XMapWindow(display,windows->info.id);
2233         break;
2234       }
2235       default:
2236         break;
2237     }
2238   } while ((state & ExitState) == 0);
2239   (void) XSelectInput(display,windows->image.id,
2240     windows->image.attributes.event_mask);
2241   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2242   if ((state & EscapeState) != 0)
2243     return(MagickTrue);
2244   /*
2245     Set font info and check boundary conditions.
2246   */
2247   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2248   if (font_info == (XFontStruct *) NULL)
2249     {
2250       XNoticeWidget(display,windows,"Unable to load font:",
2251         resource_info->font_name[font_id]);
2252       font_info=windows->font_info;
2253     }
2254   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2255     x=(int) windows->image.width-font_info->max_bounds.width;
2256   if (y < (int) (font_info->ascent+font_info->descent))
2257     y=(int) font_info->ascent+font_info->descent;
2258   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2259       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2260     return(MagickFalse);
2261   /*
2262     Initialize annotate structure.
2263   */
2264   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2265   if (annotate_info == (XAnnotateInfo *) NULL)
2266     return(MagickFalse);
2267   XGetAnnotateInfo(annotate_info);
2268   annotate_info->x=x;
2269   annotate_info->y=y;
2270   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2271     annotate_info->stencil=OpaqueStencil;
2272   else
2273     if (transparent_box == MagickFalse)
2274       annotate_info->stencil=BackgroundStencil;
2275     else
2276       annotate_info->stencil=ForegroundStencil;
2277   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2278   annotate_info->degrees=degrees;
2279   annotate_info->font_info=font_info;
2280   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2281     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2282     sizeof(*annotate_info->text));
2283   if (annotate_info->text == (char *) NULL)
2284     return(MagickFalse);
2285   /*
2286     Create cursor and set graphic context.
2287   */
2288   cursor=XCreateFontCursor(display,XC_pencil);
2289   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2290   annotate_context=windows->image.annotate_context;
2291   (void) XSetFont(display,annotate_context,font_info->fid);
2292   (void) XSetBackground(display,annotate_context,
2293     windows->pixel_info->pen_colors[box_id].pixel);
2294   (void) XSetForeground(display,annotate_context,
2295     windows->pixel_info->pen_colors[pen_id].pixel);
2296   /*
2297     Begin annotating the image with text.
2298   */
2299   (void) CloneString(&windows->command.name,"Text");
2300   windows->command.data=0;
2301   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2302   state=DefaultState;
2303   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2304   text_event.xexpose.width=(int) font_info->max_bounds.width;
2305   text_event.xexpose.height=font_info->max_bounds.ascent+
2306     font_info->max_bounds.descent;
2307   p=annotate_info->text;
2308   do
2309   {
2310     /*
2311       Display text cursor.
2312     */
2313     *p='\0';
2314     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2315     /*
2316       Wait for next event.
2317     */
2318     XScreenEvent(display,windows,&event);
2319     if (event.xany.window == windows->command.id)
2320       {
2321         /*
2322           Select a command from the Command widget.
2323         */
2324         (void) XSetBackground(display,annotate_context,
2325           windows->pixel_info->background_color.pixel);
2326         (void) XSetForeground(display,annotate_context,
2327           windows->pixel_info->foreground_color.pixel);
2328         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2329         (void) XSetBackground(display,annotate_context,
2330           windows->pixel_info->pen_colors[box_id].pixel);
2331         (void) XSetForeground(display,annotate_context,
2332           windows->pixel_info->pen_colors[pen_id].pixel);
2333         if (id < 0)
2334           continue;
2335         switch (TextCommands[id])
2336         {
2337           case TextHelpCommand:
2338           {
2339             XTextViewWidget(display,resource_info,windows,MagickFalse,
2340               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2341             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2342             break;
2343           }
2344           case TextApplyCommand:
2345           {
2346             /*
2347               Finished annotating.
2348             */
2349             annotate_info->width=(unsigned int) XTextWidth(font_info,
2350               annotate_info->text,(int) strlen(annotate_info->text));
2351             XRefreshWindow(display,&windows->image,&text_event);
2352             state|=ExitState;
2353             break;
2354           }
2355           default:
2356             break;
2357         }
2358         continue;
2359       }
2360     /*
2361       Erase text cursor.
2362     */
2363     text_event.xexpose.x=x;
2364     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2365     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2366       (unsigned int) text_event.xexpose.width,(unsigned int)
2367       text_event.xexpose.height,MagickFalse);
2368     XRefreshWindow(display,&windows->image,&text_event);
2369     switch (event.type)
2370     {
2371       case ButtonPress:
2372       {
2373         if (event.xbutton.window != windows->image.id)
2374           break;
2375         if (event.xbutton.button == Button2)
2376           {
2377             /*
2378               Request primary selection.
2379             */
2380             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2381               windows->image.id,CurrentTime);
2382             break;
2383           }
2384         break;
2385       }
2386       case Expose:
2387       {
2388         if (event.xexpose.count == 0)
2389           {
2390             XAnnotateInfo
2391               *text_info;
2392
2393             /*
2394               Refresh Image window.
2395             */
2396             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2397             text_info=annotate_info;
2398             while (text_info != (XAnnotateInfo *) NULL)
2399             {
2400               if (annotate_info->stencil == ForegroundStencil)
2401                 (void) XDrawString(display,windows->image.id,annotate_context,
2402                   text_info->x,text_info->y,text_info->text,
2403                   (int) strlen(text_info->text));
2404               else
2405                 (void) XDrawImageString(display,windows->image.id,
2406                   annotate_context,text_info->x,text_info->y,text_info->text,
2407                   (int) strlen(text_info->text));
2408               text_info=text_info->previous;
2409             }
2410             (void) XDrawString(display,windows->image.id,annotate_context,
2411               x,y,"_",1);
2412           }
2413         break;
2414       }
2415       case KeyPress:
2416       {
2417         int
2418           length;
2419
2420         if (event.xkey.window != windows->image.id)
2421           break;
2422         /*
2423           Respond to a user key press.
2424         */
2425         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2426           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2427         *(command+length)='\0';
2428         if (((event.xkey.state & ControlMask) != 0) ||
2429             ((event.xkey.state & Mod1Mask) != 0))
2430           state|=ModifierState;
2431         if ((state & ModifierState) != 0)
2432           switch ((int) key_symbol)
2433           {
2434             case XK_u:
2435             case XK_U:
2436             {
2437               key_symbol=DeleteCommand;
2438               break;
2439             }
2440             default:
2441               break;
2442           }
2443         switch ((int) key_symbol)
2444         {
2445           case XK_BackSpace:
2446           {
2447             /*
2448               Erase one character.
2449             */
2450             if (p == annotate_info->text)
2451               {
2452                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2453                   break;
2454                 else
2455                   {
2456                     /*
2457                       Go to end of the previous line of text.
2458                     */
2459                     annotate_info=annotate_info->previous;
2460                     p=annotate_info->text;
2461                     x=annotate_info->x+annotate_info->width;
2462                     y=annotate_info->y;
2463                     if (annotate_info->width != 0)
2464                       p+=strlen(annotate_info->text);
2465                     break;
2466                   }
2467               }
2468             p--;
2469             x-=XTextWidth(font_info,p,1);
2470             text_event.xexpose.x=x;
2471             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2472             XRefreshWindow(display,&windows->image,&text_event);
2473             break;
2474           }
2475           case XK_bracketleft:
2476           {
2477             key_symbol=XK_Escape;
2478             break;
2479           }
2480           case DeleteCommand:
2481           {
2482             /*
2483               Erase the entire line of text.
2484             */
2485             while (p != annotate_info->text)
2486             {
2487               p--;
2488               x-=XTextWidth(font_info,p,1);
2489               text_event.xexpose.x=x;
2490               XRefreshWindow(display,&windows->image,&text_event);
2491             }
2492             break;
2493           }
2494           case XK_Escape:
2495           case XK_F20:
2496           {
2497             /*
2498               Finished annotating.
2499             */
2500             annotate_info->width=(unsigned int) XTextWidth(font_info,
2501               annotate_info->text,(int) strlen(annotate_info->text));
2502             XRefreshWindow(display,&windows->image,&text_event);
2503             state|=ExitState;
2504             break;
2505           }
2506           default:
2507           {
2508             /*
2509               Draw a single character on the Image window.
2510             */
2511             if ((state & ModifierState) != 0)
2512               break;
2513             if (*command == '\0')
2514               break;
2515             *p=(*command);
2516             if (annotate_info->stencil == ForegroundStencil)
2517               (void) XDrawString(display,windows->image.id,annotate_context,
2518                 x,y,p,1);
2519             else
2520               (void) XDrawImageString(display,windows->image.id,
2521                 annotate_context,x,y,p,1);
2522             x+=XTextWidth(font_info,p,1);
2523             p++;
2524             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2525               break;
2526           }
2527           case XK_Return:
2528           case XK_KP_Enter:
2529           {
2530             /*
2531               Advance to the next line of text.
2532             */
2533             *p='\0';
2534             annotate_info->width=(unsigned int) XTextWidth(font_info,
2535               annotate_info->text,(int) strlen(annotate_info->text));
2536             if (annotate_info->next != (XAnnotateInfo *) NULL)
2537               {
2538                 /*
2539                   Line of text already exists.
2540                 */
2541                 annotate_info=annotate_info->next;
2542                 x=annotate_info->x;
2543                 y=annotate_info->y;
2544                 p=annotate_info->text;
2545                 break;
2546               }
2547             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2548               sizeof(*annotate_info->next));
2549             if (annotate_info->next == (XAnnotateInfo *) NULL)
2550               return(MagickFalse);
2551             *annotate_info->next=(*annotate_info);
2552             annotate_info->next->previous=annotate_info;
2553             annotate_info=annotate_info->next;
2554             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2555               windows->image.width/MagickMax((ssize_t)
2556               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2557             if (annotate_info->text == (char *) NULL)
2558               return(MagickFalse);
2559             annotate_info->y+=annotate_info->height;
2560             if (annotate_info->y > (int) windows->image.height)
2561               annotate_info->y=(int) annotate_info->height;
2562             annotate_info->next=(XAnnotateInfo *) NULL;
2563             x=annotate_info->x;
2564             y=annotate_info->y;
2565             p=annotate_info->text;
2566             break;
2567           }
2568         }
2569         break;
2570       }
2571       case KeyRelease:
2572       {
2573         /*
2574           Respond to a user key release.
2575         */
2576         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2577           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2578         state&=(~ModifierState);
2579         break;
2580       }
2581       case SelectionNotify:
2582       {
2583         Atom
2584           type;
2585
2586         int
2587           format;
2588
2589         unsigned char
2590           *data;
2591
2592         unsigned long
2593           after,
2594           length;
2595
2596         /*
2597           Obtain response from primary selection.
2598         */
2599         if (event.xselection.property == (Atom) None)
2600           break;
2601         status=XGetWindowProperty(display,event.xselection.requestor,
2602           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2603           &type,&format,&length,&after,&data);
2604         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2605             (length == 0))
2606           break;
2607         /*
2608           Annotate Image window with primary selection.
2609         */
2610         for (i=0; i < (ssize_t) length; i++)
2611         {
2612           if ((char) data[i] != '\n')
2613             {
2614               /*
2615                 Draw a single character on the Image window.
2616               */
2617               *p=(char) data[i];
2618               (void) XDrawString(display,windows->image.id,annotate_context,
2619                 x,y,p,1);
2620               x+=XTextWidth(font_info,p,1);
2621               p++;
2622               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2623                 continue;
2624             }
2625           /*
2626             Advance to the next line of text.
2627           */
2628           *p='\0';
2629           annotate_info->width=(unsigned int) XTextWidth(font_info,
2630             annotate_info->text,(int) strlen(annotate_info->text));
2631           if (annotate_info->next != (XAnnotateInfo *) NULL)
2632             {
2633               /*
2634                 Line of text already exists.
2635               */
2636               annotate_info=annotate_info->next;
2637               x=annotate_info->x;
2638               y=annotate_info->y;
2639               p=annotate_info->text;
2640               continue;
2641             }
2642           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2643             sizeof(*annotate_info->next));
2644           if (annotate_info->next == (XAnnotateInfo *) NULL)
2645             return(MagickFalse);
2646           *annotate_info->next=(*annotate_info);
2647           annotate_info->next->previous=annotate_info;
2648           annotate_info=annotate_info->next;
2649           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2650             windows->image.width/MagickMax((ssize_t)
2651             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2652           if (annotate_info->text == (char *) NULL)
2653             return(MagickFalse);
2654           annotate_info->y+=annotate_info->height;
2655           if (annotate_info->y > (int) windows->image.height)
2656             annotate_info->y=(int) annotate_info->height;
2657           annotate_info->next=(XAnnotateInfo *) NULL;
2658           x=annotate_info->x;
2659           y=annotate_info->y;
2660           p=annotate_info->text;
2661         }
2662         (void) XFree((void *) data);
2663         break;
2664       }
2665       default:
2666         break;
2667     }
2668   } while ((state & ExitState) == 0);
2669   (void) XFreeCursor(display,cursor);
2670   /*
2671     Annotation is relative to image configuration.
2672   */
2673   width=(unsigned int) image->columns;
2674   height=(unsigned int) image->rows;
2675   x=0;
2676   y=0;
2677   if (windows->image.crop_geometry != (char *) NULL)
2678     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2679   /*
2680     Initialize annotated image.
2681   */
2682   XSetCursorState(display,windows,MagickTrue);
2683   XCheckRefreshWindows(display,windows);
2684   while (annotate_info != (XAnnotateInfo *) NULL)
2685   {
2686     if (annotate_info->width == 0)
2687       {
2688         /*
2689           No text on this line--  go to the next line of text.
2690         */
2691         previous_info=annotate_info->previous;
2692         annotate_info->text=(char *)
2693           RelinquishMagickMemory(annotate_info->text);
2694         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2695         annotate_info=previous_info;
2696         continue;
2697       }
2698     /*
2699       Determine pixel index for box and pen color.
2700     */
2701     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2702     if (windows->pixel_info->colors != 0)
2703       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2704         if (windows->pixel_info->pixels[i] ==
2705             windows->pixel_info->pen_colors[box_id].pixel)
2706           {
2707             windows->pixel_info->box_index=(unsigned short) i;
2708             break;
2709           }
2710     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2711     if (windows->pixel_info->colors != 0)
2712       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2713         if (windows->pixel_info->pixels[i] ==
2714             windows->pixel_info->pen_colors[pen_id].pixel)
2715           {
2716             windows->pixel_info->pen_index=(unsigned short) i;
2717             break;
2718           }
2719     /*
2720       Define the annotate geometry string.
2721     */
2722     annotate_info->x=(int)
2723       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2724     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2725       windows->image.y)/windows->image.ximage->height;
2726     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2727       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2728       height*annotate_info->height/windows->image.ximage->height,
2729       annotate_info->x+x,annotate_info->y+y);
2730     /*
2731       Annotate image with text.
2732     */
2733     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2734     if (status == 0)
2735       return(MagickFalse);
2736     /*
2737       Free up memory.
2738     */
2739     previous_info=annotate_info->previous;
2740     annotate_info->text=DestroyString(annotate_info->text);
2741     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2742     annotate_info=previous_info;
2743   }
2744   (void) XSetForeground(display,annotate_context,
2745     windows->pixel_info->foreground_color.pixel);
2746   (void) XSetBackground(display,annotate_context,
2747     windows->pixel_info->background_color.pixel);
2748   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2749   XSetCursorState(display,windows,MagickFalse);
2750   (void) XFreeFont(display,font_info);
2751   /*
2752     Update image configuration.
2753   */
2754   XConfigureImageColormap(display,resource_info,windows,image);
2755   (void) XConfigureImage(display,resource_info,windows,image,exception);
2756   return(MagickTrue);
2757 }
2758 \f
2759 /*
2760 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2761 %                                                                             %
2762 %                                                                             %
2763 %                                                                             %
2764 +   X B a c k g r o u n d I m a g e                                           %
2765 %                                                                             %
2766 %                                                                             %
2767 %                                                                             %
2768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2769 %
2770 %  XBackgroundImage() displays the image in the background of a window.
2771 %
2772 %  The format of the XBackgroundImage method is:
2773 %
2774 %      MagickBooleanType XBackgroundImage(Display *display,
2775 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2776 %        ExceptionInfo *exception)
2777 %
2778 %  A description of each parameter follows:
2779 %
2780 %    o display: Specifies a connection to an X server; returned from
2781 %      XOpenDisplay.
2782 %
2783 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2784 %
2785 %    o windows: Specifies a pointer to a XWindows structure.
2786 %
2787 %    o image: the image.
2788 %
2789 %    o exception: return any errors or warnings in this structure.
2790 %
2791 */
2792 static MagickBooleanType XBackgroundImage(Display *display,
2793   XResourceInfo *resource_info,XWindows *windows,Image **image,
2794   ExceptionInfo *exception)
2795 {
2796 #define BackgroundImageTag  "Background/Image"
2797
2798   int
2799     status;
2800
2801   static char
2802     window_id[MaxTextExtent] = "root";
2803
2804   XResourceInfo
2805     background_resources;
2806
2807   /*
2808     Put image in background.
2809   */
2810   status=XDialogWidget(display,windows,"Background",
2811     "Enter window id (id 0x00 selects window with pointer):",window_id);
2812   if (*window_id == '\0')
2813     return(MagickFalse);
2814   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2815     exception);
2816   XInfoWidget(display,windows,BackgroundImageTag);
2817   XSetCursorState(display,windows,MagickTrue);
2818   XCheckRefreshWindows(display,windows);
2819   background_resources=(*resource_info);
2820   background_resources.window_id=window_id;
2821   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2822   status=XDisplayBackgroundImage(display,&background_resources,*image,
2823     exception);
2824   if (status != MagickFalse)
2825     XClientMessage(display,windows->image.id,windows->im_protocols,
2826       windows->im_retain_colors,CurrentTime);
2827   XSetCursorState(display,windows,MagickFalse);
2828   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2829     exception);
2830   return(MagickTrue);
2831 }
2832 \f
2833 /*
2834 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2835 %                                                                             %
2836 %                                                                             %
2837 %                                                                             %
2838 +   X C h o p I m a g e                                                       %
2839 %                                                                             %
2840 %                                                                             %
2841 %                                                                             %
2842 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2843 %
2844 %  XChopImage() chops the X image.
2845 %
2846 %  The format of the XChopImage method is:
2847 %
2848 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2849 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2850 %
2851 %  A description of each parameter follows:
2852 %
2853 %    o display: Specifies a connection to an X server; returned from
2854 %      XOpenDisplay.
2855 %
2856 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2857 %
2858 %    o windows: Specifies a pointer to a XWindows structure.
2859 %
2860 %    o image: the image.
2861 %
2862 %    o exception: return any errors or warnings in this structure.
2863 %
2864 */
2865 static MagickBooleanType XChopImage(Display *display,
2866   XResourceInfo *resource_info,XWindows *windows,Image **image,
2867   ExceptionInfo *exception)
2868 {
2869   static const char
2870     *ChopMenu[] =
2871     {
2872       "Direction",
2873       "Help",
2874       "Dismiss",
2875       (char *) NULL
2876     };
2877
2878   static ModeType
2879     direction = HorizontalChopCommand;
2880
2881   static const ModeType
2882     ChopCommands[] =
2883     {
2884       ChopDirectionCommand,
2885       ChopHelpCommand,
2886       ChopDismissCommand
2887     },
2888     DirectionCommands[] =
2889     {
2890       HorizontalChopCommand,
2891       VerticalChopCommand
2892     };
2893
2894   char
2895     text[MaxTextExtent];
2896
2897   Image
2898     *chop_image;
2899
2900   int
2901     id,
2902     x,
2903     y;
2904
2905   MagickRealType
2906     scale_factor;
2907
2908   RectangleInfo
2909     chop_info;
2910
2911   unsigned int
2912     distance,
2913     height,
2914     width;
2915
2916   size_t
2917     state;
2918
2919   XEvent
2920     event;
2921
2922   XSegment
2923     segment_info;
2924
2925   /*
2926     Map Command widget.
2927   */
2928   (void) CloneString(&windows->command.name,"Chop");
2929   windows->command.data=1;
2930   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2931   (void) XMapRaised(display,windows->command.id);
2932   XClientMessage(display,windows->image.id,windows->im_protocols,
2933     windows->im_update_widget,CurrentTime);
2934   /*
2935     Track pointer until button 1 is pressed.
2936   */
2937   XQueryPosition(display,windows->image.id,&x,&y);
2938   (void) XSelectInput(display,windows->image.id,
2939     windows->image.attributes.event_mask | PointerMotionMask);
2940   state=DefaultState;
2941   do
2942   {
2943     if (windows->info.mapped != MagickFalse)
2944       {
2945         /*
2946           Display pointer position.
2947         */
2948         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2949           x+windows->image.x,y+windows->image.y);
2950         XInfoWidget(display,windows,text);
2951       }
2952     /*
2953       Wait for next event.
2954     */
2955     XScreenEvent(display,windows,&event);
2956     if (event.xany.window == windows->command.id)
2957       {
2958         /*
2959           Select a command from the Command widget.
2960         */
2961         id=XCommandWidget(display,windows,ChopMenu,&event);
2962         if (id < 0)
2963           continue;
2964         switch (ChopCommands[id])
2965         {
2966           case ChopDirectionCommand:
2967           {
2968             char
2969               command[MaxTextExtent];
2970
2971             static const char
2972               *Directions[] =
2973               {
2974                 "horizontal",
2975                 "vertical",
2976                 (char *) NULL,
2977               };
2978
2979             /*
2980               Select a command from the pop-up menu.
2981             */
2982             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2983             if (id >= 0)
2984               direction=DirectionCommands[id];
2985             break;
2986           }
2987           case ChopHelpCommand:
2988           {
2989             XTextViewWidget(display,resource_info,windows,MagickFalse,
2990               "Help Viewer - Image Chop",ImageChopHelp);
2991             break;
2992           }
2993           case ChopDismissCommand:
2994           {
2995             /*
2996               Prematurely exit.
2997             */
2998             state|=EscapeState;
2999             state|=ExitState;
3000             break;
3001           }
3002           default:
3003             break;
3004         }
3005         continue;
3006       }
3007     switch (event.type)
3008     {
3009       case ButtonPress:
3010       {
3011         if (event.xbutton.button != Button1)
3012           break;
3013         if (event.xbutton.window != windows->image.id)
3014           break;
3015         /*
3016           User has committed to start point of chopping line.
3017         */
3018         segment_info.x1=(short int) event.xbutton.x;
3019         segment_info.x2=(short int) event.xbutton.x;
3020         segment_info.y1=(short int) event.xbutton.y;
3021         segment_info.y2=(short int) event.xbutton.y;
3022         state|=ExitState;
3023         break;
3024       }
3025       case ButtonRelease:
3026         break;
3027       case Expose:
3028         break;
3029       case KeyPress:
3030       {
3031         char
3032           command[MaxTextExtent];
3033
3034         KeySym
3035           key_symbol;
3036
3037         if (event.xkey.window != windows->image.id)
3038           break;
3039         /*
3040           Respond to a user key press.
3041         */
3042         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3043           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3044         switch ((int) key_symbol)
3045         {
3046           case XK_Escape:
3047           case XK_F20:
3048           {
3049             /*
3050               Prematurely exit.
3051             */
3052             state|=EscapeState;
3053             state|=ExitState;
3054             break;
3055           }
3056           case XK_F1:
3057           case XK_Help:
3058           {
3059             (void) XSetFunction(display,windows->image.highlight_context,
3060               GXcopy);
3061             XTextViewWidget(display,resource_info,windows,MagickFalse,
3062               "Help Viewer - Image Chop",ImageChopHelp);
3063             (void) XSetFunction(display,windows->image.highlight_context,
3064               GXinvert);
3065             break;
3066           }
3067           default:
3068           {
3069             (void) XBell(display,0);
3070             break;
3071           }
3072         }
3073         break;
3074       }
3075       case MotionNotify:
3076       {
3077         /*
3078           Map and unmap Info widget as text cursor crosses its boundaries.
3079         */
3080         x=event.xmotion.x;
3081         y=event.xmotion.y;
3082         if (windows->info.mapped != MagickFalse)
3083           {
3084             if ((x < (int) (windows->info.x+windows->info.width)) &&
3085                 (y < (int) (windows->info.y+windows->info.height)))
3086               (void) XWithdrawWindow(display,windows->info.id,
3087                 windows->info.screen);
3088           }
3089         else
3090           if ((x > (int) (windows->info.x+windows->info.width)) ||
3091               (y > (int) (windows->info.y+windows->info.height)))
3092             (void) XMapWindow(display,windows->info.id);
3093       }
3094     }
3095   } while ((state & ExitState) == 0);
3096   (void) XSelectInput(display,windows->image.id,
3097     windows->image.attributes.event_mask);
3098   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3099   if ((state & EscapeState) != 0)
3100     return(MagickTrue);
3101   /*
3102     Draw line as pointer moves until the mouse button is released.
3103   */
3104   chop_info.width=0;
3105   chop_info.height=0;
3106   chop_info.x=0;
3107   chop_info.y=0;
3108   distance=0;
3109   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3110   state=DefaultState;
3111   do
3112   {
3113     if (distance > 9)
3114       {
3115         /*
3116           Display info and draw chopping line.
3117         */
3118         if (windows->info.mapped == MagickFalse)
3119           (void) XMapWindow(display,windows->info.id);
3120         (void) FormatLocaleString(text,MaxTextExtent,
3121           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3122           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3123         XInfoWidget(display,windows,text);
3124         XHighlightLine(display,windows->image.id,
3125           windows->image.highlight_context,&segment_info);
3126       }
3127     else
3128       if (windows->info.mapped != MagickFalse)
3129         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3130     /*
3131       Wait for next event.
3132     */
3133     XScreenEvent(display,windows,&event);
3134     if (distance > 9)
3135       XHighlightLine(display,windows->image.id,
3136         windows->image.highlight_context,&segment_info);
3137     switch (event.type)
3138     {
3139       case ButtonPress:
3140       {
3141         segment_info.x2=(short int) event.xmotion.x;
3142         segment_info.y2=(short int) event.xmotion.y;
3143         break;
3144       }
3145       case ButtonRelease:
3146       {
3147         /*
3148           User has committed to chopping line.
3149         */
3150         segment_info.x2=(short int) event.xbutton.x;
3151         segment_info.y2=(short int) event.xbutton.y;
3152         state|=ExitState;
3153         break;
3154       }
3155       case Expose:
3156         break;
3157       case MotionNotify:
3158       {
3159         segment_info.x2=(short int) event.xmotion.x;
3160         segment_info.y2=(short int) event.xmotion.y;
3161       }
3162       default:
3163         break;
3164     }
3165     /*
3166       Check boundary conditions.
3167     */
3168     if (segment_info.x2 < 0)
3169       segment_info.x2=0;
3170     else
3171       if (segment_info.x2 > windows->image.ximage->width)
3172         segment_info.x2=windows->image.ximage->width;
3173     if (segment_info.y2 < 0)
3174       segment_info.y2=0;
3175     else
3176       if (segment_info.y2 > windows->image.ximage->height)
3177         segment_info.y2=windows->image.ximage->height;
3178     distance=(unsigned int)
3179       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3180        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3181     /*
3182       Compute chopping geometry.
3183     */
3184     if (direction == HorizontalChopCommand)
3185       {
3186         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3187         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3188         chop_info.height=0;
3189         chop_info.y=0;
3190         if (segment_info.x1 > (int) segment_info.x2)
3191           {
3192             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3193             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3194           }
3195       }
3196     else
3197       {
3198         chop_info.width=0;
3199         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3200         chop_info.x=0;
3201         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3202         if (segment_info.y1 > segment_info.y2)
3203           {
3204             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3205             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3206           }
3207       }
3208   } while ((state & ExitState) == 0);
3209   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3210   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3211   if (distance <= 9)
3212     return(MagickTrue);
3213   /*
3214     Image chopping is relative to image configuration.
3215   */
3216   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3217     exception);
3218   XSetCursorState(display,windows,MagickTrue);
3219   XCheckRefreshWindows(display,windows);
3220   windows->image.window_changes.width=windows->image.ximage->width-
3221     (unsigned int) chop_info.width;
3222   windows->image.window_changes.height=windows->image.ximage->height-
3223     (unsigned int) chop_info.height;
3224   width=(unsigned int) (*image)->columns;
3225   height=(unsigned int) (*image)->rows;
3226   x=0;
3227   y=0;
3228   if (windows->image.crop_geometry != (char *) NULL)
3229     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3230   scale_factor=(MagickRealType) width/windows->image.ximage->width;
3231   chop_info.x+=x;
3232   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3233   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3234   scale_factor=(MagickRealType) height/windows->image.ximage->height;
3235   chop_info.y+=y;
3236   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3237   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3238   /*
3239     Chop image.
3240   */
3241   chop_image=ChopImage(*image,&chop_info,exception);
3242   XSetCursorState(display,windows,MagickFalse);
3243   if (chop_image == (Image *) NULL)
3244     return(MagickFalse);
3245   *image=DestroyImage(*image);
3246   *image=chop_image;
3247   /*
3248     Update image configuration.
3249   */
3250   XConfigureImageColormap(display,resource_info,windows,*image);
3251   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3252   return(MagickTrue);
3253 }
3254 \f
3255 /*
3256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3257 %                                                                             %
3258 %                                                                             %
3259 %                                                                             %
3260 +   X C o l o r E d i t I m a g e                                             %
3261 %                                                                             %
3262 %                                                                             %
3263 %                                                                             %
3264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3265 %
3266 %  XColorEditImage() allows the user to interactively change the color of one
3267 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3268 %
3269 %  The format of the XColorEditImage method is:
3270 %
3271 %      MagickBooleanType XColorEditImage(Display *display,
3272 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3273 %          ExceptionInfo *exception)
3274 %
3275 %  A description of each parameter follows:
3276 %
3277 %    o display: Specifies a connection to an X server;  returned from
3278 %      XOpenDisplay.
3279 %
3280 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3281 %
3282 %    o windows: Specifies a pointer to a XWindows structure.
3283 %
3284 %    o image: the image; returned from ReadImage.
3285 %
3286 %    o exception: return any errors or warnings in this structure.
3287 %
3288 */
3289 static MagickBooleanType XColorEditImage(Display *display,
3290   XResourceInfo *resource_info,XWindows *windows,Image **image,
3291   ExceptionInfo *exception)
3292 {
3293   static const char
3294     *ColorEditMenu[] =
3295     {
3296       "Method",
3297       "Pixel Color",
3298       "Border Color",
3299       "Fuzz",
3300       "Undo",
3301       "Help",
3302       "Dismiss",
3303       (char *) NULL
3304     };
3305
3306   static const ModeType
3307     ColorEditCommands[] =
3308     {
3309       ColorEditMethodCommand,
3310       ColorEditColorCommand,
3311       ColorEditBorderCommand,
3312       ColorEditFuzzCommand,
3313       ColorEditUndoCommand,
3314       ColorEditHelpCommand,
3315       ColorEditDismissCommand
3316     };
3317
3318   static PaintMethod
3319     method = PointMethod;
3320
3321   static unsigned int
3322     pen_id = 0;
3323
3324   static XColor
3325     border_color = { 0, 0, 0, 0, 0, 0 };
3326
3327   char
3328     command[MaxTextExtent],
3329     text[MaxTextExtent];
3330
3331   Cursor
3332     cursor;
3333
3334   int
3335     entry,
3336     id,
3337     x,
3338     x_offset,
3339     y,
3340     y_offset;
3341
3342   register Quantum
3343     *q;
3344
3345   register ssize_t
3346     i;
3347
3348   unsigned int
3349     height,
3350     width;
3351
3352   size_t
3353     state;
3354
3355   XColor
3356     color;
3357
3358   XEvent
3359     event;
3360
3361   /*
3362     Map Command widget.
3363   */
3364   (void) CloneString(&windows->command.name,"Color Edit");
3365   windows->command.data=4;
3366   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3367   (void) XMapRaised(display,windows->command.id);
3368   XClientMessage(display,windows->image.id,windows->im_protocols,
3369     windows->im_update_widget,CurrentTime);
3370   /*
3371     Make cursor.
3372   */
3373   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3374     resource_info->background_color,resource_info->foreground_color);
3375   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3376   /*
3377     Track pointer until button 1 is pressed.
3378   */
3379   XQueryPosition(display,windows->image.id,&x,&y);
3380   (void) XSelectInput(display,windows->image.id,
3381     windows->image.attributes.event_mask | PointerMotionMask);
3382   state=DefaultState;
3383   do
3384   {
3385     if (windows->info.mapped != MagickFalse)
3386       {
3387         /*
3388           Display pointer position.
3389         */
3390         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3391           x+windows->image.x,y+windows->image.y);
3392         XInfoWidget(display,windows,text);
3393       }
3394     /*
3395       Wait for next event.
3396     */
3397     XScreenEvent(display,windows,&event);
3398     if (event.xany.window == windows->command.id)
3399       {
3400         /*
3401           Select a command from the Command widget.
3402         */
3403         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3404         if (id < 0)
3405           {
3406             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3407             continue;
3408           }
3409         switch (ColorEditCommands[id])
3410         {
3411           case ColorEditMethodCommand:
3412           {
3413             char
3414               **methods;
3415
3416             /*
3417               Select a method from the pop-up menu.
3418             */
3419             methods=(char **) GetCommandOptions(MagickMethodOptions);
3420             if (methods == (char **) NULL)
3421               break;
3422             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3423               (const char **) methods,command);
3424             if (entry >= 0)
3425               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3426                 MagickFalse,methods[entry]);
3427             methods=DestroyStringList(methods);
3428             break;
3429           }
3430           case ColorEditColorCommand:
3431           {
3432             const char
3433               *ColorMenu[MaxNumberPens];
3434
3435             int
3436               pen_number;
3437
3438             /*
3439               Initialize menu selections.
3440             */
3441             for (i=0; i < (int) (MaxNumberPens-2); i++)
3442               ColorMenu[i]=resource_info->pen_colors[i];
3443             ColorMenu[MaxNumberPens-2]="Browser...";
3444             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3445             /*
3446               Select a pen color from the pop-up menu.
3447             */
3448             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3449               (const char **) ColorMenu,command);
3450             if (pen_number < 0)
3451               break;
3452             if (pen_number == (MaxNumberPens-2))
3453               {
3454                 static char
3455                   color_name[MaxTextExtent] = "gray";
3456
3457                 /*
3458                   Select a pen color from a dialog.
3459                 */
3460                 resource_info->pen_colors[pen_number]=color_name;
3461                 XColorBrowserWidget(display,windows,"Select",color_name);
3462                 if (*color_name == '\0')
3463                   break;
3464               }
3465             /*
3466               Set pen color.
3467             */
3468             (void) XParseColor(display,windows->map_info->colormap,
3469               resource_info->pen_colors[pen_number],&color);
3470             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3471               (unsigned int) MaxColors,&color);
3472             windows->pixel_info->pen_colors[pen_number]=color;
3473             pen_id=(unsigned int) pen_number;
3474             break;
3475           }
3476           case ColorEditBorderCommand:
3477           {
3478             const char
3479               *ColorMenu[MaxNumberPens];
3480
3481             int
3482               pen_number;
3483
3484             /*
3485               Initialize menu selections.
3486             */
3487             for (i=0; i < (int) (MaxNumberPens-2); i++)
3488               ColorMenu[i]=resource_info->pen_colors[i];
3489             ColorMenu[MaxNumberPens-2]="Browser...";
3490             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3491             /*
3492               Select a pen color from the pop-up menu.
3493             */
3494             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3495               (const char **) ColorMenu,command);
3496             if (pen_number < 0)
3497               break;
3498             if (pen_number == (MaxNumberPens-2))
3499               {
3500                 static char
3501                   color_name[MaxTextExtent] = "gray";
3502
3503                 /*
3504                   Select a pen color from a dialog.
3505                 */
3506                 resource_info->pen_colors[pen_number]=color_name;
3507                 XColorBrowserWidget(display,windows,"Select",color_name);
3508                 if (*color_name == '\0')
3509                   break;
3510               }
3511             /*
3512               Set border color.
3513             */
3514             (void) XParseColor(display,windows->map_info->colormap,
3515               resource_info->pen_colors[pen_number],&border_color);
3516             break;
3517           }
3518           case ColorEditFuzzCommand:
3519           {
3520             static char
3521               fuzz[MaxTextExtent];
3522
3523             static const char
3524               *FuzzMenu[] =
3525               {
3526                 "0%",
3527                 "2%",
3528                 "5%",
3529                 "10%",
3530                 "15%",
3531                 "Dialog...",
3532                 (char *) NULL,
3533               };
3534
3535             /*
3536               Select a command from the pop-up menu.
3537             */
3538             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3539               command);
3540             if (entry < 0)
3541               break;
3542             if (entry != 5)
3543               {
3544                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
3545                   QuantumRange+1.0);
3546                 break;
3547               }
3548             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3549             (void) XDialogWidget(display,windows,"Ok",
3550               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3551             if (*fuzz == '\0')
3552               break;
3553             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3554             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
3555             break;
3556           }
3557           case ColorEditUndoCommand:
3558           {
3559             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3560               image,exception);
3561             break;
3562           }
3563           case ColorEditHelpCommand:
3564           default:
3565           {
3566             XTextViewWidget(display,resource_info,windows,MagickFalse,
3567               "Help Viewer - Image Annotation",ImageColorEditHelp);
3568             break;
3569           }
3570           case ColorEditDismissCommand:
3571           {
3572             /*
3573               Prematurely exit.
3574             */
3575             state|=EscapeState;
3576             state|=ExitState;
3577             break;
3578           }
3579         }
3580         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3581         continue;
3582       }
3583     switch (event.type)
3584     {
3585       case ButtonPress:
3586       {
3587         if (event.xbutton.button != Button1)
3588           break;
3589         if ((event.xbutton.window != windows->image.id) &&
3590             (event.xbutton.window != windows->magnify.id))
3591           break;
3592         /*
3593           exit loop.
3594         */
3595         x=event.xbutton.x;
3596         y=event.xbutton.y;
3597         (void) XMagickCommand(display,resource_info,windows,
3598           SaveToUndoBufferCommand,image,exception);
3599         state|=UpdateConfigurationState;
3600         break;
3601       }
3602       case ButtonRelease:
3603       {
3604         if (event.xbutton.button != Button1)
3605           break;
3606         if ((event.xbutton.window != windows->image.id) &&
3607             (event.xbutton.window != windows->magnify.id))
3608           break;
3609         /*
3610           Update colormap information.
3611         */
3612         x=event.xbutton.x;
3613         y=event.xbutton.y;
3614         XConfigureImageColormap(display,resource_info,windows,*image);
3615         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3616         XInfoWidget(display,windows,text);
3617         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3618         state&=(~UpdateConfigurationState);
3619         break;
3620       }
3621       case Expose:
3622         break;
3623       case KeyPress:
3624       {
3625         KeySym
3626           key_symbol;
3627
3628         if (event.xkey.window == windows->magnify.id)
3629           {
3630             Window
3631               window;
3632
3633             window=windows->magnify.id;
3634             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3635           }
3636         if (event.xkey.window != windows->image.id)
3637           break;
3638         /*
3639           Respond to a user key press.
3640         */
3641         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3642           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3643         switch ((int) key_symbol)
3644         {
3645           case XK_Escape:
3646           case XK_F20:
3647           {
3648             /*
3649               Prematurely exit.
3650             */
3651             state|=ExitState;
3652             break;
3653           }
3654           case XK_F1:
3655           case XK_Help:
3656           {
3657             XTextViewWidget(display,resource_info,windows,MagickFalse,
3658               "Help Viewer - Image Annotation",ImageColorEditHelp);
3659             break;
3660           }
3661           default:
3662           {
3663             (void) XBell(display,0);
3664             break;
3665           }
3666         }
3667         break;
3668       }
3669       case MotionNotify:
3670       {
3671         /*
3672           Map and unmap Info widget as cursor crosses its boundaries.
3673         */
3674         x=event.xmotion.x;
3675         y=event.xmotion.y;
3676         if (windows->info.mapped != MagickFalse)
3677           {
3678             if ((x < (int) (windows->info.x+windows->info.width)) &&
3679                 (y < (int) (windows->info.y+windows->info.height)))
3680               (void) XWithdrawWindow(display,windows->info.id,
3681                 windows->info.screen);
3682           }
3683         else
3684           if ((x > (int) (windows->info.x+windows->info.width)) ||
3685               (y > (int) (windows->info.y+windows->info.height)))
3686             (void) XMapWindow(display,windows->info.id);
3687         break;
3688       }
3689       default:
3690         break;
3691     }
3692     if (event.xany.window == windows->magnify.id)
3693       {
3694         x=windows->magnify.x-windows->image.x;
3695         y=windows->magnify.y-windows->image.y;
3696       }
3697     x_offset=x;
3698     y_offset=y;
3699     if ((state & UpdateConfigurationState) != 0)
3700       {
3701         CacheView
3702           *image_view;
3703
3704         int
3705           x,
3706           y;
3707
3708         /*
3709           Pixel edit is relative to image configuration.
3710         */
3711         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3712           MagickTrue);
3713         color=windows->pixel_info->pen_colors[pen_id];
3714         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3715         width=(unsigned int) (*image)->columns;
3716         height=(unsigned int) (*image)->rows;
3717         x=0;
3718         y=0;
3719         if (windows->image.crop_geometry != (char *) NULL)
3720           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3721             &width,&height);
3722         x_offset=(int)
3723           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3724         y_offset=(int)
3725           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3726         if ((x_offset < 0) || (y_offset < 0))
3727           continue;
3728         if ((x_offset >= (int) (*image)->columns) ||
3729             (y_offset >= (int) (*image)->rows))
3730           continue;
3731         image_view=AcquireCacheView(*image);
3732         switch (method)
3733         {
3734           case PointMethod:
3735           default:
3736           {
3737             /*
3738               Update color information using point algorithm.
3739             */
3740             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3741               return(MagickFalse);
3742             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3743               (ssize_t) y_offset,1,1,exception);
3744             if (q == (Quantum *) NULL)
3745               break;
3746             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3747             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3748             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3749             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3750             break;
3751           }
3752           case ReplaceMethod:
3753           {
3754             PixelPacket
3755               pixel,
3756               target;
3757
3758             Quantum
3759               virtual_pixel[MaxPixelChannels];
3760
3761             /*
3762               Update color information using replace algorithm.
3763             */
3764             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3765               (ssize_t) y_offset,virtual_pixel,exception);
3766             target.red=virtual_pixel[RedPixelChannel];
3767             target.green=virtual_pixel[GreenPixelChannel];
3768             target.blue=virtual_pixel[BluePixelChannel];
3769             target.alpha=virtual_pixel[AlphaPixelChannel];
3770             if ((*image)->storage_class == DirectClass)
3771               {
3772                 for (y=0; y < (int) (*image)->rows; y++)
3773                 {
3774                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3775                     (*image)->columns,1,exception);
3776                   if (q == (Quantum *) NULL)
3777                     break;
3778                   for (x=0; x < (int) (*image)->columns; x++)
3779                   {
3780                     GetPixelPacketPixel(*image,q,&pixel);
3781                     if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
3782                       {
3783                         SetPixelRed(*image,ScaleShortToQuantum(
3784                           color.red),q);
3785                         SetPixelGreen(*image,ScaleShortToQuantum(
3786                           color.green),q);
3787                         SetPixelBlue(*image,ScaleShortToQuantum(
3788                           color.blue),q);
3789                       }
3790                     q+=GetPixelChannels(*image);
3791                   }
3792                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3793                     break;
3794                 }
3795               }
3796             else
3797               {
3798                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3799                   if (IsFuzzyEquivalencePixelPacket(*image,(*image)->colormap+i,&target))
3800                     {
3801                       (*image)->colormap[i].red=ScaleShortToQuantum(
3802                         color.red);
3803                       (*image)->colormap[i].green=ScaleShortToQuantum(
3804                         color.green);
3805                       (*image)->colormap[i].blue=ScaleShortToQuantum(
3806                         color.blue);
3807                     }
3808                 (void) SyncImage(*image);
3809               }
3810             break;
3811           }
3812           case FloodfillMethod:
3813           case FillToBorderMethod:
3814           {
3815             DrawInfo
3816               *draw_info;
3817
3818             PixelInfo
3819               target;
3820
3821             /*
3822               Update color information using floodfill algorithm.
3823             */
3824             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3825               (ssize_t) y_offset,&target,exception);
3826             if (method == FillToBorderMethod)
3827               {
3828                 target.red=(MagickRealType)
3829                   ScaleShortToQuantum(border_color.red);
3830                 target.green=(MagickRealType)
3831                   ScaleShortToQuantum(border_color.green);
3832                 target.blue=(MagickRealType)
3833                   ScaleShortToQuantum(border_color.blue);
3834               }
3835             draw_info=CloneDrawInfo(resource_info->image_info,
3836               (DrawInfo *) NULL);
3837             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3838               AllCompliance,&draw_info->fill,exception);
3839             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3840               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3841               MagickFalse : MagickTrue,exception);
3842             draw_info=DestroyDrawInfo(draw_info);
3843             break;
3844           }
3845           case ResetMethod:
3846           {
3847             /*
3848               Update color information using reset algorithm.
3849             */
3850             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3851               return(MagickFalse);
3852             for (y=0; y < (int) (*image)->rows; y++)
3853             {
3854               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3855                 (*image)->columns,1,exception);
3856               if (q == (Quantum *) NULL)
3857                 break;
3858               for (x=0; x < (int) (*image)->columns; x++)
3859               {
3860                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3861                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3862                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3863                 q+=GetPixelChannels(*image);
3864               }
3865               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3866                 break;
3867             }
3868             break;
3869           }
3870         }
3871         image_view=DestroyCacheView(image_view);
3872         state&=(~UpdateConfigurationState);
3873       }
3874   } while ((state & ExitState) == 0);
3875   (void) XSelectInput(display,windows->image.id,
3876     windows->image.attributes.event_mask);
3877   XSetCursorState(display,windows,MagickFalse);
3878   (void) XFreeCursor(display,cursor);
3879   return(MagickTrue);
3880 }
3881 \f
3882 /*
3883 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3884 %                                                                             %
3885 %                                                                             %
3886 %                                                                             %
3887 +   X C o m p o s i t e I m a g e                                             %
3888 %                                                                             %
3889 %                                                                             %
3890 %                                                                             %
3891 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3892 %
3893 %  XCompositeImage() requests an image name from the user, reads the image and
3894 %  composites it with the X window image at a location the user chooses with
3895 %  the pointer.
3896 %
3897 %  The format of the XCompositeImage method is:
3898 %
3899 %      MagickBooleanType XCompositeImage(Display *display,
3900 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3901 %        ExceptionInfo *exception)
3902 %
3903 %  A description of each parameter follows:
3904 %
3905 %    o display: Specifies a connection to an X server;  returned from
3906 %      XOpenDisplay.
3907 %
3908 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3909 %
3910 %    o windows: Specifies a pointer to a XWindows structure.
3911 %
3912 %    o image: the image; returned from ReadImage.
3913 %
3914 %    o exception: return any errors or warnings in this structure.
3915 %
3916 */
3917 static MagickBooleanType XCompositeImage(Display *display,
3918   XResourceInfo *resource_info,XWindows *windows,Image *image,
3919   ExceptionInfo *exception)
3920 {
3921   static char
3922     displacement_geometry[MaxTextExtent] = "30x30",
3923     filename[MaxTextExtent] = "\0";
3924
3925   static const char
3926     *CompositeMenu[] =
3927     {
3928       "Operators",
3929       "Dissolve",
3930       "Displace",
3931       "Help",
3932       "Dismiss",
3933       (char *) NULL
3934     };
3935
3936   static CompositeOperator
3937     compose = CopyCompositeOp;
3938
3939   static const ModeType
3940     CompositeCommands[] =
3941     {
3942       CompositeOperatorsCommand,
3943       CompositeDissolveCommand,
3944       CompositeDisplaceCommand,
3945       CompositeHelpCommand,
3946       CompositeDismissCommand
3947     };
3948
3949   char
3950     text[MaxTextExtent];
3951
3952   Cursor
3953     cursor;
3954
3955   Image
3956     *composite_image;
3957
3958   int
3959     entry,
3960     id,
3961     x,
3962     y;
3963
3964   MagickRealType
3965     blend,
3966     scale_factor;
3967
3968   RectangleInfo
3969     highlight_info,
3970     composite_info;
3971
3972   unsigned int
3973     height,
3974     width;
3975
3976   size_t
3977     state;
3978
3979   XEvent
3980     event;
3981
3982   /*
3983     Request image file name from user.
3984   */
3985   XFileBrowserWidget(display,windows,"Composite",filename);
3986   if (*filename == '\0')
3987     return(MagickTrue);
3988   /*
3989     Read image.
3990   */
3991   XSetCursorState(display,windows,MagickTrue);
3992   XCheckRefreshWindows(display,windows);
3993   (void) CopyMagickString(resource_info->image_info->filename,filename,
3994     MaxTextExtent);
3995   composite_image=ReadImage(resource_info->image_info,exception);
3996   CatchException(exception);
3997   XSetCursorState(display,windows,MagickFalse);
3998   if (composite_image == (Image *) NULL)
3999     return(MagickFalse);
4000   /*
4001     Map Command widget.
4002   */
4003   (void) CloneString(&windows->command.name,"Composite");
4004   windows->command.data=1;
4005   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4006   (void) XMapRaised(display,windows->command.id);
4007   XClientMessage(display,windows->image.id,windows->im_protocols,
4008     windows->im_update_widget,CurrentTime);
4009   /*
4010     Track pointer until button 1 is pressed.
4011   */
4012   XQueryPosition(display,windows->image.id,&x,&y);
4013   (void) XSelectInput(display,windows->image.id,
4014     windows->image.attributes.event_mask | PointerMotionMask);
4015   composite_info.x=(ssize_t) windows->image.x+x;
4016   composite_info.y=(ssize_t) windows->image.y+y;
4017   composite_info.width=0;
4018   composite_info.height=0;
4019   cursor=XCreateFontCursor(display,XC_ul_angle);
4020   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4021   blend=0.0;
4022   state=DefaultState;
4023   do
4024   {
4025     if (windows->info.mapped != MagickFalse)
4026       {
4027         /*
4028           Display pointer position.
4029         */
4030         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4031           (long) composite_info.x,(long) composite_info.y);
4032         XInfoWidget(display,windows,text);
4033       }
4034     highlight_info=composite_info;
4035     highlight_info.x=composite_info.x-windows->image.x;
4036     highlight_info.y=composite_info.y-windows->image.y;
4037     XHighlightRectangle(display,windows->image.id,
4038       windows->image.highlight_context,&highlight_info);
4039     /*
4040       Wait for next event.
4041     */
4042     XScreenEvent(display,windows,&event);
4043     XHighlightRectangle(display,windows->image.id,
4044       windows->image.highlight_context,&highlight_info);
4045     if (event.xany.window == windows->command.id)
4046       {
4047         /*
4048           Select a command from the Command widget.
4049         */
4050         id=XCommandWidget(display,windows,CompositeMenu,&event);
4051         if (id < 0)
4052           continue;
4053         switch (CompositeCommands[id])
4054         {
4055           case CompositeOperatorsCommand:
4056           {
4057             char
4058               command[MaxTextExtent],
4059               **operators;
4060
4061             /*
4062               Select a command from the pop-up menu.
4063             */
4064             operators=GetCommandOptions(MagickComposeOptions);
4065             if (operators == (char **) NULL)
4066               break;
4067             entry=XMenuWidget(display,windows,CompositeMenu[id],
4068               (const char **) operators,command);
4069             if (entry >= 0)
4070               compose=(CompositeOperator) ParseCommandOption(
4071                 MagickComposeOptions,MagickFalse,operators[entry]);
4072             operators=DestroyStringList(operators);
4073             break;
4074           }
4075           case CompositeDissolveCommand:
4076           {
4077             static char
4078               factor[MaxTextExtent] = "20.0";
4079
4080             /*
4081               Dissolve the two images a given percent.
4082             */
4083             (void) XSetFunction(display,windows->image.highlight_context,
4084               GXcopy);
4085             (void) XDialogWidget(display,windows,"Dissolve",
4086               "Enter the blend factor (0.0 - 99.9%):",factor);
4087             (void) XSetFunction(display,windows->image.highlight_context,
4088               GXinvert);
4089             if (*factor == '\0')
4090               break;
4091             blend=InterpretLocaleValue(factor,(char **) NULL);
4092             compose=DissolveCompositeOp;
4093             break;
4094           }
4095           case CompositeDisplaceCommand:
4096           {
4097             /*
4098               Get horizontal and vertical scale displacement geometry.
4099             */
4100             (void) XSetFunction(display,windows->image.highlight_context,
4101               GXcopy);
4102             (void) XDialogWidget(display,windows,"Displace",
4103               "Enter the horizontal and vertical scale:",displacement_geometry);
4104             (void) XSetFunction(display,windows->image.highlight_context,
4105               GXinvert);
4106             if (*displacement_geometry == '\0')
4107               break;
4108             compose=DisplaceCompositeOp;
4109             break;
4110           }
4111           case CompositeHelpCommand:
4112           {
4113             (void) XSetFunction(display,windows->image.highlight_context,
4114               GXcopy);
4115             XTextViewWidget(display,resource_info,windows,MagickFalse,
4116               "Help Viewer - Image Composite",ImageCompositeHelp);
4117             (void) XSetFunction(display,windows->image.highlight_context,
4118               GXinvert);
4119             break;
4120           }
4121           case CompositeDismissCommand:
4122           {
4123             /*
4124               Prematurely exit.
4125             */
4126             state|=EscapeState;
4127             state|=ExitState;
4128             break;
4129           }
4130           default:
4131             break;
4132         }
4133         continue;
4134       }
4135     switch (event.type)
4136     {
4137       case ButtonPress:
4138       {
4139         if (image->debug != MagickFalse)
4140           (void) LogMagickEvent(X11Event,GetMagickModule(),
4141             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4142             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4143         if (event.xbutton.button != Button1)
4144           break;
4145         if (event.xbutton.window != windows->image.id)
4146           break;
4147         /*
4148           Change cursor.
4149         */
4150         composite_info.width=composite_image->columns;
4151         composite_info.height=composite_image->rows;
4152         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4153         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4154         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4155         break;
4156       }
4157       case ButtonRelease:
4158       {
4159         if (image->debug != MagickFalse)
4160           (void) LogMagickEvent(X11Event,GetMagickModule(),
4161             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4162             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4163         if (event.xbutton.button != Button1)
4164           break;
4165         if (event.xbutton.window != windows->image.id)
4166           break;
4167         if ((composite_info.width != 0) && (composite_info.height != 0))
4168           {
4169             /*
4170               User has selected the location of the composite image.
4171             */
4172             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4173             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4174             state|=ExitState;
4175           }
4176         break;
4177       }
4178       case Expose:
4179         break;
4180       case KeyPress:
4181       {
4182         char
4183           command[MaxTextExtent];
4184
4185         KeySym
4186           key_symbol;
4187
4188         int
4189           length;
4190
4191         if (event.xkey.window != windows->image.id)
4192           break;
4193         /*
4194           Respond to a user key press.
4195         */
4196         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4197           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4198         *(command+length)='\0';
4199         if (image->debug != MagickFalse)
4200           (void) LogMagickEvent(X11Event,GetMagickModule(),
4201             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4202         switch ((int) key_symbol)
4203         {
4204           case XK_Escape:
4205           case XK_F20:
4206           {
4207             /*
4208               Prematurely exit.
4209             */
4210             composite_image=DestroyImage(composite_image);
4211             state|=EscapeState;
4212             state|=ExitState;
4213             break;
4214           }
4215           case XK_F1:
4216           case XK_Help:
4217           {
4218             (void) XSetFunction(display,windows->image.highlight_context,
4219               GXcopy);
4220             XTextViewWidget(display,resource_info,windows,MagickFalse,
4221               "Help Viewer - Image Composite",ImageCompositeHelp);
4222             (void) XSetFunction(display,windows->image.highlight_context,
4223               GXinvert);
4224             break;
4225           }
4226           default:
4227           {
4228             (void) XBell(display,0);
4229             break;
4230           }
4231         }
4232         break;
4233       }
4234       case MotionNotify:
4235       {
4236         /*
4237           Map and unmap Info widget as text cursor crosses its boundaries.
4238         */
4239         x=event.xmotion.x;
4240         y=event.xmotion.y;
4241         if (windows->info.mapped != MagickFalse)
4242           {
4243             if ((x < (int) (windows->info.x+windows->info.width)) &&
4244                 (y < (int) (windows->info.y+windows->info.height)))
4245               (void) XWithdrawWindow(display,windows->info.id,
4246                 windows->info.screen);
4247           }
4248         else
4249           if ((x > (int) (windows->info.x+windows->info.width)) ||
4250               (y > (int) (windows->info.y+windows->info.height)))
4251             (void) XMapWindow(display,windows->info.id);
4252         composite_info.x=(ssize_t) windows->image.x+x;
4253         composite_info.y=(ssize_t) windows->image.y+y;
4254         break;
4255       }
4256       default:
4257       {
4258         if (image->debug != MagickFalse)
4259           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4260             event.type);
4261         break;
4262       }
4263     }
4264   } while ((state & ExitState) == 0);
4265   (void) XSelectInput(display,windows->image.id,
4266     windows->image.attributes.event_mask);
4267   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4268   XSetCursorState(display,windows,MagickFalse);
4269   (void) XFreeCursor(display,cursor);
4270   if ((state & EscapeState) != 0)
4271     return(MagickTrue);
4272   /*
4273     Image compositing is relative to image configuration.
4274   */
4275   XSetCursorState(display,windows,MagickTrue);
4276   XCheckRefreshWindows(display,windows);
4277   width=(unsigned int) image->columns;
4278   height=(unsigned int) image->rows;
4279   x=0;
4280   y=0;
4281   if (windows->image.crop_geometry != (char *) NULL)
4282     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4283   scale_factor=(MagickRealType) width/windows->image.ximage->width;
4284   composite_info.x+=x;
4285   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4286   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4287   scale_factor=(MagickRealType) height/windows->image.ximage->height;
4288   composite_info.y+=y;
4289   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4290   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4291   if ((composite_info.width != composite_image->columns) ||
4292       (composite_info.height != composite_image->rows))
4293     {
4294       Image
4295         *resize_image;
4296
4297       /*
4298         Scale composite image.
4299       */
4300       resize_image=ResizeImage(composite_image,composite_info.width,
4301         composite_info.height,composite_image->filter,composite_image->blur,
4302         exception);
4303       composite_image=DestroyImage(composite_image);
4304       if (resize_image == (Image *) NULL)
4305         {
4306           XSetCursorState(display,windows,MagickFalse);
4307           return(MagickFalse);
4308         }
4309       composite_image=resize_image;
4310     }
4311   if (compose == DisplaceCompositeOp)
4312     (void) SetImageArtifact(composite_image,"compose:args",
4313       displacement_geometry);
4314   if (blend != 0.0)
4315     {
4316       CacheView
4317         *image_view;
4318
4319       int
4320         y;
4321
4322       Quantum
4323         opacity;
4324
4325       register int
4326         x;
4327
4328       register Quantum
4329         *q;
4330
4331       /*
4332         Create mattes for blending.
4333       */
4334       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4335       opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4336         ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4337       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4338         return(MagickFalse);
4339       image->matte=MagickTrue;
4340       image_view=AcquireCacheView(image);
4341       for (y=0; y < (int) image->rows; y++)
4342       {
4343         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4344           exception);
4345         if (q == (Quantum *) NULL)
4346           break;
4347         for (x=0; x < (int) image->columns; x++)
4348         {
4349           SetPixelAlpha(image,opacity,q);
4350           q+=GetPixelChannels(image);
4351         }
4352         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4353           break;
4354       }
4355       image_view=DestroyCacheView(image_view);
4356     }
4357   /*
4358     Composite image with X Image window.
4359   */
4360   (void) CompositeImage(image,compose,composite_image,composite_info.x,
4361     composite_info.y);
4362   composite_image=DestroyImage(composite_image);
4363   XSetCursorState(display,windows,MagickFalse);
4364   /*
4365     Update image configuration.
4366   */
4367   XConfigureImageColormap(display,resource_info,windows,image);
4368   (void) XConfigureImage(display,resource_info,windows,image,exception);
4369   return(MagickTrue);
4370 }
4371 \f
4372 /*
4373 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4374 %                                                                             %
4375 %                                                                             %
4376 %                                                                             %
4377 +   X C o n f i g u r e I m a g e                                             %
4378 %                                                                             %
4379 %                                                                             %
4380 %                                                                             %
4381 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4382 %
4383 %  XConfigureImage() creates a new X image.  It also notifies the window
4384 %  manager of the new image size and configures the transient widows.
4385 %
4386 %  The format of the XConfigureImage method is:
4387 %
4388 %      MagickBooleanType XConfigureImage(Display *display,
4389 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4390 %        ExceptionInfo *exception)
4391 %
4392 %  A description of each parameter follows:
4393 %
4394 %    o display: Specifies a connection to an X server; returned from
4395 %      XOpenDisplay.
4396 %
4397 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4398 %
4399 %    o windows: Specifies a pointer to a XWindows structure.
4400 %
4401 %    o image: the image.
4402 %
4403 %    o exception: return any errors or warnings in this structure.
4404 %
4405 %    o exception: return any errors or warnings in this structure.
4406 %
4407 */
4408 static MagickBooleanType XConfigureImage(Display *display,
4409   XResourceInfo *resource_info,XWindows *windows,Image *image,
4410   ExceptionInfo *exception)
4411 {
4412   char
4413     geometry[MaxTextExtent];
4414
4415   MagickStatusType
4416     status;
4417
4418   size_t
4419     mask,
4420     height,
4421     width;
4422
4423   ssize_t
4424     x,
4425     y;
4426
4427   XSizeHints
4428     *size_hints;
4429
4430   XWindowChanges
4431     window_changes;
4432
4433   /*
4434     Dismiss if window dimensions are zero.
4435   */
4436   width=(unsigned int) windows->image.window_changes.width;
4437   height=(unsigned int) windows->image.window_changes.height;
4438   if (image->debug != MagickFalse)
4439     (void) LogMagickEvent(X11Event,GetMagickModule(),
4440       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4441       windows->image.ximage->height,(double) width,(double) height);
4442   if ((width*height) == 0)
4443     return(MagickTrue);
4444   x=0;
4445   y=0;
4446   /*
4447     Resize image to fit Image window dimensions.
4448   */
4449   XSetCursorState(display,windows,MagickTrue);
4450   (void) XFlush(display);
4451   if (((int) width != windows->image.ximage->width) ||
4452       ((int) height != windows->image.ximage->height))
4453     image->taint=MagickTrue;
4454   windows->magnify.x=(int)
4455     width*windows->magnify.x/windows->image.ximage->width;
4456   windows->magnify.y=(int)
4457     height*windows->magnify.y/windows->image.ximage->height;
4458   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4459   windows->image.y=(int)
4460     (height*windows->image.y/windows->image.ximage->height);
4461   status=XMakeImage(display,resource_info,&windows->image,image,
4462     (unsigned int) width,(unsigned int) height,exception);
4463   if (status == MagickFalse)
4464     XNoticeWidget(display,windows,"Unable to configure X image:",
4465       windows->image.name);
4466   /*
4467     Notify window manager of the new configuration.
4468   */
4469   if (resource_info->image_geometry != (char *) NULL)
4470     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4471       resource_info->image_geometry);
4472   else
4473     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4474       XDisplayWidth(display,windows->image.screen),
4475       XDisplayHeight(display,windows->image.screen));
4476   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4477   window_changes.width=(int) width;
4478   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4479     window_changes.width=XDisplayWidth(display,windows->image.screen);
4480   window_changes.height=(int) height;
4481   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4482     window_changes.height=XDisplayHeight(display,windows->image.screen);
4483   mask=(size_t) (CWWidth | CWHeight);
4484   if (resource_info->backdrop)
4485     {
4486       mask|=CWX | CWY;
4487       window_changes.x=(int)
4488         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4489       window_changes.y=(int)
4490         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4491     }
4492   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4493     (unsigned int) mask,&window_changes);
4494   (void) XClearWindow(display,windows->image.id);
4495   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4496   /*
4497     Update Magnify window configuration.
4498   */
4499   if (windows->magnify.mapped != MagickFalse)
4500     XMakeMagnifyImage(display,windows);
4501   windows->pan.crop_geometry=windows->image.crop_geometry;
4502   XBestIconSize(display,&windows->pan,image);
4503   while (((windows->pan.width << 1) < MaxIconSize) &&
4504          ((windows->pan.height << 1) < MaxIconSize))
4505   {
4506     windows->pan.width<<=1;
4507     windows->pan.height<<=1;
4508   }
4509   if (windows->pan.geometry != (char *) NULL)
4510     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4511       &windows->pan.width,&windows->pan.height);
4512   window_changes.width=(int) windows->pan.width;
4513   window_changes.height=(int) windows->pan.height;
4514   size_hints=XAllocSizeHints();
4515   if (size_hints != (XSizeHints *) NULL)
4516     {
4517       /*
4518         Set new size hints.
4519       */
4520       size_hints->flags=PSize | PMinSize | PMaxSize;
4521       size_hints->width=window_changes.width;
4522       size_hints->height=window_changes.height;
4523       size_hints->min_width=size_hints->width;
4524       size_hints->min_height=size_hints->height;
4525       size_hints->max_width=size_hints->width;
4526       size_hints->max_height=size_hints->height;
4527       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4528       (void) XFree((void *) size_hints);
4529     }
4530   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4531     (unsigned int) (CWWidth | CWHeight),&window_changes);
4532   /*
4533     Update icon window configuration.
4534   */
4535   windows->icon.crop_geometry=windows->image.crop_geometry;
4536   XBestIconSize(display,&windows->icon,image);
4537   window_changes.width=(int) windows->icon.width;
4538   window_changes.height=(int) windows->icon.height;
4539   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4540     (unsigned int) (CWWidth | CWHeight),&window_changes);
4541   XSetCursorState(display,windows,MagickFalse);
4542   return(status != 0 ? MagickTrue : MagickFalse);
4543 }
4544 \f
4545 /*
4546 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4547 %                                                                             %
4548 %                                                                             %
4549 %                                                                             %
4550 +   X C r o p I m a g e                                                       %
4551 %                                                                             %
4552 %                                                                             %
4553 %                                                                             %
4554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4555 %
4556 %  XCropImage() allows the user to select a region of the image and crop, copy,
4557 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4558 %  the image with XPasteImage.
4559 %
4560 %  The format of the XCropImage method is:
4561 %
4562 %      MagickBooleanType XCropImage(Display *display,
4563 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4564 %        const ClipboardMode mode,ExceptionInfo *exception)
4565 %
4566 %  A description of each parameter follows:
4567 %
4568 %    o display: Specifies a connection to an X server; returned from
4569 %      XOpenDisplay.
4570 %
4571 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4572 %
4573 %    o windows: Specifies a pointer to a XWindows structure.
4574 %
4575 %    o image: the image; returned from ReadImage.
4576 %
4577 %    o mode: This unsigned value specified whether the image should be
4578 %      cropped, copied, or cut.
4579 %
4580 %    o exception: return any errors or warnings in this structure.
4581 %
4582 */
4583 static MagickBooleanType XCropImage(Display *display,
4584   XResourceInfo *resource_info,XWindows *windows,Image *image,
4585   const ClipboardMode mode,ExceptionInfo *exception)
4586 {
4587   static const char
4588     *CropModeMenu[] =
4589     {
4590       "Help",
4591       "Dismiss",
4592       (char *) NULL
4593     },
4594     *RectifyModeMenu[] =
4595     {
4596       "Crop",
4597       "Help",
4598       "Dismiss",
4599       (char *) NULL
4600     };
4601
4602   static const ModeType
4603     CropCommands[] =
4604     {
4605       CropHelpCommand,
4606       CropDismissCommand
4607     },
4608     RectifyCommands[] =
4609     {
4610       RectifyCopyCommand,
4611       RectifyHelpCommand,
4612       RectifyDismissCommand
4613     };
4614
4615   CacheView
4616     *image_view;
4617
4618   char
4619     command[MaxTextExtent],
4620     text[MaxTextExtent];
4621
4622   Cursor
4623     cursor;
4624
4625   int
4626     id,
4627     x,
4628     y;
4629
4630   KeySym
4631     key_symbol;
4632
4633   Image
4634     *crop_image;
4635
4636   MagickRealType
4637     scale_factor;
4638
4639   RectangleInfo
4640     crop_info,
4641     highlight_info;
4642
4643   register Quantum
4644     *q;
4645
4646   unsigned int
4647     height,
4648     width;
4649
4650   size_t
4651     state;
4652
4653   XEvent
4654     event;
4655
4656   /*
4657     Map Command widget.
4658   */
4659   switch (mode)
4660   {
4661     case CopyMode:
4662     {
4663       (void) CloneString(&windows->command.name,"Copy");
4664       break;
4665     }
4666     case CropMode:
4667     {
4668       (void) CloneString(&windows->command.name,"Crop");
4669       break;
4670     }
4671     case CutMode:
4672     {
4673       (void) CloneString(&windows->command.name,"Cut");
4674       break;
4675     }
4676   }
4677   RectifyModeMenu[0]=windows->command.name;
4678   windows->command.data=0;
4679   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4680   (void) XMapRaised(display,windows->command.id);
4681   XClientMessage(display,windows->image.id,windows->im_protocols,
4682     windows->im_update_widget,CurrentTime);
4683   /*
4684     Track pointer until button 1 is pressed.
4685   */
4686   XQueryPosition(display,windows->image.id,&x,&y);
4687   (void) XSelectInput(display,windows->image.id,
4688     windows->image.attributes.event_mask | PointerMotionMask);
4689   crop_info.x=(ssize_t) windows->image.x+x;
4690   crop_info.y=(ssize_t) windows->image.y+y;
4691   crop_info.width=0;
4692   crop_info.height=0;
4693   cursor=XCreateFontCursor(display,XC_fleur);
4694   state=DefaultState;
4695   do
4696   {
4697     if (windows->info.mapped != MagickFalse)
4698       {
4699         /*
4700           Display pointer position.
4701         */
4702         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4703           (long) crop_info.x,(long) crop_info.y);
4704         XInfoWidget(display,windows,text);
4705       }
4706     /*
4707       Wait for next event.
4708     */
4709     XScreenEvent(display,windows,&event);
4710     if (event.xany.window == windows->command.id)
4711       {
4712         /*
4713           Select a command from the Command widget.
4714         */
4715         id=XCommandWidget(display,windows,CropModeMenu,&event);
4716         if (id < 0)
4717           continue;
4718         switch (CropCommands[id])
4719         {
4720           case CropHelpCommand:
4721           {
4722             switch (mode)
4723             {
4724               case CopyMode:
4725               {
4726                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4727                   "Help Viewer - Image Copy",ImageCopyHelp);
4728                 break;
4729               }
4730               case CropMode:
4731               {
4732                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4733                   "Help Viewer - Image Crop",ImageCropHelp);
4734                 break;
4735               }
4736               case CutMode:
4737               {
4738                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4739                   "Help Viewer - Image Cut",ImageCutHelp);
4740                 break;
4741               }
4742             }
4743             break;
4744           }
4745           case CropDismissCommand:
4746           {
4747             /*
4748               Prematurely exit.
4749             */
4750             state|=EscapeState;
4751             state|=ExitState;
4752             break;
4753           }
4754           default:
4755             break;
4756         }
4757         continue;
4758       }
4759     switch (event.type)
4760     {
4761       case ButtonPress:
4762       {
4763         if (event.xbutton.button != Button1)
4764           break;
4765         if (event.xbutton.window != windows->image.id)
4766           break;
4767         /*
4768           Note first corner of cropping rectangle-- exit loop.
4769         */
4770         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4771         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4772         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4773         state|=ExitState;
4774         break;
4775       }
4776       case ButtonRelease:
4777         break;
4778       case Expose:
4779         break;
4780       case KeyPress:
4781       {
4782         if (event.xkey.window != windows->image.id)
4783           break;
4784         /*
4785           Respond to a user key press.
4786         */
4787         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4788           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4789         switch ((int) key_symbol)
4790         {
4791           case XK_Escape:
4792           case XK_F20:
4793           {
4794             /*
4795               Prematurely exit.
4796             */
4797             state|=EscapeState;
4798             state|=ExitState;
4799             break;
4800           }
4801           case XK_F1:
4802           case XK_Help:
4803           {
4804             switch (mode)
4805             {
4806               case CopyMode:
4807               {
4808                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4809                   "Help Viewer - Image Copy",ImageCopyHelp);
4810                 break;
4811               }
4812               case CropMode:
4813               {
4814                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4815                   "Help Viewer - Image Crop",ImageCropHelp);
4816                 break;
4817               }
4818               case CutMode:
4819               {
4820                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4821                   "Help Viewer - Image Cut",ImageCutHelp);
4822                 break;
4823               }
4824             }
4825             break;
4826           }
4827           default:
4828           {
4829             (void) XBell(display,0);
4830             break;
4831           }
4832         }
4833         break;
4834       }
4835       case MotionNotify:
4836       {
4837         if (event.xmotion.window != windows->image.id)
4838           break;
4839         /*
4840           Map and unmap Info widget as text cursor crosses its boundaries.
4841         */
4842         x=event.xmotion.x;
4843         y=event.xmotion.y;
4844         if (windows->info.mapped != MagickFalse)
4845           {
4846             if ((x < (int) (windows->info.x+windows->info.width)) &&
4847                 (y < (int) (windows->info.y+windows->info.height)))
4848               (void) XWithdrawWindow(display,windows->info.id,
4849                 windows->info.screen);
4850           }
4851         else
4852           if ((x > (int) (windows->info.x+windows->info.width)) ||
4853               (y > (int) (windows->info.y+windows->info.height)))
4854             (void) XMapWindow(display,windows->info.id);
4855         crop_info.x=(ssize_t) windows->image.x+x;
4856         crop_info.y=(ssize_t) windows->image.y+y;
4857         break;
4858       }
4859       default:
4860         break;
4861     }
4862   } while ((state & ExitState) == 0);
4863   (void) XSelectInput(display,windows->image.id,
4864     windows->image.attributes.event_mask);
4865   if ((state & EscapeState) != 0)
4866     {
4867       /*
4868         User want to exit without cropping.
4869       */
4870       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4871       (void) XFreeCursor(display,cursor);
4872       return(MagickTrue);
4873     }
4874   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4875   do
4876   {
4877     /*
4878       Size rectangle as pointer moves until the mouse button is released.
4879     */
4880     x=(int) crop_info.x;
4881     y=(int) crop_info.y;
4882     crop_info.width=0;
4883     crop_info.height=0;
4884     state=DefaultState;
4885     do
4886     {
4887       highlight_info=crop_info;
4888       highlight_info.x=crop_info.x-windows->image.x;
4889       highlight_info.y=crop_info.y-windows->image.y;
4890       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4891         {
4892           /*
4893             Display info and draw cropping rectangle.
4894           */
4895           if (windows->info.mapped == MagickFalse)
4896             (void) XMapWindow(display,windows->info.id);
4897           (void) FormatLocaleString(text,MaxTextExtent,
4898             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4899             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4900           XInfoWidget(display,windows,text);
4901           XHighlightRectangle(display,windows->image.id,
4902             windows->image.highlight_context,&highlight_info);
4903         }
4904       else
4905         if (windows->info.mapped != MagickFalse)
4906           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4907       /*
4908         Wait for next event.
4909       */
4910       XScreenEvent(display,windows,&event);
4911       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4912         XHighlightRectangle(display,windows->image.id,
4913           windows->image.highlight_context,&highlight_info);
4914       switch (event.type)
4915       {
4916         case ButtonPress:
4917         {
4918           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4919           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4920           break;
4921         }
4922         case ButtonRelease:
4923         {
4924           /*
4925             User has committed to cropping rectangle.
4926           */
4927           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4928           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4929           XSetCursorState(display,windows,MagickFalse);
4930           state|=ExitState;
4931           windows->command.data=0;
4932           (void) XCommandWidget(display,windows,RectifyModeMenu,
4933             (XEvent *) NULL);
4934           break;
4935         }
4936         case Expose:
4937           break;
4938         case MotionNotify:
4939         {
4940           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4941           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4942         }
4943         default:
4944           break;
4945       }
4946       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4947           ((state & ExitState) != 0))
4948         {
4949           /*
4950             Check boundary conditions.
4951           */
4952           if (crop_info.x < 0)
4953             crop_info.x=0;
4954           else
4955             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4956               crop_info.x=(ssize_t) windows->image.ximage->width;
4957           if ((int) crop_info.x < x)
4958             crop_info.width=(unsigned int) (x-crop_info.x);
4959           else
4960             {
4961               crop_info.width=(unsigned int) (crop_info.x-x);
4962               crop_info.x=(ssize_t) x;
4963             }
4964           if (crop_info.y < 0)
4965             crop_info.y=0;
4966           else
4967             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4968               crop_info.y=(ssize_t) windows->image.ximage->height;
4969           if ((int) crop_info.y < y)
4970             crop_info.height=(unsigned int) (y-crop_info.y);
4971           else
4972             {
4973               crop_info.height=(unsigned int) (crop_info.y-y);
4974               crop_info.y=(ssize_t) y;
4975             }
4976         }
4977     } while ((state & ExitState) == 0);
4978     /*
4979       Wait for user to grab a corner of the rectangle or press return.
4980     */
4981     state=DefaultState;
4982     (void) XMapWindow(display,windows->info.id);
4983     do
4984     {
4985       if (windows->info.mapped != MagickFalse)
4986         {
4987           /*
4988             Display pointer position.
4989           */
4990           (void) FormatLocaleString(text,MaxTextExtent,
4991             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4992             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4993           XInfoWidget(display,windows,text);
4994         }
4995       highlight_info=crop_info;
4996       highlight_info.x=crop_info.x-windows->image.x;
4997       highlight_info.y=crop_info.y-windows->image.y;
4998       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4999         {
5000           state|=EscapeState;
5001           state|=ExitState;
5002           break;
5003         }
5004       XHighlightRectangle(display,windows->image.id,
5005         windows->image.highlight_context,&highlight_info);
5006       XScreenEvent(display,windows,&event);
5007       if (event.xany.window == windows->command.id)
5008         {
5009           /*
5010             Select a command from the Command widget.
5011           */
5012           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5013           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5014           (void) XSetFunction(display,windows->image.highlight_context,
5015             GXinvert);
5016           XHighlightRectangle(display,windows->image.id,
5017             windows->image.highlight_context,&highlight_info);
5018           if (id >= 0)
5019             switch (RectifyCommands[id])
5020             {
5021               case RectifyCopyCommand:
5022               {
5023                 state|=ExitState;
5024                 break;
5025               }
5026               case RectifyHelpCommand:
5027               {
5028                 (void) XSetFunction(display,windows->image.highlight_context,
5029                   GXcopy);
5030                 switch (mode)
5031                 {
5032                   case CopyMode:
5033                   {
5034                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5035                       "Help Viewer - Image Copy",ImageCopyHelp);
5036                     break;
5037                   }
5038                   case CropMode:
5039                   {
5040                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5041                       "Help Viewer - Image Crop",ImageCropHelp);
5042                     break;
5043                   }
5044                   case CutMode:
5045                   {
5046                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5047                       "Help Viewer - Image Cut",ImageCutHelp);
5048                     break;
5049                   }
5050                 }
5051                 (void) XSetFunction(display,windows->image.highlight_context,
5052                   GXinvert);
5053                 break;
5054               }
5055               case RectifyDismissCommand:
5056               {
5057                 /*
5058                   Prematurely exit.
5059                 */
5060                 state|=EscapeState;
5061                 state|=ExitState;
5062                 break;
5063               }
5064               default:
5065                 break;
5066             }
5067           continue;
5068         }
5069       XHighlightRectangle(display,windows->image.id,
5070         windows->image.highlight_context,&highlight_info);
5071       switch (event.type)
5072       {
5073         case ButtonPress:
5074         {
5075           if (event.xbutton.button != Button1)
5076             break;
5077           if (event.xbutton.window != windows->image.id)
5078             break;
5079           x=windows->image.x+event.xbutton.x;
5080           y=windows->image.y+event.xbutton.y;
5081           if ((x < (int) (crop_info.x+RoiDelta)) &&
5082               (x > (int) (crop_info.x-RoiDelta)) &&
5083               (y < (int) (crop_info.y+RoiDelta)) &&
5084               (y > (int) (crop_info.y-RoiDelta)))
5085             {
5086               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5087               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5088               state|=UpdateConfigurationState;
5089               break;
5090             }
5091           if ((x < (int) (crop_info.x+RoiDelta)) &&
5092               (x > (int) (crop_info.x-RoiDelta)) &&
5093               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5094               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5095             {
5096               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5097               state|=UpdateConfigurationState;
5098               break;
5099             }
5100           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5101               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5102               (y < (int) (crop_info.y+RoiDelta)) &&
5103               (y > (int) (crop_info.y-RoiDelta)))
5104             {
5105               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5106               state|=UpdateConfigurationState;
5107               break;
5108             }
5109           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5110               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5111               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5112               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5113             {
5114               state|=UpdateConfigurationState;
5115               break;
5116             }
5117         }
5118         case ButtonRelease:
5119         {
5120           if (event.xbutton.window == windows->pan.id)
5121             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5122                 (highlight_info.y != crop_info.y-windows->image.y))
5123               XHighlightRectangle(display,windows->image.id,
5124                 windows->image.highlight_context,&highlight_info);
5125           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5126             event.xbutton.time);
5127           break;
5128         }
5129         case Expose:
5130         {
5131           if (event.xexpose.window == windows->image.id)
5132             if (event.xexpose.count == 0)
5133               {
5134                 event.xexpose.x=(int) highlight_info.x;
5135                 event.xexpose.y=(int) highlight_info.y;
5136                 event.xexpose.width=(int) highlight_info.width;
5137                 event.xexpose.height=(int) highlight_info.height;
5138                 XRefreshWindow(display,&windows->image,&event);
5139               }
5140           if (event.xexpose.window == windows->info.id)
5141             if (event.xexpose.count == 0)
5142               XInfoWidget(display,windows,text);
5143           break;
5144         }
5145         case KeyPress:
5146         {
5147           if (event.xkey.window != windows->image.id)
5148             break;
5149           /*
5150             Respond to a user key press.
5151           */
5152           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5153             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5154           switch ((int) key_symbol)
5155           {
5156             case XK_Escape:
5157             case XK_F20:
5158               state|=EscapeState;
5159             case XK_Return:
5160             {
5161               state|=ExitState;
5162               break;
5163             }
5164             case XK_Home:
5165             case XK_KP_Home:
5166             {
5167               crop_info.x=(ssize_t) (windows->image.width/2L-
5168                 crop_info.width/2L);
5169               crop_info.y=(ssize_t) (windows->image.height/2L-
5170                 crop_info.height/2L);
5171               break;
5172             }
5173             case XK_Left:
5174             case XK_KP_Left:
5175             {
5176               crop_info.x--;
5177               break;
5178             }
5179             case XK_Up:
5180             case XK_KP_Up:
5181             case XK_Next:
5182             {
5183               crop_info.y--;
5184               break;
5185             }
5186             case XK_Right:
5187             case XK_KP_Right:
5188             {
5189               crop_info.x++;
5190               break;
5191             }
5192             case XK_Prior:
5193             case XK_Down:
5194             case XK_KP_Down:
5195             {
5196               crop_info.y++;
5197               break;
5198             }
5199             case XK_F1:
5200             case XK_Help:
5201             {
5202               (void) XSetFunction(display,windows->image.highlight_context,
5203                 GXcopy);
5204               switch (mode)
5205               {
5206                 case CopyMode:
5207                 {
5208                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5209                     "Help Viewer - Image Copy",ImageCopyHelp);
5210                   break;
5211                 }
5212                 case CropMode:
5213                 {
5214                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5215                     "Help Viewer - Image Cropg",ImageCropHelp);
5216                   break;
5217                 }
5218                 case CutMode:
5219                 {
5220                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5221                     "Help Viewer - Image Cutg",ImageCutHelp);
5222                   break;
5223                 }
5224               }
5225               (void) XSetFunction(display,windows->image.highlight_context,
5226                 GXinvert);
5227               break;
5228             }
5229             default:
5230             {
5231               (void) XBell(display,0);
5232               break;
5233             }
5234           }
5235           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5236             event.xkey.time);
5237           break;
5238         }
5239         case KeyRelease:
5240           break;
5241         case MotionNotify:
5242         {
5243           if (event.xmotion.window != windows->image.id)
5244             break;
5245           /*
5246             Map and unmap Info widget as text cursor crosses its boundaries.
5247           */
5248           x=event.xmotion.x;
5249           y=event.xmotion.y;
5250           if (windows->info.mapped != MagickFalse)
5251             {
5252               if ((x < (int) (windows->info.x+windows->info.width)) &&
5253                   (y < (int) (windows->info.y+windows->info.height)))
5254                 (void) XWithdrawWindow(display,windows->info.id,
5255                   windows->info.screen);
5256             }
5257           else
5258             if ((x > (int) (windows->info.x+windows->info.width)) ||
5259                 (y > (int) (windows->info.y+windows->info.height)))
5260               (void) XMapWindow(display,windows->info.id);
5261           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5262           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5263           break;
5264         }
5265         case SelectionRequest:
5266         {
5267           XSelectionEvent
5268             notify;
5269
5270           XSelectionRequestEvent
5271             *request;
5272
5273           /*
5274             Set primary selection.
5275           */
5276           (void) FormatLocaleString(text,MaxTextExtent,
5277             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5278             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5279           request=(&(event.xselectionrequest));
5280           (void) XChangeProperty(request->display,request->requestor,
5281             request->property,request->target,8,PropModeReplace,
5282             (unsigned char *) text,(int) strlen(text));
5283           notify.type=SelectionNotify;
5284           notify.display=request->display;
5285           notify.requestor=request->requestor;
5286           notify.selection=request->selection;
5287           notify.target=request->target;
5288           notify.time=request->time;
5289           if (request->property == None)
5290             notify.property=request->target;
5291           else
5292             notify.property=request->property;
5293           (void) XSendEvent(request->display,request->requestor,False,0,
5294             (XEvent *) &notify);
5295         }
5296         default:
5297           break;
5298       }
5299       if ((state & UpdateConfigurationState) != 0)
5300         {
5301           (void) XPutBackEvent(display,&event);
5302           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5303           break;
5304         }
5305     } while ((state & ExitState) == 0);
5306   } while ((state & ExitState) == 0);
5307   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5308   XSetCursorState(display,windows,MagickFalse);
5309   if ((state & EscapeState) != 0)
5310     return(MagickTrue);
5311   if (mode == CropMode)
5312     if (((int) crop_info.width != windows->image.ximage->width) ||
5313         ((int) crop_info.height != windows->image.ximage->height))
5314       {
5315         /*
5316           Reconfigure Image window as defined by cropping rectangle.
5317         */
5318         XSetCropGeometry(display,windows,&crop_info,image);
5319         windows->image.window_changes.width=(int) crop_info.width;
5320         windows->image.window_changes.height=(int) crop_info.height;
5321         (void) XConfigureImage(display,resource_info,windows,image,exception);
5322         return(MagickTrue);
5323       }
5324   /*
5325     Copy image before applying image transforms.
5326   */
5327   XSetCursorState(display,windows,MagickTrue);
5328   XCheckRefreshWindows(display,windows);
5329   width=(unsigned int) image->columns;
5330   height=(unsigned int) image->rows;
5331   x=0;
5332   y=0;
5333   if (windows->image.crop_geometry != (char *) NULL)
5334     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5335   scale_factor=(MagickRealType) width/windows->image.ximage->width;
5336   crop_info.x+=x;
5337   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5338   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5339   scale_factor=(MagickRealType) height/windows->image.ximage->height;
5340   crop_info.y+=y;
5341   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5342   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5343   crop_image=CropImage(image,&crop_info,exception);
5344   XSetCursorState(display,windows,MagickFalse);
5345   if (crop_image == (Image *) NULL)
5346     return(MagickFalse);
5347   if (resource_info->copy_image != (Image *) NULL)
5348     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5349   resource_info->copy_image=crop_image;
5350   if (mode == CopyMode)
5351     {
5352       (void) XConfigureImage(display,resource_info,windows,image,exception);
5353       return(MagickTrue);
5354     }
5355   /*
5356     Cut image.
5357   */
5358   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5359     return(MagickFalse);
5360   image->matte=MagickTrue;
5361   image_view=AcquireCacheView(image);
5362   for (y=0; y < (int) crop_info.height; y++)
5363   {
5364     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5365       crop_info.width,1,exception);
5366     if (q == (Quantum *) NULL)
5367       break;
5368     for (x=0; x < (int) crop_info.width; x++)
5369     {
5370       SetPixelAlpha(image,TransparentAlpha,q);
5371       q+=GetPixelChannels(image);
5372     }
5373     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5374       break;
5375   }
5376   image_view=DestroyCacheView(image_view);
5377   /*
5378     Update image configuration.
5379   */
5380   XConfigureImageColormap(display,resource_info,windows,image);
5381   (void) XConfigureImage(display,resource_info,windows,image,exception);
5382   return(MagickTrue);
5383 }
5384 \f
5385 /*
5386 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5387 %                                                                             %
5388 %                                                                             %
5389 %                                                                             %
5390 +   X D r a w I m a g e                                                       %
5391 %                                                                             %
5392 %                                                                             %
5393 %                                                                             %
5394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5395 %
5396 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5397 %  the image.
5398 %
5399 %  The format of the XDrawEditImage method is:
5400 %
5401 %      MagickBooleanType XDrawEditImage(Display *display,
5402 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5403 %        ExceptionInfo *exception)
5404 %
5405 %  A description of each parameter follows:
5406 %
5407 %    o display: Specifies a connection to an X server; returned from
5408 %      XOpenDisplay.
5409 %
5410 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5411 %
5412 %    o windows: Specifies a pointer to a XWindows structure.
5413 %
5414 %    o image: the image.
5415 %
5416 %    o exception: return any errors or warnings in this structure.
5417 %
5418 */
5419 static MagickBooleanType XDrawEditImage(Display *display,
5420   XResourceInfo *resource_info,XWindows *windows,Image **image,
5421   ExceptionInfo *exception)
5422 {
5423   static const char
5424     *DrawMenu[] =
5425     {
5426       "Element",
5427       "Color",
5428       "Stipple",
5429       "Width",
5430       "Undo",
5431       "Help",
5432       "Dismiss",
5433       (char *) NULL
5434     };
5435
5436   static ElementType
5437     element = PointElement;
5438
5439   static const ModeType
5440     DrawCommands[] =
5441     {
5442       DrawElementCommand,
5443       DrawColorCommand,
5444       DrawStippleCommand,
5445       DrawWidthCommand,
5446       DrawUndoCommand,
5447       DrawHelpCommand,
5448       DrawDismissCommand
5449     };
5450
5451   static Pixmap
5452     stipple = (Pixmap) NULL;
5453
5454   static unsigned int
5455     pen_id = 0,
5456     line_width = 1;
5457
5458   char
5459     command[MaxTextExtent],
5460     text[MaxTextExtent];
5461
5462   Cursor
5463     cursor;
5464
5465   int
5466     entry,
5467     id,
5468     number_coordinates,
5469     x,
5470     y;
5471
5472   MagickRealType
5473     degrees;
5474
5475   MagickStatusType
5476     status;
5477
5478   RectangleInfo
5479     rectangle_info;
5480
5481   register int
5482     i;
5483
5484   unsigned int
5485     distance,
5486     height,
5487     max_coordinates,
5488     width;
5489
5490   size_t
5491     state;
5492
5493   Window
5494     root_window;
5495
5496   XDrawInfo
5497     draw_info;
5498
5499   XEvent
5500     event;
5501
5502   XPoint
5503     *coordinate_info;
5504
5505   XSegment
5506     line_info;
5507
5508   /*
5509     Allocate polygon info.
5510   */
5511   max_coordinates=2048;
5512   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5513     sizeof(*coordinate_info));
5514   if (coordinate_info == (XPoint *) NULL)
5515     {
5516       (void) ThrowMagickException(exception,GetMagickModule(),
5517         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5518       return(MagickFalse);
5519     }
5520   /*
5521     Map Command widget.
5522   */
5523   (void) CloneString(&windows->command.name,"Draw");
5524   windows->command.data=4;
5525   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5526   (void) XMapRaised(display,windows->command.id);
5527   XClientMessage(display,windows->image.id,windows->im_protocols,
5528     windows->im_update_widget,CurrentTime);
5529   /*
5530     Wait for first button press.
5531   */
5532   root_window=XRootWindow(display,XDefaultScreen(display));
5533   draw_info.stencil=OpaqueStencil;
5534   status=MagickTrue;
5535   cursor=XCreateFontCursor(display,XC_tcross);
5536   for ( ; ; )
5537   {
5538     XQueryPosition(display,windows->image.id,&x,&y);
5539     (void) XSelectInput(display,windows->image.id,
5540       windows->image.attributes.event_mask | PointerMotionMask);
5541     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5542     state=DefaultState;
5543     do
5544     {
5545       if (windows->info.mapped != MagickFalse)
5546         {
5547           /*
5548             Display pointer position.
5549           */
5550           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5551             x+windows->image.x,y+windows->image.y);
5552           XInfoWidget(display,windows,text);
5553         }
5554       /*
5555         Wait for next event.
5556       */
5557       XScreenEvent(display,windows,&event);
5558       if (event.xany.window == windows->command.id)
5559         {
5560           /*
5561             Select a command from the Command widget.
5562           */
5563           id=XCommandWidget(display,windows,DrawMenu,&event);
5564           if (id < 0)
5565             continue;
5566           switch (DrawCommands[id])
5567           {
5568             case DrawElementCommand:
5569             {
5570               static const char
5571                 *Elements[] =
5572                 {
5573                   "point",
5574                   "line",
5575                   "rectangle",
5576                   "fill rectangle",
5577                   "circle",
5578                   "fill circle",
5579                   "ellipse",
5580                   "fill ellipse",
5581                   "polygon",
5582                   "fill polygon",
5583                   (char *) NULL,
5584                 };
5585
5586               /*
5587                 Select a command from the pop-up menu.
5588               */
5589               element=(ElementType) (XMenuWidget(display,windows,
5590                 DrawMenu[id],Elements,command)+1);
5591               break;
5592             }
5593             case DrawColorCommand:
5594             {
5595               const char
5596                 *ColorMenu[MaxNumberPens+1];
5597
5598               int
5599                 pen_number;
5600
5601               MagickBooleanType
5602                 transparent;
5603
5604               XColor
5605                 color;
5606
5607               /*
5608                 Initialize menu selections.
5609               */
5610               for (i=0; i < (int) (MaxNumberPens-2); i++)
5611                 ColorMenu[i]=resource_info->pen_colors[i];
5612               ColorMenu[MaxNumberPens-2]="transparent";
5613               ColorMenu[MaxNumberPens-1]="Browser...";
5614               ColorMenu[MaxNumberPens]=(char *) NULL;
5615               /*
5616                 Select a pen color from the pop-up menu.
5617               */
5618               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5619                 (const char **) ColorMenu,command);
5620               if (pen_number < 0)
5621                 break;
5622               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5623                 MagickFalse;
5624               if (transparent != MagickFalse)
5625                 {
5626                   draw_info.stencil=TransparentStencil;
5627                   break;
5628                 }
5629               if (pen_number == (MaxNumberPens-1))
5630                 {
5631                   static char
5632                     color_name[MaxTextExtent] = "gray";
5633
5634                   /*
5635                     Select a pen color from a dialog.
5636                   */
5637                   resource_info->pen_colors[pen_number]=color_name;
5638                   XColorBrowserWidget(display,windows,"Select",color_name);
5639                   if (*color_name == '\0')
5640                     break;
5641                 }
5642               /*
5643                 Set pen color.
5644               */
5645               (void) XParseColor(display,windows->map_info->colormap,
5646                 resource_info->pen_colors[pen_number],&color);
5647               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5648                 (unsigned int) MaxColors,&color);
5649               windows->pixel_info->pen_colors[pen_number]=color;
5650               pen_id=(unsigned int) pen_number;
5651               draw_info.stencil=OpaqueStencil;
5652               break;
5653             }
5654             case DrawStippleCommand:
5655             {
5656               Image
5657                 *stipple_image;
5658
5659               ImageInfo
5660                 *image_info;
5661
5662               int
5663                 status;
5664
5665               static char
5666                 filename[MaxTextExtent] = "\0";
5667
5668               static const char
5669                 *StipplesMenu[] =
5670                 {
5671                   "Brick",
5672                   "Diagonal",
5673                   "Scales",
5674                   "Vertical",
5675                   "Wavy",
5676                   "Translucent",
5677                   "Opaque",
5678                   (char *) NULL,
5679                   (char *) NULL,
5680                 };
5681
5682               /*
5683                 Select a command from the pop-up menu.
5684               */
5685               StipplesMenu[7]="Open...";
5686               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5687                 command);
5688               if (entry < 0)
5689                 break;
5690               if (stipple != (Pixmap) NULL)
5691                 (void) XFreePixmap(display,stipple);
5692               stipple=(Pixmap) NULL;
5693               if (entry != 7)
5694                 {
5695                   switch (entry)
5696                   {
5697                     case 0:
5698                     {
5699                       stipple=XCreateBitmapFromData(display,root_window,
5700                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5701                       break;
5702                     }
5703                     case 1:
5704                     {
5705                       stipple=XCreateBitmapFromData(display,root_window,
5706                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5707                       break;
5708                     }
5709                     case 2:
5710                     {
5711                       stipple=XCreateBitmapFromData(display,root_window,
5712                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5713                       break;
5714                     }
5715                     case 3:
5716                     {
5717                       stipple=XCreateBitmapFromData(display,root_window,
5718                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5719                       break;
5720                     }
5721                     case 4:
5722                     {
5723                       stipple=XCreateBitmapFromData(display,root_window,
5724                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5725                       break;
5726                     }
5727                     case 5:
5728                     {
5729                       stipple=XCreateBitmapFromData(display,root_window,
5730                         (char *) HighlightBitmap,HighlightWidth,
5731                         HighlightHeight);
5732                       break;
5733                     }
5734                     case 6:
5735                     default:
5736                     {
5737                       stipple=XCreateBitmapFromData(display,root_window,
5738                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5739                       break;
5740                     }
5741                   }
5742                   break;
5743                 }
5744               XFileBrowserWidget(display,windows,"Stipple",filename);
5745               if (*filename == '\0')
5746                 break;
5747               /*
5748                 Read image.
5749               */
5750               XSetCursorState(display,windows,MagickTrue);
5751               XCheckRefreshWindows(display,windows);
5752               image_info=AcquireImageInfo();
5753               (void) CopyMagickString(image_info->filename,filename,
5754                 MaxTextExtent);
5755               stipple_image=ReadImage(image_info,exception);
5756               CatchException(exception);
5757               XSetCursorState(display,windows,MagickFalse);
5758               if (stipple_image == (Image *) NULL)
5759                 break;
5760               (void) AcquireUniqueFileResource(filename);
5761               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5762                 "xbm:%s",filename);
5763               (void) WriteImage(image_info,stipple_image,exception);
5764               stipple_image=DestroyImage(stipple_image);
5765               image_info=DestroyImageInfo(image_info);
5766               status=XReadBitmapFile(display,root_window,filename,&width,
5767                 &height,&stipple,&x,&y);
5768               (void) RelinquishUniqueFileResource(filename);
5769               if ((status != BitmapSuccess) != 0)
5770                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5771                   filename);
5772               break;
5773             }
5774             case DrawWidthCommand:
5775             {
5776               static char
5777                 width[MaxTextExtent] = "0";
5778
5779               static const char
5780                 *WidthsMenu[] =
5781                 {
5782                   "1",
5783                   "2",
5784                   "4",
5785                   "8",
5786                   "16",
5787                   "Dialog...",
5788                   (char *) NULL,
5789                 };
5790
5791               /*
5792                 Select a command from the pop-up menu.
5793               */
5794               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5795                 command);
5796               if (entry < 0)
5797                 break;
5798               if (entry != 5)
5799                 {
5800                   line_width=(unsigned int) StringToUnsignedLong(
5801                     WidthsMenu[entry]);
5802                   break;
5803                 }
5804               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5805                 width);
5806               if (*width == '\0')
5807                 break;
5808               line_width=(unsigned int) StringToUnsignedLong(width);
5809               break;
5810             }
5811             case DrawUndoCommand:
5812             {
5813               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5814                 image,exception);
5815               break;
5816             }
5817             case DrawHelpCommand:
5818             {
5819               XTextViewWidget(display,resource_info,windows,MagickFalse,
5820                 "Help Viewer - Image Rotation",ImageDrawHelp);
5821               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5822               break;
5823             }
5824             case DrawDismissCommand:
5825             {
5826               /*
5827                 Prematurely exit.
5828               */
5829               state|=EscapeState;
5830               state|=ExitState;
5831               break;
5832             }
5833             default:
5834               break;
5835           }
5836           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5837           continue;
5838         }
5839       switch (event.type)
5840       {
5841         case ButtonPress:
5842         {
5843           if (event.xbutton.button != Button1)
5844             break;
5845           if (event.xbutton.window != windows->image.id)
5846             break;
5847           /*
5848             exit loop.
5849           */
5850           x=event.xbutton.x;
5851           y=event.xbutton.y;
5852           state|=ExitState;
5853           break;
5854         }
5855         case ButtonRelease:
5856           break;
5857         case Expose:
5858           break;
5859         case KeyPress:
5860         {
5861           KeySym
5862             key_symbol;
5863
5864           if (event.xkey.window != windows->image.id)
5865             break;
5866           /*
5867             Respond to a user key press.
5868           */
5869           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5870             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5871           switch ((int) key_symbol)
5872           {
5873             case XK_Escape:
5874             case XK_F20:
5875             {
5876               /*
5877                 Prematurely exit.
5878               */
5879               state|=EscapeState;
5880               state|=ExitState;
5881               break;
5882             }
5883             case XK_F1:
5884             case XK_Help:
5885             {
5886               XTextViewWidget(display,resource_info,windows,MagickFalse,
5887                 "Help Viewer - Image Rotation",ImageDrawHelp);
5888               break;
5889             }
5890             default:
5891             {
5892               (void) XBell(display,0);
5893               break;
5894             }
5895           }
5896           break;
5897         }
5898         case MotionNotify:
5899         {
5900           /*
5901             Map and unmap Info widget as text cursor crosses its boundaries.
5902           */
5903           x=event.xmotion.x;
5904           y=event.xmotion.y;
5905           if (windows->info.mapped != MagickFalse)
5906             {
5907               if ((x < (int) (windows->info.x+windows->info.width)) &&
5908                   (y < (int) (windows->info.y+windows->info.height)))
5909                 (void) XWithdrawWindow(display,windows->info.id,
5910                   windows->info.screen);
5911             }
5912           else
5913             if ((x > (int) (windows->info.x+windows->info.width)) ||
5914                 (y > (int) (windows->info.y+windows->info.height)))
5915               (void) XMapWindow(display,windows->info.id);
5916           break;
5917         }
5918       }
5919     } while ((state & ExitState) == 0);
5920     (void) XSelectInput(display,windows->image.id,
5921       windows->image.attributes.event_mask);
5922     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5923     if ((state & EscapeState) != 0)
5924       break;
5925     /*
5926       Draw element as pointer moves until the button is released.
5927     */
5928     distance=0;
5929     degrees=0.0;
5930     line_info.x1=x;
5931     line_info.y1=y;
5932     line_info.x2=x;
5933     line_info.y2=y;
5934     rectangle_info.x=(ssize_t) x;
5935     rectangle_info.y=(ssize_t) y;
5936     rectangle_info.width=0;
5937     rectangle_info.height=0;
5938     number_coordinates=1;
5939     coordinate_info->x=x;
5940     coordinate_info->y=y;
5941     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5942     state=DefaultState;
5943     do
5944     {
5945       switch (element)
5946       {
5947         case PointElement:
5948         default:
5949         {
5950           if (number_coordinates > 1)
5951             {
5952               (void) XDrawLines(display,windows->image.id,
5953                 windows->image.highlight_context,coordinate_info,
5954                 number_coordinates,CoordModeOrigin);
5955               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5956                 coordinate_info[number_coordinates-1].x,
5957                 coordinate_info[number_coordinates-1].y);
5958               XInfoWidget(display,windows,text);
5959             }
5960           break;
5961         }
5962         case LineElement:
5963         {
5964           if (distance > 9)
5965             {
5966               /*
5967                 Display angle of the line.
5968               */
5969               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5970                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5971               (void) FormatLocaleString(text,MaxTextExtent," %g",
5972                 (double) degrees);
5973               XInfoWidget(display,windows,text);
5974               XHighlightLine(display,windows->image.id,
5975                 windows->image.highlight_context,&line_info);
5976             }
5977           else
5978             if (windows->info.mapped != MagickFalse)
5979               (void) XWithdrawWindow(display,windows->info.id,
5980                 windows->info.screen);
5981           break;
5982         }
5983         case RectangleElement:
5984         case FillRectangleElement:
5985         {
5986           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5987             {
5988               /*
5989                 Display info and draw drawing rectangle.
5990               */
5991               (void) FormatLocaleString(text,MaxTextExtent,
5992                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5993                 (double) rectangle_info.height,(double) rectangle_info.x,
5994                 (double) rectangle_info.y);
5995               XInfoWidget(display,windows,text);
5996               XHighlightRectangle(display,windows->image.id,
5997                 windows->image.highlight_context,&rectangle_info);
5998             }
5999           else
6000             if (windows->info.mapped != MagickFalse)
6001               (void) XWithdrawWindow(display,windows->info.id,
6002                 windows->info.screen);
6003           break;
6004         }
6005         case CircleElement:
6006         case FillCircleElement:
6007         case EllipseElement:
6008         case FillEllipseElement:
6009         {
6010           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6011             {
6012               /*
6013                 Display info and draw drawing rectangle.
6014               */
6015               (void) FormatLocaleString(text,MaxTextExtent,
6016                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6017                 (double) rectangle_info.height,(double) rectangle_info.x,
6018                 (double) rectangle_info.y);
6019               XInfoWidget(display,windows,text);
6020               XHighlightEllipse(display,windows->image.id,
6021                 windows->image.highlight_context,&rectangle_info);
6022             }
6023           else
6024             if (windows->info.mapped != MagickFalse)
6025               (void) XWithdrawWindow(display,windows->info.id,
6026                 windows->info.screen);
6027           break;
6028         }
6029         case PolygonElement:
6030         case FillPolygonElement:
6031         {
6032           if (number_coordinates > 1)
6033             (void) XDrawLines(display,windows->image.id,
6034               windows->image.highlight_context,coordinate_info,
6035               number_coordinates,CoordModeOrigin);
6036           if (distance > 9)
6037             {
6038               /*
6039                 Display angle of the line.
6040               */
6041               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6042                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6043               (void) FormatLocaleString(text,MaxTextExtent," %g",
6044                 (double) degrees);
6045               XInfoWidget(display,windows,text);
6046               XHighlightLine(display,windows->image.id,
6047                 windows->image.highlight_context,&line_info);
6048             }
6049           else
6050             if (windows->info.mapped != MagickFalse)
6051               (void) XWithdrawWindow(display,windows->info.id,
6052                 windows->info.screen);
6053           break;
6054         }
6055       }
6056       /*
6057         Wait for next event.
6058       */
6059       XScreenEvent(display,windows,&event);
6060       switch (element)
6061       {
6062         case PointElement:
6063         default:
6064         {
6065           if (number_coordinates > 1)
6066             (void) XDrawLines(display,windows->image.id,
6067               windows->image.highlight_context,coordinate_info,
6068               number_coordinates,CoordModeOrigin);
6069           break;
6070         }
6071         case LineElement:
6072         {
6073           if (distance > 9)
6074             XHighlightLine(display,windows->image.id,
6075               windows->image.highlight_context,&line_info);
6076           break;
6077         }
6078         case RectangleElement:
6079         case FillRectangleElement:
6080         {
6081           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6082             XHighlightRectangle(display,windows->image.id,
6083               windows->image.highlight_context,&rectangle_info);
6084           break;
6085         }
6086         case CircleElement:
6087         case FillCircleElement:
6088         case EllipseElement:
6089         case FillEllipseElement:
6090         {
6091           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6092             XHighlightEllipse(display,windows->image.id,
6093               windows->image.highlight_context,&rectangle_info);
6094           break;
6095         }
6096         case PolygonElement:
6097         case FillPolygonElement:
6098         {
6099           if (number_coordinates > 1)
6100             (void) XDrawLines(display,windows->image.id,
6101               windows->image.highlight_context,coordinate_info,
6102               number_coordinates,CoordModeOrigin);
6103           if (distance > 9)
6104             XHighlightLine(display,windows->image.id,
6105               windows->image.highlight_context,&line_info);
6106           break;
6107         }
6108       }
6109       switch (event.type)
6110       {
6111         case ButtonPress:
6112           break;
6113         case ButtonRelease:
6114         {
6115           /*
6116             User has committed to element.
6117           */
6118           line_info.x2=event.xbutton.x;
6119           line_info.y2=event.xbutton.y;
6120           rectangle_info.x=(ssize_t) event.xbutton.x;
6121           rectangle_info.y=(ssize_t) event.xbutton.y;
6122           coordinate_info[number_coordinates].x=event.xbutton.x;
6123           coordinate_info[number_coordinates].y=event.xbutton.y;
6124           if (((element != PolygonElement) &&
6125                (element != FillPolygonElement)) || (distance <= 9))
6126             {
6127               state|=ExitState;
6128               break;
6129             }
6130           number_coordinates++;
6131           if (number_coordinates < (int) max_coordinates)
6132             {
6133               line_info.x1=event.xbutton.x;
6134               line_info.y1=event.xbutton.y;
6135               break;
6136             }
6137           max_coordinates<<=1;
6138           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6139             max_coordinates,sizeof(*coordinate_info));
6140           if (coordinate_info == (XPoint *) NULL)
6141             (void) ThrowMagickException(exception,GetMagickModule(),
6142               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6143           break;
6144         }
6145         case Expose:
6146           break;
6147         case MotionNotify:
6148         {
6149           if (event.xmotion.window != windows->image.id)
6150             break;
6151           if (element != PointElement)
6152             {
6153               line_info.x2=event.xmotion.x;
6154               line_info.y2=event.xmotion.y;
6155               rectangle_info.x=(ssize_t) event.xmotion.x;
6156               rectangle_info.y=(ssize_t) event.xmotion.y;
6157               break;
6158             }
6159           coordinate_info[number_coordinates].x=event.xbutton.x;
6160           coordinate_info[number_coordinates].y=event.xbutton.y;
6161           number_coordinates++;
6162           if (number_coordinates < (int) max_coordinates)
6163             break;
6164           max_coordinates<<=1;
6165           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6166             max_coordinates,sizeof(*coordinate_info));
6167           if (coordinate_info == (XPoint *) NULL)
6168             (void) ThrowMagickException(exception,GetMagickModule(),
6169               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6170           break;
6171         }
6172         default:
6173           break;
6174       }
6175       /*
6176         Check boundary conditions.
6177       */
6178       if (line_info.x2 < 0)
6179         line_info.x2=0;
6180       else
6181         if (line_info.x2 > (int) windows->image.width)
6182           line_info.x2=(short) windows->image.width;
6183       if (line_info.y2 < 0)
6184         line_info.y2=0;
6185       else
6186         if (line_info.y2 > (int) windows->image.height)
6187           line_info.y2=(short) windows->image.height;
6188       distance=(unsigned int)
6189         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6190          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6191       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6192           ((state & ExitState) != 0))
6193         {
6194           if (rectangle_info.x < 0)
6195             rectangle_info.x=0;
6196           else
6197             if (rectangle_info.x > (ssize_t) windows->image.width)
6198               rectangle_info.x=(ssize_t) windows->image.width;
6199           if ((int) rectangle_info.x < x)
6200             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6201           else
6202             {
6203               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6204               rectangle_info.x=(ssize_t) x;
6205             }
6206           if (rectangle_info.y < 0)
6207             rectangle_info.y=0;
6208           else
6209             if (rectangle_info.y > (ssize_t) windows->image.height)
6210               rectangle_info.y=(ssize_t) windows->image.height;
6211           if ((int) rectangle_info.y < y)
6212             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6213           else
6214             {
6215               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6216               rectangle_info.y=(ssize_t) y;
6217             }
6218         }
6219     } while ((state & ExitState) == 0);
6220     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6221     if ((element == PointElement) || (element == PolygonElement) ||
6222         (element == FillPolygonElement))
6223       {
6224         /*
6225           Determine polygon bounding box.
6226         */
6227         rectangle_info.x=(ssize_t) coordinate_info->x;
6228         rectangle_info.y=(ssize_t) coordinate_info->y;
6229         x=coordinate_info->x;
6230         y=coordinate_info->y;
6231         for (i=1; i < number_coordinates; i++)
6232         {
6233           if (coordinate_info[i].x > x)
6234             x=coordinate_info[i].x;
6235           if (coordinate_info[i].y > y)
6236             y=coordinate_info[i].y;
6237           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6238             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6239           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6240             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6241         }
6242         rectangle_info.width=(size_t) (x-rectangle_info.x);
6243         rectangle_info.height=(size_t) (y-rectangle_info.y);
6244         for (i=0; i < number_coordinates; i++)
6245         {
6246           coordinate_info[i].x-=rectangle_info.x;
6247           coordinate_info[i].y-=rectangle_info.y;
6248         }
6249       }
6250     else
6251       if (distance <= 9)
6252         continue;
6253       else
6254         if ((element == RectangleElement) ||
6255             (element == CircleElement) || (element == EllipseElement))
6256           {
6257             rectangle_info.width--;
6258             rectangle_info.height--;
6259           }
6260     /*
6261       Drawing is relative to image configuration.
6262     */
6263     draw_info.x=(int) rectangle_info.x;
6264     draw_info.y=(int) rectangle_info.y;
6265     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6266       image,exception);
6267     width=(unsigned int) (*image)->columns;
6268     height=(unsigned int) (*image)->rows;
6269     x=0;
6270     y=0;
6271     if (windows->image.crop_geometry != (char *) NULL)
6272       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6273     draw_info.x+=windows->image.x-(line_width/2);
6274     if (draw_info.x < 0)
6275       draw_info.x=0;
6276     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6277     draw_info.y+=windows->image.y-(line_width/2);
6278     if (draw_info.y < 0)
6279       draw_info.y=0;
6280     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6281     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6282     if (draw_info.width > (unsigned int) (*image)->columns)
6283       draw_info.width=(unsigned int) (*image)->columns;
6284     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6285     if (draw_info.height > (unsigned int) (*image)->rows)
6286       draw_info.height=(unsigned int) (*image)->rows;
6287     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6288       width*draw_info.width/windows->image.ximage->width,
6289       height*draw_info.height/windows->image.ximage->height,
6290       draw_info.x+x,draw_info.y+y);
6291     /*
6292       Initialize drawing attributes.
6293     */
6294     draw_info.degrees=0.0;
6295     draw_info.element=element;
6296     draw_info.stipple=stipple;
6297     draw_info.line_width=line_width;
6298     draw_info.line_info=line_info;
6299     if (line_info.x1 > (int) (line_width/2))
6300       draw_info.line_info.x1=(short) line_width/2;
6301     if (line_info.y1 > (int) (line_width/2))
6302       draw_info.line_info.y1=(short) line_width/2;
6303     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6304     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6305     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6306       {
6307         draw_info.line_info.x2=(-draw_info.line_info.x2);
6308         draw_info.line_info.y2=(-draw_info.line_info.y2);
6309       }
6310     if (draw_info.line_info.x2 < 0)
6311       {
6312         draw_info.line_info.x2=(-draw_info.line_info.x2);
6313         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6314       }
6315     if (draw_info.line_info.y2 < 0)
6316       {
6317         draw_info.line_info.y2=(-draw_info.line_info.y2);
6318         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6319       }
6320     draw_info.rectangle_info=rectangle_info;
6321     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6322       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6323     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6324       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6325     draw_info.number_coordinates=(unsigned int) number_coordinates;
6326     draw_info.coordinate_info=coordinate_info;
6327     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6328     /*
6329       Draw element on image.
6330     */
6331     XSetCursorState(display,windows,MagickTrue);
6332     XCheckRefreshWindows(display,windows);
6333     status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6334     XSetCursorState(display,windows,MagickFalse);
6335     /*
6336       Update image colormap and return to image drawing.
6337     */
6338     XConfigureImageColormap(display,resource_info,windows,*image);
6339     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6340   }
6341   XSetCursorState(display,windows,MagickFalse);
6342   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6343   return(status != 0 ? MagickTrue : MagickFalse);
6344 }
6345 \f
6346 /*
6347 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6348 %                                                                             %
6349 %                                                                             %
6350 %                                                                             %
6351 +   X D r a w P a n R e c t a n g l e                                         %
6352 %                                                                             %
6353 %                                                                             %
6354 %                                                                             %
6355 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6356 %
6357 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6358 %  displays a zoom image and the rectangle shows which portion of the image is
6359 %  displayed in the Image window.
6360 %
6361 %  The format of the XDrawPanRectangle method is:
6362 %
6363 %      XDrawPanRectangle(Display *display,XWindows *windows)
6364 %
6365 %  A description of each parameter follows:
6366 %
6367 %    o display: Specifies a connection to an X server;  returned from
6368 %      XOpenDisplay.
6369 %
6370 %    o windows: Specifies a pointer to a XWindows structure.
6371 %
6372 */
6373 static void XDrawPanRectangle(Display *display,XWindows *windows)
6374 {
6375   MagickRealType
6376     scale_factor;
6377
6378   RectangleInfo
6379     highlight_info;
6380
6381   /*
6382     Determine dimensions of the panning rectangle.
6383   */
6384   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6385   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6386   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6387   scale_factor=(MagickRealType)
6388     windows->pan.height/windows->image.ximage->height;
6389   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6390   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6391   /*
6392     Display the panning rectangle.
6393   */
6394   (void) XClearWindow(display,windows->pan.id);
6395   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6396     &highlight_info);
6397 }
6398 \f
6399 /*
6400 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6401 %                                                                             %
6402 %                                                                             %
6403 %                                                                             %
6404 +   X I m a g e C a c h e                                                     %
6405 %                                                                             %
6406 %                                                                             %
6407 %                                                                             %
6408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6409 %
6410 %  XImageCache() handles the creation, manipulation, and destruction of the
6411 %  image cache (undo and redo buffers).
6412 %
6413 %  The format of the XImageCache method is:
6414 %
6415 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6416 %        XWindows *windows,const CommandType command,Image **image,
6417 %        ExceptionInfo *exception)
6418 %
6419 %  A description of each parameter follows:
6420 %
6421 %    o display: Specifies a connection to an X server; returned from
6422 %      XOpenDisplay.
6423 %
6424 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6425 %
6426 %    o windows: Specifies a pointer to a XWindows structure.
6427 %
6428 %    o command: Specifies a command to perform.
6429 %
6430 %    o image: the image;  XImageCache may transform the image and return a new
6431 %      image pointer.
6432 %
6433 %    o exception: return any errors or warnings in this structure.
6434 %
6435 */
6436 static void XImageCache(Display *display,XResourceInfo *resource_info,
6437   XWindows *windows,const CommandType command,Image **image,
6438   ExceptionInfo *exception)
6439 {
6440   Image
6441     *cache_image;
6442
6443   static Image
6444     *redo_image = (Image *) NULL,
6445     *undo_image = (Image *) NULL;
6446
6447   switch (command)
6448   {
6449     case FreeBuffersCommand:
6450     {
6451       /*
6452         Free memory from the undo and redo cache.
6453       */
6454       while (undo_image != (Image *) NULL)
6455       {
6456         cache_image=undo_image;
6457         undo_image=GetPreviousImageInList(undo_image);
6458         cache_image->list=DestroyImage(cache_image->list);
6459         cache_image=DestroyImage(cache_image);
6460       }
6461       undo_image=NewImageList();
6462       if (redo_image != (Image *) NULL)
6463         redo_image=DestroyImage(redo_image);
6464       redo_image=NewImageList();
6465       return;
6466     }
6467     case UndoCommand:
6468     {
6469       char
6470         image_geometry[MaxTextExtent];
6471
6472       /*
6473         Undo the last image transformation.
6474       */
6475       if (undo_image == (Image *) NULL)
6476         {
6477           (void) XBell(display,0);
6478           return;
6479         }
6480       cache_image=undo_image;
6481       undo_image=GetPreviousImageInList(undo_image);
6482       windows->image.window_changes.width=(int) cache_image->columns;
6483       windows->image.window_changes.height=(int) cache_image->rows;
6484       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6485         windows->image.ximage->width,windows->image.ximage->height);
6486       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6487       if (windows->image.crop_geometry != (char *) NULL)
6488         windows->image.crop_geometry=(char *)
6489           RelinquishMagickMemory(windows->image.crop_geometry);
6490       windows->image.crop_geometry=cache_image->geometry;
6491       if (redo_image != (Image *) NULL)
6492         redo_image=DestroyImage(redo_image);
6493       redo_image=(*image);
6494       *image=cache_image->list;
6495       cache_image=DestroyImage(cache_image);
6496       if (windows->image.orphan != MagickFalse)
6497         return;
6498       XConfigureImageColormap(display,resource_info,windows,*image);
6499       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6500       return;
6501     }
6502     case CutCommand:
6503     case PasteCommand:
6504     case ApplyCommand:
6505     case HalfSizeCommand:
6506     case OriginalSizeCommand:
6507     case DoubleSizeCommand:
6508     case ResizeCommand:
6509     case TrimCommand:
6510     case CropCommand:
6511     case ChopCommand:
6512     case FlipCommand:
6513     case FlopCommand:
6514     case RotateRightCommand:
6515     case RotateLeftCommand:
6516     case RotateCommand:
6517     case ShearCommand:
6518     case RollCommand:
6519     case NegateCommand:
6520     case ContrastStretchCommand:
6521     case SigmoidalContrastCommand:
6522     case NormalizeCommand:
6523     case EqualizeCommand:
6524     case HueCommand:
6525     case SaturationCommand:
6526     case BrightnessCommand:
6527     case GammaCommand:
6528     case SpiffCommand:
6529     case DullCommand:
6530     case GrayscaleCommand:
6531     case MapCommand:
6532     case QuantizeCommand:
6533     case DespeckleCommand:
6534     case EmbossCommand:
6535     case ReduceNoiseCommand:
6536     case AddNoiseCommand:
6537     case SharpenCommand:
6538     case BlurCommand:
6539     case ThresholdCommand:
6540     case EdgeDetectCommand:
6541     case SpreadCommand:
6542     case ShadeCommand:
6543     case RaiseCommand:
6544     case SegmentCommand:
6545     case SolarizeCommand:
6546     case SepiaToneCommand:
6547     case SwirlCommand:
6548     case ImplodeCommand:
6549     case VignetteCommand:
6550     case WaveCommand:
6551     case OilPaintCommand:
6552     case CharcoalDrawCommand:
6553     case AnnotateCommand:
6554     case AddBorderCommand:
6555     case AddFrameCommand:
6556     case CompositeCommand:
6557     case CommentCommand:
6558     case LaunchCommand:
6559     case RegionofInterestCommand:
6560     case SaveToUndoBufferCommand:
6561     case RedoCommand:
6562     {
6563       Image
6564         *previous_image;
6565
6566       ssize_t
6567         bytes;
6568
6569       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6570       if (undo_image != (Image *) NULL)
6571         {
6572           /*
6573             Ensure the undo cache has enough memory available.
6574           */
6575           previous_image=undo_image;
6576           while (previous_image != (Image *) NULL)
6577           {
6578             bytes+=previous_image->list->columns*previous_image->list->rows*
6579               sizeof(PixelPacket);
6580             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6581               {
6582                 previous_image=GetPreviousImageInList(previous_image);
6583                 continue;
6584               }
6585             bytes-=previous_image->list->columns*previous_image->list->rows*
6586               sizeof(PixelPacket);
6587             if (previous_image == undo_image)
6588               undo_image=NewImageList();
6589             else
6590               previous_image->next->previous=NewImageList();
6591             break;
6592           }
6593           while (previous_image != (Image *) NULL)
6594           {
6595             /*
6596               Delete any excess memory from undo cache.
6597             */
6598             cache_image=previous_image;
6599             previous_image=GetPreviousImageInList(previous_image);
6600             cache_image->list=DestroyImage(cache_image->list);
6601             cache_image=DestroyImage(cache_image);
6602           }
6603         }
6604       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6605         break;
6606       /*
6607         Save image before transformations are applied.
6608       */
6609       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6610       if (cache_image == (Image *) NULL)
6611         break;
6612       XSetCursorState(display,windows,MagickTrue);
6613       XCheckRefreshWindows(display,windows);
6614       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6615       XSetCursorState(display,windows,MagickFalse);
6616       if (cache_image->list == (Image *) NULL)
6617         {
6618           cache_image=DestroyImage(cache_image);
6619           break;
6620         }
6621       cache_image->columns=(size_t) windows->image.ximage->width;
6622       cache_image->rows=(size_t) windows->image.ximage->height;
6623       cache_image->geometry=windows->image.crop_geometry;
6624       if (windows->image.crop_geometry != (char *) NULL)
6625         {
6626           cache_image->geometry=AcquireString((char *) NULL);
6627           (void) CopyMagickString(cache_image->geometry,
6628             windows->image.crop_geometry,MaxTextExtent);
6629         }
6630       if (undo_image == (Image *) NULL)
6631         {
6632           undo_image=cache_image;
6633           break;
6634         }
6635       undo_image->next=cache_image;
6636       undo_image->next->previous=undo_image;
6637       undo_image=undo_image->next;
6638       break;
6639     }
6640     default:
6641       break;
6642   }
6643   if (command == RedoCommand)
6644     {
6645       /*
6646         Redo the last image transformation.
6647       */
6648       if (redo_image == (Image *) NULL)
6649         {
6650           (void) XBell(display,0);
6651           return;
6652         }
6653       windows->image.window_changes.width=(int) redo_image->columns;
6654       windows->image.window_changes.height=(int) redo_image->rows;
6655       if (windows->image.crop_geometry != (char *) NULL)
6656         windows->image.crop_geometry=(char *)
6657           RelinquishMagickMemory(windows->image.crop_geometry);
6658       windows->image.crop_geometry=redo_image->geometry;
6659       *image=DestroyImage(*image);
6660       *image=redo_image;
6661       redo_image=NewImageList();
6662       if (windows->image.orphan != MagickFalse)
6663         return;
6664       XConfigureImageColormap(display,resource_info,windows,*image);
6665       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6666       return;
6667     }
6668   if (command != InfoCommand)
6669     return;
6670   /*
6671     Display image info.
6672   */
6673   XSetCursorState(display,windows,MagickTrue);
6674   XCheckRefreshWindows(display,windows);
6675   XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6676   XSetCursorState(display,windows,MagickFalse);
6677 }
6678 \f
6679 /*
6680 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6681 %                                                                             %
6682 %                                                                             %
6683 %                                                                             %
6684 +   X I m a g e W i n d o w C o m m a n d                                     %
6685 %                                                                             %
6686 %                                                                             %
6687 %                                                                             %
6688 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6689 %
6690 %  XImageWindowCommand() makes a transform to the image or Image window as
6691 %  specified by a user menu button or keyboard command.
6692 %
6693 %  The format of the XImageWindowCommand method is:
6694 %
6695 %      CommandType XImageWindowCommand(Display *display,
6696 %        XResourceInfo *resource_info,XWindows *windows,
6697 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6698 %        ExceptionInfo *exception)
6699 %
6700 %  A description of each parameter follows:
6701 %
6702 %    o nexus:  Method XImageWindowCommand returns an image when the
6703 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6704 %      image is returned.
6705 %
6706 %    o display: Specifies a connection to an X server; returned from
6707 %      XOpenDisplay.
6708 %
6709 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6710 %
6711 %    o windows: Specifies a pointer to a XWindows structure.
6712 %
6713 %    o state: key mask.
6714 %
6715 %    o key_symbol: Specifies a command to perform.
6716 %
6717 %    o image: the image;  XImageWIndowCommand may transform the image and
6718 %      return a new image pointer.
6719 %
6720 %    o exception: return any errors or warnings in this structure.
6721 %
6722 */
6723 static CommandType XImageWindowCommand(Display *display,
6724   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6725   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6726 {
6727   static char
6728     delta[MaxTextExtent] = "";
6729
6730   static const char
6731     Digits[] = "01234567890";
6732
6733   static KeySym
6734     last_symbol = XK_0;
6735
6736   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6737     {
6738       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6739         {
6740           *delta='\0';
6741           resource_info->quantum=1;
6742         }
6743       last_symbol=key_symbol;
6744       delta[strlen(delta)+1]='\0';
6745       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6746       resource_info->quantum=StringToLong(delta);
6747       return(NullCommand);
6748     }
6749   last_symbol=key_symbol;
6750   if (resource_info->immutable)
6751     {
6752       /*
6753         Virtual image window has a restricted command set.
6754       */
6755       switch (key_symbol)
6756       {
6757         case XK_question:
6758           return(InfoCommand);
6759         case XK_p:
6760         case XK_Print:
6761           return(PrintCommand);
6762         case XK_space:
6763           return(NextCommand);
6764         case XK_q:
6765         case XK_Escape:
6766           return(QuitCommand);
6767         default:
6768           break;
6769       }
6770       return(NullCommand);
6771     }
6772   switch ((int) key_symbol)
6773   {
6774     case XK_o:
6775     {
6776       if ((state & ControlMask) == 0)
6777         break;
6778       return(OpenCommand);
6779     }
6780     case XK_space:
6781       return(NextCommand);
6782     case XK_BackSpace:
6783       return(FormerCommand);
6784     case XK_s:
6785     {
6786       if ((state & Mod1Mask) != 0)
6787         return(SwirlCommand);
6788       if ((state & ControlMask) == 0)
6789         return(ShearCommand);
6790       return(SaveCommand);
6791     }
6792     case XK_p:
6793     case XK_Print:
6794     {
6795       if ((state & Mod1Mask) != 0)
6796         return(OilPaintCommand);
6797       if ((state & Mod4Mask) != 0)
6798         return(ColorCommand);
6799       if ((state & ControlMask) == 0)
6800         return(NullCommand);
6801       return(PrintCommand);
6802     }
6803     case XK_d:
6804     {
6805       if ((state & Mod4Mask) != 0)
6806         return(DrawCommand);
6807       if ((state & ControlMask) == 0)
6808         return(NullCommand);
6809       return(DeleteCommand);
6810     }
6811     case XK_Select:
6812     {
6813       if ((state & ControlMask) == 0)
6814         return(NullCommand);
6815       return(SelectCommand);
6816     }
6817     case XK_n:
6818     {
6819       if ((state & ControlMask) == 0)
6820         return(NullCommand);
6821       return(NewCommand);
6822     }
6823     case XK_q:
6824     case XK_Escape:
6825       return(QuitCommand);
6826     case XK_z:
6827     case XK_Undo:
6828     {
6829       if ((state & ControlMask) == 0)
6830         return(NullCommand);
6831       return(UndoCommand);
6832     }
6833     case XK_r:
6834     case XK_Redo:
6835     {
6836       if ((state & ControlMask) == 0)
6837         return(RollCommand);
6838       return(RedoCommand);
6839     }
6840     case XK_x:
6841     {
6842       if ((state & ControlMask) == 0)
6843         return(NullCommand);
6844       return(CutCommand);
6845     }
6846     case XK_c:
6847     {
6848       if ((state & Mod1Mask) != 0)
6849         return(CharcoalDrawCommand);
6850       if ((state & ControlMask) == 0)
6851         return(CropCommand);
6852       return(CopyCommand);
6853     }
6854     case XK_v:
6855     case XK_Insert:
6856     {
6857       if ((state & Mod4Mask) != 0)
6858         return(CompositeCommand);
6859       if ((state & ControlMask) == 0)
6860         return(FlipCommand);
6861       return(PasteCommand);
6862     }
6863     case XK_less:
6864       return(HalfSizeCommand);
6865     case XK_minus:
6866       return(OriginalSizeCommand);
6867     case XK_greater:
6868       return(DoubleSizeCommand);
6869     case XK_percent:
6870       return(ResizeCommand);
6871     case XK_at:
6872       return(RefreshCommand);
6873     case XK_bracketleft:
6874       return(ChopCommand);
6875     case XK_h:
6876       return(FlopCommand);
6877     case XK_slash:
6878       return(RotateRightCommand);
6879     case XK_backslash:
6880       return(RotateLeftCommand);
6881     case XK_asterisk:
6882       return(RotateCommand);
6883     case XK_t:
6884       return(TrimCommand);
6885     case XK_H:
6886       return(HueCommand);
6887     case XK_S:
6888       return(SaturationCommand);
6889     case XK_L:
6890       return(BrightnessCommand);
6891     case XK_G:
6892       return(GammaCommand);
6893     case XK_C:
6894       return(SpiffCommand);
6895     case XK_Z:
6896       return(DullCommand);
6897     case XK_N:
6898       return(NormalizeCommand);
6899     case XK_equal:
6900       return(EqualizeCommand);
6901     case XK_asciitilde:
6902       return(NegateCommand);
6903     case XK_period:
6904       return(GrayscaleCommand);
6905     case XK_numbersign:
6906       return(QuantizeCommand);
6907     case XK_F2:
6908       return(DespeckleCommand);
6909     case XK_F3:
6910       return(EmbossCommand);
6911     case XK_F4:
6912       return(ReduceNoiseCommand);
6913     case XK_F5:
6914       return(AddNoiseCommand);
6915     case XK_F6:
6916       return(SharpenCommand);
6917     case XK_F7:
6918       return(BlurCommand);
6919     case XK_F8:
6920       return(ThresholdCommand);
6921     case XK_F9:
6922       return(EdgeDetectCommand);
6923     case XK_F10:
6924       return(SpreadCommand);
6925     case XK_F11:
6926       return(ShadeCommand);
6927     case XK_F12:
6928       return(RaiseCommand);
6929     case XK_F13:
6930       return(SegmentCommand);
6931     case XK_i:
6932     {
6933       if ((state & Mod1Mask) == 0)
6934         return(NullCommand);
6935       return(ImplodeCommand);
6936     }
6937     case XK_w:
6938     {
6939       if ((state & Mod1Mask) == 0)
6940         return(NullCommand);
6941       return(WaveCommand);
6942     }
6943     case XK_m:
6944     {
6945       if ((state & Mod4Mask) == 0)
6946         return(NullCommand);
6947       return(MatteCommand);
6948     }
6949     case XK_b:
6950     {
6951       if ((state & Mod4Mask) == 0)
6952         return(NullCommand);
6953       return(AddBorderCommand);
6954     }
6955     case XK_f:
6956     {
6957       if ((state & Mod4Mask) == 0)
6958         return(NullCommand);
6959       return(AddFrameCommand);
6960     }
6961     case XK_exclam:
6962     {
6963       if ((state & Mod4Mask) == 0)
6964         return(NullCommand);
6965       return(CommentCommand);
6966     }
6967     case XK_a:
6968     {
6969       if ((state & Mod1Mask) != 0)
6970         return(ApplyCommand);
6971       if ((state & Mod4Mask) != 0)
6972         return(AnnotateCommand);
6973       if ((state & ControlMask) == 0)
6974         return(NullCommand);
6975       return(RegionofInterestCommand);
6976     }
6977     case XK_question:
6978       return(InfoCommand);
6979     case XK_plus:
6980       return(ZoomCommand);
6981     case XK_P:
6982     {
6983       if ((state & ShiftMask) == 0)
6984         return(NullCommand);
6985       return(ShowPreviewCommand);
6986     }
6987     case XK_Execute:
6988       return(LaunchCommand);
6989     case XK_F1:
6990       return(HelpCommand);
6991     case XK_Find:
6992       return(BrowseDocumentationCommand);
6993     case XK_Menu:
6994     {
6995       (void) XMapRaised(display,windows->command.id);
6996       return(NullCommand);
6997     }
6998     case XK_Next:
6999     case XK_Prior:
7000     case XK_Home:
7001     case XK_KP_Home:
7002     {
7003       XTranslateImage(display,windows,*image,key_symbol);
7004       return(NullCommand);
7005     }
7006     case XK_Up:
7007     case XK_KP_Up:
7008     case XK_Down:
7009     case XK_KP_Down:
7010     case XK_Left:
7011     case XK_KP_Left:
7012     case XK_Right:
7013     case XK_KP_Right:
7014     {
7015       if ((state & Mod1Mask) != 0)
7016         {
7017           RectangleInfo
7018             crop_info;
7019
7020           /*
7021             Trim one pixel from edge of image.
7022           */
7023           crop_info.x=0;
7024           crop_info.y=0;
7025           crop_info.width=(size_t) windows->image.ximage->width;
7026           crop_info.height=(size_t) windows->image.ximage->height;
7027           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7028             {
7029               if (resource_info->quantum >= (int) crop_info.height)
7030                 resource_info->quantum=(int) crop_info.height-1;
7031               crop_info.height-=resource_info->quantum;
7032             }
7033           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7034             {
7035               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7036                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7037               crop_info.y+=resource_info->quantum;
7038               crop_info.height-=resource_info->quantum;
7039             }
7040           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7041             {
7042               if (resource_info->quantum >= (int) crop_info.width)
7043                 resource_info->quantum=(int) crop_info.width-1;
7044               crop_info.width-=resource_info->quantum;
7045             }
7046           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7047             {
7048               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7049                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7050               crop_info.x+=resource_info->quantum;
7051               crop_info.width-=resource_info->quantum;
7052             }
7053           if ((int) (windows->image.x+windows->image.width) >
7054               (int) crop_info.width)
7055             windows->image.x=(int) (crop_info.width-windows->image.width);
7056           if ((int) (windows->image.y+windows->image.height) >
7057               (int) crop_info.height)
7058             windows->image.y=(int) (crop_info.height-windows->image.height);
7059           XSetCropGeometry(display,windows,&crop_info,*image);
7060           windows->image.window_changes.width=(int) crop_info.width;
7061           windows->image.window_changes.height=(int) crop_info.height;
7062           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7063           (void) XConfigureImage(display,resource_info,windows,*image,
7064             exception);
7065           return(NullCommand);
7066         }
7067       XTranslateImage(display,windows,*image,key_symbol);
7068       return(NullCommand);
7069     }
7070     default:
7071       return(NullCommand);
7072   }
7073   return(NullCommand);
7074 }
7075 \f
7076 /*
7077 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7078 %                                                                             %
7079 %                                                                             %
7080 %                                                                             %
7081 +   X M a g i c k C o m m a n d                                               %
7082 %                                                                             %
7083 %                                                                             %
7084 %                                                                             %
7085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7086 %
7087 %  XMagickCommand() makes a transform to the image or Image window as
7088 %  specified by a user menu button or keyboard command.
7089 %
7090 %  The format of the XMagickCommand method is:
7091 %
7092 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7093 %        XWindows *windows,const CommandType command,Image **image,
7094 %        ExceptionInfo *exception)
7095 %
7096 %  A description of each parameter follows:
7097 %
7098 %    o display: Specifies a connection to an X server; returned from
7099 %      XOpenDisplay.
7100 %
7101 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7102 %
7103 %    o windows: Specifies a pointer to a XWindows structure.
7104 %
7105 %    o command: Specifies a command to perform.
7106 %
7107 %    o image: the image;  XMagickCommand may transform the image and return a
7108 %      new image pointer.
7109 %
7110 %    o exception: return any errors or warnings in this structure.
7111 %
7112 */
7113 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7114   XWindows *windows,const CommandType command,Image **image,
7115   ExceptionInfo *exception)
7116 {
7117   char
7118     filename[MaxTextExtent],
7119     geometry[MaxTextExtent],
7120     modulate_factors[MaxTextExtent];
7121
7122   GeometryInfo
7123     geometry_info;
7124
7125   Image
7126     *nexus;
7127
7128   ImageInfo
7129     *image_info;
7130
7131   int
7132     x,
7133     y;
7134
7135   MagickStatusType
7136     flags,
7137     status;
7138
7139   QuantizeInfo
7140     quantize_info;
7141
7142   RectangleInfo
7143     page_geometry;
7144
7145   register int
7146     i;
7147
7148   static char
7149     color[MaxTextExtent] = "gray";
7150
7151   unsigned int
7152     height,
7153     width;
7154
7155   /*
7156     Process user command.
7157   */
7158   XCheckRefreshWindows(display,windows);
7159   XImageCache(display,resource_info,windows,command,image,exception);
7160   nexus=NewImageList();
7161   windows->image.window_changes.width=windows->image.ximage->width;
7162   windows->image.window_changes.height=windows->image.ximage->height;
7163   image_info=CloneImageInfo(resource_info->image_info);
7164   SetGeometryInfo(&geometry_info);
7165   GetQuantizeInfo(&quantize_info);
7166   switch (command)
7167   {
7168     case OpenCommand:
7169     {
7170       /*
7171         Load image.
7172       */
7173       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7174       break;
7175     }
7176     case NextCommand:
7177     {
7178       /*
7179         Display next image.
7180       */
7181       for (i=0; i < resource_info->quantum; i++)
7182         XClientMessage(display,windows->image.id,windows->im_protocols,
7183           windows->im_next_image,CurrentTime);
7184       break;
7185     }
7186     case FormerCommand:
7187     {
7188       /*
7189         Display former image.
7190       */
7191       for (i=0; i < resource_info->quantum; i++)
7192         XClientMessage(display,windows->image.id,windows->im_protocols,
7193           windows->im_former_image,CurrentTime);
7194       break;
7195     }
7196     case SelectCommand:
7197     {
7198       int
7199         status;
7200
7201       /*
7202         Select image.
7203       */
7204       status=chdir(resource_info->home_directory);
7205       if (status == -1)
7206         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7207           "UnableToOpenFile","%s",resource_info->home_directory);
7208       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7209       break;
7210     }
7211     case SaveCommand:
7212     {
7213       /*
7214         Save image.
7215       */
7216       status=XSaveImage(display,resource_info,windows,*image,exception);
7217       if (status == MagickFalse)
7218         {
7219           char
7220             message[MaxTextExtent];
7221
7222           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7223             exception->reason != (char *) NULL ? exception->reason : "",
7224             exception->description != (char *) NULL ? exception->description :
7225             "");
7226           XNoticeWidget(display,windows,"Unable to save file:",message);
7227           break;
7228         }
7229       break;
7230     }
7231     case PrintCommand:
7232     {
7233       /*
7234         Print image.
7235       */
7236       status=XPrintImage(display,resource_info,windows,*image,exception);
7237       if (status == MagickFalse)
7238         {
7239           char
7240             message[MaxTextExtent];
7241
7242           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7243             exception->reason != (char *) NULL ? exception->reason : "",
7244             exception->description != (char *) NULL ? exception->description :
7245             "");
7246           XNoticeWidget(display,windows,"Unable to print file:",message);
7247           break;
7248         }
7249       break;
7250     }
7251     case DeleteCommand:
7252     {
7253       static char
7254         filename[MaxTextExtent] = "\0";
7255
7256       /*
7257         Delete image file.
7258       */
7259       XFileBrowserWidget(display,windows,"Delete",filename);
7260       if (*filename == '\0')
7261         break;
7262       status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
7263       if (status != MagickFalse)
7264         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7265       break;
7266     }
7267     case NewCommand:
7268     {
7269       int
7270         status;
7271
7272       static char
7273         color[MaxTextExtent] = "gray",
7274         geometry[MaxTextExtent] = "640x480";
7275
7276       static const char
7277         *format = "gradient";
7278
7279       /*
7280         Query user for canvas geometry.
7281       */
7282       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7283         geometry);
7284       if (*geometry == '\0')
7285         break;
7286       if (status == 0)
7287         format="xc";
7288       XColorBrowserWidget(display,windows,"Select",color);
7289       if (*color == '\0')
7290         break;
7291       /*
7292         Create canvas.
7293       */
7294       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7295         "%s:%s",format,color);
7296       (void) CloneString(&image_info->size,geometry);
7297       nexus=ReadImage(image_info,exception);
7298       CatchException(exception);
7299       XClientMessage(display,windows->image.id,windows->im_protocols,
7300         windows->im_next_image,CurrentTime);
7301       break;
7302     }
7303     case VisualDirectoryCommand:
7304     {
7305       /*
7306         Visual Image directory.
7307       */
7308       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7309       break;
7310     }
7311     case QuitCommand:
7312     {
7313       /*
7314         exit program.
7315       */
7316       if (resource_info->confirm_exit == MagickFalse)
7317         XClientMessage(display,windows->image.id,windows->im_protocols,
7318           windows->im_exit,CurrentTime);
7319       else
7320         {
7321           int
7322             status;
7323
7324           /*
7325             Confirm program exit.
7326           */
7327           status=XConfirmWidget(display,windows,"Do you really want to exit",
7328             resource_info->client_name);
7329           if (status > 0)
7330             XClientMessage(display,windows->image.id,windows->im_protocols,
7331               windows->im_exit,CurrentTime);
7332         }
7333       break;
7334     }
7335     case CutCommand:
7336     {
7337       /*
7338         Cut image.
7339       */
7340       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7341       break;
7342     }
7343     case CopyCommand:
7344     {
7345       /*
7346         Copy image.
7347       */
7348       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7349         exception);
7350       break;
7351     }
7352     case PasteCommand:
7353     {
7354       /*
7355         Paste image.
7356       */
7357       status=XPasteImage(display,resource_info,windows,*image,exception);
7358       if (status == MagickFalse)
7359         {
7360           XNoticeWidget(display,windows,"Unable to paste X image",
7361             (*image)->filename);
7362           break;
7363         }
7364       break;
7365     }
7366     case HalfSizeCommand:
7367     {
7368       /*
7369         Half image size.
7370       */
7371       windows->image.window_changes.width=windows->image.ximage->width/2;
7372       windows->image.window_changes.height=windows->image.ximage->height/2;
7373       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7374       break;
7375     }
7376     case OriginalSizeCommand:
7377     {
7378       /*
7379         Original image size.
7380       */
7381       windows->image.window_changes.width=(int) (*image)->columns;
7382       windows->image.window_changes.height=(int) (*image)->rows;
7383       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7384       break;
7385     }
7386     case DoubleSizeCommand:
7387     {
7388       /*
7389         Double the image size.
7390       */
7391       windows->image.window_changes.width=windows->image.ximage->width << 1;
7392       windows->image.window_changes.height=windows->image.ximage->height << 1;
7393       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7394       break;
7395     }
7396     case ResizeCommand:
7397     {
7398       int
7399         status;
7400
7401       size_t
7402         height,
7403         width;
7404
7405       ssize_t
7406         x,
7407         y;
7408
7409       /*
7410         Resize image.
7411       */
7412       width=(size_t) windows->image.ximage->width;
7413       height=(size_t) windows->image.ximage->height;
7414       x=0;
7415       y=0;
7416       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7417         (double) width,(double) height);
7418       status=XDialogWidget(display,windows,"Resize",
7419         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7420       if (*geometry == '\0')
7421         break;
7422       if (status == 0)
7423         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7424       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7425       windows->image.window_changes.width=(int) width;
7426       windows->image.window_changes.height=(int) height;
7427       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7428       break;
7429     }
7430     case ApplyCommand:
7431     {
7432       char
7433         image_geometry[MaxTextExtent];
7434
7435       if ((windows->image.crop_geometry == (char *) NULL) &&
7436           ((int) (*image)->columns == windows->image.ximage->width) &&
7437           ((int) (*image)->rows == windows->image.ximage->height))
7438         break;
7439       /*
7440         Apply size transforms to image.
7441       */
7442       XSetCursorState(display,windows,MagickTrue);
7443       XCheckRefreshWindows(display,windows);
7444       /*
7445         Crop and/or scale displayed image.
7446       */
7447       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7448         windows->image.ximage->width,windows->image.ximage->height);
7449       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7450       if (windows->image.crop_geometry != (char *) NULL)
7451         windows->image.crop_geometry=(char *)
7452           RelinquishMagickMemory(windows->image.crop_geometry);
7453       windows->image.x=0;
7454       windows->image.y=0;
7455       XConfigureImageColormap(display,resource_info,windows,*image);
7456       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7457       break;
7458     }
7459     case RefreshCommand:
7460     {
7461       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7462       break;
7463     }
7464     case RestoreCommand:
7465     {
7466       /*
7467         Restore Image window to its original size.
7468       */
7469       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7470           (windows->image.height == (unsigned int) (*image)->rows) &&
7471           (windows->image.crop_geometry == (char *) NULL))
7472         {
7473           (void) XBell(display,0);
7474           break;
7475         }
7476       windows->image.window_changes.width=(int) (*image)->columns;
7477       windows->image.window_changes.height=(int) (*image)->rows;
7478       if (windows->image.crop_geometry != (char *) NULL)
7479         {
7480           windows->image.crop_geometry=(char *)
7481             RelinquishMagickMemory(windows->image.crop_geometry);
7482           windows->image.crop_geometry=(char *) NULL;
7483           windows->image.x=0;
7484           windows->image.y=0;
7485         }
7486       XConfigureImageColormap(display,resource_info,windows,*image);
7487       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7488       break;
7489     }
7490     case CropCommand:
7491     {
7492       /*
7493         Crop image.
7494       */
7495       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7496         exception);
7497       break;
7498     }
7499     case ChopCommand:
7500     {
7501       /*
7502         Chop image.
7503       */
7504       status=XChopImage(display,resource_info,windows,image,exception);
7505       if (status == MagickFalse)
7506         {
7507           XNoticeWidget(display,windows,"Unable to cut X image",
7508             (*image)->filename);
7509           break;
7510         }
7511       break;
7512     }
7513     case FlopCommand:
7514     {
7515       Image
7516         *flop_image;
7517
7518       /*
7519         Flop image scanlines.
7520       */
7521       XSetCursorState(display,windows,MagickTrue);
7522       XCheckRefreshWindows(display,windows);
7523       flop_image=FlopImage(*image,exception);
7524       if (flop_image != (Image *) NULL)
7525         {
7526           *image=DestroyImage(*image);
7527           *image=flop_image;
7528         }
7529       CatchException(exception);
7530       XSetCursorState(display,windows,MagickFalse);
7531       if (windows->image.crop_geometry != (char *) NULL)
7532         {
7533           /*
7534             Flop crop geometry.
7535           */
7536           width=(unsigned int) (*image)->columns;
7537           height=(unsigned int) (*image)->rows;
7538           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7539             &width,&height);
7540           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7541             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7542         }
7543       if (windows->image.orphan != MagickFalse)
7544         break;
7545       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7546       break;
7547     }
7548     case FlipCommand:
7549     {
7550       Image
7551         *flip_image;
7552
7553       /*
7554         Flip image scanlines.
7555       */
7556       XSetCursorState(display,windows,MagickTrue);
7557       XCheckRefreshWindows(display,windows);
7558       flip_image=FlipImage(*image,exception);
7559       if (flip_image != (Image *) NULL)
7560         {
7561           *image=DestroyImage(*image);
7562           *image=flip_image;
7563         }
7564       CatchException(exception);
7565       XSetCursorState(display,windows,MagickFalse);
7566       if (windows->image.crop_geometry != (char *) NULL)
7567         {
7568           /*
7569             Flip crop geometry.
7570           */
7571           width=(unsigned int) (*image)->columns;
7572           height=(unsigned int) (*image)->rows;
7573           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7574             &width,&height);
7575           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7576             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7577         }
7578       if (windows->image.orphan != MagickFalse)
7579         break;
7580       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7581       break;
7582     }
7583     case RotateRightCommand:
7584     {
7585       /*
7586         Rotate image 90 degrees clockwise.
7587       */
7588       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7589       if (status == MagickFalse)
7590         {
7591           XNoticeWidget(display,windows,"Unable to rotate X image",
7592             (*image)->filename);
7593           break;
7594         }
7595       break;
7596     }
7597     case RotateLeftCommand:
7598     {
7599       /*
7600         Rotate image 90 degrees counter-clockwise.
7601       */
7602       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7603       if (status == MagickFalse)
7604         {
7605           XNoticeWidget(display,windows,"Unable to rotate X image",
7606             (*image)->filename);
7607           break;
7608         }
7609       break;
7610     }
7611     case RotateCommand:
7612     {
7613       /*
7614         Rotate image.
7615       */
7616       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7617       if (status == MagickFalse)
7618         {
7619           XNoticeWidget(display,windows,"Unable to rotate X image",
7620             (*image)->filename);
7621           break;
7622         }
7623       break;
7624     }
7625     case ShearCommand:
7626     {
7627       Image
7628         *shear_image;
7629
7630       static char
7631         geometry[MaxTextExtent] = "45.0x45.0";
7632
7633       /*
7634         Query user for shear color and geometry.
7635       */
7636       XColorBrowserWidget(display,windows,"Select",color);
7637       if (*color == '\0')
7638         break;
7639       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7640         geometry);
7641       if (*geometry == '\0')
7642         break;
7643       /*
7644         Shear image.
7645       */
7646       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7647         exception);
7648       XSetCursorState(display,windows,MagickTrue);
7649       XCheckRefreshWindows(display,windows);
7650       (void) QueryColorCompliance(color,AllCompliance,
7651         &(*image)->background_color,exception);
7652       flags=ParseGeometry(geometry,&geometry_info);
7653       if ((flags & SigmaValue) == 0)
7654         geometry_info.sigma=geometry_info.rho;
7655       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7656         exception);
7657       if (shear_image != (Image *) NULL)
7658         {
7659           *image=DestroyImage(*image);
7660           *image=shear_image;
7661         }
7662       CatchException(exception);
7663       XSetCursorState(display,windows,MagickFalse);
7664       if (windows->image.orphan != MagickFalse)
7665         break;
7666       windows->image.window_changes.width=(int) (*image)->columns;
7667       windows->image.window_changes.height=(int) (*image)->rows;
7668       XConfigureImageColormap(display,resource_info,windows,*image);
7669       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7670       break;
7671     }
7672     case RollCommand:
7673     {
7674       Image
7675         *roll_image;
7676
7677       static char
7678         geometry[MaxTextExtent] = "+2+2";
7679
7680       /*
7681         Query user for the roll geometry.
7682       */
7683       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7684         geometry);
7685       if (*geometry == '\0')
7686         break;
7687       /*
7688         Roll image.
7689       */
7690       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7691         exception);
7692       XSetCursorState(display,windows,MagickTrue);
7693       XCheckRefreshWindows(display,windows);
7694       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7695         exception);
7696       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7697         exception);
7698       if (roll_image != (Image *) NULL)
7699         {
7700           *image=DestroyImage(*image);
7701           *image=roll_image;
7702         }
7703       CatchException(exception);
7704       XSetCursorState(display,windows,MagickFalse);
7705       if (windows->image.orphan != MagickFalse)
7706         break;
7707       windows->image.window_changes.width=(int) (*image)->columns;
7708       windows->image.window_changes.height=(int) (*image)->rows;
7709       XConfigureImageColormap(display,resource_info,windows,*image);
7710       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7711       break;
7712     }
7713     case TrimCommand:
7714     {
7715       static char
7716         fuzz[MaxTextExtent];
7717
7718       /*
7719         Query user for the fuzz factor.
7720       */
7721       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7722         (*image)->fuzz/(QuantumRange+1.0));
7723       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7724       if (*fuzz == '\0')
7725         break;
7726       (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7727       /*
7728         Trim image.
7729       */
7730       status=XTrimImage(display,resource_info,windows,*image,exception);
7731       if (status == MagickFalse)
7732         {
7733           XNoticeWidget(display,windows,"Unable to trim X image",
7734             (*image)->filename);
7735           break;
7736         }
7737       break;
7738     }
7739     case HueCommand:
7740     {
7741       static char
7742         hue_percent[MaxTextExtent] = "110";
7743
7744       /*
7745         Query user for percent hue change.
7746       */
7747       (void) XDialogWidget(display,windows,"Apply",
7748         "Enter percent change in image hue (0-200):",hue_percent);
7749       if (*hue_percent == '\0')
7750         break;
7751       /*
7752         Vary the image hue.
7753       */
7754       XSetCursorState(display,windows,MagickTrue);
7755       XCheckRefreshWindows(display,windows);
7756       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7757       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7758         MaxTextExtent);
7759       (void) ModulateImage(*image,modulate_factors,exception);
7760       XSetCursorState(display,windows,MagickFalse);
7761       if (windows->image.orphan != MagickFalse)
7762         break;
7763       XConfigureImageColormap(display,resource_info,windows,*image);
7764       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7765       break;
7766     }
7767     case SaturationCommand:
7768     {
7769       static char
7770         saturation_percent[MaxTextExtent] = "110";
7771
7772       /*
7773         Query user for percent saturation change.
7774       */
7775       (void) XDialogWidget(display,windows,"Apply",
7776         "Enter percent change in color saturation (0-200):",saturation_percent);
7777       if (*saturation_percent == '\0')
7778         break;
7779       /*
7780         Vary color saturation.
7781       */
7782       XSetCursorState(display,windows,MagickTrue);
7783       XCheckRefreshWindows(display,windows);
7784       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7785       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7786         MaxTextExtent);
7787       (void) ModulateImage(*image,modulate_factors,exception);
7788       XSetCursorState(display,windows,MagickFalse);
7789       if (windows->image.orphan != MagickFalse)
7790         break;
7791       XConfigureImageColormap(display,resource_info,windows,*image);
7792       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7793       break;
7794     }
7795     case BrightnessCommand:
7796     {
7797       static char
7798         brightness_percent[MaxTextExtent] = "110";
7799
7800       /*
7801         Query user for percent brightness change.
7802       */
7803       (void) XDialogWidget(display,windows,"Apply",
7804         "Enter percent change in color brightness (0-200):",brightness_percent);
7805       if (*brightness_percent == '\0')
7806         break;
7807       /*
7808         Vary the color brightness.
7809       */
7810       XSetCursorState(display,windows,MagickTrue);
7811       XCheckRefreshWindows(display,windows);
7812       (void) CopyMagickString(modulate_factors,brightness_percent,
7813         MaxTextExtent);
7814       (void) ModulateImage(*image,modulate_factors,exception);
7815       XSetCursorState(display,windows,MagickFalse);
7816       if (windows->image.orphan != MagickFalse)
7817         break;
7818       XConfigureImageColormap(display,resource_info,windows,*image);
7819       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7820       break;
7821     }
7822     case GammaCommand:
7823     {
7824       static char
7825         factor[MaxTextExtent] = "1.6";
7826
7827       /*
7828         Query user for gamma value.
7829       */
7830       (void) XDialogWidget(display,windows,"Gamma",
7831         "Enter gamma value (e.g. 1.2):",factor);
7832       if (*factor == '\0')
7833         break;
7834       /*
7835         Gamma correct image.
7836       */
7837       XSetCursorState(display,windows,MagickTrue);
7838       XCheckRefreshWindows(display,windows);
7839       (void) GammaImage(*image,atof(factor),exception);
7840       XSetCursorState(display,windows,MagickFalse);
7841       if (windows->image.orphan != MagickFalse)
7842         break;
7843       XConfigureImageColormap(display,resource_info,windows,*image);
7844       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7845       break;
7846     }
7847     case SpiffCommand:
7848     {
7849       /*
7850         Sharpen the image contrast.
7851       */
7852       XSetCursorState(display,windows,MagickTrue);
7853       XCheckRefreshWindows(display,windows);
7854       (void) ContrastImage(*image,MagickTrue,exception);
7855       XSetCursorState(display,windows,MagickFalse);
7856       if (windows->image.orphan != MagickFalse)
7857         break;
7858       XConfigureImageColormap(display,resource_info,windows,*image);
7859       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7860       break;
7861     }
7862     case DullCommand:
7863     {
7864       /*
7865         Dull the image contrast.
7866       */
7867       XSetCursorState(display,windows,MagickTrue);
7868       XCheckRefreshWindows(display,windows);
7869       (void) ContrastImage(*image,MagickFalse,exception);
7870       XSetCursorState(display,windows,MagickFalse);
7871       if (windows->image.orphan != MagickFalse)
7872         break;
7873       XConfigureImageColormap(display,resource_info,windows,*image);
7874       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7875       break;
7876     }
7877     case ContrastStretchCommand:
7878     {
7879       double
7880         black_point,
7881         white_point;
7882
7883       static char
7884         levels[MaxTextExtent] = "1%";
7885
7886       /*
7887         Query user for gamma value.
7888       */
7889       (void) XDialogWidget(display,windows,"Contrast Stretch",
7890         "Enter black and white points:",levels);
7891       if (*levels == '\0')
7892         break;
7893       /*
7894         Contrast stretch image.
7895       */
7896       XSetCursorState(display,windows,MagickTrue);
7897       XCheckRefreshWindows(display,windows);
7898       flags=ParseGeometry(levels,&geometry_info);
7899       black_point=geometry_info.rho;
7900       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7901       if ((flags & PercentValue) != 0)
7902         {
7903           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7904           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7905         }
7906       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7907       (void) ContrastStretchImage(*image,black_point,white_point,
7908         exception);
7909       XSetCursorState(display,windows,MagickFalse);
7910       if (windows->image.orphan != MagickFalse)
7911         break;
7912       XConfigureImageColormap(display,resource_info,windows,*image);
7913       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7914       break;
7915     }
7916     case SigmoidalContrastCommand:
7917     {
7918       GeometryInfo
7919         geometry_info;
7920
7921       MagickStatusType
7922         flags;
7923
7924       static char
7925         levels[MaxTextExtent] = "3x50%";
7926
7927       /*
7928         Query user for gamma value.
7929       */
7930       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7931         "Enter contrast and midpoint:",levels);
7932       if (*levels == '\0')
7933         break;
7934       /*
7935         Contrast stretch image.
7936       */
7937       XSetCursorState(display,windows,MagickTrue);
7938       XCheckRefreshWindows(display,windows);
7939       flags=ParseGeometry(levels,&geometry_info);
7940       if ((flags & SigmaValue) == 0)
7941         geometry_info.sigma=1.0*QuantumRange/2.0;
7942       if ((flags & PercentValue) != 0)
7943         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7944       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7945         geometry_info.sigma,exception);
7946       XSetCursorState(display,windows,MagickFalse);
7947       if (windows->image.orphan != MagickFalse)
7948         break;
7949       XConfigureImageColormap(display,resource_info,windows,*image);
7950       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7951       break;
7952     }
7953     case NormalizeCommand:
7954     {
7955       /*
7956         Perform histogram normalization on the image.
7957       */
7958       XSetCursorState(display,windows,MagickTrue);
7959       XCheckRefreshWindows(display,windows);
7960       (void) NormalizeImage(*image,exception);
7961       XSetCursorState(display,windows,MagickFalse);
7962       if (windows->image.orphan != MagickFalse)
7963         break;
7964       XConfigureImageColormap(display,resource_info,windows,*image);
7965       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7966       break;
7967     }
7968     case EqualizeCommand:
7969     {
7970       /*
7971         Perform histogram equalization on the image.
7972       */
7973       XSetCursorState(display,windows,MagickTrue);
7974       XCheckRefreshWindows(display,windows);
7975       (void) EqualizeImage(*image,exception);
7976       XSetCursorState(display,windows,MagickFalse);
7977       if (windows->image.orphan != MagickFalse)
7978         break;
7979       XConfigureImageColormap(display,resource_info,windows,*image);
7980       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7981       break;
7982     }
7983     case NegateCommand:
7984     {
7985       /*
7986         Negate colors in image.
7987       */
7988       XSetCursorState(display,windows,MagickTrue);
7989       XCheckRefreshWindows(display,windows);
7990       (void) NegateImage(*image,MagickFalse,exception);
7991       XSetCursorState(display,windows,MagickFalse);
7992       if (windows->image.orphan != MagickFalse)
7993         break;
7994       XConfigureImageColormap(display,resource_info,windows,*image);
7995       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7996       break;
7997     }
7998     case GrayscaleCommand:
7999     {
8000       /*
8001         Convert image to grayscale.
8002       */
8003       XSetCursorState(display,windows,MagickTrue);
8004       XCheckRefreshWindows(display,windows);
8005       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
8006         GrayscaleType : GrayscaleMatteType,exception);
8007       XSetCursorState(display,windows,MagickFalse);
8008       if (windows->image.orphan != MagickFalse)
8009         break;
8010       XConfigureImageColormap(display,resource_info,windows,*image);
8011       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8012       break;
8013     }
8014     case MapCommand:
8015     {
8016       Image
8017         *affinity_image;
8018
8019       static char
8020         filename[MaxTextExtent] = "\0";
8021
8022       /*
8023         Request image file name from user.
8024       */
8025       XFileBrowserWidget(display,windows,"Map",filename);
8026       if (*filename == '\0')
8027         break;
8028       /*
8029         Map image.
8030       */
8031       XSetCursorState(display,windows,MagickTrue);
8032       XCheckRefreshWindows(display,windows);
8033       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8034       affinity_image=ReadImage(image_info,exception);
8035       if (affinity_image != (Image *) NULL)
8036         {
8037           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8038           affinity_image=DestroyImage(affinity_image);
8039         }
8040       CatchException(exception);
8041       XSetCursorState(display,windows,MagickFalse);
8042       if (windows->image.orphan != MagickFalse)
8043         break;
8044       XConfigureImageColormap(display,resource_info,windows,*image);
8045       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8046       break;
8047     }
8048     case QuantizeCommand:
8049     {
8050       int
8051         status;
8052
8053       static char
8054         colors[MaxTextExtent] = "256";
8055
8056       /*
8057         Query user for maximum number of colors.
8058       */
8059       status=XDialogWidget(display,windows,"Quantize",
8060         "Maximum number of colors:",colors);
8061       if (*colors == '\0')
8062         break;
8063       /*
8064         Color reduce the image.
8065       */
8066       XSetCursorState(display,windows,MagickTrue);
8067       XCheckRefreshWindows(display,windows);
8068       quantize_info.number_colors=StringToUnsignedLong(colors);
8069       quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8070       (void) QuantizeImage(&quantize_info,*image,exception);
8071       XSetCursorState(display,windows,MagickFalse);
8072       if (windows->image.orphan != MagickFalse)
8073         break;
8074       XConfigureImageColormap(display,resource_info,windows,*image);
8075       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8076       break;
8077     }
8078     case DespeckleCommand:
8079     {
8080       Image
8081         *despeckle_image;
8082
8083       /*
8084         Despeckle image.
8085       */
8086       XSetCursorState(display,windows,MagickTrue);
8087       XCheckRefreshWindows(display,windows);
8088       despeckle_image=DespeckleImage(*image,exception);
8089       if (despeckle_image != (Image *) NULL)
8090         {
8091           *image=DestroyImage(*image);
8092           *image=despeckle_image;
8093         }
8094       CatchException(exception);
8095       XSetCursorState(display,windows,MagickFalse);
8096       if (windows->image.orphan != MagickFalse)
8097         break;
8098       XConfigureImageColormap(display,resource_info,windows,*image);
8099       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8100       break;
8101     }
8102     case EmbossCommand:
8103     {
8104       Image
8105         *emboss_image;
8106
8107       static char
8108         radius[MaxTextExtent] = "0.0x1.0";
8109
8110       /*
8111         Query user for emboss radius.
8112       */
8113       (void) XDialogWidget(display,windows,"Emboss",
8114         "Enter the emboss radius and standard deviation:",radius);
8115       if (*radius == '\0')
8116         break;
8117       /*
8118         Reduce noise in the image.
8119       */
8120       XSetCursorState(display,windows,MagickTrue);
8121       XCheckRefreshWindows(display,windows);
8122       flags=ParseGeometry(radius,&geometry_info);
8123       if ((flags & SigmaValue) == 0)
8124         geometry_info.sigma=1.0;
8125       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8126         exception);
8127       if (emboss_image != (Image *) NULL)
8128         {
8129           *image=DestroyImage(*image);
8130           *image=emboss_image;
8131         }
8132       CatchException(exception);
8133       XSetCursorState(display,windows,MagickFalse);
8134       if (windows->image.orphan != MagickFalse)
8135         break;
8136       XConfigureImageColormap(display,resource_info,windows,*image);
8137       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8138       break;
8139     }
8140     case ReduceNoiseCommand:
8141     {
8142       Image
8143         *noise_image;
8144
8145       static char
8146         radius[MaxTextExtent] = "0";
8147
8148       /*
8149         Query user for noise radius.
8150       */
8151       (void) XDialogWidget(display,windows,"Reduce Noise",
8152         "Enter the noise radius:",radius);
8153       if (*radius == '\0')
8154         break;
8155       /*
8156         Reduce noise in the image.
8157       */
8158       XSetCursorState(display,windows,MagickTrue);
8159       XCheckRefreshWindows(display,windows);
8160       flags=ParseGeometry(radius,&geometry_info);
8161       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8162         geometry_info.rho,(size_t) geometry_info.rho,exception);
8163       if (noise_image != (Image *) NULL)
8164         {
8165           *image=DestroyImage(*image);
8166           *image=noise_image;
8167         }
8168       CatchException(exception);
8169       XSetCursorState(display,windows,MagickFalse);
8170       if (windows->image.orphan != MagickFalse)
8171         break;
8172       XConfigureImageColormap(display,resource_info,windows,*image);
8173       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8174       break;
8175     }
8176     case AddNoiseCommand:
8177     {
8178       char
8179         **noises;
8180
8181       Image
8182         *noise_image;
8183
8184       static char
8185         noise_type[MaxTextExtent] = "Gaussian";
8186
8187       /*
8188         Add noise to the image.
8189       */
8190       noises=GetCommandOptions(MagickNoiseOptions);
8191       if (noises == (char **) NULL)
8192         break;
8193       XListBrowserWidget(display,windows,&windows->widget,
8194         (const char **) noises,"Add Noise",
8195         "Select a type of noise to add to your image:",noise_type);
8196       noises=DestroyStringList(noises);
8197       if (*noise_type == '\0')
8198         break;
8199       XSetCursorState(display,windows,MagickTrue);
8200       XCheckRefreshWindows(display,windows);
8201       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8202         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8203       if (noise_image != (Image *) NULL)
8204         {
8205           *image=DestroyImage(*image);
8206           *image=noise_image;
8207         }
8208       CatchException(exception);
8209       XSetCursorState(display,windows,MagickFalse);
8210       if (windows->image.orphan != MagickFalse)
8211         break;
8212       XConfigureImageColormap(display,resource_info,windows,*image);
8213       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8214       break;
8215     }
8216     case SharpenCommand:
8217     {
8218       Image
8219         *sharp_image;
8220
8221       static char
8222         radius[MaxTextExtent] = "0.0x1.0";
8223
8224       /*
8225         Query user for sharpen radius.
8226       */
8227       (void) XDialogWidget(display,windows,"Sharpen",
8228         "Enter the sharpen radius and standard deviation:",radius);
8229       if (*radius == '\0')
8230         break;
8231       /*
8232         Sharpen image scanlines.
8233       */
8234       XSetCursorState(display,windows,MagickTrue);
8235       XCheckRefreshWindows(display,windows);
8236       flags=ParseGeometry(radius,&geometry_info);
8237       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8238         geometry_info.xi,exception);
8239       if (sharp_image != (Image *) NULL)
8240         {
8241           *image=DestroyImage(*image);
8242           *image=sharp_image;
8243         }
8244       CatchException(exception);
8245       XSetCursorState(display,windows,MagickFalse);
8246       if (windows->image.orphan != MagickFalse)
8247         break;
8248       XConfigureImageColormap(display,resource_info,windows,*image);
8249       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8250       break;
8251     }
8252     case BlurCommand:
8253     {
8254       Image
8255         *blur_image;
8256
8257       static char
8258         radius[MaxTextExtent] = "0.0x1.0";
8259
8260       /*
8261         Query user for blur radius.
8262       */
8263       (void) XDialogWidget(display,windows,"Blur",
8264         "Enter the blur radius and standard deviation:",radius);
8265       if (*radius == '\0')
8266         break;
8267       /*
8268         Blur an image.
8269       */
8270       XSetCursorState(display,windows,MagickTrue);
8271       XCheckRefreshWindows(display,windows);
8272       flags=ParseGeometry(radius,&geometry_info);
8273       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8274         geometry_info.xi,exception);
8275       if (blur_image != (Image *) NULL)
8276         {
8277           *image=DestroyImage(*image);
8278           *image=blur_image;
8279         }
8280       CatchException(exception);
8281       XSetCursorState(display,windows,MagickFalse);
8282       if (windows->image.orphan != MagickFalse)
8283         break;
8284       XConfigureImageColormap(display,resource_info,windows,*image);
8285       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8286       break;
8287     }
8288     case ThresholdCommand:
8289     {
8290       double
8291         threshold;
8292
8293       static char
8294         factor[MaxTextExtent] = "128";
8295
8296       /*
8297         Query user for threshold value.
8298       */
8299       (void) XDialogWidget(display,windows,"Threshold",
8300         "Enter threshold value:",factor);
8301       if (*factor == '\0')
8302         break;
8303       /*
8304         Gamma correct image.
8305       */
8306       XSetCursorState(display,windows,MagickTrue);
8307       XCheckRefreshWindows(display,windows);
8308       threshold=SiPrefixToDouble(factor,QuantumRange);
8309       (void) BilevelImage(*image,threshold);
8310       XSetCursorState(display,windows,MagickFalse);
8311       if (windows->image.orphan != MagickFalse)
8312         break;
8313       XConfigureImageColormap(display,resource_info,windows,*image);
8314       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8315       break;
8316     }
8317     case EdgeDetectCommand:
8318     {
8319       Image
8320         *edge_image;
8321
8322       static char
8323         radius[MaxTextExtent] = "0";
8324
8325       /*
8326         Query user for edge factor.
8327       */
8328       (void) XDialogWidget(display,windows,"Detect Edges",
8329         "Enter the edge detect radius:",radius);
8330       if (*radius == '\0')
8331         break;
8332       /*
8333         Detect edge in image.
8334       */
8335       XSetCursorState(display,windows,MagickTrue);
8336       XCheckRefreshWindows(display,windows);
8337       flags=ParseGeometry(radius,&geometry_info);
8338       edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8339         exception);
8340       if (edge_image != (Image *) NULL)
8341         {
8342           *image=DestroyImage(*image);
8343           *image=edge_image;
8344         }
8345       CatchException(exception);
8346       XSetCursorState(display,windows,MagickFalse);
8347       if (windows->image.orphan != MagickFalse)
8348         break;
8349       XConfigureImageColormap(display,resource_info,windows,*image);
8350       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8351       break;
8352     }
8353     case SpreadCommand:
8354     {
8355       Image
8356         *spread_image;
8357
8358       static char
8359         amount[MaxTextExtent] = "2";
8360
8361       /*
8362         Query user for spread amount.
8363       */
8364       (void) XDialogWidget(display,windows,"Spread",
8365         "Enter the displacement amount:",amount);
8366       if (*amount == '\0')
8367         break;
8368       /*
8369         Displace image pixels by a random amount.
8370       */
8371       XSetCursorState(display,windows,MagickTrue);
8372       XCheckRefreshWindows(display,windows);
8373       flags=ParseGeometry(amount,&geometry_info);
8374       spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8375         exception);
8376       if (spread_image != (Image *) NULL)
8377         {
8378           *image=DestroyImage(*image);
8379           *image=spread_image;
8380         }
8381       CatchException(exception);
8382       XSetCursorState(display,windows,MagickFalse);
8383       if (windows->image.orphan != MagickFalse)
8384         break;
8385       XConfigureImageColormap(display,resource_info,windows,*image);
8386       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8387       break;
8388     }
8389     case ShadeCommand:
8390     {
8391       Image
8392         *shade_image;
8393
8394       int
8395         status;
8396
8397       static char
8398         geometry[MaxTextExtent] = "30x30";
8399
8400       /*
8401         Query user for the shade geometry.
8402       */
8403       status=XDialogWidget(display,windows,"Shade",
8404         "Enter the azimuth and elevation of the light source:",geometry);
8405       if (*geometry == '\0')
8406         break;
8407       /*
8408         Shade image pixels.
8409       */
8410       XSetCursorState(display,windows,MagickTrue);
8411       XCheckRefreshWindows(display,windows);
8412       flags=ParseGeometry(geometry,&geometry_info);
8413       if ((flags & SigmaValue) == 0)
8414         geometry_info.sigma=1.0;
8415       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8416         geometry_info.rho,geometry_info.sigma,exception);
8417       if (shade_image != (Image *) NULL)
8418         {
8419           *image=DestroyImage(*image);
8420           *image=shade_image;
8421         }
8422       CatchException(exception);
8423       XSetCursorState(display,windows,MagickFalse);
8424       if (windows->image.orphan != MagickFalse)
8425         break;
8426       XConfigureImageColormap(display,resource_info,windows,*image);
8427       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8428       break;
8429     }
8430     case RaiseCommand:
8431     {
8432       static char
8433         bevel_width[MaxTextExtent] = "10";
8434
8435       /*
8436         Query user for bevel width.
8437       */
8438       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8439       if (*bevel_width == '\0')
8440         break;
8441       /*
8442         Raise an image.
8443       */
8444       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8445         exception);
8446       XSetCursorState(display,windows,MagickTrue);
8447       XCheckRefreshWindows(display,windows);
8448       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8449         exception);
8450       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8451       XSetCursorState(display,windows,MagickFalse);
8452       if (windows->image.orphan != MagickFalse)
8453         break;
8454       XConfigureImageColormap(display,resource_info,windows,*image);
8455       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8456       break;
8457     }
8458     case SegmentCommand:
8459     {
8460       static char
8461         threshold[MaxTextExtent] = "1.0x1.5";
8462
8463       /*
8464         Query user for smoothing threshold.
8465       */
8466       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8467         threshold);
8468       if (*threshold == '\0')
8469         break;
8470       /*
8471         Segment an image.
8472       */
8473       XSetCursorState(display,windows,MagickTrue);
8474       XCheckRefreshWindows(display,windows);
8475       flags=ParseGeometry(threshold,&geometry_info);
8476       if ((flags & SigmaValue) == 0)
8477         geometry_info.sigma=1.0;
8478       (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8479         geometry_info.sigma,exception);
8480       XSetCursorState(display,windows,MagickFalse);
8481       if (windows->image.orphan != MagickFalse)
8482         break;
8483       XConfigureImageColormap(display,resource_info,windows,*image);
8484       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8485       break;
8486     }
8487     case SepiaToneCommand:
8488     {
8489       double
8490         threshold;
8491
8492       Image
8493         *sepia_image;
8494
8495       static char
8496         factor[MaxTextExtent] = "80%";
8497
8498       /*
8499         Query user for sepia-tone factor.
8500       */
8501       (void) XDialogWidget(display,windows,"Sepia Tone",
8502         "Enter the sepia tone factor (0 - 99.9%):",factor);
8503       if (*factor == '\0')
8504         break;
8505       /*
8506         Sepia tone image pixels.
8507       */
8508       XSetCursorState(display,windows,MagickTrue);
8509       XCheckRefreshWindows(display,windows);
8510       threshold=SiPrefixToDouble(factor,QuantumRange);
8511       sepia_image=SepiaToneImage(*image,threshold,exception);
8512       if (sepia_image != (Image *) NULL)
8513         {
8514           *image=DestroyImage(*image);
8515           *image=sepia_image;
8516         }
8517       CatchException(exception);
8518       XSetCursorState(display,windows,MagickFalse);
8519       if (windows->image.orphan != MagickFalse)
8520         break;
8521       XConfigureImageColormap(display,resource_info,windows,*image);
8522       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8523       break;
8524     }
8525     case SolarizeCommand:
8526     {
8527       double
8528         threshold;
8529
8530       static char
8531         factor[MaxTextExtent] = "60%";
8532
8533       /*
8534         Query user for solarize factor.
8535       */
8536       (void) XDialogWidget(display,windows,"Solarize",
8537         "Enter the solarize factor (0 - 99.9%):",factor);
8538       if (*factor == '\0')
8539         break;
8540       /*
8541         Solarize image pixels.
8542       */
8543       XSetCursorState(display,windows,MagickTrue);
8544       XCheckRefreshWindows(display,windows);
8545       threshold=SiPrefixToDouble(factor,QuantumRange);
8546       (void) SolarizeImage(*image,threshold,exception);
8547       XSetCursorState(display,windows,MagickFalse);
8548       if (windows->image.orphan != MagickFalse)
8549         break;
8550       XConfigureImageColormap(display,resource_info,windows,*image);
8551       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8552       break;
8553     }
8554     case SwirlCommand:
8555     {
8556       Image
8557         *swirl_image;
8558
8559       static char
8560         degrees[MaxTextExtent] = "60";
8561
8562       /*
8563         Query user for swirl angle.
8564       */
8565       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8566         degrees);
8567       if (*degrees == '\0')
8568         break;
8569       /*
8570         Swirl image pixels about the center.
8571       */
8572       XSetCursorState(display,windows,MagickTrue);
8573       XCheckRefreshWindows(display,windows);
8574       flags=ParseGeometry(degrees,&geometry_info);
8575       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8576         exception);
8577       if (swirl_image != (Image *) NULL)
8578         {
8579           *image=DestroyImage(*image);
8580           *image=swirl_image;
8581         }
8582       CatchException(exception);
8583       XSetCursorState(display,windows,MagickFalse);
8584       if (windows->image.orphan != MagickFalse)
8585         break;
8586       XConfigureImageColormap(display,resource_info,windows,*image);
8587       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8588       break;
8589     }
8590     case ImplodeCommand:
8591     {
8592       Image
8593         *implode_image;
8594
8595       static char
8596         factor[MaxTextExtent] = "0.3";
8597
8598       /*
8599         Query user for implode factor.
8600       */
8601       (void) XDialogWidget(display,windows,"Implode",
8602         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8603       if (*factor == '\0')
8604         break;
8605       /*
8606         Implode image pixels about the center.
8607       */
8608       XSetCursorState(display,windows,MagickTrue);
8609       XCheckRefreshWindows(display,windows);
8610       flags=ParseGeometry(factor,&geometry_info);
8611       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8612         exception);
8613       if (implode_image != (Image *) NULL)
8614         {
8615           *image=DestroyImage(*image);
8616           *image=implode_image;
8617         }
8618       CatchException(exception);
8619       XSetCursorState(display,windows,MagickFalse);
8620       if (windows->image.orphan != MagickFalse)
8621         break;
8622       XConfigureImageColormap(display,resource_info,windows,*image);
8623       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8624       break;
8625     }
8626     case VignetteCommand:
8627     {
8628       Image
8629         *vignette_image;
8630
8631       static char
8632         geometry[MaxTextExtent] = "0x20";
8633
8634       /*
8635         Query user for the vignette geometry.
8636       */
8637       (void) XDialogWidget(display,windows,"Vignette",
8638         "Enter the radius, sigma, and x and y offsets:",geometry);
8639       if (*geometry == '\0')
8640         break;
8641       /*
8642         Soften the edges of the image in vignette style
8643       */
8644       XSetCursorState(display,windows,MagickTrue);
8645       XCheckRefreshWindows(display,windows);
8646       flags=ParseGeometry(geometry,&geometry_info);
8647       if ((flags & SigmaValue) == 0)
8648         geometry_info.sigma=1.0;
8649       if ((flags & XiValue) == 0)
8650         geometry_info.xi=0.1*(*image)->columns;
8651       if ((flags & PsiValue) == 0)
8652         geometry_info.psi=0.1*(*image)->rows;
8653       vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8654         (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8655         0.5),exception);
8656       if (vignette_image != (Image *) NULL)
8657         {
8658           *image=DestroyImage(*image);
8659           *image=vignette_image;
8660         }
8661       CatchException(exception);
8662       XSetCursorState(display,windows,MagickFalse);
8663       if (windows->image.orphan != MagickFalse)
8664         break;
8665       XConfigureImageColormap(display,resource_info,windows,*image);
8666       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8667       break;
8668     }
8669     case WaveCommand:
8670     {
8671       Image
8672         *wave_image;
8673
8674       static char
8675         geometry[MaxTextExtent] = "25x150";
8676
8677       /*
8678         Query user for the wave geometry.
8679       */
8680       (void) XDialogWidget(display,windows,"Wave",
8681         "Enter the amplitude and length of the wave:",geometry);
8682       if (*geometry == '\0')
8683         break;
8684       /*
8685         Alter an image along a sine wave.
8686       */
8687       XSetCursorState(display,windows,MagickTrue);
8688       XCheckRefreshWindows(display,windows);
8689       flags=ParseGeometry(geometry,&geometry_info);
8690       if ((flags & SigmaValue) == 0)
8691         geometry_info.sigma=1.0;
8692       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8693         (*image)->interpolate,exception);
8694       if (wave_image != (Image *) NULL)
8695         {
8696           *image=DestroyImage(*image);
8697           *image=wave_image;
8698         }
8699       CatchException(exception);
8700       XSetCursorState(display,windows,MagickFalse);
8701       if (windows->image.orphan != MagickFalse)
8702         break;
8703       XConfigureImageColormap(display,resource_info,windows,*image);
8704       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8705       break;
8706     }
8707     case OilPaintCommand:
8708     {
8709       Image
8710         *paint_image;
8711
8712       static char
8713         radius[MaxTextExtent] = "0";
8714
8715       /*
8716         Query user for circular neighborhood radius.
8717       */
8718       (void) XDialogWidget(display,windows,"Oil Paint",
8719         "Enter the mask radius:",radius);
8720       if (*radius == '\0')
8721         break;
8722       /*
8723         OilPaint image scanlines.
8724       */
8725       XSetCursorState(display,windows,MagickTrue);
8726       XCheckRefreshWindows(display,windows);
8727       flags=ParseGeometry(radius,&geometry_info);
8728       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8729         exception);
8730       if (paint_image != (Image *) NULL)
8731         {
8732           *image=DestroyImage(*image);
8733           *image=paint_image;
8734         }
8735       CatchException(exception);
8736       XSetCursorState(display,windows,MagickFalse);
8737       if (windows->image.orphan != MagickFalse)
8738         break;
8739       XConfigureImageColormap(display,resource_info,windows,*image);
8740       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8741       break;
8742     }
8743     case CharcoalDrawCommand:
8744     {
8745       Image
8746         *charcoal_image;
8747
8748       static char
8749         radius[MaxTextExtent] = "0x1";
8750
8751       /*
8752         Query user for charcoal radius.
8753       */
8754       (void) XDialogWidget(display,windows,"Charcoal Draw",
8755         "Enter the charcoal radius and sigma:",radius);
8756       if (*radius == '\0')
8757         break;
8758       /*
8759         Charcoal the image.
8760       */
8761       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8762         exception);
8763       XSetCursorState(display,windows,MagickTrue);
8764       XCheckRefreshWindows(display,windows);
8765       flags=ParseGeometry(radius,&geometry_info);
8766       if ((flags & SigmaValue) == 0)
8767         geometry_info.sigma=geometry_info.rho;
8768       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8769         geometry_info.xi,exception);
8770       if (charcoal_image != (Image *) NULL)
8771         {
8772           *image=DestroyImage(*image);
8773           *image=charcoal_image;
8774         }
8775       CatchException(exception);
8776       XSetCursorState(display,windows,MagickFalse);
8777       if (windows->image.orphan != MagickFalse)
8778         break;
8779       XConfigureImageColormap(display,resource_info,windows,*image);
8780       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8781       break;
8782     }
8783     case AnnotateCommand:
8784     {
8785       /*
8786         Annotate the image with text.
8787       */
8788       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8789       if (status == MagickFalse)
8790         {
8791           XNoticeWidget(display,windows,"Unable to annotate X image",
8792             (*image)->filename);
8793           break;
8794         }
8795       break;
8796     }
8797     case DrawCommand:
8798     {
8799       /*
8800         Draw image.
8801       */
8802       status=XDrawEditImage(display,resource_info,windows,image,exception);
8803       if (status == MagickFalse)
8804         {
8805           XNoticeWidget(display,windows,"Unable to draw on the X image",
8806             (*image)->filename);
8807           break;
8808         }
8809       break;
8810     }
8811     case ColorCommand:
8812     {
8813       /*
8814         Color edit.
8815       */
8816       status=XColorEditImage(display,resource_info,windows,image,exception);
8817       if (status == MagickFalse)
8818         {
8819           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8820             (*image)->filename);
8821           break;
8822         }
8823       break;
8824     }
8825     case MatteCommand:
8826     {
8827       /*
8828         Matte edit.
8829       */
8830       status=XMatteEditImage(display,resource_info,windows,image,exception);
8831       if (status == MagickFalse)
8832         {
8833           XNoticeWidget(display,windows,"Unable to matte edit X image",
8834             (*image)->filename);
8835           break;
8836         }
8837       break;
8838     }
8839     case CompositeCommand:
8840     {
8841       /*
8842         Composite image.
8843       */
8844       status=XCompositeImage(display,resource_info,windows,*image,
8845         exception);
8846       if (status == MagickFalse)
8847         {
8848           XNoticeWidget(display,windows,"Unable to composite X image",
8849             (*image)->filename);
8850           break;
8851         }
8852       break;
8853     }
8854     case AddBorderCommand:
8855     {
8856       Image
8857         *border_image;
8858
8859       static char
8860         geometry[MaxTextExtent] = "6x6";
8861
8862       /*
8863         Query user for border color and geometry.
8864       */
8865       XColorBrowserWidget(display,windows,"Select",color);
8866       if (*color == '\0')
8867         break;
8868       (void) XDialogWidget(display,windows,"Add Border",
8869         "Enter border geometry:",geometry);
8870       if (*geometry == '\0')
8871         break;
8872       /*
8873         Add a border to the image.
8874       */
8875       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8876         exception);
8877       XSetCursorState(display,windows,MagickTrue);
8878       XCheckRefreshWindows(display,windows);
8879       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8880         exception);
8881       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8882         exception);
8883       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8884         exception);
8885       if (border_image != (Image *) NULL)
8886         {
8887           *image=DestroyImage(*image);
8888           *image=border_image;
8889         }
8890       CatchException(exception);
8891       XSetCursorState(display,windows,MagickFalse);
8892       if (windows->image.orphan != MagickFalse)
8893         break;
8894       windows->image.window_changes.width=(int) (*image)->columns;
8895       windows->image.window_changes.height=(int) (*image)->rows;
8896       XConfigureImageColormap(display,resource_info,windows,*image);
8897       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8898       break;
8899     }
8900     case AddFrameCommand:
8901     {
8902       FrameInfo
8903         frame_info;
8904
8905       Image
8906         *frame_image;
8907
8908       static char
8909         geometry[MaxTextExtent] = "6x6";
8910
8911       /*
8912         Query user for frame color and geometry.
8913       */
8914       XColorBrowserWidget(display,windows,"Select",color);
8915       if (*color == '\0')
8916         break;
8917       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8918         geometry);
8919       if (*geometry == '\0')
8920         break;
8921       /*
8922         Surround image with an ornamental border.
8923       */
8924       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8925         exception);
8926       XSetCursorState(display,windows,MagickTrue);
8927       XCheckRefreshWindows(display,windows);
8928       (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8929         exception);
8930       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8931         exception);
8932       frame_info.width=page_geometry.width;
8933       frame_info.height=page_geometry.height;
8934       frame_info.outer_bevel=page_geometry.x;
8935       frame_info.inner_bevel=page_geometry.y;
8936       frame_info.x=(ssize_t) frame_info.width;
8937       frame_info.y=(ssize_t) frame_info.height;
8938       frame_info.width=(*image)->columns+2*frame_info.width;
8939       frame_info.height=(*image)->rows+2*frame_info.height;
8940       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8941       if (frame_image != (Image *) NULL)
8942         {
8943           *image=DestroyImage(*image);
8944           *image=frame_image;
8945         }
8946       CatchException(exception);
8947       XSetCursorState(display,windows,MagickFalse);
8948       if (windows->image.orphan != MagickFalse)
8949         break;
8950       windows->image.window_changes.width=(int) (*image)->columns;
8951       windows->image.window_changes.height=(int) (*image)->rows;
8952       XConfigureImageColormap(display,resource_info,windows,*image);
8953       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8954       break;
8955     }
8956     case CommentCommand:
8957     {
8958       const char
8959         *value;
8960
8961       FILE
8962         *file;
8963
8964       int
8965         unique_file;
8966
8967       /*
8968         Edit image comment.
8969       */
8970       unique_file=AcquireUniqueFileResource(image_info->filename);
8971       if (unique_file == -1)
8972         XNoticeWidget(display,windows,"Unable to edit image comment",
8973           image_info->filename);
8974       value=GetImageProperty(*image,"comment");
8975       if (value == (char *) NULL)
8976         unique_file=close(unique_file)-1;
8977       else
8978         {
8979           register const char
8980             *p;
8981
8982           file=fdopen(unique_file,"w");
8983           if (file == (FILE *) NULL)
8984             {
8985               XNoticeWidget(display,windows,"Unable to edit image comment",
8986                 image_info->filename);
8987               break;
8988             }
8989           for (p=value; *p != '\0'; p++)
8990             (void) fputc((int) *p,file);
8991           (void) fputc('\n',file);
8992           (void) fclose(file);
8993         }
8994       XSetCursorState(display,windows,MagickTrue);
8995       XCheckRefreshWindows(display,windows);
8996       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8997         exception);
8998       if (status == MagickFalse)
8999         XNoticeWidget(display,windows,"Unable to edit image comment",
9000           (char *) NULL);
9001       else
9002         {
9003           char
9004             *comment;
9005
9006           comment=FileToString(image_info->filename,~0UL,exception);
9007           if (comment != (char *) NULL)
9008             {
9009               (void) SetImageProperty(*image,"comment",comment);
9010               (*image)->taint=MagickTrue;
9011             }
9012         }
9013       (void) RelinquishUniqueFileResource(image_info->filename);
9014       XSetCursorState(display,windows,MagickFalse);
9015       break;
9016     }
9017     case LaunchCommand:
9018     {
9019       /*
9020         Launch program.
9021       */
9022       XSetCursorState(display,windows,MagickTrue);
9023       XCheckRefreshWindows(display,windows);
9024       (void) AcquireUniqueFilename(filename);
9025       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9026         filename);
9027       status=WriteImage(image_info,*image,exception);
9028       if (status == MagickFalse)
9029         XNoticeWidget(display,windows,"Unable to launch image editor",
9030           (char *) NULL);
9031       else
9032         {
9033           nexus=ReadImage(resource_info->image_info,exception);
9034           CatchException(exception);
9035           XClientMessage(display,windows->image.id,windows->im_protocols,
9036             windows->im_next_image,CurrentTime);
9037         }
9038       (void) RelinquishUniqueFileResource(filename);
9039       XSetCursorState(display,windows,MagickFalse);
9040       break;
9041     }
9042     case RegionofInterestCommand:
9043     {
9044       /*
9045         Apply an image processing technique to a region of interest.
9046       */
9047       (void) XROIImage(display,resource_info,windows,image,exception);
9048       break;
9049     }
9050     case InfoCommand:
9051       break;
9052     case ZoomCommand:
9053     {
9054       /*
9055         Zoom image.
9056       */
9057       if (windows->magnify.mapped != MagickFalse)
9058         (void) XRaiseWindow(display,windows->magnify.id);
9059       else
9060         {
9061           /*
9062             Make magnify image.
9063           */
9064           XSetCursorState(display,windows,MagickTrue);
9065           (void) XMapRaised(display,windows->magnify.id);
9066           XSetCursorState(display,windows,MagickFalse);
9067         }
9068       break;
9069     }
9070     case ShowPreviewCommand:
9071     {
9072       char
9073         **previews;
9074
9075       Image
9076         *preview_image;
9077
9078       static char
9079         preview_type[MaxTextExtent] = "Gamma";
9080
9081       /*
9082         Select preview type from menu.
9083       */
9084       previews=GetCommandOptions(MagickPreviewOptions);
9085       if (previews == (char **) NULL)
9086         break;
9087       XListBrowserWidget(display,windows,&windows->widget,
9088         (const char **) previews,"Preview",
9089         "Select an enhancement, effect, or F/X:",preview_type);
9090       previews=DestroyStringList(previews);
9091       if (*preview_type == '\0')
9092         break;
9093       /*
9094         Show image preview.
9095       */
9096       XSetCursorState(display,windows,MagickTrue);
9097       XCheckRefreshWindows(display,windows);
9098       image_info->preview_type=(PreviewType)
9099         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9100       image_info->group=(ssize_t) windows->image.id;
9101       (void) DeleteImageProperty(*image,"label");
9102       (void) SetImageProperty(*image,"label","Preview");
9103       (void) AcquireUniqueFilename(filename);
9104       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9105         filename);
9106       status=WriteImage(image_info,*image,exception);
9107       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9108       preview_image=ReadImage(image_info,exception);
9109       (void) RelinquishUniqueFileResource(filename);
9110       if (preview_image == (Image *) NULL)
9111         break;
9112       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9113         filename);
9114       status=WriteImage(image_info,preview_image,exception);
9115       preview_image=DestroyImage(preview_image);
9116       if (status == MagickFalse)
9117         XNoticeWidget(display,windows,"Unable to show image preview",
9118           (*image)->filename);
9119       XDelay(display,1500);
9120       XSetCursorState(display,windows,MagickFalse);
9121       break;
9122     }
9123     case ShowHistogramCommand:
9124     {
9125       Image
9126         *histogram_image;
9127
9128       /*
9129         Show image histogram.
9130       */
9131       XSetCursorState(display,windows,MagickTrue);
9132       XCheckRefreshWindows(display,windows);
9133       image_info->group=(ssize_t) windows->image.id;
9134       (void) DeleteImageProperty(*image,"label");
9135       (void) SetImageProperty(*image,"label","Histogram");
9136       (void) AcquireUniqueFilename(filename);
9137       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9138         filename);
9139       status=WriteImage(image_info,*image,exception);
9140       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9141       histogram_image=ReadImage(image_info,exception);
9142       (void) RelinquishUniqueFileResource(filename);
9143       if (histogram_image == (Image *) NULL)
9144         break;
9145       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9146         "show:%s",filename);
9147       status=WriteImage(image_info,histogram_image,exception);
9148       histogram_image=DestroyImage(histogram_image);
9149       if (status == MagickFalse)
9150         XNoticeWidget(display,windows,"Unable to show histogram",
9151           (*image)->filename);
9152       XDelay(display,1500);
9153       XSetCursorState(display,windows,MagickFalse);
9154       break;
9155     }
9156     case ShowMatteCommand:
9157     {
9158       Image
9159         *matte_image;
9160
9161       if ((*image)->matte == MagickFalse)
9162         {
9163           XNoticeWidget(display,windows,
9164             "Image does not have any matte information",(*image)->filename);
9165           break;
9166         }
9167       /*
9168         Show image matte.
9169       */
9170       XSetCursorState(display,windows,MagickTrue);
9171       XCheckRefreshWindows(display,windows);
9172       image_info->group=(ssize_t) windows->image.id;
9173       (void) DeleteImageProperty(*image,"label");
9174       (void) SetImageProperty(*image,"label","Matte");
9175       (void) AcquireUniqueFilename(filename);
9176       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9177         filename);
9178       status=WriteImage(image_info,*image,exception);
9179       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9180       matte_image=ReadImage(image_info,exception);
9181       (void) RelinquishUniqueFileResource(filename);
9182       if (matte_image == (Image *) NULL)
9183         break;
9184       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9185         filename);
9186       status=WriteImage(image_info,matte_image,exception);
9187       matte_image=DestroyImage(matte_image);
9188       if (status == MagickFalse)
9189         XNoticeWidget(display,windows,"Unable to show matte",
9190           (*image)->filename);
9191       XDelay(display,1500);
9192       XSetCursorState(display,windows,MagickFalse);
9193       break;
9194     }
9195     case BackgroundCommand:
9196     {
9197       /*
9198         Background image.
9199       */
9200       status=XBackgroundImage(display,resource_info,windows,image,exception);
9201       if (status == MagickFalse)
9202         break;
9203       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9204       if (nexus != (Image *) NULL)
9205         XClientMessage(display,windows->image.id,windows->im_protocols,
9206           windows->im_next_image,CurrentTime);
9207       break;
9208     }
9209     case SlideShowCommand:
9210     {
9211       static char
9212         delay[MaxTextExtent] = "5";
9213
9214       /*
9215         Display next image after pausing.
9216       */
9217       (void) XDialogWidget(display,windows,"Slide Show",
9218         "Pause how many 1/100ths of a second between images:",delay);
9219       if (*delay == '\0')
9220         break;
9221       resource_info->delay=StringToUnsignedLong(delay);
9222       XClientMessage(display,windows->image.id,windows->im_protocols,
9223         windows->im_next_image,CurrentTime);
9224       break;
9225     }
9226     case PreferencesCommand:
9227     {
9228       /*
9229         Set user preferences.
9230       */
9231       status=XPreferencesWidget(display,resource_info,windows);
9232       if (status == MagickFalse)
9233         break;
9234       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9235       if (nexus != (Image *) NULL)
9236         XClientMessage(display,windows->image.id,windows->im_protocols,
9237           windows->im_next_image,CurrentTime);
9238       break;
9239     }
9240     case HelpCommand:
9241     {
9242       /*
9243         User requested help.
9244       */
9245       XTextViewWidget(display,resource_info,windows,MagickFalse,
9246         "Help Viewer - Display",DisplayHelp);
9247       break;
9248     }
9249     case BrowseDocumentationCommand:
9250     {
9251       Atom
9252         mozilla_atom;
9253
9254       Window
9255         mozilla_window,
9256         root_window;
9257
9258       /*
9259         Browse the ImageMagick documentation.
9260       */
9261       root_window=XRootWindow(display,XDefaultScreen(display));
9262       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9263       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9264       if (mozilla_window != (Window) NULL)
9265         {
9266           char
9267             command[MaxTextExtent],
9268             *url;
9269
9270           /*
9271             Display documentation using Netscape remote control.
9272           */
9273           url=GetMagickHomeURL();
9274           (void) FormatLocaleString(command,MaxTextExtent,
9275             "openurl(%s,new-tab)",url);
9276           url=DestroyString(url);
9277           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9278           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9279             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9280           XSetCursorState(display,windows,MagickFalse);
9281           break;
9282         }
9283       XSetCursorState(display,windows,MagickTrue);
9284       XCheckRefreshWindows(display,windows);
9285       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9286         exception);
9287       if (status == MagickFalse)
9288         XNoticeWidget(display,windows,"Unable to browse documentation",
9289           (char *) NULL);
9290       XDelay(display,1500);
9291       XSetCursorState(display,windows,MagickFalse);
9292       break;
9293     }
9294     case VersionCommand:
9295     {
9296       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9297         GetMagickCopyright());
9298       break;
9299     }
9300     case SaveToUndoBufferCommand:
9301       break;
9302     default:
9303     {
9304       (void) XBell(display,0);
9305       break;
9306     }
9307   }
9308   image_info=DestroyImageInfo(image_info);
9309   return(nexus);
9310 }
9311 \f
9312 /*
9313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9314 %                                                                             %
9315 %                                                                             %
9316 %                                                                             %
9317 +   X M a g n i f y I m a g e                                                 %
9318 %                                                                             %
9319 %                                                                             %
9320 %                                                                             %
9321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9322 %
9323 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9324 %  The magnified portion is displayed in a separate window.
9325 %
9326 %  The format of the XMagnifyImage method is:
9327 %
9328 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9329 %
9330 %  A description of each parameter follows:
9331 %
9332 %    o display: Specifies a connection to an X server;  returned from
9333 %      XOpenDisplay.
9334 %
9335 %    o windows: Specifies a pointer to a XWindows structure.
9336 %
9337 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9338 %      the entire image is refreshed.
9339 %
9340 */
9341 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9342 {
9343   char
9344     text[MaxTextExtent];
9345
9346   register int
9347     x,
9348     y;
9349
9350   size_t
9351     state;
9352
9353   /*
9354     Update magnified image until the mouse button is released.
9355   */
9356   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9357   state=DefaultState;
9358   x=event->xbutton.x;
9359   y=event->xbutton.y;
9360   windows->magnify.x=(int) windows->image.x+x;
9361   windows->magnify.y=(int) windows->image.y+y;
9362   do
9363   {
9364     /*
9365       Map and unmap Info widget as text cursor crosses its boundaries.
9366     */
9367     if (windows->info.mapped != MagickFalse)
9368       {
9369         if ((x < (int) (windows->info.x+windows->info.width)) &&
9370             (y < (int) (windows->info.y+windows->info.height)))
9371           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9372       }
9373     else
9374       if ((x > (int) (windows->info.x+windows->info.width)) ||
9375           (y > (int) (windows->info.y+windows->info.height)))
9376         (void) XMapWindow(display,windows->info.id);
9377     if (windows->info.mapped != MagickFalse)
9378       {
9379         /*
9380           Display pointer position.
9381         */
9382         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9383           windows->magnify.x,windows->magnify.y);
9384         XInfoWidget(display,windows,text);
9385       }
9386     /*
9387       Wait for next event.
9388     */
9389     XScreenEvent(display,windows,event);
9390     switch (event->type)
9391     {
9392       case ButtonPress:
9393         break;
9394       case ButtonRelease:
9395       {
9396         /*
9397           User has finished magnifying image.
9398         */
9399         x=event->xbutton.x;
9400         y=event->xbutton.y;
9401         state|=ExitState;
9402         break;
9403       }
9404       case Expose:
9405         break;
9406       case MotionNotify:
9407       {
9408         x=event->xmotion.x;
9409         y=event->xmotion.y;
9410         break;
9411       }
9412       default:
9413         break;
9414     }
9415     /*
9416       Check boundary conditions.
9417     */
9418     if (x < 0)
9419       x=0;
9420     else
9421       if (x >= (int) windows->image.width)
9422         x=(int) windows->image.width-1;
9423     if (y < 0)
9424       y=0;
9425     else
9426      if (y >= (int) windows->image.height)
9427        y=(int) windows->image.height-1;
9428   } while ((state & ExitState) == 0);
9429   /*
9430     Display magnified image.
9431   */
9432   XSetCursorState(display,windows,MagickFalse);
9433 }
9434 \f
9435 /*
9436 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9437 %                                                                             %
9438 %                                                                             %
9439 %                                                                             %
9440 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9441 %                                                                             %
9442 %                                                                             %
9443 %                                                                             %
9444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9445 %
9446 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9447 %  pixel as specified by the key symbol.
9448 %
9449 %  The format of the XMagnifyWindowCommand method is:
9450 %
9451 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9452 %        const MagickStatusType state,const KeySym key_symbol)
9453 %
9454 %  A description of each parameter follows:
9455 %
9456 %    o display: Specifies a connection to an X server; returned from
9457 %      XOpenDisplay.
9458 %
9459 %    o windows: Specifies a pointer to a XWindows structure.
9460 %
9461 %    o state: key mask.
9462 %
9463 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9464 %      to trim.
9465 %
9466 */
9467 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9468   const MagickStatusType state,const KeySym key_symbol)
9469 {
9470   unsigned int
9471     quantum;
9472
9473   /*
9474     User specified a magnify factor or position.
9475   */
9476   quantum=1;
9477   if ((state & Mod1Mask) != 0)
9478     quantum=10;
9479   switch ((int) key_symbol)
9480   {
9481     case QuitCommand:
9482     {
9483       (void) XWithdrawWindow(display,windows->magnify.id,
9484         windows->magnify.screen);
9485       break;
9486     }
9487     case XK_Home:
9488     case XK_KP_Home:
9489     {
9490       windows->magnify.x=(int) windows->image.width/2;
9491       windows->magnify.y=(int) windows->image.height/2;
9492       break;
9493     }
9494     case XK_Left:
9495     case XK_KP_Left:
9496     {
9497       if (windows->magnify.x > 0)
9498         windows->magnify.x-=quantum;
9499       break;
9500     }
9501     case XK_Up:
9502     case XK_KP_Up:
9503     {
9504       if (windows->magnify.y > 0)
9505         windows->magnify.y-=quantum;
9506       break;
9507     }
9508     case XK_Right:
9509     case XK_KP_Right:
9510     {
9511       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9512         windows->magnify.x+=quantum;
9513       break;
9514     }
9515     case XK_Down:
9516     case XK_KP_Down:
9517     {
9518       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9519         windows->magnify.y+=quantum;
9520       break;
9521     }
9522     case XK_0:
9523     case XK_1:
9524     case XK_2:
9525     case XK_3:
9526     case XK_4:
9527     case XK_5:
9528     case XK_6:
9529     case XK_7:
9530     case XK_8:
9531     case XK_9:
9532     {
9533       windows->magnify.data=(key_symbol-XK_0);
9534       break;
9535     }
9536     case XK_KP_0:
9537     case XK_KP_1:
9538     case XK_KP_2:
9539     case XK_KP_3:
9540     case XK_KP_4:
9541     case XK_KP_5:
9542     case XK_KP_6:
9543     case XK_KP_7:
9544     case XK_KP_8:
9545     case XK_KP_9:
9546     {
9547       windows->magnify.data=(key_symbol-XK_KP_0);
9548       break;
9549     }
9550     default:
9551       break;
9552   }
9553   XMakeMagnifyImage(display,windows);
9554 }
9555 \f
9556 /*
9557 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9558 %                                                                             %
9559 %                                                                             %
9560 %                                                                             %
9561 +   X M a k e P a n I m a g e                                                 %
9562 %                                                                             %
9563 %                                                                             %
9564 %                                                                             %
9565 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9566 %
9567 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9568 %  icon window.
9569 %
9570 %  The format of the XMakePanImage method is:
9571 %
9572 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9573 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9574 %
9575 %  A description of each parameter follows:
9576 %
9577 %    o display: Specifies a connection to an X server;  returned from
9578 %      XOpenDisplay.
9579 %
9580 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9581 %
9582 %    o windows: Specifies a pointer to a XWindows structure.
9583 %
9584 %    o image: the image.
9585 %
9586 %    o exception: return any errors or warnings in this structure.
9587 %
9588 */
9589 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9590   XWindows *windows,Image *image,ExceptionInfo *exception)
9591 {
9592   MagickStatusType
9593     status;
9594
9595   /*
9596     Create and display image for panning icon.
9597   */
9598   XSetCursorState(display,windows,MagickTrue);
9599   XCheckRefreshWindows(display,windows);
9600   windows->pan.x=(int) windows->image.x;
9601   windows->pan.y=(int) windows->image.y;
9602   status=XMakeImage(display,resource_info,&windows->pan,image,
9603     windows->pan.width,windows->pan.height,exception);
9604   if (status == MagickFalse)
9605     ThrowXWindowFatalException(ResourceLimitError,
9606      "MemoryAllocationFailed",image->filename);
9607   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9608     windows->pan.pixmap);
9609   (void) XClearWindow(display,windows->pan.id);
9610   XDrawPanRectangle(display,windows);
9611   XSetCursorState(display,windows,MagickFalse);
9612 }
9613 \f
9614 /*
9615 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9616 %                                                                             %
9617 %                                                                             %
9618 %                                                                             %
9619 +   X M a t t a E d i t I m a g e                                             %
9620 %                                                                             %
9621 %                                                                             %
9622 %                                                                             %
9623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9624 %
9625 %  XMatteEditImage() allows the user to interactively change the Matte channel
9626 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9627 %  before the matte information is stored.
9628 %
9629 %  The format of the XMatteEditImage method is:
9630 %
9631 %      MagickBooleanType XMatteEditImage(Display *display,
9632 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9633 %        ExceptionInfo *exception)
9634 %
9635 %  A description of each parameter follows:
9636 %
9637 %    o display: Specifies a connection to an X server;  returned from
9638 %      XOpenDisplay.
9639 %
9640 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9641 %
9642 %    o windows: Specifies a pointer to a XWindows structure.
9643 %
9644 %    o image: the image; returned from ReadImage.
9645 %
9646 %    o exception: return any errors or warnings in this structure.
9647 %
9648 */
9649 static MagickBooleanType XMatteEditImage(Display *display,
9650   XResourceInfo *resource_info,XWindows *windows,Image **image,
9651   ExceptionInfo *exception)
9652 {
9653   static char
9654     matte[MaxTextExtent] = "0";
9655
9656   static const char
9657     *MatteEditMenu[] =
9658     {
9659       "Method",
9660       "Border Color",
9661       "Fuzz",
9662       "Matte Value",
9663       "Undo",
9664       "Help",
9665       "Dismiss",
9666       (char *) NULL
9667     };
9668
9669   static const ModeType
9670     MatteEditCommands[] =
9671     {
9672       MatteEditMethod,
9673       MatteEditBorderCommand,
9674       MatteEditFuzzCommand,
9675       MatteEditValueCommand,
9676       MatteEditUndoCommand,
9677       MatteEditHelpCommand,
9678       MatteEditDismissCommand
9679     };
9680
9681   static PaintMethod
9682     method = PointMethod;
9683
9684   static XColor
9685     border_color = { 0, 0, 0, 0, 0, 0 };
9686
9687   char
9688     command[MaxTextExtent],
9689     text[MaxTextExtent];
9690
9691   Cursor
9692     cursor;
9693
9694   int
9695     entry,
9696     id,
9697     x,
9698     x_offset,
9699     y,
9700     y_offset;
9701
9702   register int
9703     i;
9704
9705   register Quantum
9706     *q;
9707
9708   unsigned int
9709     height,
9710     width;
9711
9712   size_t
9713     state;
9714
9715   XEvent
9716     event;
9717
9718   /*
9719     Map Command widget.
9720   */
9721   (void) CloneString(&windows->command.name,"Matte Edit");
9722   windows->command.data=4;
9723   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9724   (void) XMapRaised(display,windows->command.id);
9725   XClientMessage(display,windows->image.id,windows->im_protocols,
9726     windows->im_update_widget,CurrentTime);
9727   /*
9728     Make cursor.
9729   */
9730   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9731     resource_info->background_color,resource_info->foreground_color);
9732   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9733   /*
9734     Track pointer until button 1 is pressed.
9735   */
9736   XQueryPosition(display,windows->image.id,&x,&y);
9737   (void) XSelectInput(display,windows->image.id,
9738     windows->image.attributes.event_mask | PointerMotionMask);
9739   state=DefaultState;
9740   do
9741   {
9742     if (windows->info.mapped != MagickFalse)
9743       {
9744         /*
9745           Display pointer position.
9746         */
9747         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9748           x+windows->image.x,y+windows->image.y);
9749         XInfoWidget(display,windows,text);
9750       }
9751     /*
9752       Wait for next event.
9753     */
9754     XScreenEvent(display,windows,&event);
9755     if (event.xany.window == windows->command.id)
9756       {
9757         /*
9758           Select a command from the Command widget.
9759         */
9760         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9761         if (id < 0)
9762           {
9763             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9764             continue;
9765           }
9766         switch (MatteEditCommands[id])
9767         {
9768           case MatteEditMethod:
9769           {
9770             char
9771               **methods;
9772
9773             /*
9774               Select a method from the pop-up menu.
9775             */
9776             methods=GetCommandOptions(MagickMethodOptions);
9777             if (methods == (char **) NULL)
9778               break;
9779             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9780               (const char **) methods,command);
9781             if (entry >= 0)
9782               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9783                 MagickFalse,methods[entry]);
9784             methods=DestroyStringList(methods);
9785             break;
9786           }
9787           case MatteEditBorderCommand:
9788           {
9789             const char
9790               *ColorMenu[MaxNumberPens];
9791
9792             int
9793               pen_number;
9794
9795             /*
9796               Initialize menu selections.
9797             */
9798             for (i=0; i < (int) (MaxNumberPens-2); i++)
9799               ColorMenu[i]=resource_info->pen_colors[i];
9800             ColorMenu[MaxNumberPens-2]="Browser...";
9801             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9802             /*
9803               Select a pen color from the pop-up menu.
9804             */
9805             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9806               (const char **) ColorMenu,command);
9807             if (pen_number < 0)
9808               break;
9809             if (pen_number == (MaxNumberPens-2))
9810               {
9811                 static char
9812                   color_name[MaxTextExtent] = "gray";
9813
9814                 /*
9815                   Select a pen color from a dialog.
9816                 */
9817                 resource_info->pen_colors[pen_number]=color_name;
9818                 XColorBrowserWidget(display,windows,"Select",color_name);
9819                 if (*color_name == '\0')
9820                   break;
9821               }
9822             /*
9823               Set border color.
9824             */
9825             (void) XParseColor(display,windows->map_info->colormap,
9826               resource_info->pen_colors[pen_number],&border_color);
9827             break;
9828           }
9829           case MatteEditFuzzCommand:
9830           {
9831             static char
9832               fuzz[MaxTextExtent];
9833
9834             static const char
9835               *FuzzMenu[] =
9836               {
9837                 "0%",
9838                 "2%",
9839                 "5%",
9840                 "10%",
9841                 "15%",
9842                 "Dialog...",
9843                 (char *) NULL,
9844               };
9845
9846             /*
9847               Select a command from the pop-up menu.
9848             */
9849             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9850               command);
9851             if (entry < 0)
9852               break;
9853             if (entry != 5)
9854               {
9855                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
9856                   QuantumRange+1.0);
9857                 break;
9858               }
9859             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9860             (void) XDialogWidget(display,windows,"Ok",
9861               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9862             if (*fuzz == '\0')
9863               break;
9864             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9865             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9866             break;
9867           }
9868           case MatteEditValueCommand:
9869           {
9870             static char
9871               message[MaxTextExtent];
9872
9873             static const char
9874               *MatteMenu[] =
9875               {
9876                 "Opaque",
9877                 "Transparent",
9878                 "Dialog...",
9879                 (char *) NULL,
9880               };
9881
9882             /*
9883               Select a command from the pop-up menu.
9884             */
9885             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9886               command);
9887             if (entry < 0)
9888               break;
9889             if (entry != 2)
9890               {
9891                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9892                   OpaqueAlpha);
9893                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9894                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9895                     (Quantum) TransparentAlpha);
9896                 break;
9897               }
9898             (void) FormatLocaleString(message,MaxTextExtent,
9899               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9900               QuantumRange);
9901             (void) XDialogWidget(display,windows,"Matte",message,matte);
9902             if (*matte == '\0')
9903               break;
9904             break;
9905           }
9906           case MatteEditUndoCommand:
9907           {
9908             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9909               image,exception);
9910             break;
9911           }
9912           case MatteEditHelpCommand:
9913           {
9914             XTextViewWidget(display,resource_info,windows,MagickFalse,
9915               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9916             break;
9917           }
9918           case MatteEditDismissCommand:
9919           {
9920             /*
9921               Prematurely exit.
9922             */
9923             state|=EscapeState;
9924             state|=ExitState;
9925             break;
9926           }
9927           default:
9928             break;
9929         }
9930         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9931         continue;
9932       }
9933     switch (event.type)
9934     {
9935       case ButtonPress:
9936       {
9937         if (event.xbutton.button != Button1)
9938           break;
9939         if ((event.xbutton.window != windows->image.id) &&
9940             (event.xbutton.window != windows->magnify.id))
9941           break;
9942         /*
9943           Update matte data.
9944         */
9945         x=event.xbutton.x;
9946         y=event.xbutton.y;
9947         (void) XMagickCommand(display,resource_info,windows,
9948           SaveToUndoBufferCommand,image,exception);
9949         state|=UpdateConfigurationState;
9950         break;
9951       }
9952       case ButtonRelease:
9953       {
9954         if (event.xbutton.button != Button1)
9955           break;
9956         if ((event.xbutton.window != windows->image.id) &&
9957             (event.xbutton.window != windows->magnify.id))
9958           break;
9959         /*
9960           Update colormap information.
9961         */
9962         x=event.xbutton.x;
9963         y=event.xbutton.y;
9964         XConfigureImageColormap(display,resource_info,windows,*image);
9965         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9966         XInfoWidget(display,windows,text);
9967         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9968         state&=(~UpdateConfigurationState);
9969         break;
9970       }
9971       case Expose:
9972         break;
9973       case KeyPress:
9974       {
9975         char
9976           command[MaxTextExtent];
9977
9978         KeySym
9979           key_symbol;
9980
9981         if (event.xkey.window == windows->magnify.id)
9982           {
9983             Window
9984               window;
9985
9986             window=windows->magnify.id;
9987             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9988           }
9989         if (event.xkey.window != windows->image.id)
9990           break;
9991         /*
9992           Respond to a user key press.
9993         */
9994         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9995           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9996         switch ((int) key_symbol)
9997         {
9998           case XK_Escape:
9999           case XK_F20:
10000           {
10001             /*
10002               Prematurely exit.
10003             */
10004             state|=ExitState;
10005             break;
10006           }
10007           case XK_F1:
10008           case XK_Help:
10009           {
10010             XTextViewWidget(display,resource_info,windows,MagickFalse,
10011               "Help Viewer - Matte Edit",ImageMatteEditHelp);
10012             break;
10013           }
10014           default:
10015           {
10016             (void) XBell(display,0);
10017             break;
10018           }
10019         }
10020         break;
10021       }
10022       case MotionNotify:
10023       {
10024         /*
10025           Map and unmap Info widget as cursor crosses its boundaries.
10026         */
10027         x=event.xmotion.x;
10028         y=event.xmotion.y;
10029         if (windows->info.mapped != MagickFalse)
10030           {
10031             if ((x < (int) (windows->info.x+windows->info.width)) &&
10032                 (y < (int) (windows->info.y+windows->info.height)))
10033               (void) XWithdrawWindow(display,windows->info.id,
10034                 windows->info.screen);
10035           }
10036         else
10037           if ((x > (int) (windows->info.x+windows->info.width)) ||
10038               (y > (int) (windows->info.y+windows->info.height)))
10039             (void) XMapWindow(display,windows->info.id);
10040         break;
10041       }
10042       default:
10043         break;
10044     }
10045     if (event.xany.window == windows->magnify.id)
10046       {
10047         x=windows->magnify.x-windows->image.x;
10048         y=windows->magnify.y-windows->image.y;
10049       }
10050     x_offset=x;
10051     y_offset=y;
10052     if ((state & UpdateConfigurationState) != 0)
10053       {
10054         CacheView
10055           *image_view;
10056
10057         int
10058           x,
10059           y;
10060
10061         /*
10062           Matte edit is relative to image configuration.
10063         */
10064         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10065           MagickTrue);
10066         XPutPixel(windows->image.ximage,x_offset,y_offset,
10067           windows->pixel_info->background_color.pixel);
10068         width=(unsigned int) (*image)->columns;
10069         height=(unsigned int) (*image)->rows;
10070         x=0;
10071         y=0;
10072         if (windows->image.crop_geometry != (char *) NULL)
10073           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10074             &height);
10075         x_offset=(int) (width*(windows->image.x+x_offset)/
10076           windows->image.ximage->width+x);
10077         y_offset=(int) (height*(windows->image.y+y_offset)/
10078           windows->image.ximage->height+y);
10079         if ((x_offset < 0) || (y_offset < 0))
10080           continue;
10081         if ((x_offset >= (int) (*image)->columns) ||
10082             (y_offset >= (int) (*image)->rows))
10083           continue;
10084         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10085           return(MagickFalse);
10086         (*image)->matte=MagickTrue;
10087         image_view=AcquireCacheView(*image);
10088         switch (method)
10089         {
10090           case PointMethod:
10091           default:
10092           {
10093             /*
10094               Update matte information using point algorithm.
10095             */
10096             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10097               (ssize_t) y_offset,1,1,exception);
10098             if (q == (Quantum *) NULL)
10099               break;
10100             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10101             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10102             break;
10103           }
10104           case ReplaceMethod:
10105           {
10106             PixelPacket
10107               pixel,
10108               target;
10109
10110             Quantum
10111               virtual_pixel[MaxPixelChannels];
10112
10113             /*
10114               Update matte information using replace algorithm.
10115             */
10116             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10117               (ssize_t) y_offset,virtual_pixel,exception);
10118             target.red=virtual_pixel[RedPixelChannel];
10119             target.green=virtual_pixel[GreenPixelChannel];
10120             target.blue=virtual_pixel[BluePixelChannel];
10121             target.alpha=virtual_pixel[AlphaPixelChannel];
10122             for (y=0; y < (int) (*image)->rows; y++)
10123             {
10124               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10125                 (*image)->columns,1,exception);
10126               if (q == (Quantum *) NULL)
10127                 break;
10128               for (x=0; x < (int) (*image)->columns; x++)
10129               {
10130                 GetPixelPacketPixel(*image,q,&pixel);
10131                 if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
10132                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10133                 q+=GetPixelChannels(*image);
10134               }
10135               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10136                 break;
10137             }
10138             break;
10139           }
10140           case FloodfillMethod:
10141           case FillToBorderMethod:
10142           {
10143             ChannelType
10144               channel_mask;
10145
10146             DrawInfo
10147               *draw_info;
10148
10149             PixelInfo
10150               target;
10151
10152             /*
10153               Update matte information using floodfill algorithm.
10154             */
10155             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10156               (ssize_t) y_offset,&target,exception);
10157             if (method == FillToBorderMethod)
10158               {
10159                 target.red=(MagickRealType) ScaleShortToQuantum(
10160                   border_color.red);
10161                 target.green=(MagickRealType) ScaleShortToQuantum(
10162                   border_color.green);
10163                 target.blue=(MagickRealType) ScaleShortToQuantum(
10164                   border_color.blue);
10165               }
10166             draw_info=CloneDrawInfo(resource_info->image_info,
10167               (DrawInfo *) NULL);
10168             draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10169               (char **) NULL));
10170             channel_mask=SetPixelChannelMask(*image,AlphaChannel); 
10171             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10172               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10173               MagickFalse : MagickTrue,exception);
10174             (void) SetPixelChannelMap(*image,channel_mask);
10175             draw_info=DestroyDrawInfo(draw_info);
10176             break;
10177           }
10178           case ResetMethod:
10179           {
10180             /*
10181               Update matte information using reset algorithm.
10182             */
10183             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10184               return(MagickFalse);
10185             for (y=0; y < (int) (*image)->rows; y++)
10186             {
10187               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10188                 (*image)->columns,1,exception);
10189               if (q == (Quantum *) NULL)
10190                 break;
10191               for (x=0; x < (int) (*image)->columns; x++)
10192               {
10193                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10194                 q+=GetPixelChannels(*image);
10195               }
10196               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10197                 break;
10198             }
10199             if (StringToLong(matte) == (long) OpaqueAlpha)
10200               (*image)->matte=MagickFalse;
10201             break;
10202           }
10203         }
10204         image_view=DestroyCacheView(image_view);
10205         state&=(~UpdateConfigurationState);
10206       }
10207   } while ((state & ExitState) == 0);
10208   (void) XSelectInput(display,windows->image.id,
10209     windows->image.attributes.event_mask);
10210   XSetCursorState(display,windows,MagickFalse);
10211   (void) XFreeCursor(display,cursor);
10212   return(MagickTrue);
10213 }
10214 \f
10215 /*
10216 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10217 %                                                                             %
10218 %                                                                             %
10219 %                                                                             %
10220 +   X O p e n I m a g e                                                       %
10221 %                                                                             %
10222 %                                                                             %
10223 %                                                                             %
10224 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10225 %
10226 %  XOpenImage() loads an image from a file.
10227 %
10228 %  The format of the XOpenImage method is:
10229 %
10230 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10231 %       XWindows *windows,const unsigned int command)
10232 %
10233 %  A description of each parameter follows:
10234 %
10235 %    o display: Specifies a connection to an X server; returned from
10236 %      XOpenDisplay.
10237 %
10238 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10239 %
10240 %    o windows: Specifies a pointer to a XWindows structure.
10241 %
10242 %    o command: A value other than zero indicates that the file is selected
10243 %      from the command line argument list.
10244 %
10245 */
10246 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10247   XWindows *windows,const MagickBooleanType command)
10248 {
10249   const MagickInfo
10250     *magick_info;
10251
10252   ExceptionInfo
10253     *exception;
10254
10255   Image
10256     *nexus;
10257
10258   ImageInfo
10259     *image_info;
10260
10261   static char
10262     filename[MaxTextExtent] = "\0";
10263
10264   /*
10265     Request file name from user.
10266   */
10267   if (command == MagickFalse)
10268     XFileBrowserWidget(display,windows,"Open",filename);
10269   else
10270     {
10271       char
10272         **filelist,
10273         **files;
10274
10275       int
10276         count,
10277         status;
10278
10279       register int
10280         i,
10281         j;
10282
10283       /*
10284         Select next image from the command line.
10285       */
10286       status=XGetCommand(display,windows->image.id,&files,&count);
10287       if (status == 0)
10288         {
10289           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10290           return((Image *) NULL);
10291         }
10292       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10293       if (filelist == (char **) NULL)
10294         {
10295           ThrowXWindowFatalException(ResourceLimitError,
10296             "MemoryAllocationFailed","...");
10297           (void) XFreeStringList(files);
10298           return((Image *) NULL);
10299         }
10300       j=0;
10301       for (i=1; i < count; i++)
10302         if (*files[i] != '-')
10303           filelist[j++]=files[i];
10304       filelist[j]=(char *) NULL;
10305       XListBrowserWidget(display,windows,&windows->widget,
10306         (const char **) filelist,"Load","Select Image to Load:",filename);
10307       filelist=(char **) RelinquishMagickMemory(filelist);
10308       (void) XFreeStringList(files);
10309     }
10310   if (*filename == '\0')
10311     return((Image *) NULL);
10312   image_info=CloneImageInfo(resource_info->image_info);
10313   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10314     (void *) NULL);
10315   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10316   exception=AcquireExceptionInfo();
10317   (void) SetImageInfo(image_info,0,exception);
10318   if (LocaleCompare(image_info->magick,"X") == 0)
10319     {
10320       char
10321         seconds[MaxTextExtent];
10322
10323       /*
10324         User may want to delay the X server screen grab.
10325       */
10326       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10327       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10328         seconds);
10329       if (*seconds == '\0')
10330         return((Image *) NULL);
10331       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10332     }
10333   magick_info=GetMagickInfo(image_info->magick,exception);
10334   if ((magick_info != (const MagickInfo *) NULL) &&
10335       (magick_info->raw != MagickFalse))
10336     {
10337       char
10338         geometry[MaxTextExtent];
10339
10340       /*
10341         Request image size from the user.
10342       */
10343       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10344       if (image_info->size != (char *) NULL)
10345         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10346       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10347         geometry);
10348       (void) CloneString(&image_info->size,geometry);
10349     }
10350   /*
10351     Load the image.
10352   */
10353   XSetCursorState(display,windows,MagickTrue);
10354   XCheckRefreshWindows(display,windows);
10355   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10356   nexus=ReadImage(image_info,exception);
10357   CatchException(exception);
10358   XSetCursorState(display,windows,MagickFalse);
10359   if (nexus != (Image *) NULL)
10360     XClientMessage(display,windows->image.id,windows->im_protocols,
10361       windows->im_next_image,CurrentTime);
10362   else
10363     {
10364       char
10365         *text,
10366         **textlist;
10367
10368       /*
10369         Unknown image format.
10370       */
10371       text=FileToString(filename,~0,exception);
10372       if (text == (char *) NULL)
10373         return((Image *) NULL);
10374       textlist=StringToList(text);
10375       if (textlist != (char **) NULL)
10376         {
10377           char
10378             title[MaxTextExtent];
10379
10380           register int
10381             i;
10382
10383           (void) FormatLocaleString(title,MaxTextExtent,
10384             "Unknown format: %s",filename);
10385           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10386             (const char **) textlist);
10387           for (i=0; textlist[i] != (char *) NULL; i++)
10388             textlist[i]=DestroyString(textlist[i]);
10389           textlist=(char **) RelinquishMagickMemory(textlist);
10390         }
10391       text=DestroyString(text);
10392     }
10393   exception=DestroyExceptionInfo(exception);
10394   image_info=DestroyImageInfo(image_info);
10395   return(nexus);
10396 }
10397 \f
10398 /*
10399 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10400 %                                                                             %
10401 %                                                                             %
10402 %                                                                             %
10403 +   X P a n I m a g e                                                         %
10404 %                                                                             %
10405 %                                                                             %
10406 %                                                                             %
10407 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10408 %
10409 %  XPanImage() pans the image until the mouse button is released.
10410 %
10411 %  The format of the XPanImage method is:
10412 %
10413 %      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10414 %
10415 %  A description of each parameter follows:
10416 %
10417 %    o display: Specifies a connection to an X server;  returned from
10418 %      XOpenDisplay.
10419 %
10420 %    o windows: Specifies a pointer to a XWindows structure.
10421 %
10422 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10423 %      the entire image is refreshed.
10424 %
10425 */
10426 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10427 {
10428   char
10429     text[MaxTextExtent];
10430
10431   Cursor
10432     cursor;
10433
10434   MagickRealType
10435     x_factor,
10436     y_factor;
10437
10438   RectangleInfo
10439     pan_info;
10440
10441   size_t
10442     state;
10443
10444   /*
10445     Define cursor.
10446   */
10447   if ((windows->image.ximage->width > (int) windows->image.width) &&
10448       (windows->image.ximage->height > (int) windows->image.height))
10449     cursor=XCreateFontCursor(display,XC_fleur);
10450   else
10451     if (windows->image.ximage->width > (int) windows->image.width)
10452       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10453     else
10454       if (windows->image.ximage->height > (int) windows->image.height)
10455         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10456       else
10457         cursor=XCreateFontCursor(display,XC_arrow);
10458   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10459   /*
10460     Pan image as pointer moves until the mouse button is released.
10461   */
10462   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10463   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10464   pan_info.width=windows->pan.width*windows->image.width/
10465     windows->image.ximage->width;
10466   pan_info.height=windows->pan.height*windows->image.height/
10467     windows->image.ximage->height;
10468   pan_info.x=0;
10469   pan_info.y=0;
10470   state=UpdateConfigurationState;
10471   do
10472   {
10473     switch (event->type)
10474     {
10475       case ButtonPress:
10476       {
10477         /*
10478           User choose an initial pan location.
10479         */
10480         pan_info.x=(ssize_t) event->xbutton.x;
10481         pan_info.y=(ssize_t) event->xbutton.y;
10482         state|=UpdateConfigurationState;
10483         break;
10484       }
10485       case ButtonRelease:
10486       {
10487         /*
10488           User has finished panning the image.
10489         */
10490         pan_info.x=(ssize_t) event->xbutton.x;
10491         pan_info.y=(ssize_t) event->xbutton.y;
10492         state|=UpdateConfigurationState | ExitState;
10493         break;
10494       }
10495       case MotionNotify:
10496       {
10497         pan_info.x=(ssize_t) event->xmotion.x;
10498         pan_info.y=(ssize_t) event->xmotion.y;
10499         state|=UpdateConfigurationState;
10500       }
10501       default:
10502         break;
10503     }
10504     if ((state & UpdateConfigurationState) != 0)
10505       {
10506         /*
10507           Check boundary conditions.
10508         */
10509         if (pan_info.x < (ssize_t) (pan_info.width/2))
10510           pan_info.x=0;
10511         else
10512           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10513         if (pan_info.x < 0)
10514           pan_info.x=0;
10515         else
10516           if ((int) (pan_info.x+windows->image.width) >
10517               windows->image.ximage->width)
10518             pan_info.x=(ssize_t)
10519               (windows->image.ximage->width-windows->image.width);
10520         if (pan_info.y < (ssize_t) (pan_info.height/2))
10521           pan_info.y=0;
10522         else
10523           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10524         if (pan_info.y < 0)
10525           pan_info.y=0;
10526         else
10527           if ((int) (pan_info.y+windows->image.height) >
10528               windows->image.ximage->height)
10529             pan_info.y=(ssize_t)
10530               (windows->image.ximage->height-windows->image.height);
10531         if ((windows->image.x != (int) pan_info.x) ||
10532             (windows->image.y != (int) pan_info.y))
10533           {
10534             /*
10535               Display image pan offset.
10536             */
10537             windows->image.x=(int) pan_info.x;
10538             windows->image.y=(int) pan_info.y;
10539             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10540               windows->image.width,windows->image.height,windows->image.x,
10541               windows->image.y);
10542             XInfoWidget(display,windows,text);
10543             /*
10544               Refresh Image window.
10545             */
10546             XDrawPanRectangle(display,windows);
10547             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10548           }
10549         state&=(~UpdateConfigurationState);
10550       }
10551     /*
10552       Wait for next event.
10553     */
10554     if ((state & ExitState) == 0)
10555       XScreenEvent(display,windows,event);
10556   } while ((state & ExitState) == 0);
10557   /*
10558     Restore cursor.
10559   */
10560   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10561   (void) XFreeCursor(display,cursor);
10562   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10563 }
10564 \f
10565 /*
10566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10567 %                                                                             %
10568 %                                                                             %
10569 %                                                                             %
10570 +   X P a s t e I m a g e                                                     %
10571 %                                                                             %
10572 %                                                                             %
10573 %                                                                             %
10574 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10575 %
10576 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10577 %  window image at a location the user chooses with the pointer.
10578 %
10579 %  The format of the XPasteImage method is:
10580 %
10581 %      MagickBooleanType XPasteImage(Display *display,
10582 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10583 %        ExceptionInfo *exception)
10584 %
10585 %  A description of each parameter follows:
10586 %
10587 %    o display: Specifies a connection to an X server;  returned from
10588 %      XOpenDisplay.
10589 %
10590 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10591 %
10592 %    o windows: Specifies a pointer to a XWindows structure.
10593 %
10594 %    o image: the image; returned from ReadImage.
10595 %
10596 %    o exception: return any errors or warnings in this structure.
10597 %
10598 */
10599 static MagickBooleanType XPasteImage(Display *display,
10600   XResourceInfo *resource_info,XWindows *windows,Image *image,
10601   ExceptionInfo *exception)
10602 {
10603   static const char
10604     *PasteMenu[] =
10605     {
10606       "Operator",
10607       "Help",
10608       "Dismiss",
10609       (char *) NULL
10610     };
10611
10612   static const ModeType
10613     PasteCommands[] =
10614     {
10615       PasteOperatorsCommand,
10616       PasteHelpCommand,
10617       PasteDismissCommand
10618     };
10619
10620   static CompositeOperator
10621     compose = CopyCompositeOp;
10622
10623   char
10624     text[MaxTextExtent];
10625
10626   Cursor
10627     cursor;
10628
10629   Image
10630     *paste_image;
10631
10632   int
10633     entry,
10634     id,
10635     x,
10636     y;
10637
10638   MagickRealType
10639     scale_factor;
10640
10641   RectangleInfo
10642     highlight_info,
10643     paste_info;
10644
10645   unsigned int
10646     height,
10647     width;
10648
10649   size_t
10650     state;
10651
10652   XEvent
10653     event;
10654
10655   /*
10656     Copy image.
10657   */
10658   if (resource_info->copy_image == (Image *) NULL)
10659     return(MagickFalse);
10660   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10661   /*
10662     Map Command widget.
10663   */
10664   (void) CloneString(&windows->command.name,"Paste");
10665   windows->command.data=1;
10666   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10667   (void) XMapRaised(display,windows->command.id);
10668   XClientMessage(display,windows->image.id,windows->im_protocols,
10669     windows->im_update_widget,CurrentTime);
10670   /*
10671     Track pointer until button 1 is pressed.
10672   */
10673   XSetCursorState(display,windows,MagickFalse);
10674   XQueryPosition(display,windows->image.id,&x,&y);
10675   (void) XSelectInput(display,windows->image.id,
10676     windows->image.attributes.event_mask | PointerMotionMask);
10677   paste_info.x=(ssize_t) windows->image.x+x;
10678   paste_info.y=(ssize_t) windows->image.y+y;
10679   paste_info.width=0;
10680   paste_info.height=0;
10681   cursor=XCreateFontCursor(display,XC_ul_angle);
10682   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10683   state=DefaultState;
10684   do
10685   {
10686     if (windows->info.mapped != MagickFalse)
10687       {
10688         /*
10689           Display pointer position.
10690         */
10691         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10692           (long) paste_info.x,(long) paste_info.y);
10693         XInfoWidget(display,windows,text);
10694       }
10695     highlight_info=paste_info;
10696     highlight_info.x=paste_info.x-windows->image.x;
10697     highlight_info.y=paste_info.y-windows->image.y;
10698     XHighlightRectangle(display,windows->image.id,
10699       windows->image.highlight_context,&highlight_info);
10700     /*
10701       Wait for next event.
10702     */
10703     XScreenEvent(display,windows,&event);
10704     XHighlightRectangle(display,windows->image.id,
10705       windows->image.highlight_context,&highlight_info);
10706     if (event.xany.window == windows->command.id)
10707       {
10708         /*
10709           Select a command from the Command widget.
10710         */
10711         id=XCommandWidget(display,windows,PasteMenu,&event);
10712         if (id < 0)
10713           continue;
10714         switch (PasteCommands[id])
10715         {
10716           case PasteOperatorsCommand:
10717           {
10718             char
10719               command[MaxTextExtent],
10720               **operators;
10721
10722             /*
10723               Select a command from the pop-up menu.
10724             */
10725             operators=GetCommandOptions(MagickComposeOptions);
10726             if (operators == (char **) NULL)
10727               break;
10728             entry=XMenuWidget(display,windows,PasteMenu[id],
10729               (const char **) operators,command);
10730             if (entry >= 0)
10731               compose=(CompositeOperator) ParseCommandOption(
10732                 MagickComposeOptions,MagickFalse,operators[entry]);
10733             operators=DestroyStringList(operators);
10734             break;
10735           }
10736           case PasteHelpCommand:
10737           {
10738             XTextViewWidget(display,resource_info,windows,MagickFalse,
10739               "Help Viewer - Image Composite",ImagePasteHelp);
10740             break;
10741           }
10742           case PasteDismissCommand:
10743           {
10744             /*
10745               Prematurely exit.
10746             */
10747             state|=EscapeState;
10748             state|=ExitState;
10749             break;
10750           }
10751           default:
10752             break;
10753         }
10754         continue;
10755       }
10756     switch (event.type)
10757     {
10758       case ButtonPress:
10759       {
10760         if (image->debug != MagickFalse)
10761           (void) LogMagickEvent(X11Event,GetMagickModule(),
10762             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10763             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10764         if (event.xbutton.button != Button1)
10765           break;
10766         if (event.xbutton.window != windows->image.id)
10767           break;
10768         /*
10769           Paste rectangle is relative to image configuration.
10770         */
10771         width=(unsigned int) image->columns;
10772         height=(unsigned int) image->rows;
10773         x=0;
10774         y=0;
10775         if (windows->image.crop_geometry != (char *) NULL)
10776           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10777             &width,&height);
10778         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10779         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10780         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10781         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10782         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10783         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10784         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10785         break;
10786       }
10787       case ButtonRelease:
10788       {
10789         if (image->debug != MagickFalse)
10790           (void) LogMagickEvent(X11Event,GetMagickModule(),
10791             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10792             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10793         if (event.xbutton.button != Button1)
10794           break;
10795         if (event.xbutton.window != windows->image.id)
10796           break;
10797         if ((paste_info.width != 0) && (paste_info.height != 0))
10798           {
10799             /*
10800               User has selected the location of the paste image.
10801             */
10802             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10803             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10804             state|=ExitState;
10805           }
10806         break;
10807       }
10808       case Expose:
10809         break;
10810       case KeyPress:
10811       {
10812         char
10813           command[MaxTextExtent];
10814
10815         KeySym
10816           key_symbol;
10817
10818         int
10819           length;
10820
10821         if (event.xkey.window != windows->image.id)
10822           break;
10823         /*
10824           Respond to a user key press.
10825         */
10826         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10827           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10828         *(command+length)='\0';
10829         if (image->debug != MagickFalse)
10830           (void) LogMagickEvent(X11Event,GetMagickModule(),
10831             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10832         switch ((int) key_symbol)
10833         {
10834           case XK_Escape:
10835           case XK_F20:
10836           {
10837             /*
10838               Prematurely exit.
10839             */
10840             paste_image=DestroyImage(paste_image);
10841             state|=EscapeState;
10842             state|=ExitState;
10843             break;
10844           }
10845           case XK_F1:
10846           case XK_Help:
10847           {
10848             (void) XSetFunction(display,windows->image.highlight_context,
10849               GXcopy);
10850             XTextViewWidget(display,resource_info,windows,MagickFalse,
10851               "Help Viewer - Image Composite",ImagePasteHelp);
10852             (void) XSetFunction(display,windows->image.highlight_context,
10853               GXinvert);
10854             break;
10855           }
10856           default:
10857           {
10858             (void) XBell(display,0);
10859             break;
10860           }
10861         }
10862         break;
10863       }
10864       case MotionNotify:
10865       {
10866         /*
10867           Map and unmap Info widget as text cursor crosses its boundaries.
10868         */
10869         x=event.xmotion.x;
10870         y=event.xmotion.y;
10871         if (windows->info.mapped != MagickFalse)
10872           {
10873             if ((x < (int) (windows->info.x+windows->info.width)) &&
10874                 (y < (int) (windows->info.y+windows->info.height)))
10875               (void) XWithdrawWindow(display,windows->info.id,
10876                 windows->info.screen);
10877           }
10878         else
10879           if ((x > (int) (windows->info.x+windows->info.width)) ||
10880               (y > (int) (windows->info.y+windows->info.height)))
10881             (void) XMapWindow(display,windows->info.id);
10882         paste_info.x=(ssize_t) windows->image.x+x;
10883         paste_info.y=(ssize_t) windows->image.y+y;
10884         break;
10885       }
10886       default:
10887       {
10888         if (image->debug != MagickFalse)
10889           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10890             event.type);
10891         break;
10892       }
10893     }
10894   } while ((state & ExitState) == 0);
10895   (void) XSelectInput(display,windows->image.id,
10896     windows->image.attributes.event_mask);
10897   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10898   XSetCursorState(display,windows,MagickFalse);
10899   (void) XFreeCursor(display,cursor);
10900   if ((state & EscapeState) != 0)
10901     return(MagickTrue);
10902   /*
10903     Image pasting is relative to image configuration.
10904   */
10905   XSetCursorState(display,windows,MagickTrue);
10906   XCheckRefreshWindows(display,windows);
10907   width=(unsigned int) image->columns;
10908   height=(unsigned int) image->rows;
10909   x=0;
10910   y=0;
10911   if (windows->image.crop_geometry != (char *) NULL)
10912     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10913   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10914   paste_info.x+=x;
10915   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10916   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10917   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10918   paste_info.y+=y;
10919   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10920   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10921   /*
10922     Paste image with X Image window.
10923   */
10924   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10925   paste_image=DestroyImage(paste_image);
10926   XSetCursorState(display,windows,MagickFalse);
10927   /*
10928     Update image colormap.
10929   */
10930   XConfigureImageColormap(display,resource_info,windows,image);
10931   (void) XConfigureImage(display,resource_info,windows,image,exception);
10932   return(MagickTrue);
10933 }
10934 \f
10935 /*
10936 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10937 %                                                                             %
10938 %                                                                             %
10939 %                                                                             %
10940 +   X P r i n t I m a g e                                                     %
10941 %                                                                             %
10942 %                                                                             %
10943 %                                                                             %
10944 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10945 %
10946 %  XPrintImage() prints an image to a Postscript printer.
10947 %
10948 %  The format of the XPrintImage method is:
10949 %
10950 %      MagickBooleanType XPrintImage(Display *display,
10951 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10952 %        ExceptionInfo *exception)
10953 %
10954 %  A description of each parameter follows:
10955 %
10956 %    o display: Specifies a connection to an X server; returned from
10957 %      XOpenDisplay.
10958 %
10959 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10960 %
10961 %    o windows: Specifies a pointer to a XWindows structure.
10962 %
10963 %    o image: the image.
10964 %
10965 %    o exception: return any errors or warnings in this structure.
10966 %
10967 */
10968 static MagickBooleanType XPrintImage(Display *display,
10969   XResourceInfo *resource_info,XWindows *windows,Image *image,
10970   ExceptionInfo *exception)
10971 {
10972   char
10973     filename[MaxTextExtent],
10974     geometry[MaxTextExtent];
10975
10976   Image
10977     *print_image;
10978
10979   ImageInfo
10980     *image_info;
10981
10982   MagickStatusType
10983     status;
10984
10985   /*
10986     Request Postscript page geometry from user.
10987   */
10988   image_info=CloneImageInfo(resource_info->image_info);
10989   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10990   if (image_info->page != (char *) NULL)
10991     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10992   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10993     "Select Postscript Page Geometry:",geometry);
10994   if (*geometry == '\0')
10995     return(MagickTrue);
10996   image_info->page=GetPageGeometry(geometry);
10997   /*
10998     Apply image transforms.
10999   */
11000   XSetCursorState(display,windows,MagickTrue);
11001   XCheckRefreshWindows(display,windows);
11002   print_image=CloneImage(image,0,0,MagickTrue,exception);
11003   if (print_image == (Image *) NULL)
11004     return(MagickFalse);
11005   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11006     windows->image.ximage->width,windows->image.ximage->height);
11007   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
11008   /*
11009     Print image.
11010   */
11011   (void) AcquireUniqueFilename(filename);
11012   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11013     filename);
11014   status=WriteImage(image_info,print_image,exception);
11015   (void) RelinquishUniqueFileResource(filename);
11016   print_image=DestroyImage(print_image);
11017   image_info=DestroyImageInfo(image_info);
11018   XSetCursorState(display,windows,MagickFalse);
11019   return(status != 0 ? MagickTrue : MagickFalse);
11020 }
11021 \f
11022 /*
11023 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11024 %                                                                             %
11025 %                                                                             %
11026 %                                                                             %
11027 +   X R O I I m a g e                                                         %
11028 %                                                                             %
11029 %                                                                             %
11030 %                                                                             %
11031 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11032 %
11033 %  XROIImage() applies an image processing technique to a region of interest.
11034 %
11035 %  The format of the XROIImage method is:
11036 %
11037 %      MagickBooleanType XROIImage(Display *display,
11038 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11039 %        ExceptionInfo *exception)
11040 %
11041 %  A description of each parameter follows:
11042 %
11043 %    o display: Specifies a connection to an X server; returned from
11044 %      XOpenDisplay.
11045 %
11046 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11047 %
11048 %    o windows: Specifies a pointer to a XWindows structure.
11049 %
11050 %    o image: the image; returned from ReadImage.
11051 %
11052 %    o exception: return any errors or warnings in this structure.
11053 %
11054 */
11055 static MagickBooleanType XROIImage(Display *display,
11056   XResourceInfo *resource_info,XWindows *windows,Image **image,
11057   ExceptionInfo *exception)
11058 {
11059 #define ApplyMenus  7
11060
11061   static const char
11062     *ROIMenu[] =
11063     {
11064       "Help",
11065       "Dismiss",
11066       (char *) NULL
11067     },
11068     *ApplyMenu[] =
11069     {
11070       "File",
11071       "Edit",
11072       "Transform",
11073       "Enhance",
11074       "Effects",
11075       "F/X",
11076       "Miscellany",
11077       "Help",
11078       "Dismiss",
11079       (char *) NULL
11080     },
11081     *FileMenu[] =
11082     {
11083       "Save...",
11084       "Print...",
11085       (char *) NULL
11086     },
11087     *EditMenu[] =
11088     {
11089       "Undo",
11090       "Redo",
11091       (char *) NULL
11092     },
11093     *TransformMenu[] =
11094     {
11095       "Flop",
11096       "Flip",
11097       "Rotate Right",
11098       "Rotate Left",
11099       (char *) NULL
11100     },
11101     *EnhanceMenu[] =
11102     {
11103       "Hue...",
11104       "Saturation...",
11105       "Brightness...",
11106       "Gamma...",
11107       "Spiff",
11108       "Dull",
11109       "Contrast Stretch...",
11110       "Sigmoidal Contrast...",
11111       "Normalize",
11112       "Equalize",
11113       "Negate",
11114       "Grayscale",
11115       "Map...",
11116       "Quantize...",
11117       (char *) NULL
11118     },
11119     *EffectsMenu[] =
11120     {
11121       "Despeckle",
11122       "Emboss",
11123       "Reduce Noise",
11124       "Add Noise",
11125       "Sharpen...",
11126       "Blur...",
11127       "Threshold...",
11128       "Edge Detect...",
11129       "Spread...",
11130       "Shade...",
11131       "Raise...",
11132       "Segment...",
11133       (char *) NULL
11134     },
11135     *FXMenu[] =
11136     {
11137       "Solarize...",
11138       "Sepia Tone...",
11139       "Swirl...",
11140       "Implode...",
11141       "Vignette...",
11142       "Wave...",
11143       "Oil Paint...",
11144       "Charcoal Draw...",
11145       (char *) NULL
11146     },
11147     *MiscellanyMenu[] =
11148     {
11149       "Image Info",
11150       "Zoom Image",
11151       "Show Preview...",
11152       "Show Histogram",
11153       "Show Matte",
11154       (char *) NULL
11155     };
11156
11157   static const char
11158     **Menus[ApplyMenus] =
11159     {
11160       FileMenu,
11161       EditMenu,
11162       TransformMenu,
11163       EnhanceMenu,
11164       EffectsMenu,
11165       FXMenu,
11166       MiscellanyMenu
11167     };
11168
11169   static const CommandType
11170     ApplyCommands[] =
11171     {
11172       NullCommand,
11173       NullCommand,
11174       NullCommand,
11175       NullCommand,
11176       NullCommand,
11177       NullCommand,
11178       NullCommand,
11179       HelpCommand,
11180       QuitCommand
11181     },
11182     FileCommands[] =
11183     {
11184       SaveCommand,
11185       PrintCommand
11186     },
11187     EditCommands[] =
11188     {
11189       UndoCommand,
11190       RedoCommand
11191     },
11192     TransformCommands[] =
11193     {
11194       FlopCommand,
11195       FlipCommand,
11196       RotateRightCommand,
11197       RotateLeftCommand
11198     },
11199     EnhanceCommands[] =
11200     {
11201       HueCommand,
11202       SaturationCommand,
11203       BrightnessCommand,
11204       GammaCommand,
11205       SpiffCommand,
11206       DullCommand,
11207       ContrastStretchCommand,
11208       SigmoidalContrastCommand,
11209       NormalizeCommand,
11210       EqualizeCommand,
11211       NegateCommand,
11212       GrayscaleCommand,
11213       MapCommand,
11214       QuantizeCommand
11215     },
11216     EffectsCommands[] =
11217     {
11218       DespeckleCommand,
11219       EmbossCommand,
11220       ReduceNoiseCommand,
11221       AddNoiseCommand,
11222       SharpenCommand,
11223       BlurCommand,
11224       EdgeDetectCommand,
11225       SpreadCommand,
11226       ShadeCommand,
11227       RaiseCommand,
11228       SegmentCommand
11229     },
11230     FXCommands[] =
11231     {
11232       SolarizeCommand,
11233       SepiaToneCommand,
11234       SwirlCommand,
11235       ImplodeCommand,
11236       VignetteCommand,
11237       WaveCommand,
11238       OilPaintCommand,
11239       CharcoalDrawCommand
11240     },
11241     MiscellanyCommands[] =
11242     {
11243       InfoCommand,
11244       ZoomCommand,
11245       ShowPreviewCommand,
11246       ShowHistogramCommand,
11247       ShowMatteCommand
11248     },
11249     ROICommands[] =
11250     {
11251       ROIHelpCommand,
11252       ROIDismissCommand
11253     };
11254
11255   static const CommandType
11256     *Commands[ApplyMenus] =
11257     {
11258       FileCommands,
11259       EditCommands,
11260       TransformCommands,
11261       EnhanceCommands,
11262       EffectsCommands,
11263       FXCommands,
11264       MiscellanyCommands
11265     };
11266
11267   char
11268     command[MaxTextExtent],
11269     text[MaxTextExtent];
11270
11271   CommandType
11272     command_type;
11273
11274   Cursor
11275     cursor;
11276
11277   Image
11278     *roi_image;
11279
11280   int
11281     entry,
11282     id,
11283     x,
11284     y;
11285
11286   MagickRealType
11287     scale_factor;
11288
11289   MagickProgressMonitor
11290     progress_monitor;
11291
11292   RectangleInfo
11293     crop_info,
11294     highlight_info,
11295     roi_info;
11296
11297   unsigned int
11298     height,
11299     width;
11300
11301   size_t
11302     state;
11303
11304   XEvent
11305     event;
11306
11307   /*
11308     Map Command widget.
11309   */
11310   (void) CloneString(&windows->command.name,"ROI");
11311   windows->command.data=0;
11312   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11313   (void) XMapRaised(display,windows->command.id);
11314   XClientMessage(display,windows->image.id,windows->im_protocols,
11315     windows->im_update_widget,CurrentTime);
11316   /*
11317     Track pointer until button 1 is pressed.
11318   */
11319   XQueryPosition(display,windows->image.id,&x,&y);
11320   (void) XSelectInput(display,windows->image.id,
11321     windows->image.attributes.event_mask | PointerMotionMask);
11322   roi_info.x=(ssize_t) windows->image.x+x;
11323   roi_info.y=(ssize_t) windows->image.y+y;
11324   roi_info.width=0;
11325   roi_info.height=0;
11326   cursor=XCreateFontCursor(display,XC_fleur);
11327   state=DefaultState;
11328   do
11329   {
11330     if (windows->info.mapped != MagickFalse)
11331       {
11332         /*
11333           Display pointer position.
11334         */
11335         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11336           (long) roi_info.x,(long) roi_info.y);
11337         XInfoWidget(display,windows,text);
11338       }
11339     /*
11340       Wait for next event.
11341     */
11342     XScreenEvent(display,windows,&event);
11343     if (event.xany.window == windows->command.id)
11344       {
11345         /*
11346           Select a command from the Command widget.
11347         */
11348         id=XCommandWidget(display,windows,ROIMenu,&event);
11349         if (id < 0)
11350           continue;
11351         switch (ROICommands[id])
11352         {
11353           case ROIHelpCommand:
11354           {
11355             XTextViewWidget(display,resource_info,windows,MagickFalse,
11356               "Help Viewer - Region of Interest",ImageROIHelp);
11357             break;
11358           }
11359           case ROIDismissCommand:
11360           {
11361             /*
11362               Prematurely exit.
11363             */
11364             state|=EscapeState;
11365             state|=ExitState;
11366             break;
11367           }
11368           default:
11369             break;
11370         }
11371         continue;
11372       }
11373     switch (event.type)
11374     {
11375       case ButtonPress:
11376       {
11377         if (event.xbutton.button != Button1)
11378           break;
11379         if (event.xbutton.window != windows->image.id)
11380           break;
11381         /*
11382           Note first corner of region of interest rectangle-- exit loop.
11383         */
11384         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11385         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11386         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11387         state|=ExitState;
11388         break;
11389       }
11390       case ButtonRelease:
11391         break;
11392       case Expose:
11393         break;
11394       case KeyPress:
11395       {
11396         KeySym
11397           key_symbol;
11398
11399         if (event.xkey.window != windows->image.id)
11400           break;
11401         /*
11402           Respond to a user key press.
11403         */
11404         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11405           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11406         switch ((int) key_symbol)
11407         {
11408           case XK_Escape:
11409           case XK_F20:
11410           {
11411             /*
11412               Prematurely exit.
11413             */
11414             state|=EscapeState;
11415             state|=ExitState;
11416             break;
11417           }
11418           case XK_F1:
11419           case XK_Help:
11420           {
11421             XTextViewWidget(display,resource_info,windows,MagickFalse,
11422               "Help Viewer - Region of Interest",ImageROIHelp);
11423             break;
11424           }
11425           default:
11426           {
11427             (void) XBell(display,0);
11428             break;
11429           }
11430         }
11431         break;
11432       }
11433       case MotionNotify:
11434       {
11435         /*
11436           Map and unmap Info widget as text cursor crosses its boundaries.
11437         */
11438         x=event.xmotion.x;
11439         y=event.xmotion.y;
11440         if (windows->info.mapped != MagickFalse)
11441           {
11442             if ((x < (int) (windows->info.x+windows->info.width)) &&
11443                 (y < (int) (windows->info.y+windows->info.height)))
11444               (void) XWithdrawWindow(display,windows->info.id,
11445                 windows->info.screen);
11446           }
11447         else
11448           if ((x > (int) (windows->info.x+windows->info.width)) ||
11449               (y > (int) (windows->info.y+windows->info.height)))
11450             (void) XMapWindow(display,windows->info.id);
11451         roi_info.x=(ssize_t) windows->image.x+x;
11452         roi_info.y=(ssize_t) windows->image.y+y;
11453         break;
11454       }
11455       default:
11456         break;
11457     }
11458   } while ((state & ExitState) == 0);
11459   (void) XSelectInput(display,windows->image.id,
11460     windows->image.attributes.event_mask);
11461   if ((state & EscapeState) != 0)
11462     {
11463       /*
11464         User want to exit without region of interest.
11465       */
11466       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11467       (void) XFreeCursor(display,cursor);
11468       return(MagickTrue);
11469     }
11470   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11471   do
11472   {
11473     /*
11474       Size rectangle as pointer moves until the mouse button is released.
11475     */
11476     x=(int) roi_info.x;
11477     y=(int) roi_info.y;
11478     roi_info.width=0;
11479     roi_info.height=0;
11480     state=DefaultState;
11481     do
11482     {
11483       highlight_info=roi_info;
11484       highlight_info.x=roi_info.x-windows->image.x;
11485       highlight_info.y=roi_info.y-windows->image.y;
11486       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11487         {
11488           /*
11489             Display info and draw region of interest rectangle.
11490           */
11491           if (windows->info.mapped == MagickFalse)
11492             (void) XMapWindow(display,windows->info.id);
11493           (void) FormatLocaleString(text,MaxTextExtent,
11494             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11495             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11496           XInfoWidget(display,windows,text);
11497           XHighlightRectangle(display,windows->image.id,
11498             windows->image.highlight_context,&highlight_info);
11499         }
11500       else
11501         if (windows->info.mapped != MagickFalse)
11502           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11503       /*
11504         Wait for next event.
11505       */
11506       XScreenEvent(display,windows,&event);
11507       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11508         XHighlightRectangle(display,windows->image.id,
11509           windows->image.highlight_context,&highlight_info);
11510       switch (event.type)
11511       {
11512         case ButtonPress:
11513         {
11514           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11515           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11516           break;
11517         }
11518         case ButtonRelease:
11519         {
11520           /*
11521             User has committed to region of interest rectangle.
11522           */
11523           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11524           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11525           XSetCursorState(display,windows,MagickFalse);
11526           state|=ExitState;
11527           if (LocaleCompare(windows->command.name,"Apply") == 0)
11528             break;
11529           (void) CloneString(&windows->command.name,"Apply");
11530           windows->command.data=ApplyMenus;
11531           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11532           break;
11533         }
11534         case Expose:
11535           break;
11536         case MotionNotify:
11537         {
11538           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11539           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11540         }
11541         default:
11542           break;
11543       }
11544       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11545           ((state & ExitState) != 0))
11546         {
11547           /*
11548             Check boundary conditions.
11549           */
11550           if (roi_info.x < 0)
11551             roi_info.x=0;
11552           else
11553             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11554               roi_info.x=(ssize_t) windows->image.ximage->width;
11555           if ((int) roi_info.x < x)
11556             roi_info.width=(unsigned int) (x-roi_info.x);
11557           else
11558             {
11559               roi_info.width=(unsigned int) (roi_info.x-x);
11560               roi_info.x=(ssize_t) x;
11561             }
11562           if (roi_info.y < 0)
11563             roi_info.y=0;
11564           else
11565             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11566               roi_info.y=(ssize_t) windows->image.ximage->height;
11567           if ((int) roi_info.y < y)
11568             roi_info.height=(unsigned int) (y-roi_info.y);
11569           else
11570             {
11571               roi_info.height=(unsigned int) (roi_info.y-y);
11572               roi_info.y=(ssize_t) y;
11573             }
11574         }
11575     } while ((state & ExitState) == 0);
11576     /*
11577       Wait for user to grab a corner of the rectangle or press return.
11578     */
11579     state=DefaultState;
11580     command_type=NullCommand;
11581     (void) XMapWindow(display,windows->info.id);
11582     do
11583     {
11584       if (windows->info.mapped != MagickFalse)
11585         {
11586           /*
11587             Display pointer position.
11588           */
11589           (void) FormatLocaleString(text,MaxTextExtent,
11590             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11591             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11592           XInfoWidget(display,windows,text);
11593         }
11594       highlight_info=roi_info;
11595       highlight_info.x=roi_info.x-windows->image.x;
11596       highlight_info.y=roi_info.y-windows->image.y;
11597       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11598         {
11599           state|=EscapeState;
11600           state|=ExitState;
11601           break;
11602         }
11603       if ((state & UpdateRegionState) != 0)
11604         {
11605           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11606           switch (command_type)
11607           {
11608             case UndoCommand:
11609             case RedoCommand:
11610             {
11611               (void) XMagickCommand(display,resource_info,windows,command_type,
11612                 image,exception);
11613               break;
11614             }
11615             default:
11616             {
11617               /*
11618                 Region of interest is relative to image configuration.
11619               */
11620               progress_monitor=SetImageProgressMonitor(*image,
11621                 (MagickProgressMonitor) NULL,(*image)->client_data);
11622               crop_info=roi_info;
11623               width=(unsigned int) (*image)->columns;
11624               height=(unsigned int) (*image)->rows;
11625               x=0;
11626               y=0;
11627               if (windows->image.crop_geometry != (char *) NULL)
11628                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11629                   &width,&height);
11630               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11631               crop_info.x+=x;
11632               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11633               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11634               scale_factor=(MagickRealType)
11635                 height/windows->image.ximage->height;
11636               crop_info.y+=y;
11637               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11638               crop_info.height=(unsigned int)
11639                 (scale_factor*crop_info.height+0.5);
11640               roi_image=CropImage(*image,&crop_info,exception);
11641               (void) SetImageProgressMonitor(*image,progress_monitor,
11642                 (*image)->client_data);
11643               if (roi_image == (Image *) NULL)
11644                 continue;
11645               /*
11646                 Apply image processing technique to the region of interest.
11647               */
11648               windows->image.orphan=MagickTrue;
11649               (void) XMagickCommand(display,resource_info,windows,command_type,
11650                 &roi_image,exception);
11651               progress_monitor=SetImageProgressMonitor(*image,
11652                 (MagickProgressMonitor) NULL,(*image)->client_data);
11653               (void) XMagickCommand(display,resource_info,windows,
11654                 SaveToUndoBufferCommand,image,exception);
11655               windows->image.orphan=MagickFalse;
11656               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11657                 crop_info.x,crop_info.y);
11658               roi_image=DestroyImage(roi_image);
11659               (void) SetImageProgressMonitor(*image,progress_monitor,
11660                 (*image)->client_data);
11661               break;
11662             }
11663           }
11664           if (command_type != InfoCommand)
11665             {
11666               XConfigureImageColormap(display,resource_info,windows,*image);
11667               (void) XConfigureImage(display,resource_info,windows,*image,exception);
11668             }
11669           XCheckRefreshWindows(display,windows);
11670           XInfoWidget(display,windows,text);
11671           (void) XSetFunction(display,windows->image.highlight_context,
11672             GXinvert);
11673           state&=(~UpdateRegionState);
11674         }
11675       XHighlightRectangle(display,windows->image.id,
11676         windows->image.highlight_context,&highlight_info);
11677       XScreenEvent(display,windows,&event);
11678       if (event.xany.window == windows->command.id)
11679         {
11680           /*
11681             Select a command from the Command widget.
11682           */
11683           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11684           command_type=NullCommand;
11685           id=XCommandWidget(display,windows,ApplyMenu,&event);
11686           if (id >= 0)
11687             {
11688               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11689               command_type=ApplyCommands[id];
11690               if (id < ApplyMenus)
11691                 {
11692                   /*
11693                     Select a command from a pop-up menu.
11694                   */
11695                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11696                     (const char **) Menus[id],command);
11697                   if (entry >= 0)
11698                     {
11699                       (void) CopyMagickString(command,Menus[id][entry],
11700                         MaxTextExtent);
11701                       command_type=Commands[id][entry];
11702                     }
11703                 }
11704             }
11705           (void) XSetFunction(display,windows->image.highlight_context,
11706             GXinvert);
11707           XHighlightRectangle(display,windows->image.id,
11708             windows->image.highlight_context,&highlight_info);
11709           if (command_type == HelpCommand)
11710             {
11711               (void) XSetFunction(display,windows->image.highlight_context,
11712                 GXcopy);
11713               XTextViewWidget(display,resource_info,windows,MagickFalse,
11714                 "Help Viewer - Region of Interest",ImageROIHelp);
11715               (void) XSetFunction(display,windows->image.highlight_context,
11716                 GXinvert);
11717               continue;
11718             }
11719           if (command_type == QuitCommand)
11720             {
11721               /*
11722                 exit.
11723               */
11724               state|=EscapeState;
11725               state|=ExitState;
11726               continue;
11727             }
11728           if (command_type != NullCommand)
11729             state|=UpdateRegionState;
11730           continue;
11731         }
11732       XHighlightRectangle(display,windows->image.id,
11733         windows->image.highlight_context,&highlight_info);
11734       switch (event.type)
11735       {
11736         case ButtonPress:
11737         {
11738           x=windows->image.x;
11739           y=windows->image.y;
11740           if (event.xbutton.button != Button1)
11741             break;
11742           if (event.xbutton.window != windows->image.id)
11743             break;
11744           x=windows->image.x+event.xbutton.x;
11745           y=windows->image.y+event.xbutton.y;
11746           if ((x < (int) (roi_info.x+RoiDelta)) &&
11747               (x > (int) (roi_info.x-RoiDelta)) &&
11748               (y < (int) (roi_info.y+RoiDelta)) &&
11749               (y > (int) (roi_info.y-RoiDelta)))
11750             {
11751               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11752               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11753               state|=UpdateConfigurationState;
11754               break;
11755             }
11756           if ((x < (int) (roi_info.x+RoiDelta)) &&
11757               (x > (int) (roi_info.x-RoiDelta)) &&
11758               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11759               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11760             {
11761               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11762               state|=UpdateConfigurationState;
11763               break;
11764             }
11765           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11766               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11767               (y < (int) (roi_info.y+RoiDelta)) &&
11768               (y > (int) (roi_info.y-RoiDelta)))
11769             {
11770               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11771               state|=UpdateConfigurationState;
11772               break;
11773             }
11774           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11775               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11776               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11777               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11778             {
11779               state|=UpdateConfigurationState;
11780               break;
11781             }
11782         }
11783         case ButtonRelease:
11784         {
11785           if (event.xbutton.window == windows->pan.id)
11786             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11787                 (highlight_info.y != crop_info.y-windows->image.y))
11788               XHighlightRectangle(display,windows->image.id,
11789                 windows->image.highlight_context,&highlight_info);
11790           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11791             event.xbutton.time);
11792           break;
11793         }
11794         case Expose:
11795         {
11796           if (event.xexpose.window == windows->image.id)
11797             if (event.xexpose.count == 0)
11798               {
11799                 event.xexpose.x=(int) highlight_info.x;
11800                 event.xexpose.y=(int) highlight_info.y;
11801                 event.xexpose.width=(int) highlight_info.width;
11802                 event.xexpose.height=(int) highlight_info.height;
11803                 XRefreshWindow(display,&windows->image,&event);
11804               }
11805           if (event.xexpose.window == windows->info.id)
11806             if (event.xexpose.count == 0)
11807               XInfoWidget(display,windows,text);
11808           break;
11809         }
11810         case KeyPress:
11811         {
11812           KeySym
11813             key_symbol;
11814
11815           if (event.xkey.window != windows->image.id)
11816             break;
11817           /*
11818             Respond to a user key press.
11819           */
11820           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11821             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11822           switch ((int) key_symbol)
11823           {
11824             case XK_Shift_L:
11825             case XK_Shift_R:
11826               break;
11827             case XK_Escape:
11828             case XK_F20:
11829               state|=EscapeState;
11830             case XK_Return:
11831             {
11832               state|=ExitState;
11833               break;
11834             }
11835             case XK_Home:
11836             case XK_KP_Home:
11837             {
11838               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11839               roi_info.y=(ssize_t) (windows->image.height/2L-
11840                 roi_info.height/2L);
11841               break;
11842             }
11843             case XK_Left:
11844             case XK_KP_Left:
11845             {
11846               roi_info.x--;
11847               break;
11848             }
11849             case XK_Up:
11850             case XK_KP_Up:
11851             case XK_Next:
11852             {
11853               roi_info.y--;
11854               break;
11855             }
11856             case XK_Right:
11857             case XK_KP_Right:
11858             {
11859               roi_info.x++;
11860               break;
11861             }
11862             case XK_Prior:
11863             case XK_Down:
11864             case XK_KP_Down:
11865             {
11866               roi_info.y++;
11867               break;
11868             }
11869             case XK_F1:
11870             case XK_Help:
11871             {
11872               (void) XSetFunction(display,windows->image.highlight_context,
11873                 GXcopy);
11874               XTextViewWidget(display,resource_info,windows,MagickFalse,
11875                 "Help Viewer - Region of Interest",ImageROIHelp);
11876               (void) XSetFunction(display,windows->image.highlight_context,
11877                 GXinvert);
11878               break;
11879             }
11880             default:
11881             {
11882               command_type=XImageWindowCommand(display,resource_info,windows,
11883                 event.xkey.state,key_symbol,image,exception);
11884               if (command_type != NullCommand)
11885                 state|=UpdateRegionState;
11886               break;
11887             }
11888           }
11889           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11890             event.xkey.time);
11891           break;
11892         }
11893         case KeyRelease:
11894           break;
11895         case MotionNotify:
11896         {
11897           if (event.xbutton.window != windows->image.id)
11898             break;
11899           /*
11900             Map and unmap Info widget as text cursor crosses its boundaries.
11901           */
11902           x=event.xmotion.x;
11903           y=event.xmotion.y;
11904           if (windows->info.mapped != MagickFalse)
11905             {
11906               if ((x < (int) (windows->info.x+windows->info.width)) &&
11907                   (y < (int) (windows->info.y+windows->info.height)))
11908                 (void) XWithdrawWindow(display,windows->info.id,
11909                   windows->info.screen);
11910             }
11911           else
11912             if ((x > (int) (windows->info.x+windows->info.width)) ||
11913                 (y > (int) (windows->info.y+windows->info.height)))
11914               (void) XMapWindow(display,windows->info.id);
11915           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11916           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11917           break;
11918         }
11919         case SelectionRequest:
11920         {
11921           XSelectionEvent
11922             notify;
11923
11924           XSelectionRequestEvent
11925             *request;
11926
11927           /*
11928             Set primary selection.
11929           */
11930           (void) FormatLocaleString(text,MaxTextExtent,
11931             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11932             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11933           request=(&(event.xselectionrequest));
11934           (void) XChangeProperty(request->display,request->requestor,
11935             request->property,request->target,8,PropModeReplace,
11936             (unsigned char *) text,(int) strlen(text));
11937           notify.type=SelectionNotify;
11938           notify.display=request->display;
11939           notify.requestor=request->requestor;
11940           notify.selection=request->selection;
11941           notify.target=request->target;
11942           notify.time=request->time;
11943           if (request->property == None)
11944             notify.property=request->target;
11945           else
11946             notify.property=request->property;
11947           (void) XSendEvent(request->display,request->requestor,False,0,
11948             (XEvent *) &notify);
11949         }
11950         default:
11951           break;
11952       }
11953       if ((state & UpdateConfigurationState) != 0)
11954         {
11955           (void) XPutBackEvent(display,&event);
11956           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11957           break;
11958         }
11959     } while ((state & ExitState) == 0);
11960   } while ((state & ExitState) == 0);
11961   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11962   XSetCursorState(display,windows,MagickFalse);
11963   if ((state & EscapeState) != 0)
11964     return(MagickTrue);
11965   return(MagickTrue);
11966 }
11967 \f
11968 /*
11969 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11970 %                                                                             %
11971 %                                                                             %
11972 %                                                                             %
11973 +   X R o t a t e I m a g e                                                   %
11974 %                                                                             %
11975 %                                                                             %
11976 %                                                                             %
11977 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11978 %
11979 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11980 %  rotation angle is computed from the slope of a line drawn by the user.
11981 %
11982 %  The format of the XRotateImage method is:
11983 %
11984 %      MagickBooleanType XRotateImage(Display *display,
11985 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11986 %        Image **image,ExceptionInfo *exception)
11987 %
11988 %  A description of each parameter follows:
11989 %
11990 %    o display: Specifies a connection to an X server; returned from
11991 %      XOpenDisplay.
11992 %
11993 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11994 %
11995 %    o windows: Specifies a pointer to a XWindows structure.
11996 %
11997 %    o degrees: Specifies the number of degrees to rotate the image.
11998 %
11999 %    o image: the image.
12000 %
12001 %    o exception: return any errors or warnings in this structure.
12002 %
12003 */
12004 static MagickBooleanType XRotateImage(Display *display,
12005   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12006   ExceptionInfo *exception)
12007 {
12008   static const char
12009     *RotateMenu[] =
12010     {
12011       "Pixel Color",
12012       "Direction",
12013       "Help",
12014       "Dismiss",
12015       (char *) NULL
12016     };
12017
12018   static ModeType
12019     direction = HorizontalRotateCommand;
12020
12021   static const ModeType
12022     DirectionCommands[] =
12023     {
12024       HorizontalRotateCommand,
12025       VerticalRotateCommand
12026     },
12027     RotateCommands[] =
12028     {
12029       RotateColorCommand,
12030       RotateDirectionCommand,
12031       RotateHelpCommand,
12032       RotateDismissCommand
12033     };
12034
12035   static unsigned int
12036     pen_id = 0;
12037
12038   char
12039     command[MaxTextExtent],
12040     text[MaxTextExtent];
12041
12042   Image
12043     *rotate_image;
12044
12045   int
12046     id,
12047     x,
12048     y;
12049
12050   MagickRealType
12051     normalized_degrees;
12052
12053   register int
12054     i;
12055
12056   unsigned int
12057     height,
12058     rotations,
12059     width;
12060
12061   if (degrees == 0.0)
12062     {
12063       unsigned int
12064         distance;
12065
12066       size_t
12067         state;
12068
12069       XEvent
12070         event;
12071
12072       XSegment
12073         rotate_info;
12074
12075       /*
12076         Map Command widget.
12077       */
12078       (void) CloneString(&windows->command.name,"Rotate");
12079       windows->command.data=2;
12080       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12081       (void) XMapRaised(display,windows->command.id);
12082       XClientMessage(display,windows->image.id,windows->im_protocols,
12083         windows->im_update_widget,CurrentTime);
12084       /*
12085         Wait for first button press.
12086       */
12087       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12088       XQueryPosition(display,windows->image.id,&x,&y);
12089       rotate_info.x1=x;
12090       rotate_info.y1=y;
12091       rotate_info.x2=x;
12092       rotate_info.y2=y;
12093       state=DefaultState;
12094       do
12095       {
12096         XHighlightLine(display,windows->image.id,
12097           windows->image.highlight_context,&rotate_info);
12098         /*
12099           Wait for next event.
12100         */
12101         XScreenEvent(display,windows,&event);
12102         XHighlightLine(display,windows->image.id,
12103           windows->image.highlight_context,&rotate_info);
12104         if (event.xany.window == windows->command.id)
12105           {
12106             /*
12107               Select a command from the Command widget.
12108             */
12109             id=XCommandWidget(display,windows,RotateMenu,&event);
12110             if (id < 0)
12111               continue;
12112             (void) XSetFunction(display,windows->image.highlight_context,
12113               GXcopy);
12114             switch (RotateCommands[id])
12115             {
12116               case RotateColorCommand:
12117               {
12118                 const char
12119                   *ColorMenu[MaxNumberPens];
12120
12121                 int
12122                   pen_number;
12123
12124                 XColor
12125                   color;
12126
12127                 /*
12128                   Initialize menu selections.
12129                 */
12130                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12131                   ColorMenu[i]=resource_info->pen_colors[i];
12132                 ColorMenu[MaxNumberPens-2]="Browser...";
12133                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12134                 /*
12135                   Select a pen color from the pop-up menu.
12136                 */
12137                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12138                   (const char **) ColorMenu,command);
12139                 if (pen_number < 0)
12140                   break;
12141                 if (pen_number == (MaxNumberPens-2))
12142                   {
12143                     static char
12144                       color_name[MaxTextExtent] = "gray";
12145
12146                     /*
12147                       Select a pen color from a dialog.
12148                     */
12149                     resource_info->pen_colors[pen_number]=color_name;
12150                     XColorBrowserWidget(display,windows,"Select",color_name);
12151                     if (*color_name == '\0')
12152                       break;
12153                   }
12154                 /*
12155                   Set pen color.
12156                 */
12157                 (void) XParseColor(display,windows->map_info->colormap,
12158                   resource_info->pen_colors[pen_number],&color);
12159                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12160                   (unsigned int) MaxColors,&color);
12161                 windows->pixel_info->pen_colors[pen_number]=color;
12162                 pen_id=(unsigned int) pen_number;
12163                 break;
12164               }
12165               case RotateDirectionCommand:
12166               {
12167                 static const char
12168                   *Directions[] =
12169                   {
12170                     "horizontal",
12171                     "vertical",
12172                     (char *) NULL,
12173                   };
12174
12175                 /*
12176                   Select a command from the pop-up menu.
12177                 */
12178                 id=XMenuWidget(display,windows,RotateMenu[id],
12179                   Directions,command);
12180                 if (id >= 0)
12181                   direction=DirectionCommands[id];
12182                 break;
12183               }
12184               case RotateHelpCommand:
12185               {
12186                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12187                   "Help Viewer - Image Rotation",ImageRotateHelp);
12188                 break;
12189               }
12190               case RotateDismissCommand:
12191               {
12192                 /*
12193                   Prematurely exit.
12194                 */
12195                 state|=EscapeState;
12196                 state|=ExitState;
12197                 break;
12198               }
12199               default:
12200                 break;
12201             }
12202             (void) XSetFunction(display,windows->image.highlight_context,
12203               GXinvert);
12204             continue;
12205           }
12206         switch (event.type)
12207         {
12208           case ButtonPress:
12209           {
12210             if (event.xbutton.button != Button1)
12211               break;
12212             if (event.xbutton.window != windows->image.id)
12213               break;
12214             /*
12215               exit loop.
12216             */
12217             (void) XSetFunction(display,windows->image.highlight_context,
12218               GXcopy);
12219             rotate_info.x1=event.xbutton.x;
12220             rotate_info.y1=event.xbutton.y;
12221             state|=ExitState;
12222             break;
12223           }
12224           case ButtonRelease:
12225             break;
12226           case Expose:
12227             break;
12228           case KeyPress:
12229           {
12230             char
12231               command[MaxTextExtent];
12232
12233             KeySym
12234               key_symbol;
12235
12236             if (event.xkey.window != windows->image.id)
12237               break;
12238             /*
12239               Respond to a user key press.
12240             */
12241             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12242               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12243             switch ((int) key_symbol)
12244             {
12245               case XK_Escape:
12246               case XK_F20:
12247               {
12248                 /*
12249                   Prematurely exit.
12250                 */
12251                 state|=EscapeState;
12252                 state|=ExitState;
12253                 break;
12254               }
12255               case XK_F1:
12256               case XK_Help:
12257               {
12258                 (void) XSetFunction(display,windows->image.highlight_context,
12259                   GXcopy);
12260                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12261                   "Help Viewer - Image Rotation",ImageRotateHelp);
12262                 (void) XSetFunction(display,windows->image.highlight_context,
12263                   GXinvert);
12264                 break;
12265               }
12266               default:
12267               {
12268                 (void) XBell(display,0);
12269                 break;
12270               }
12271             }
12272             break;
12273           }
12274           case MotionNotify:
12275           {
12276             rotate_info.x1=event.xmotion.x;
12277             rotate_info.y1=event.xmotion.y;
12278           }
12279         }
12280         rotate_info.x2=rotate_info.x1;
12281         rotate_info.y2=rotate_info.y1;
12282         if (direction == HorizontalRotateCommand)
12283           rotate_info.x2+=32;
12284         else
12285           rotate_info.y2-=32;
12286       } while ((state & ExitState) == 0);
12287       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12288       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12289       if ((state & EscapeState) != 0)
12290         return(MagickTrue);
12291       /*
12292         Draw line as pointer moves until the mouse button is released.
12293       */
12294       distance=0;
12295       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12296       state=DefaultState;
12297       do
12298       {
12299         if (distance > 9)
12300           {
12301             /*
12302               Display info and draw rotation line.
12303             */
12304             if (windows->info.mapped == MagickFalse)
12305               (void) XMapWindow(display,windows->info.id);
12306             (void) FormatLocaleString(text,MaxTextExtent," %g",
12307               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12308             XInfoWidget(display,windows,text);
12309             XHighlightLine(display,windows->image.id,
12310               windows->image.highlight_context,&rotate_info);
12311           }
12312         else
12313           if (windows->info.mapped != MagickFalse)
12314             (void) XWithdrawWindow(display,windows->info.id,
12315               windows->info.screen);
12316         /*
12317           Wait for next event.
12318         */
12319         XScreenEvent(display,windows,&event);
12320         if (distance > 9)
12321           XHighlightLine(display,windows->image.id,
12322             windows->image.highlight_context,&rotate_info);
12323         switch (event.type)
12324         {
12325           case ButtonPress:
12326             break;
12327           case ButtonRelease:
12328           {
12329             /*
12330               User has committed to rotation line.
12331             */
12332             rotate_info.x2=event.xbutton.x;
12333             rotate_info.y2=event.xbutton.y;
12334             state|=ExitState;
12335             break;
12336           }
12337           case Expose:
12338             break;
12339           case MotionNotify:
12340           {
12341             rotate_info.x2=event.xmotion.x;
12342             rotate_info.y2=event.xmotion.y;
12343           }
12344           default:
12345             break;
12346         }
12347         /*
12348           Check boundary conditions.
12349         */
12350         if (rotate_info.x2 < 0)
12351           rotate_info.x2=0;
12352         else
12353           if (rotate_info.x2 > (int) windows->image.width)
12354             rotate_info.x2=(short) windows->image.width;
12355         if (rotate_info.y2 < 0)
12356           rotate_info.y2=0;
12357         else
12358           if (rotate_info.y2 > (int) windows->image.height)
12359             rotate_info.y2=(short) windows->image.height;
12360         /*
12361           Compute rotation angle from the slope of the line.
12362         */
12363         degrees=0.0;
12364         distance=(unsigned int)
12365           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12366           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12367         if (distance > 9)
12368           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12369             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12370       } while ((state & ExitState) == 0);
12371       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12372       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12373       if (distance <= 9)
12374         return(MagickTrue);
12375     }
12376   if (direction == VerticalRotateCommand)
12377     degrees-=90.0;
12378   if (degrees == 0.0)
12379     return(MagickTrue);
12380   /*
12381     Rotate image.
12382   */
12383   normalized_degrees=degrees;
12384   while (normalized_degrees < -45.0)
12385     normalized_degrees+=360.0;
12386   for (rotations=0; normalized_degrees > 45.0; rotations++)
12387     normalized_degrees-=90.0;
12388   if (normalized_degrees != 0.0)
12389     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12390       exception);
12391   XSetCursorState(display,windows,MagickTrue);
12392   XCheckRefreshWindows(display,windows);
12393   (*image)->background_color.red=ScaleShortToQuantum(
12394     windows->pixel_info->pen_colors[pen_id].red);
12395   (*image)->background_color.green=ScaleShortToQuantum(
12396     windows->pixel_info->pen_colors[pen_id].green);
12397   (*image)->background_color.blue=ScaleShortToQuantum(
12398     windows->pixel_info->pen_colors[pen_id].blue);
12399   rotate_image=RotateImage(*image,degrees,exception);
12400   XSetCursorState(display,windows,MagickFalse);
12401   if (rotate_image == (Image *) NULL)
12402     return(MagickFalse);
12403   *image=DestroyImage(*image);
12404   *image=rotate_image;
12405   if (windows->image.crop_geometry != (char *) NULL)
12406     {
12407       /*
12408         Rotate crop geometry.
12409       */
12410       width=(unsigned int) (*image)->columns;
12411       height=(unsigned int) (*image)->rows;
12412       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12413       switch (rotations % 4)
12414       {
12415         default:
12416         case 0:
12417           break;
12418         case 1:
12419         {
12420           /*
12421             Rotate 90 degrees.
12422           */
12423           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12424             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12425             (int) height-y,x);
12426           break;
12427         }
12428         case 2:
12429         {
12430           /*
12431             Rotate 180 degrees.
12432           */
12433           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12434             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12435           break;
12436         }
12437         case 3:
12438         {
12439           /*
12440             Rotate 270 degrees.
12441           */
12442           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12443             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12444           break;
12445         }
12446       }
12447     }
12448   if (windows->image.orphan != MagickFalse)
12449     return(MagickTrue);
12450   if (normalized_degrees != 0.0)
12451     {
12452       /*
12453         Update image colormap.
12454       */
12455       windows->image.window_changes.width=(int) (*image)->columns;
12456       windows->image.window_changes.height=(int) (*image)->rows;
12457       if (windows->image.crop_geometry != (char *) NULL)
12458         {
12459           /*
12460             Obtain dimensions of image from crop geometry.
12461           */
12462           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12463             &width,&height);
12464           windows->image.window_changes.width=(int) width;
12465           windows->image.window_changes.height=(int) height;
12466         }
12467       XConfigureImageColormap(display,resource_info,windows,*image);
12468     }
12469   else
12470     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12471       {
12472         windows->image.window_changes.width=windows->image.ximage->height;
12473         windows->image.window_changes.height=windows->image.ximage->width;
12474       }
12475   /*
12476     Update image configuration.
12477   */
12478   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12479   return(MagickTrue);
12480 }
12481 \f
12482 /*
12483 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12484 %                                                                             %
12485 %                                                                             %
12486 %                                                                             %
12487 +   X S a v e I m a g e                                                       %
12488 %                                                                             %
12489 %                                                                             %
12490 %                                                                             %
12491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12492 %
12493 %  XSaveImage() saves an image to a file.
12494 %
12495 %  The format of the XSaveImage method is:
12496 %
12497 %      MagickBooleanType XSaveImage(Display *display,
12498 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12499 %        ExceptionInfo *exception)
12500 %
12501 %  A description of each parameter follows:
12502 %
12503 %    o display: Specifies a connection to an X server; returned from
12504 %      XOpenDisplay.
12505 %
12506 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12507 %
12508 %    o windows: Specifies a pointer to a XWindows structure.
12509 %
12510 %    o image: the image.
12511 %
12512 %    o exception: return any errors or warnings in this structure.
12513 %
12514 */
12515 static MagickBooleanType XSaveImage(Display *display,
12516   XResourceInfo *resource_info,XWindows *windows,Image *image,
12517   ExceptionInfo *exception)
12518 {
12519   char
12520     filename[MaxTextExtent],
12521     geometry[MaxTextExtent];
12522
12523   Image
12524     *save_image;
12525
12526   ImageInfo
12527     *image_info;
12528
12529   MagickStatusType
12530     status;
12531
12532   /*
12533     Request file name from user.
12534   */
12535   if (resource_info->write_filename != (char *) NULL)
12536     (void) CopyMagickString(filename,resource_info->write_filename,
12537       MaxTextExtent);
12538   else
12539     {
12540       char
12541         path[MaxTextExtent];
12542
12543       int
12544         status;
12545
12546       GetPathComponent(image->filename,HeadPath,path);
12547       GetPathComponent(image->filename,TailPath,filename);
12548       if (*path != '\0')
12549         {
12550           status=chdir(path);
12551           if (status == -1)
12552             (void) ThrowMagickException(exception,GetMagickModule(),
12553               FileOpenError,"UnableToOpenFile","%s",path);
12554         }
12555     }
12556   XFileBrowserWidget(display,windows,"Save",filename);
12557   if (*filename == '\0')
12558     return(MagickTrue);
12559   if (IsPathAccessible(filename) != MagickFalse)
12560     {
12561       int
12562         status;
12563
12564       /*
12565         File exists-- seek user's permission before overwriting.
12566       */
12567       status=XConfirmWidget(display,windows,"Overwrite",filename);
12568       if (status <= 0)
12569         return(MagickTrue);
12570     }
12571   image_info=CloneImageInfo(resource_info->image_info);
12572   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12573   (void) SetImageInfo(image_info,1,exception);
12574   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12575       (LocaleCompare(image_info->magick,"JPG") == 0))
12576     {
12577       char
12578         quality[MaxTextExtent];
12579
12580       int
12581         status;
12582
12583       /*
12584         Request JPEG quality from user.
12585       */
12586       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12587         image->quality);
12588       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12589         quality);
12590       if (*quality == '\0')
12591         return(MagickTrue);
12592       image->quality=StringToUnsignedLong(quality);
12593       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12594     }
12595   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12596       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12597       (LocaleCompare(image_info->magick,"PS") == 0) ||
12598       (LocaleCompare(image_info->magick,"PS2") == 0))
12599     {
12600       char
12601         geometry[MaxTextExtent];
12602
12603       /*
12604         Request page geometry from user.
12605       */
12606       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12607       if (LocaleCompare(image_info->magick,"PDF") == 0)
12608         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12609       if (image_info->page != (char *) NULL)
12610         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12611       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12612         "Select page geometry:",geometry);
12613       if (*geometry != '\0')
12614         image_info->page=GetPageGeometry(geometry);
12615     }
12616   /*
12617     Apply image transforms.
12618   */
12619   XSetCursorState(display,windows,MagickTrue);
12620   XCheckRefreshWindows(display,windows);
12621   save_image=CloneImage(image,0,0,MagickTrue,exception);
12622   if (save_image == (Image *) NULL)
12623     return(MagickFalse);
12624   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12625     windows->image.ximage->width,windows->image.ximage->height);
12626   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12627   /*
12628     Write image.
12629   */
12630   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12631   status=WriteImage(image_info,save_image,exception);
12632   if (status != MagickFalse)
12633     image->taint=MagickFalse;
12634   save_image=DestroyImage(save_image);
12635   image_info=DestroyImageInfo(image_info);
12636   XSetCursorState(display,windows,MagickFalse);
12637   return(status != 0 ? MagickTrue : MagickFalse);
12638 }
12639 \f
12640 /*
12641 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12642 %                                                                             %
12643 %                                                                             %
12644 %                                                                             %
12645 +   X S c r e e n E v e n t                                                   %
12646 %                                                                             %
12647 %                                                                             %
12648 %                                                                             %
12649 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12650 %
12651 %  XScreenEvent() handles global events associated with the Pan and Magnify
12652 %  windows.
12653 %
12654 %  The format of the XScreenEvent function is:
12655 %
12656 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12657 %
12658 %  A description of each parameter follows:
12659 %
12660 %    o display: Specifies a pointer to the Display structure;  returned from
12661 %      XOpenDisplay.
12662 %
12663 %    o windows: Specifies a pointer to a XWindows structure.
12664 %
12665 %    o event: Specifies a pointer to a X11 XEvent structure.
12666 %
12667 %
12668 */
12669
12670 #if defined(__cplusplus) || defined(c_plusplus)
12671 extern "C" {
12672 #endif
12673
12674 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12675 {
12676   register XWindows
12677     *windows;
12678
12679   windows=(XWindows *) data;
12680   if ((event->type == ClientMessage) &&
12681       (event->xclient.window == windows->image.id))
12682     return(MagickFalse);
12683   return(MagickTrue);
12684 }
12685
12686 #if defined(__cplusplus) || defined(c_plusplus)
12687 }
12688 #endif
12689
12690 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12691 {
12692   register int
12693     x,
12694     y;
12695
12696   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12697   if (event->xany.window == windows->command.id)
12698     return;
12699   switch (event->type)
12700   {
12701     case ButtonPress:
12702     case ButtonRelease:
12703     {
12704       if ((event->xbutton.button == Button3) &&
12705           (event->xbutton.state & Mod1Mask))
12706         {
12707           /*
12708             Convert Alt-Button3 to Button2.
12709           */
12710           event->xbutton.button=Button2;
12711           event->xbutton.state&=(~Mod1Mask);
12712         }
12713       if (event->xbutton.window == windows->backdrop.id)
12714         {
12715           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12716             event->xbutton.time);
12717           break;
12718         }
12719       if (event->xbutton.window == windows->pan.id)
12720         {
12721           XPanImage(display,windows,event);
12722           break;
12723         }
12724       if (event->xbutton.window == windows->image.id)
12725         if (event->xbutton.button == Button2)
12726           {
12727             /*
12728               Update magnified image.
12729             */
12730             x=event->xbutton.x;
12731             y=event->xbutton.y;
12732             if (x < 0)
12733               x=0;
12734             else
12735               if (x >= (int) windows->image.width)
12736                 x=(int) (windows->image.width-1);
12737             windows->magnify.x=(int) windows->image.x+x;
12738             if (y < 0)
12739               y=0;
12740             else
12741              if (y >= (int) windows->image.height)
12742                y=(int) (windows->image.height-1);
12743             windows->magnify.y=windows->image.y+y;
12744             if (windows->magnify.mapped == MagickFalse)
12745               (void) XMapRaised(display,windows->magnify.id);
12746             XMakeMagnifyImage(display,windows);
12747             if (event->type == ButtonRelease)
12748               (void) XWithdrawWindow(display,windows->info.id,
12749                 windows->info.screen);
12750             break;
12751           }
12752       break;
12753     }
12754     case ClientMessage:
12755     {
12756       /*
12757         If client window delete message, exit.
12758       */
12759       if (event->xclient.message_type != windows->wm_protocols)
12760         break;
12761       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12762         break;
12763       if (event->xclient.window == windows->magnify.id)
12764         {
12765           (void) XWithdrawWindow(display,windows->magnify.id,
12766             windows->magnify.screen);
12767           break;
12768         }
12769       break;
12770     }
12771     case ConfigureNotify:
12772     {
12773       if (event->xconfigure.window == windows->magnify.id)
12774         {
12775           unsigned int
12776             magnify;
12777
12778           /*
12779             Magnify window has a new configuration.
12780           */
12781           windows->magnify.width=(unsigned int) event->xconfigure.width;
12782           windows->magnify.height=(unsigned int) event->xconfigure.height;
12783           if (windows->magnify.mapped == MagickFalse)
12784             break;
12785           magnify=1;
12786           while ((int) magnify <= event->xconfigure.width)
12787             magnify<<=1;
12788           while ((int) magnify <= event->xconfigure.height)
12789             magnify<<=1;
12790           magnify>>=1;
12791           if (((int) magnify != event->xconfigure.width) ||
12792               ((int) magnify != event->xconfigure.height))
12793             {
12794               XWindowChanges
12795                 window_changes;
12796
12797               window_changes.width=(int) magnify;
12798               window_changes.height=(int) magnify;
12799               (void) XReconfigureWMWindow(display,windows->magnify.id,
12800                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12801                 &window_changes);
12802               break;
12803             }
12804           XMakeMagnifyImage(display,windows);
12805           break;
12806         }
12807       break;
12808     }
12809     case Expose:
12810     {
12811       if (event->xexpose.window == windows->image.id)
12812         {
12813           XRefreshWindow(display,&windows->image,event);
12814           break;
12815         }
12816       if (event->xexpose.window == windows->pan.id)
12817         if (event->xexpose.count == 0)
12818           {
12819             XDrawPanRectangle(display,windows);
12820             break;
12821           }
12822       if (event->xexpose.window == windows->magnify.id)
12823         if (event->xexpose.count == 0)
12824           {
12825             XMakeMagnifyImage(display,windows);
12826             break;
12827           }
12828       break;
12829     }
12830     case KeyPress:
12831     {
12832       char
12833         command[MaxTextExtent];
12834
12835       KeySym
12836         key_symbol;
12837
12838       if (event->xkey.window != windows->magnify.id)
12839         break;
12840       /*
12841         Respond to a user key press.
12842       */
12843       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12844         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12845       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12846       break;
12847     }
12848     case MapNotify:
12849     {
12850       if (event->xmap.window == windows->magnify.id)
12851         {
12852           windows->magnify.mapped=MagickTrue;
12853           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12854           break;
12855         }
12856       if (event->xmap.window == windows->info.id)
12857         {
12858           windows->info.mapped=MagickTrue;
12859           break;
12860         }
12861       break;
12862     }
12863     case MotionNotify:
12864     {
12865       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12866       if (event->xmotion.window == windows->image.id)
12867         if (windows->magnify.mapped != MagickFalse)
12868           {
12869             /*
12870               Update magnified image.
12871             */
12872             x=event->xmotion.x;
12873             y=event->xmotion.y;
12874             if (x < 0)
12875               x=0;
12876             else
12877               if (x >= (int) windows->image.width)
12878                 x=(int) (windows->image.width-1);
12879             windows->magnify.x=(int) windows->image.x+x;
12880             if (y < 0)
12881               y=0;
12882             else
12883              if (y >= (int) windows->image.height)
12884                y=(int) (windows->image.height-1);
12885             windows->magnify.y=windows->image.y+y;
12886             XMakeMagnifyImage(display,windows);
12887           }
12888       break;
12889     }
12890     case UnmapNotify:
12891     {
12892       if (event->xunmap.window == windows->magnify.id)
12893         {
12894           windows->magnify.mapped=MagickFalse;
12895           break;
12896         }
12897       if (event->xunmap.window == windows->info.id)
12898         {
12899           windows->info.mapped=MagickFalse;
12900           break;
12901         }
12902       break;
12903     }
12904     default:
12905       break;
12906   }
12907 }
12908 \f
12909 /*
12910 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12911 %                                                                             %
12912 %                                                                             %
12913 %                                                                             %
12914 +   X S e t C r o p G e o m e t r y                                           %
12915 %                                                                             %
12916 %                                                                             %
12917 %                                                                             %
12918 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12919 %
12920 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12921 %  and translates it to a cropping geometry relative to the image.
12922 %
12923 %  The format of the XSetCropGeometry method is:
12924 %
12925 %      void XSetCropGeometry(Display *display,XWindows *windows,
12926 %        RectangleInfo *crop_info,Image *image)
12927 %
12928 %  A description of each parameter follows:
12929 %
12930 %    o display: Specifies a connection to an X server; returned from
12931 %      XOpenDisplay.
12932 %
12933 %    o windows: Specifies a pointer to a XWindows structure.
12934 %
12935 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12936 %      Image window to crop.
12937 %
12938 %    o image: the image.
12939 %
12940 */
12941 static void XSetCropGeometry(Display *display,XWindows *windows,
12942   RectangleInfo *crop_info,Image *image)
12943 {
12944   char
12945     text[MaxTextExtent];
12946
12947   int
12948     x,
12949     y;
12950
12951   MagickRealType
12952     scale_factor;
12953
12954   unsigned int
12955     height,
12956     width;
12957
12958   if (windows->info.mapped != MagickFalse)
12959     {
12960       /*
12961         Display info on cropping rectangle.
12962       */
12963       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12964         (double) crop_info->width,(double) crop_info->height,(double)
12965         crop_info->x,(double) crop_info->y);
12966       XInfoWidget(display,windows,text);
12967     }
12968   /*
12969     Cropping geometry is relative to any previous crop geometry.
12970   */
12971   x=0;
12972   y=0;
12973   width=(unsigned int) image->columns;
12974   height=(unsigned int) image->rows;
12975   if (windows->image.crop_geometry != (char *) NULL)
12976     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12977   else
12978     windows->image.crop_geometry=AcquireString((char *) NULL);
12979   /*
12980     Define the crop geometry string from the cropping rectangle.
12981   */
12982   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12983   if (crop_info->x > 0)
12984     x+=(int) (scale_factor*crop_info->x+0.5);
12985   width=(unsigned int) (scale_factor*crop_info->width+0.5);
12986   if (width == 0)
12987     width=1;
12988   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12989   if (crop_info->y > 0)
12990     y+=(int) (scale_factor*crop_info->y+0.5);
12991   height=(unsigned int) (scale_factor*crop_info->height+0.5);
12992   if (height == 0)
12993     height=1;
12994   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12995     "%ux%u%+d%+d",width,height,x,y);
12996 }
12997 \f
12998 /*
12999 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13000 %                                                                             %
13001 %                                                                             %
13002 %                                                                             %
13003 +   X T i l e I m a g e                                                       %
13004 %                                                                             %
13005 %                                                                             %
13006 %                                                                             %
13007 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13008 %
13009 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13010 %  The load or delete command is chosen from a menu.
13011 %
13012 %  The format of the XTileImage method is:
13013 %
13014 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13015 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13016 %
13017 %  A description of each parameter follows:
13018 %
13019 %    o tile_image:  XTileImage reads or deletes the tile image
13020 %      and returns it.  A null image is returned if an error occurs.
13021 %
13022 %    o display: Specifies a connection to an X server;  returned from
13023 %      XOpenDisplay.
13024 %
13025 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13026 %
13027 %    o windows: Specifies a pointer to a XWindows structure.
13028 %
13029 %    o image: the image; returned from ReadImage.
13030 %
13031 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13032 %      the entire image is refreshed.
13033 %
13034 %    o exception: return any errors or warnings in this structure.
13035 %
13036 */
13037 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13038   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13039 {
13040   static const char
13041     *VerbMenu[] =
13042     {
13043       "Load",
13044       "Next",
13045       "Former",
13046       "Delete",
13047       "Update",
13048       (char *) NULL,
13049     };
13050
13051   static const ModeType
13052     TileCommands[] =
13053     {
13054       TileLoadCommand,
13055       TileNextCommand,
13056       TileFormerCommand,
13057       TileDeleteCommand,
13058       TileUpdateCommand
13059     };
13060
13061   char
13062     command[MaxTextExtent],
13063     filename[MaxTextExtent];
13064
13065   Image
13066     *tile_image;
13067
13068   int
13069     id,
13070     status,
13071     tile,
13072     x,
13073     y;
13074
13075   MagickRealType
13076     scale_factor;
13077
13078   register char
13079     *p,
13080     *q;
13081
13082   register int
13083     i;
13084
13085   unsigned int
13086     height,
13087     width;
13088
13089   /*
13090     Tile image is relative to montage image configuration.
13091   */
13092   x=0;
13093   y=0;
13094   width=(unsigned int) image->columns;
13095   height=(unsigned int) image->rows;
13096   if (windows->image.crop_geometry != (char *) NULL)
13097     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13098   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13099   event->xbutton.x+=windows->image.x;
13100   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13101   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13102   event->xbutton.y+=windows->image.y;
13103   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13104   /*
13105     Determine size and location of each tile in the visual image directory.
13106   */
13107   width=(unsigned int) image->columns;
13108   height=(unsigned int) image->rows;
13109   x=0;
13110   y=0;
13111   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13112   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13113     (event->xbutton.x-x)/width;
13114   if (tile < 0)
13115     {
13116       /*
13117         Button press is outside any tile.
13118       */
13119       (void) XBell(display,0);
13120       return((Image *) NULL);
13121     }
13122   /*
13123     Determine file name from the tile directory.
13124   */
13125   p=image->directory;
13126   for (i=tile; (i != 0) && (*p != '\0'); )
13127   {
13128     if (*p == '\n')
13129       i--;
13130     p++;
13131   }
13132   if (*p == '\0')
13133     {
13134       /*
13135         Button press is outside any tile.
13136       */
13137       (void) XBell(display,0);
13138       return((Image *) NULL);
13139     }
13140   /*
13141     Select a command from the pop-up menu.
13142   */
13143   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13144   if (id < 0)
13145     return((Image *) NULL);
13146   q=p;
13147   while ((*q != '\n') && (*q != '\0'))
13148     q++;
13149   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13150   /*
13151     Perform command for the selected tile.
13152   */
13153   XSetCursorState(display,windows,MagickTrue);
13154   XCheckRefreshWindows(display,windows);
13155   tile_image=NewImageList();
13156   switch (TileCommands[id])
13157   {
13158     case TileLoadCommand:
13159     {
13160       /*
13161         Load tile image.
13162       */
13163       XCheckRefreshWindows(display,windows);
13164       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13165         MaxTextExtent);
13166       (void) CopyMagickString(resource_info->image_info->filename,filename,
13167         MaxTextExtent);
13168       tile_image=ReadImage(resource_info->image_info,exception);
13169       CatchException(exception);
13170       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13171       break;
13172     }
13173     case TileNextCommand:
13174     {
13175       /*
13176         Display next image.
13177       */
13178       XClientMessage(display,windows->image.id,windows->im_protocols,
13179         windows->im_next_image,CurrentTime);
13180       break;
13181     }
13182     case TileFormerCommand:
13183     {
13184       /*
13185         Display former image.
13186       */
13187       XClientMessage(display,windows->image.id,windows->im_protocols,
13188         windows->im_former_image,CurrentTime);
13189       break;
13190     }
13191     case TileDeleteCommand:
13192     {
13193       /*
13194         Delete tile image.
13195       */
13196       if (IsPathAccessible(filename) == MagickFalse)
13197         {
13198           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13199           break;
13200         }
13201       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13202       if (status <= 0)
13203         break;
13204       status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
13205       if (status != MagickFalse)
13206         {
13207           XNoticeWidget(display,windows,"Unable to delete image file:",
13208             filename);
13209           break;
13210         }
13211     }
13212     case TileUpdateCommand:
13213     {
13214       int
13215         x_offset,
13216         y_offset;
13217
13218       PixelPacket
13219         pixel;
13220
13221       Quantum
13222         virtual_pixel[MaxPixelChannels];
13223
13224       register int
13225         j;
13226
13227       register Quantum
13228         *s;
13229
13230       /*
13231         Ensure all the images exist.
13232       */
13233       tile=0;
13234       for (p=image->directory; *p != '\0'; p++)
13235       {
13236         CacheView
13237           *image_view;
13238
13239         q=p;
13240         while ((*q != '\n') && (*q != '\0'))
13241           q++;
13242         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13243         p=q;
13244         if (IsPathAccessible(filename) != MagickFalse)
13245           {
13246             tile++;
13247             continue;
13248           }
13249         /*
13250           Overwrite tile with background color.
13251         */
13252         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13253         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13254         image_view=AcquireCacheView(image);
13255         (void) GetOneCacheViewVirtualPixel(image_view,0,0,virtual_pixel,
13256           exception);
13257         pixel.red=virtual_pixel[RedPixelChannel];
13258         pixel.green=virtual_pixel[GreenPixelChannel];
13259         pixel.blue=virtual_pixel[BluePixelChannel];
13260         pixel.alpha=virtual_pixel[AlphaPixelChannel];
13261         for (i=0; i < (int) height; i++)
13262         {
13263           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13264             y_offset+i,width,1,exception);
13265           if (s == (Quantum *) NULL)
13266             break;
13267           for (j=0; j < (int) width; j++)
13268           {
13269             SetPixelPacket(image,&pixel,s);
13270             s+=GetPixelChannels(image);
13271           }
13272           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13273             break;
13274         }
13275         image_view=DestroyCacheView(image_view);
13276         tile++;
13277       }
13278       windows->image.window_changes.width=(int) image->columns;
13279       windows->image.window_changes.height=(int) image->rows;
13280       XConfigureImageColormap(display,resource_info,windows,image);
13281       (void) XConfigureImage(display,resource_info,windows,image,exception);
13282       break;
13283     }
13284     default:
13285       break;
13286   }
13287   XSetCursorState(display,windows,MagickFalse);
13288   return(tile_image);
13289 }
13290 \f
13291 /*
13292 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13293 %                                                                             %
13294 %                                                                             %
13295 %                                                                             %
13296 +   X T r a n s l a t e I m a g e                                             %
13297 %                                                                             %
13298 %                                                                             %
13299 %                                                                             %
13300 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13301 %
13302 %  XTranslateImage() translates the image within an Image window by one pixel
13303 %  as specified by the key symbol.  If the image has a `montage string the
13304 %  translation is respect to the width and height contained within the string.
13305 %
13306 %  The format of the XTranslateImage method is:
13307 %
13308 %      void XTranslateImage(Display *display,XWindows *windows,
13309 %        Image *image,const KeySym key_symbol)
13310 %
13311 %  A description of each parameter follows:
13312 %
13313 %    o display: Specifies a connection to an X server; returned from
13314 %      XOpenDisplay.
13315 %
13316 %    o windows: Specifies a pointer to a XWindows structure.
13317 %
13318 %    o image: the image.
13319 %
13320 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13321 %      to trim.
13322 %
13323 */
13324 static void XTranslateImage(Display *display,XWindows *windows,
13325   Image *image,const KeySym key_symbol)
13326 {
13327   char
13328     text[MaxTextExtent];
13329
13330   int
13331     x,
13332     y;
13333
13334   unsigned int
13335     x_offset,
13336     y_offset;
13337
13338   /*
13339     User specified a pan position offset.
13340   */
13341   x_offset=windows->image.width;
13342   y_offset=windows->image.height;
13343   if (image->montage != (char *) NULL)
13344     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13345   switch ((int) key_symbol)
13346   {
13347     case XK_Home:
13348     case XK_KP_Home:
13349     {
13350       windows->image.x=(int) windows->image.width/2;
13351       windows->image.y=(int) windows->image.height/2;
13352       break;
13353     }
13354     case XK_Left:
13355     case XK_KP_Left:
13356     {
13357       windows->image.x-=x_offset;
13358       break;
13359     }
13360     case XK_Next:
13361     case XK_Up:
13362     case XK_KP_Up:
13363     {
13364       windows->image.y-=y_offset;
13365       break;
13366     }
13367     case XK_Right:
13368     case XK_KP_Right:
13369     {
13370       windows->image.x+=x_offset;
13371       break;
13372     }
13373     case XK_Prior:
13374     case XK_Down:
13375     case XK_KP_Down:
13376     {
13377       windows->image.y+=y_offset;
13378       break;
13379     }
13380     default:
13381       return;
13382   }
13383   /*
13384     Check boundary conditions.
13385   */
13386   if (windows->image.x < 0)
13387     windows->image.x=0;
13388   else
13389     if ((int) (windows->image.x+windows->image.width) >
13390         windows->image.ximage->width)
13391       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13392   if (windows->image.y < 0)
13393     windows->image.y=0;
13394   else
13395     if ((int) (windows->image.y+windows->image.height) >
13396         windows->image.ximage->height)
13397       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13398   /*
13399     Refresh Image window.
13400   */
13401   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13402     windows->image.width,windows->image.height,windows->image.x,
13403     windows->image.y);
13404   XInfoWidget(display,windows,text);
13405   XCheckRefreshWindows(display,windows);
13406   XDrawPanRectangle(display,windows);
13407   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13408   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13409 }
13410 \f
13411 /*
13412 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13413 %                                                                             %
13414 %                                                                             %
13415 %                                                                             %
13416 +   X T r i m I m a g e                                                       %
13417 %                                                                             %
13418 %                                                                             %
13419 %                                                                             %
13420 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13421 %
13422 %  XTrimImage() trims the edges from the Image window.
13423 %
13424 %  The format of the XTrimImage method is:
13425 %
13426 %      MagickBooleanType XTrimImage(Display *display,
13427 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13428 %        ExceptionInfo *exception)
13429 %
13430 %  A description of each parameter follows:
13431 %
13432 %    o display: Specifies a connection to an X server; returned from
13433 %      XOpenDisplay.
13434 %
13435 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13436 %
13437 %    o windows: Specifies a pointer to a XWindows structure.
13438 %
13439 %    o image: the image.
13440 %
13441 %    o exception: return any errors or warnings in this structure.
13442 %
13443 */
13444 static MagickBooleanType XTrimImage(Display *display,
13445   XResourceInfo *resource_info,XWindows *windows,Image *image,
13446   ExceptionInfo *exception)
13447 {
13448   RectangleInfo
13449     trim_info;
13450
13451   register int
13452     x,
13453     y;
13454
13455   size_t
13456     background,
13457     pixel;
13458
13459   /*
13460     Trim edges from image.
13461   */
13462   XSetCursorState(display,windows,MagickTrue);
13463   XCheckRefreshWindows(display,windows);
13464   /*
13465     Crop the left edge.
13466   */
13467   background=XGetPixel(windows->image.ximage,0,0);
13468   trim_info.width=(size_t) windows->image.ximage->width;
13469   for (x=0; x < windows->image.ximage->width; x++)
13470   {
13471     for (y=0; y < windows->image.ximage->height; y++)
13472     {
13473       pixel=XGetPixel(windows->image.ximage,x,y);
13474       if (pixel != background)
13475         break;
13476     }
13477     if (y < windows->image.ximage->height)
13478       break;
13479   }
13480   trim_info.x=(ssize_t) x;
13481   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13482     {
13483       XSetCursorState(display,windows,MagickFalse);
13484       return(MagickFalse);
13485     }
13486   /*
13487     Crop the right edge.
13488   */
13489   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13490   for (x=windows->image.ximage->width-1; x != 0; x--)
13491   {
13492     for (y=0; y < windows->image.ximage->height; y++)
13493     {
13494       pixel=XGetPixel(windows->image.ximage,x,y);
13495       if (pixel != background)
13496         break;
13497     }
13498     if (y < windows->image.ximage->height)
13499       break;
13500   }
13501   trim_info.width=(size_t) (x-trim_info.x+1);
13502   /*
13503     Crop the top edge.
13504   */
13505   background=XGetPixel(windows->image.ximage,0,0);
13506   trim_info.height=(size_t) windows->image.ximage->height;
13507   for (y=0; y < windows->image.ximage->height; y++)
13508   {
13509     for (x=0; x < windows->image.ximage->width; x++)
13510     {
13511       pixel=XGetPixel(windows->image.ximage,x,y);
13512       if (pixel != background)
13513         break;
13514     }
13515     if (x < windows->image.ximage->width)
13516       break;
13517   }
13518   trim_info.y=(ssize_t) y;
13519   /*
13520     Crop the bottom edge.
13521   */
13522   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13523   for (y=windows->image.ximage->height-1; y != 0; y--)
13524   {
13525     for (x=0; x < windows->image.ximage->width; x++)
13526     {
13527       pixel=XGetPixel(windows->image.ximage,x,y);
13528       if (pixel != background)
13529         break;
13530     }
13531     if (x < windows->image.ximage->width)
13532       break;
13533   }
13534   trim_info.height=(size_t) y-trim_info.y+1;
13535   if (((unsigned int) trim_info.width != windows->image.width) ||
13536       ((unsigned int) trim_info.height != windows->image.height))
13537     {
13538       /*
13539         Reconfigure Image window as defined by the trimming rectangle.
13540       */
13541       XSetCropGeometry(display,windows,&trim_info,image);
13542       windows->image.window_changes.width=(int) trim_info.width;
13543       windows->image.window_changes.height=(int) trim_info.height;
13544       (void) XConfigureImage(display,resource_info,windows,image,exception);
13545     }
13546   XSetCursorState(display,windows,MagickFalse);
13547   return(MagickTrue);
13548 }
13549 \f
13550 /*
13551 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13552 %                                                                             %
13553 %                                                                             %
13554 %                                                                             %
13555 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13556 %                                                                             %
13557 %                                                                             %
13558 %                                                                             %
13559 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13560 %
13561 %  XVisualDirectoryImage() creates a Visual Image Directory.
13562 %
13563 %  The format of the XVisualDirectoryImage method is:
13564 %
13565 %      Image *XVisualDirectoryImage(Display *display,
13566 %        XResourceInfo *resource_info,XWindows *windows,
13567 %        ExceptionInfo *exception)
13568 %
13569 %  A description of each parameter follows:
13570 %
13571 %    o display: Specifies a connection to an X server; returned from
13572 %      XOpenDisplay.
13573 %
13574 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13575 %
13576 %    o windows: Specifies a pointer to a XWindows structure.
13577 %
13578 %    o exception: return any errors or warnings in this structure.
13579 %
13580 */
13581 static Image *XVisualDirectoryImage(Display *display,
13582   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13583 {
13584 #define TileImageTag  "Scale/Image"
13585 #define XClientName  "montage"
13586
13587   char
13588     **filelist;
13589
13590   Image
13591     *images,
13592     *montage_image,
13593     *next_image,
13594     *thumbnail_image;
13595
13596   ImageInfo
13597     *read_info;
13598
13599   int
13600     number_files;
13601
13602   MagickBooleanType
13603     backdrop;
13604
13605   MagickStatusType
13606     status;
13607
13608   MontageInfo
13609     *montage_info;
13610
13611   RectangleInfo
13612     geometry;
13613
13614   register int
13615     i;
13616
13617   static char
13618     filename[MaxTextExtent] = "\0",
13619     filenames[MaxTextExtent] = "*";
13620
13621   XResourceInfo
13622     background_resources;
13623
13624   /*
13625     Request file name from user.
13626   */
13627   XFileBrowserWidget(display,windows,"Directory",filenames);
13628   if (*filenames == '\0')
13629     return((Image *) NULL);
13630   /*
13631     Expand the filenames.
13632   */
13633   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13634   if (filelist == (char **) NULL)
13635     {
13636       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13637         filenames);
13638       return((Image *) NULL);
13639     }
13640   number_files=1;
13641   filelist[0]=filenames;
13642   status=ExpandFilenames(&number_files,&filelist);
13643   if ((status == MagickFalse) || (number_files == 0))
13644     {
13645       if (number_files == 0)
13646         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13647       else
13648         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13649           filenames);
13650       return((Image *) NULL);
13651     }
13652   /*
13653     Set image background resources.
13654   */
13655   background_resources=(*resource_info);
13656   background_resources.window_id=AcquireString("");
13657   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13658     "0x%lx",windows->image.id);
13659   background_resources.backdrop=MagickTrue;
13660   /*
13661     Read each image and convert them to a tile.
13662   */
13663   backdrop=(windows->visual_info->klass == TrueColor) ||
13664     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13665   read_info=CloneImageInfo(resource_info->image_info);
13666   (void) SetImageOption(read_info,"jpeg:size","120x120");
13667   (void) CloneString(&read_info->size,DefaultTileGeometry);
13668   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13669     (void *) NULL);
13670   images=NewImageList();
13671   XSetCursorState(display,windows,MagickTrue);
13672   XCheckRefreshWindows(display,windows);
13673   for (i=0; i < (int) number_files; i++)
13674   {
13675     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13676     filelist[i]=DestroyString(filelist[i]);
13677     *read_info->magick='\0';
13678     next_image=ReadImage(read_info,exception);
13679     CatchException(exception);
13680     if (next_image != (Image *) NULL)
13681       {
13682         (void) DeleteImageProperty(next_image,"label");
13683         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13684           read_info,next_image,DefaultTileLabel,exception));
13685         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13686           exception);
13687         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13688           geometry.height,exception);
13689         if (thumbnail_image != (Image *) NULL)
13690           {
13691             next_image=DestroyImage(next_image);
13692             next_image=thumbnail_image;
13693           }
13694         if (backdrop)
13695           {
13696             (void) XDisplayBackgroundImage(display,&background_resources,
13697               next_image,exception);
13698             XSetCursorState(display,windows,MagickTrue);
13699           }
13700         AppendImageToList(&images,next_image);
13701         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13702           {
13703             MagickBooleanType
13704               proceed;
13705
13706             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13707               (MagickSizeType) number_files);
13708             if (proceed == MagickFalse)
13709               break;
13710           }
13711       }
13712   }
13713   filelist=(char **) RelinquishMagickMemory(filelist);
13714   if (images == (Image *) NULL)
13715     {
13716       read_info=DestroyImageInfo(read_info);
13717       XSetCursorState(display,windows,MagickFalse);
13718       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13719       return((Image *) NULL);
13720     }
13721   /*
13722     Create the Visual Image Directory.
13723   */
13724   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13725   montage_info->pointsize=10;
13726   if (resource_info->font != (char *) NULL)
13727     (void) CloneString(&montage_info->font,resource_info->font);
13728   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13729   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13730     images),exception);
13731   images=DestroyImageList(images);
13732   montage_info=DestroyMontageInfo(montage_info);
13733   read_info=DestroyImageInfo(read_info);
13734   XSetCursorState(display,windows,MagickFalse);
13735   if (montage_image == (Image *) NULL)
13736     return(montage_image);
13737   XClientMessage(display,windows->image.id,windows->im_protocols,
13738     windows->im_next_image,CurrentTime);
13739   return(montage_image);
13740 }
13741 \f
13742 /*
13743 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13744 %                                                                             %
13745 %                                                                             %
13746 %                                                                             %
13747 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13748 %                                                                             %
13749 %                                                                             %
13750 %                                                                             %
13751 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13752 %
13753 %  XDisplayBackgroundImage() displays an image in the background of a window.
13754 %
13755 %  The format of the XDisplayBackgroundImage method is:
13756 %
13757 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13758 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13759 %
13760 %  A description of each parameter follows:
13761 %
13762 %    o display: Specifies a connection to an X server;  returned from
13763 %      XOpenDisplay.
13764 %
13765 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13766 %
13767 %    o image: the image.
13768 %
13769 %    o exception: return any errors or warnings in this structure.
13770 %
13771 */
13772 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13773   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13774 {
13775   char
13776     geometry[MaxTextExtent],
13777     visual_type[MaxTextExtent];
13778
13779   int
13780     height,
13781     status,
13782     width;
13783
13784   RectangleInfo
13785     geometry_info;
13786
13787   static XPixelInfo
13788     pixel;
13789
13790   static XStandardColormap
13791     *map_info;
13792
13793   static XVisualInfo
13794     *visual_info = (XVisualInfo *) NULL;
13795
13796   static XWindowInfo
13797     window_info;
13798
13799   size_t
13800     delay;
13801
13802   Window
13803     root_window;
13804
13805   XGCValues
13806     context_values;
13807
13808   XResourceInfo
13809     resources;
13810
13811   XWindowAttributes
13812     window_attributes;
13813
13814   /*
13815     Determine target window.
13816   */
13817   assert(image != (Image *) NULL);
13818   assert(image->signature == MagickSignature);
13819   if (image->debug != MagickFalse)
13820     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13821   resources=(*resource_info);
13822   window_info.id=(Window) NULL;
13823   root_window=XRootWindow(display,XDefaultScreen(display));
13824   if (LocaleCompare(resources.window_id,"root") == 0)
13825     window_info.id=root_window;
13826   else
13827     {
13828       if (isdigit((unsigned char) *resources.window_id) != 0)
13829         window_info.id=XWindowByID(display,root_window,
13830           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13831       if (window_info.id == (Window) NULL)
13832         window_info.id=XWindowByName(display,root_window,resources.window_id);
13833     }
13834   if (window_info.id == (Window) NULL)
13835     {
13836       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13837         resources.window_id);
13838       return(MagickFalse);
13839     }
13840   /*
13841     Determine window visual id.
13842   */
13843   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13844   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13845   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13846   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13847   if (status != 0)
13848     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13849       XVisualIDFromVisual(window_attributes.visual));
13850   if (visual_info == (XVisualInfo *) NULL)
13851     {
13852       /*
13853         Allocate standard colormap.
13854       */
13855       map_info=XAllocStandardColormap();
13856       if (map_info == (XStandardColormap *) NULL)
13857         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13858           image->filename);
13859       map_info->colormap=(Colormap) NULL;
13860       pixel.pixels=(unsigned long *) NULL;
13861       /*
13862         Initialize visual info.
13863       */
13864       resources.map_type=(char *) NULL;
13865       resources.visual_type=visual_type;
13866       visual_info=XBestVisualInfo(display,map_info,&resources);
13867       if (visual_info == (XVisualInfo *) NULL)
13868         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13869           resources.visual_type);
13870       /*
13871         Initialize window info.
13872       */
13873       window_info.ximage=(XImage *) NULL;
13874       window_info.matte_image=(XImage *) NULL;
13875       window_info.pixmap=(Pixmap) NULL;
13876       window_info.matte_pixmap=(Pixmap) NULL;
13877     }
13878   /*
13879     Free previous root colors.
13880   */
13881   if (window_info.id == root_window)
13882     (void) XDestroyWindowColors(display,root_window);
13883   /*
13884     Initialize Standard Colormap.
13885   */
13886   resources.colormap=SharedColormap;
13887   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13888   /*
13889     Graphic context superclass.
13890   */
13891   context_values.background=pixel.background_color.pixel;
13892   context_values.foreground=pixel.foreground_color.pixel;
13893   pixel.annotate_context=XCreateGC(display,window_info.id,
13894     (size_t) (GCBackground | GCForeground),&context_values);
13895   if (pixel.annotate_context == (GC) NULL)
13896     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13897       image->filename);
13898   /*
13899     Initialize Image window attributes.
13900   */
13901   window_info.name=AcquireString("\0");
13902   window_info.icon_name=AcquireString("\0");
13903   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13904     &resources,&window_info);
13905   /*
13906     Create the X image.
13907   */
13908   window_info.width=(unsigned int) image->columns;
13909   window_info.height=(unsigned int) image->rows;
13910   if ((image->columns != window_info.width) ||
13911       (image->rows != window_info.height))
13912     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13913       image->filename);
13914   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13915     window_attributes.width,window_attributes.height);
13916   geometry_info.width=window_info.width;
13917   geometry_info.height=window_info.height;
13918   geometry_info.x=(ssize_t) window_info.x;
13919   geometry_info.y=(ssize_t) window_info.y;
13920   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13921     &geometry_info.width,&geometry_info.height);
13922   window_info.width=(unsigned int) geometry_info.width;
13923   window_info.height=(unsigned int) geometry_info.height;
13924   window_info.x=(int) geometry_info.x;
13925   window_info.y=(int) geometry_info.y;
13926   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13927     window_info.height,exception);
13928   if (status == MagickFalse)
13929     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13930       image->filename);
13931   window_info.x=0;
13932   window_info.y=0;
13933   if (image->debug != MagickFalse)
13934     {
13935       (void) LogMagickEvent(X11Event,GetMagickModule(),
13936         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13937         (double) image->columns,(double) image->rows);
13938       if (image->colors != 0)
13939         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13940           image->colors);
13941       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13942     }
13943   /*
13944     Adjust image dimensions as specified by backdrop or geometry options.
13945   */
13946   width=(int) window_info.width;
13947   height=(int) window_info.height;
13948   if (resources.backdrop != MagickFalse)
13949     {
13950       /*
13951         Center image on window.
13952       */
13953       window_info.x=(window_attributes.width/2)-
13954         (window_info.ximage->width/2);
13955       window_info.y=(window_attributes.height/2)-
13956         (window_info.ximage->height/2);
13957       width=window_attributes.width;
13958       height=window_attributes.height;
13959     }
13960   if ((resources.image_geometry != (char *) NULL) &&
13961       (*resources.image_geometry != '\0'))
13962     {
13963       char
13964         default_geometry[MaxTextExtent];
13965
13966       int
13967         flags,
13968         gravity;
13969
13970       XSizeHints
13971         *size_hints;
13972
13973       /*
13974         User specified geometry.
13975       */
13976       size_hints=XAllocSizeHints();
13977       if (size_hints == (XSizeHints *) NULL)
13978         ThrowXWindowFatalException(ResourceLimitFatalError,
13979           "MemoryAllocationFailed",image->filename);
13980       size_hints->flags=0L;
13981       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13982         width,height);
13983       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13984         default_geometry,window_info.border_width,size_hints,&window_info.x,
13985         &window_info.y,&width,&height,&gravity);
13986       if (flags & (XValue | YValue))
13987         {
13988           width=window_attributes.width;
13989           height=window_attributes.height;
13990         }
13991       (void) XFree((void *) size_hints);
13992     }
13993   /*
13994     Create the X pixmap.
13995   */
13996   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13997     (unsigned int) height,window_info.depth);
13998   if (window_info.pixmap == (Pixmap) NULL)
13999     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14000       image->filename);
14001   /*
14002     Display pixmap on the window.
14003   */
14004   if (((unsigned int) width > window_info.width) ||
14005       ((unsigned int) height > window_info.height))
14006     (void) XFillRectangle(display,window_info.pixmap,
14007       window_info.annotate_context,0,0,(unsigned int) width,
14008       (unsigned int) height);
14009   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14010     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14011     window_info.width,(unsigned int) window_info.height);
14012   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14013   (void) XClearWindow(display,window_info.id);
14014   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14015   XDelay(display,delay == 0UL ? 10UL : delay);
14016   (void) XSync(display,MagickFalse);
14017   return(window_info.id == root_window ? MagickTrue : MagickFalse);
14018 }
14019 \f
14020 /*
14021 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14022 %                                                                             %
14023 %                                                                             %
14024 %                                                                             %
14025 +   X D i s p l a y I m a g e                                                 %
14026 %                                                                             %
14027 %                                                                             %
14028 %                                                                             %
14029 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14030 %
14031 %  XDisplayImage() displays an image via X11.  A new image is created and
14032 %  returned if the user interactively transforms the displayed image.
14033 %
14034 %  The format of the XDisplayImage method is:
14035 %
14036 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14037 %        char **argv,int argc,Image **image,size_t *state,
14038 %        ExceptionInfo *exception)
14039 %
14040 %  A description of each parameter follows:
14041 %
14042 %    o nexus:  Method XDisplayImage returns an image when the
14043 %      user chooses 'Open Image' from the command menu or picks a tile
14044 %      from the image directory.  Otherwise a null image is returned.
14045 %
14046 %    o display: Specifies a connection to an X server;  returned from
14047 %      XOpenDisplay.
14048 %
14049 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14050 %
14051 %    o argv: Specifies the application's argument list.
14052 %
14053 %    o argc: Specifies the number of arguments.
14054 %
14055 %    o image: Specifies an address to an address of an Image structure;
14056 %
14057 %    o exception: return any errors or warnings in this structure.
14058 %
14059 */
14060 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14061   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14062 {
14063 #define MagnifySize  256  /* must be a power of 2 */
14064 #define MagickMenus  10
14065 #define MagickTitle  "Commands"
14066
14067   static const char
14068     *CommandMenu[] =
14069     {
14070       "File",
14071       "Edit",
14072       "View",
14073       "Transform",
14074       "Enhance",
14075       "Effects",
14076       "F/X",
14077       "Image Edit",
14078       "Miscellany",
14079       "Help",
14080       (char *) NULL
14081     },
14082     *FileMenu[] =
14083     {
14084       "Open...",
14085       "Next",
14086       "Former",
14087       "Select...",
14088       "Save...",
14089       "Print...",
14090       "Delete...",
14091       "New...",
14092       "Visual Directory...",
14093       "Quit",
14094       (char *) NULL
14095     },
14096     *EditMenu[] =
14097     {
14098       "Undo",
14099       "Redo",
14100       "Cut",
14101       "Copy",
14102       "Paste",
14103       (char *) NULL
14104     },
14105     *ViewMenu[] =
14106     {
14107       "Half Size",
14108       "Original Size",
14109       "Double Size",
14110       "Resize...",
14111       "Apply",
14112       "Refresh",
14113       "Restore",
14114       (char *) NULL
14115     },
14116     *TransformMenu[] =
14117     {
14118       "Crop",
14119       "Chop",
14120       "Flop",
14121       "Flip",
14122       "Rotate Right",
14123       "Rotate Left",
14124       "Rotate...",
14125       "Shear...",
14126       "Roll...",
14127       "Trim Edges",
14128       (char *) NULL
14129     },
14130     *EnhanceMenu[] =
14131     {
14132       "Hue...",
14133       "Saturation...",
14134       "Brightness...",
14135       "Gamma...",
14136       "Spiff",
14137       "Dull",
14138       "Contrast Stretch...",
14139       "Sigmoidal Contrast...",
14140       "Normalize",
14141       "Equalize",
14142       "Negate",
14143       "Grayscale",
14144       "Map...",
14145       "Quantize...",
14146       (char *) NULL
14147     },
14148     *EffectsMenu[] =
14149     {
14150       "Despeckle",
14151       "Emboss",
14152       "Reduce Noise",
14153       "Add Noise...",
14154       "Sharpen...",
14155       "Blur...",
14156       "Threshold...",
14157       "Edge Detect...",
14158       "Spread...",
14159       "Shade...",
14160       "Raise...",
14161       "Segment...",
14162       (char *) NULL
14163     },
14164     *FXMenu[] =
14165     {
14166       "Solarize...",
14167       "Sepia Tone...",
14168       "Swirl...",
14169       "Implode...",
14170       "Vignette...",
14171       "Wave...",
14172       "Oil Paint...",
14173       "Charcoal Draw...",
14174       (char *) NULL
14175     },
14176     *ImageEditMenu[] =
14177     {
14178       "Annotate...",
14179       "Draw...",
14180       "Color...",
14181       "Matte...",
14182       "Composite...",
14183       "Add Border...",
14184       "Add Frame...",
14185       "Comment...",
14186       "Launch...",
14187       "Region of Interest...",
14188       (char *) NULL
14189     },
14190     *MiscellanyMenu[] =
14191     {
14192       "Image Info",
14193       "Zoom Image",
14194       "Show Preview...",
14195       "Show Histogram",
14196       "Show Matte",
14197       "Background...",
14198       "Slide Show...",
14199       "Preferences...",
14200       (char *) NULL
14201     },
14202     *HelpMenu[] =
14203     {
14204       "Overview",
14205       "Browse Documentation",
14206       "About Display",
14207       (char *) NULL
14208     },
14209     *ShortCutsMenu[] =
14210     {
14211       "Next",
14212       "Former",
14213       "Open...",
14214       "Save...",
14215       "Print...",
14216       "Undo",
14217       "Restore",
14218       "Image Info",
14219       "Quit",
14220       (char *) NULL
14221     },
14222     *VirtualMenu[] =
14223     {
14224       "Image Info",
14225       "Print",
14226       "Next",
14227       "Quit",
14228       (char *) NULL
14229     };
14230
14231   static const char
14232     **Menus[MagickMenus] =
14233     {
14234       FileMenu,
14235       EditMenu,
14236       ViewMenu,
14237       TransformMenu,
14238       EnhanceMenu,
14239       EffectsMenu,
14240       FXMenu,
14241       ImageEditMenu,
14242       MiscellanyMenu,
14243       HelpMenu
14244     };
14245
14246   static CommandType
14247     CommandMenus[] =
14248     {
14249       NullCommand,
14250       NullCommand,
14251       NullCommand,
14252       NullCommand,
14253       NullCommand,
14254       NullCommand,
14255       NullCommand,
14256       NullCommand,
14257       NullCommand,
14258       NullCommand,
14259     },
14260     FileCommands[] =
14261     {
14262       OpenCommand,
14263       NextCommand,
14264       FormerCommand,
14265       SelectCommand,
14266       SaveCommand,
14267       PrintCommand,
14268       DeleteCommand,
14269       NewCommand,
14270       VisualDirectoryCommand,
14271       QuitCommand
14272     },
14273     EditCommands[] =
14274     {
14275       UndoCommand,
14276       RedoCommand,
14277       CutCommand,
14278       CopyCommand,
14279       PasteCommand
14280     },
14281     ViewCommands[] =
14282     {
14283       HalfSizeCommand,
14284       OriginalSizeCommand,
14285       DoubleSizeCommand,
14286       ResizeCommand,
14287       ApplyCommand,
14288       RefreshCommand,
14289       RestoreCommand
14290     },
14291     TransformCommands[] =
14292     {
14293       CropCommand,
14294       ChopCommand,
14295       FlopCommand,
14296       FlipCommand,
14297       RotateRightCommand,
14298       RotateLeftCommand,
14299       RotateCommand,
14300       ShearCommand,
14301       RollCommand,
14302       TrimCommand
14303     },
14304     EnhanceCommands[] =
14305     {
14306       HueCommand,
14307       SaturationCommand,
14308       BrightnessCommand,
14309       GammaCommand,
14310       SpiffCommand,
14311       DullCommand,
14312       ContrastStretchCommand,
14313       SigmoidalContrastCommand,
14314       NormalizeCommand,
14315       EqualizeCommand,
14316       NegateCommand,
14317       GrayscaleCommand,
14318       MapCommand,
14319       QuantizeCommand
14320     },
14321     EffectsCommands[] =
14322     {
14323       DespeckleCommand,
14324       EmbossCommand,
14325       ReduceNoiseCommand,
14326       AddNoiseCommand,
14327       SharpenCommand,
14328       BlurCommand,
14329       ThresholdCommand,
14330       EdgeDetectCommand,
14331       SpreadCommand,
14332       ShadeCommand,
14333       RaiseCommand,
14334       SegmentCommand
14335     },
14336     FXCommands[] =
14337     {
14338       SolarizeCommand,
14339       SepiaToneCommand,
14340       SwirlCommand,
14341       ImplodeCommand,
14342       VignetteCommand,
14343       WaveCommand,
14344       OilPaintCommand,
14345       CharcoalDrawCommand
14346     },
14347     ImageEditCommands[] =
14348     {
14349       AnnotateCommand,
14350       DrawCommand,
14351       ColorCommand,
14352       MatteCommand,
14353       CompositeCommand,
14354       AddBorderCommand,
14355       AddFrameCommand,
14356       CommentCommand,
14357       LaunchCommand,
14358       RegionofInterestCommand
14359     },
14360     MiscellanyCommands[] =
14361     {
14362       InfoCommand,
14363       ZoomCommand,
14364       ShowPreviewCommand,
14365       ShowHistogramCommand,
14366       ShowMatteCommand,
14367       BackgroundCommand,
14368       SlideShowCommand,
14369       PreferencesCommand
14370     },
14371     HelpCommands[] =
14372     {
14373       HelpCommand,
14374       BrowseDocumentationCommand,
14375       VersionCommand
14376     },
14377     ShortCutsCommands[] =
14378     {
14379       NextCommand,
14380       FormerCommand,
14381       OpenCommand,
14382       SaveCommand,
14383       PrintCommand,
14384       UndoCommand,
14385       RestoreCommand,
14386       InfoCommand,
14387       QuitCommand
14388     },
14389     VirtualCommands[] =
14390     {
14391       InfoCommand,
14392       PrintCommand,
14393       NextCommand,
14394       QuitCommand
14395     };
14396
14397   static CommandType
14398     *Commands[MagickMenus] =
14399     {
14400       FileCommands,
14401       EditCommands,
14402       ViewCommands,
14403       TransformCommands,
14404       EnhanceCommands,
14405       EffectsCommands,
14406       FXCommands,
14407       ImageEditCommands,
14408       MiscellanyCommands,
14409       HelpCommands
14410     };
14411
14412   char
14413     command[MaxTextExtent],
14414     *directory,
14415     geometry[MaxTextExtent],
14416     resource_name[MaxTextExtent];
14417
14418   CommandType
14419     command_type;
14420
14421   Image
14422     *display_image,
14423     *nexus;
14424
14425   int
14426     entry,
14427     id;
14428
14429   KeySym
14430     key_symbol;
14431
14432   MagickStatusType
14433     context_mask,
14434     status;
14435
14436   RectangleInfo
14437     geometry_info;
14438
14439   register int
14440     i;
14441
14442   static char
14443     working_directory[MaxTextExtent];
14444
14445   static XPoint
14446     vid_info;
14447
14448   static XWindowInfo
14449     *magick_windows[MaxXWindows];
14450
14451   static unsigned int
14452     number_windows;
14453
14454   struct stat
14455     attributes;
14456
14457   time_t
14458     timer,
14459     timestamp,
14460     update_time;
14461
14462   unsigned int
14463     height,
14464     width;
14465
14466   size_t
14467     delay;
14468
14469   WarningHandler
14470     warning_handler;
14471
14472   Window
14473     root_window;
14474
14475   XClassHint
14476     *class_hints;
14477
14478   XEvent
14479     event;
14480
14481   XFontStruct
14482     *font_info;
14483
14484   XGCValues
14485     context_values;
14486
14487   XPixelInfo
14488     *icon_pixel,
14489     *pixel;
14490
14491   XResourceInfo
14492     *icon_resources;
14493
14494   XStandardColormap
14495     *icon_map,
14496     *map_info;
14497
14498   XVisualInfo
14499     *icon_visual,
14500     *visual_info;
14501
14502   XWindowChanges
14503     window_changes;
14504
14505   XWindows
14506     *windows;
14507
14508   XWMHints
14509     *manager_hints;
14510
14511   assert(image != (Image **) NULL);
14512   assert((*image)->signature == MagickSignature);
14513   if ((*image)->debug != MagickFalse)
14514     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14515   display_image=(*image);
14516   warning_handler=(WarningHandler) NULL;
14517   windows=XSetWindows((XWindows *) ~0);
14518   if (windows != (XWindows *) NULL)
14519     {
14520       int
14521         status;
14522
14523       status=chdir(working_directory);
14524       if (status == -1)
14525         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14526           "UnableToOpenFile","%s",working_directory);
14527       warning_handler=resource_info->display_warnings ?
14528         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14529       warning_handler=resource_info->display_warnings ?
14530         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14531     }
14532   else
14533     {
14534       /*
14535         Allocate windows structure.
14536       */
14537       resource_info->colors=display_image->colors;
14538       windows=XSetWindows(XInitializeWindows(display,resource_info));
14539       if (windows == (XWindows *) NULL)
14540         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14541           (*image)->filename);
14542       /*
14543         Initialize window id's.
14544       */
14545       number_windows=0;
14546       magick_windows[number_windows++]=(&windows->icon);
14547       magick_windows[number_windows++]=(&windows->backdrop);
14548       magick_windows[number_windows++]=(&windows->image);
14549       magick_windows[number_windows++]=(&windows->info);
14550       magick_windows[number_windows++]=(&windows->command);
14551       magick_windows[number_windows++]=(&windows->widget);
14552       magick_windows[number_windows++]=(&windows->popup);
14553       magick_windows[number_windows++]=(&windows->magnify);
14554       magick_windows[number_windows++]=(&windows->pan);
14555       for (i=0; i < (int) number_windows; i++)
14556         magick_windows[i]->id=(Window) NULL;
14557       vid_info.x=0;
14558       vid_info.y=0;
14559     }
14560   /*
14561     Initialize font info.
14562   */
14563   if (windows->font_info != (XFontStruct *) NULL)
14564     (void) XFreeFont(display,windows->font_info);
14565   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14566   if (windows->font_info == (XFontStruct *) NULL)
14567     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14568       resource_info->font);
14569   /*
14570     Initialize Standard Colormap.
14571   */
14572   map_info=windows->map_info;
14573   icon_map=windows->icon_map;
14574   visual_info=windows->visual_info;
14575   icon_visual=windows->icon_visual;
14576   pixel=windows->pixel_info;
14577   icon_pixel=windows->icon_pixel;
14578   font_info=windows->font_info;
14579   icon_resources=windows->icon_resources;
14580   class_hints=windows->class_hints;
14581   manager_hints=windows->manager_hints;
14582   root_window=XRootWindow(display,visual_info->screen);
14583   nexus=NewImageList();
14584   if (display_image->debug != MagickFalse)
14585     {
14586       (void) LogMagickEvent(X11Event,GetMagickModule(),
14587         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14588         (double) display_image->scene,(double) display_image->columns,
14589         (double) display_image->rows);
14590       if (display_image->colors != 0)
14591         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14592           display_image->colors);
14593       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14594         display_image->magick);
14595     }
14596   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14597     map_info,pixel);
14598   display_image->taint=MagickFalse;
14599   /*
14600     Initialize graphic context.
14601   */
14602   windows->context.id=(Window) NULL;
14603   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14604     resource_info,&windows->context);
14605   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14606   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14607   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14608   manager_hints->flags=InputHint | StateHint;
14609   manager_hints->input=MagickFalse;
14610   manager_hints->initial_state=WithdrawnState;
14611   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14612     &windows->context);
14613   if (display_image->debug != MagickFalse)
14614     (void) LogMagickEvent(X11Event,GetMagickModule(),
14615       "Window id: 0x%lx (context)",windows->context.id);
14616   context_values.background=pixel->background_color.pixel;
14617   context_values.font=font_info->fid;
14618   context_values.foreground=pixel->foreground_color.pixel;
14619   context_values.graphics_exposures=MagickFalse;
14620   context_mask=(MagickStatusType)
14621     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14622   if (pixel->annotate_context != (GC) NULL)
14623     (void) XFreeGC(display,pixel->annotate_context);
14624   pixel->annotate_context=XCreateGC(display,windows->context.id,
14625     context_mask,&context_values);
14626   if (pixel->annotate_context == (GC) NULL)
14627     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14628       display_image->filename);
14629   context_values.background=pixel->depth_color.pixel;
14630   if (pixel->widget_context != (GC) NULL)
14631     (void) XFreeGC(display,pixel->widget_context);
14632   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14633     &context_values);
14634   if (pixel->widget_context == (GC) NULL)
14635     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14636       display_image->filename);
14637   context_values.background=pixel->foreground_color.pixel;
14638   context_values.foreground=pixel->background_color.pixel;
14639   context_values.plane_mask=context_values.background ^
14640     context_values.foreground;
14641   if (pixel->highlight_context != (GC) NULL)
14642     (void) XFreeGC(display,pixel->highlight_context);
14643   pixel->highlight_context=XCreateGC(display,windows->context.id,
14644     (size_t) (context_mask | GCPlaneMask),&context_values);
14645   if (pixel->highlight_context == (GC) NULL)
14646     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14647       display_image->filename);
14648   (void) XDestroyWindow(display,windows->context.id);
14649   /*
14650     Initialize icon window.
14651   */
14652   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14653     icon_resources,&windows->icon);
14654   windows->icon.geometry=resource_info->icon_geometry;
14655   XBestIconSize(display,&windows->icon,display_image);
14656   windows->icon.attributes.colormap=XDefaultColormap(display,
14657     icon_visual->screen);
14658   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14659   manager_hints->flags=InputHint | StateHint;
14660   manager_hints->input=MagickFalse;
14661   manager_hints->initial_state=IconicState;
14662   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14663     &windows->icon);
14664   if (display_image->debug != MagickFalse)
14665     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14666       windows->icon.id);
14667   /*
14668     Initialize graphic context for icon window.
14669   */
14670   if (icon_pixel->annotate_context != (GC) NULL)
14671     (void) XFreeGC(display,icon_pixel->annotate_context);
14672   context_values.background=icon_pixel->background_color.pixel;
14673   context_values.foreground=icon_pixel->foreground_color.pixel;
14674   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14675     (size_t) (GCBackground | GCForeground),&context_values);
14676   if (icon_pixel->annotate_context == (GC) NULL)
14677     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14678       display_image->filename);
14679   windows->icon.annotate_context=icon_pixel->annotate_context;
14680   /*
14681     Initialize Image window.
14682   */
14683   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14684     &windows->image);
14685   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14686   if (resource_info->use_shared_memory == MagickFalse)
14687     windows->image.shared_memory=MagickFalse;
14688   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14689     {
14690       char
14691         *title;
14692
14693       title=InterpretImageProperties(resource_info->image_info,display_image,
14694         resource_info->title,exception);
14695       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14696       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14697       title=DestroyString(title);
14698     }
14699   else
14700     {
14701       char
14702         filename[MaxTextExtent];
14703
14704       /*
14705         Window name is the base of the filename.
14706       */
14707       GetPathComponent(display_image->magick_filename,TailPath,filename);
14708       if (display_image->scene == 0)
14709         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14710           "%s: %s",MagickPackageName,filename);
14711       else
14712         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14713           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14714           (double) display_image->scene,(double) GetImageListLength(
14715           display_image));
14716       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14717     }
14718   if (resource_info->immutable)
14719     windows->image.immutable=MagickTrue;
14720   windows->image.use_pixmap=resource_info->use_pixmap;
14721   windows->image.geometry=resource_info->image_geometry;
14722   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14723     XDisplayWidth(display,visual_info->screen),
14724     XDisplayHeight(display,visual_info->screen));
14725   geometry_info.width=display_image->columns;
14726   geometry_info.height=display_image->rows;
14727   geometry_info.x=0;
14728   geometry_info.y=0;
14729   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14730     &geometry_info.width,&geometry_info.height);
14731   windows->image.width=(unsigned int) geometry_info.width;
14732   windows->image.height=(unsigned int) geometry_info.height;
14733   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14734     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14735     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14736     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14737   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14738     resource_info,&windows->backdrop);
14739   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14740     {
14741       /*
14742         Initialize backdrop window.
14743       */
14744       windows->backdrop.x=0;
14745       windows->backdrop.y=0;
14746       (void) CloneString(&windows->backdrop.name,"Backdrop");
14747       windows->backdrop.flags=(size_t) (USSize | USPosition);
14748       windows->backdrop.width=(unsigned int)
14749         XDisplayWidth(display,visual_info->screen);
14750       windows->backdrop.height=(unsigned int)
14751         XDisplayHeight(display,visual_info->screen);
14752       windows->backdrop.border_width=0;
14753       windows->backdrop.immutable=MagickTrue;
14754       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14755         ButtonReleaseMask;
14756       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14757         StructureNotifyMask;
14758       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14759       manager_hints->icon_window=windows->icon.id;
14760       manager_hints->input=MagickTrue;
14761       manager_hints->initial_state=resource_info->iconic ? IconicState :
14762         NormalState;
14763       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14764         &windows->backdrop);
14765       if (display_image->debug != MagickFalse)
14766         (void) LogMagickEvent(X11Event,GetMagickModule(),
14767           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14768       (void) XMapWindow(display,windows->backdrop.id);
14769       (void) XClearWindow(display,windows->backdrop.id);
14770       if (windows->image.id != (Window) NULL)
14771         {
14772           (void) XDestroyWindow(display,windows->image.id);
14773           windows->image.id=(Window) NULL;
14774         }
14775       /*
14776         Position image in the center the backdrop.
14777       */
14778       windows->image.flags|=USPosition;
14779       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14780         (windows->image.width/2);
14781       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14782         (windows->image.height/2);
14783     }
14784   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14785   manager_hints->icon_window=windows->icon.id;
14786   manager_hints->input=MagickTrue;
14787   manager_hints->initial_state=resource_info->iconic ? IconicState :
14788     NormalState;
14789   if (windows->group_leader.id != (Window) NULL)
14790     {
14791       /*
14792         Follow the leader.
14793       */
14794       manager_hints->flags|=WindowGroupHint;
14795       manager_hints->window_group=windows->group_leader.id;
14796       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14797       if (display_image->debug != MagickFalse)
14798         (void) LogMagickEvent(X11Event,GetMagickModule(),
14799           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14800     }
14801   XMakeWindow(display,
14802     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14803     argv,argc,class_hints,manager_hints,&windows->image);
14804   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14805     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14806   if (windows->group_leader.id != (Window) NULL)
14807     (void) XSetTransientForHint(display,windows->image.id,
14808       windows->group_leader.id);
14809   if (display_image->debug != MagickFalse)
14810     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14811       windows->image.id);
14812   /*
14813     Initialize Info widget.
14814   */
14815   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14816     &windows->info);
14817   (void) CloneString(&windows->info.name,"Info");
14818   (void) CloneString(&windows->info.icon_name,"Info");
14819   windows->info.border_width=1;
14820   windows->info.x=2;
14821   windows->info.y=2;
14822   windows->info.flags|=PPosition;
14823   windows->info.attributes.win_gravity=UnmapGravity;
14824   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14825     StructureNotifyMask;
14826   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14827   manager_hints->input=MagickFalse;
14828   manager_hints->initial_state=NormalState;
14829   manager_hints->window_group=windows->image.id;
14830   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14831     &windows->info);
14832   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14833     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14834   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14835     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14836   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14837   if (windows->image.mapped != MagickFalse)
14838     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14839   if (display_image->debug != MagickFalse)
14840     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14841       windows->info.id);
14842   /*
14843     Initialize Command widget.
14844   */
14845   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14846     resource_info,&windows->command);
14847   windows->command.data=MagickMenus;
14848   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14849   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14850     resource_info->client_name);
14851   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14852     resource_name,"geometry",(char *) NULL);
14853   (void) CloneString(&windows->command.name,MagickTitle);
14854   windows->command.border_width=0;
14855   windows->command.flags|=PPosition;
14856   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14857     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14858     OwnerGrabButtonMask | StructureNotifyMask;
14859   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14860   manager_hints->input=MagickTrue;
14861   manager_hints->initial_state=NormalState;
14862   manager_hints->window_group=windows->image.id;
14863   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14864     &windows->command);
14865   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14866     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14867     HighlightHeight);
14868   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14869     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14870   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14871   if (windows->command.mapped != MagickFalse)
14872     (void) XMapRaised(display,windows->command.id);
14873   if (display_image->debug != MagickFalse)
14874     (void) LogMagickEvent(X11Event,GetMagickModule(),
14875       "Window id: 0x%lx (command)",windows->command.id);
14876   /*
14877     Initialize Widget window.
14878   */
14879   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14880     resource_info,&windows->widget);
14881   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14882     resource_info->client_name);
14883   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14884     resource_name,"geometry",(char *) NULL);
14885   windows->widget.border_width=0;
14886   windows->widget.flags|=PPosition;
14887   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14888     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14889     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14890     StructureNotifyMask;
14891   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14892   manager_hints->input=MagickTrue;
14893   manager_hints->initial_state=NormalState;
14894   manager_hints->window_group=windows->image.id;
14895   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14896     &windows->widget);
14897   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14898     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14899   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14900     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14901   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14902   if (display_image->debug != MagickFalse)
14903     (void) LogMagickEvent(X11Event,GetMagickModule(),
14904       "Window id: 0x%lx (widget)",windows->widget.id);
14905   /*
14906     Initialize popup window.
14907   */
14908   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14909     resource_info,&windows->popup);
14910   windows->popup.border_width=0;
14911   windows->popup.flags|=PPosition;
14912   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14913     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14914     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14915   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14916   manager_hints->input=MagickTrue;
14917   manager_hints->initial_state=NormalState;
14918   manager_hints->window_group=windows->image.id;
14919   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14920     &windows->popup);
14921   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14922     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14923   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14924     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14925   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14926   if (display_image->debug != MagickFalse)
14927     (void) LogMagickEvent(X11Event,GetMagickModule(),
14928       "Window id: 0x%lx (pop up)",windows->popup.id);
14929   /*
14930     Initialize Magnify window and cursor.
14931   */
14932   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14933     resource_info,&windows->magnify);
14934   if (resource_info->use_shared_memory == MagickFalse)
14935     windows->magnify.shared_memory=MagickFalse;
14936   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14937     resource_info->client_name);
14938   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14939     resource_name,"geometry",(char *) NULL);
14940   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14941     resource_info->magnify);
14942   if (windows->magnify.cursor != (Cursor) NULL)
14943     (void) XFreeCursor(display,windows->magnify.cursor);
14944   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14945     map_info->colormap,resource_info->background_color,
14946     resource_info->foreground_color);
14947   if (windows->magnify.cursor == (Cursor) NULL)
14948     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14949       display_image->filename);
14950   windows->magnify.width=MagnifySize;
14951   windows->magnify.height=MagnifySize;
14952   windows->magnify.flags|=PPosition;
14953   windows->magnify.min_width=MagnifySize;
14954   windows->magnify.min_height=MagnifySize;
14955   windows->magnify.width_inc=MagnifySize;
14956   windows->magnify.height_inc=MagnifySize;
14957   windows->magnify.data=resource_info->magnify;
14958   windows->magnify.attributes.cursor=windows->magnify.cursor;
14959   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14960     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14961     StructureNotifyMask;
14962   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14963   manager_hints->input=MagickTrue;
14964   manager_hints->initial_state=NormalState;
14965   manager_hints->window_group=windows->image.id;
14966   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14967     &windows->magnify);
14968   if (display_image->debug != MagickFalse)
14969     (void) LogMagickEvent(X11Event,GetMagickModule(),
14970       "Window id: 0x%lx (magnify)",windows->magnify.id);
14971   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14972   /*
14973     Initialize panning window.
14974   */
14975   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14976     resource_info,&windows->pan);
14977   (void) CloneString(&windows->pan.name,"Pan Icon");
14978   windows->pan.width=windows->icon.width;
14979   windows->pan.height=windows->icon.height;
14980   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14981     resource_info->client_name);
14982   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14983     resource_name,"geometry",(char *) NULL);
14984   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14985     &windows->pan.width,&windows->pan.height);
14986   windows->pan.flags|=PPosition;
14987   windows->pan.immutable=MagickTrue;
14988   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14989     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14990     StructureNotifyMask;
14991   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14992   manager_hints->input=MagickFalse;
14993   manager_hints->initial_state=NormalState;
14994   manager_hints->window_group=windows->image.id;
14995   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14996     &windows->pan);
14997   if (display_image->debug != MagickFalse)
14998     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14999       windows->pan.id);
15000   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15001   if (windows->info.mapped != MagickFalse)
15002     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15003   if ((windows->image.mapped == MagickFalse) ||
15004       (windows->backdrop.id != (Window) NULL))
15005     (void) XMapWindow(display,windows->image.id);
15006   /*
15007     Set our progress monitor and warning handlers.
15008   */
15009   if (warning_handler == (WarningHandler) NULL)
15010     {
15011       warning_handler=resource_info->display_warnings ?
15012         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15013       warning_handler=resource_info->display_warnings ?
15014         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15015     }
15016   /*
15017     Initialize Image and Magnify X images.
15018   */
15019   windows->image.x=0;
15020   windows->image.y=0;
15021   windows->magnify.shape=MagickFalse;
15022   width=(unsigned int) display_image->columns;
15023   height=(unsigned int) display_image->rows;
15024   if ((display_image->columns != width) || (display_image->rows != height))
15025     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15026       display_image->filename);
15027   status=XMakeImage(display,resource_info,&windows->image,display_image,
15028     width,height,exception);
15029   if (status == MagickFalse)
15030     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15031       display_image->filename);
15032   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15033     windows->magnify.width,windows->magnify.height,exception);
15034   if (status == MagickFalse)
15035     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15036       display_image->filename);
15037   if (windows->magnify.mapped != MagickFalse)
15038     (void) XMapRaised(display,windows->magnify.id);
15039   if (windows->pan.mapped != MagickFalse)
15040     (void) XMapRaised(display,windows->pan.id);
15041   windows->image.window_changes.width=(int) display_image->columns;
15042   windows->image.window_changes.height=(int) display_image->rows;
15043   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15044   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15045   (void) XSync(display,MagickFalse);
15046   /*
15047     Respond to events.
15048   */
15049   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15050   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15051   update_time=0;
15052   if (resource_info->update != MagickFalse)
15053     {
15054       MagickBooleanType
15055         status;
15056
15057       /*
15058         Determine when file data was last modified.
15059       */
15060       status=GetPathAttributes(display_image->filename,&attributes);
15061       if (status != MagickFalse)
15062         update_time=attributes.st_mtime;
15063     }
15064   *state&=(~FormerImageState);
15065   *state&=(~MontageImageState);
15066   *state&=(~NextImageState);
15067   do
15068   {
15069     /*
15070       Handle a window event.
15071     */
15072     if (windows->image.mapped != MagickFalse)
15073       if ((display_image->delay != 0) || (resource_info->update != 0))
15074         {
15075           if (timer < time((time_t *) NULL))
15076             {
15077               if (resource_info->update == MagickFalse)
15078                 *state|=NextImageState | ExitState;
15079               else
15080                 {
15081                   MagickBooleanType
15082                     status;
15083
15084                   /*
15085                     Determine if image file was modified.
15086                   */
15087                   status=GetPathAttributes(display_image->filename,&attributes);
15088                   if (status != MagickFalse)
15089                     if (update_time != attributes.st_mtime)
15090                       {
15091                         /*
15092                           Redisplay image.
15093                         */
15094                         (void) FormatLocaleString(
15095                           resource_info->image_info->filename,MaxTextExtent,
15096                           "%s:%s",display_image->magick,
15097                           display_image->filename);
15098                         nexus=ReadImage(resource_info->image_info,
15099                           &display_image->exception);
15100                         if (nexus != (Image *) NULL)
15101                           {
15102                             nexus=DestroyImage(nexus);
15103                             *state|=NextImageState | ExitState;
15104                           }
15105                       }
15106                   delay=display_image->delay/MagickMax(
15107                     display_image->ticks_per_second,1L);
15108                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15109                 }
15110             }
15111           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15112             {
15113               /*
15114                 Do not block if delay > 0.
15115               */
15116               XDelay(display,SuspendTime << 2);
15117               continue;
15118             }
15119         }
15120     timestamp=time((time_t *) NULL);
15121     (void) XNextEvent(display,&event);
15122     if (windows->image.stasis == MagickFalse)
15123       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15124         MagickTrue : MagickFalse;
15125     if (windows->magnify.stasis == MagickFalse)
15126       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15127         MagickTrue : MagickFalse;
15128     if (event.xany.window == windows->command.id)
15129       {
15130         /*
15131           Select a command from the Command widget.
15132         */
15133         id=XCommandWidget(display,windows,CommandMenu,&event);
15134         if (id < 0)
15135           continue;
15136         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15137         command_type=CommandMenus[id];
15138         if (id < MagickMenus)
15139           {
15140             /*
15141               Select a command from a pop-up menu.
15142             */
15143             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15144               command);
15145             if (entry < 0)
15146               continue;
15147             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15148             command_type=Commands[id][entry];
15149           }
15150         if (command_type != NullCommand)
15151           nexus=XMagickCommand(display,resource_info,windows,command_type,
15152             &display_image,exception);
15153         continue;
15154       }
15155     switch (event.type)
15156     {
15157       case ButtonPress:
15158       {
15159         if (display_image->debug != MagickFalse)
15160           (void) LogMagickEvent(X11Event,GetMagickModule(),
15161             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15162             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15163         if ((event.xbutton.button == Button3) &&
15164             (event.xbutton.state & Mod1Mask))
15165           {
15166             /*
15167               Convert Alt-Button3 to Button2.
15168             */
15169             event.xbutton.button=Button2;
15170             event.xbutton.state&=(~Mod1Mask);
15171           }
15172         if (event.xbutton.window == windows->backdrop.id)
15173           {
15174             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15175               event.xbutton.time);
15176             break;
15177           }
15178         if (event.xbutton.window == windows->image.id)
15179           {
15180             switch (event.xbutton.button)
15181             {
15182               case Button1:
15183               {
15184                 if (resource_info->immutable)
15185                   {
15186                     /*
15187                       Select a command from the Virtual menu.
15188                     */
15189                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15190                       command);
15191                     if (entry >= 0)
15192                       nexus=XMagickCommand(display,resource_info,windows,
15193                         VirtualCommands[entry],&display_image,exception);
15194                     break;
15195                   }
15196                 /*
15197                   Map/unmap Command widget.
15198                 */
15199                 if (windows->command.mapped != MagickFalse)
15200                   (void) XWithdrawWindow(display,windows->command.id,
15201                     windows->command.screen);
15202                 else
15203                   {
15204                     (void) XCommandWidget(display,windows,CommandMenu,
15205                       (XEvent *) NULL);
15206                     (void) XMapRaised(display,windows->command.id);
15207                   }
15208                 break;
15209               }
15210               case Button2:
15211               {
15212                 /*
15213                   User pressed the image magnify button.
15214                 */
15215                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15216                   &display_image,exception);
15217                 XMagnifyImage(display,windows,&event);
15218                 break;
15219               }
15220               case Button3:
15221               {
15222                 if (resource_info->immutable)
15223                   {
15224                     /*
15225                       Select a command from the Virtual menu.
15226                     */
15227                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15228                       command);
15229                     if (entry >= 0)
15230                       nexus=XMagickCommand(display,resource_info,windows,
15231                         VirtualCommands[entry],&display_image,exception);
15232                     break;
15233                   }
15234                 if (display_image->montage != (char *) NULL)
15235                   {
15236                     /*
15237                       Open or delete a tile from a visual image directory.
15238                     */
15239                     nexus=XTileImage(display,resource_info,windows,
15240                       display_image,&event,exception);
15241                     if (nexus != (Image *) NULL)
15242                       *state|=MontageImageState | NextImageState | ExitState;
15243                     vid_info.x=(short int) windows->image.x;
15244                     vid_info.y=(short int) windows->image.y;
15245                     break;
15246                   }
15247                 /*
15248                   Select a command from the Short Cuts menu.
15249                 */
15250                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15251                   command);
15252                 if (entry >= 0)
15253                   nexus=XMagickCommand(display,resource_info,windows,
15254                     ShortCutsCommands[entry],&display_image,exception);
15255                 break;
15256               }
15257               case Button4:
15258               {
15259                 /*
15260                   Wheel up.
15261                 */
15262                 XTranslateImage(display,windows,*image,XK_Up);
15263                 break;
15264               }
15265               case Button5:
15266               {
15267                 /*
15268                   Wheel down.
15269                 */
15270                 XTranslateImage(display,windows,*image,XK_Down);
15271                 break;
15272               }
15273               default:
15274                 break;
15275             }
15276             break;
15277           }
15278         if (event.xbutton.window == windows->magnify.id)
15279           {
15280             int
15281               factor;
15282
15283             static const char
15284               *MagnifyMenu[] =
15285               {
15286                 "2",
15287                 "4",
15288                 "5",
15289                 "6",
15290                 "7",
15291                 "8",
15292                 "9",
15293                 "3",
15294                 (char *) NULL,
15295               };
15296
15297             static KeySym
15298               MagnifyCommands[] =
15299               {
15300                 XK_2,
15301                 XK_4,
15302                 XK_5,
15303                 XK_6,
15304                 XK_7,
15305                 XK_8,
15306                 XK_9,
15307                 XK_3
15308               };
15309
15310             /*
15311               Select a magnify factor from the pop-up menu.
15312             */
15313             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15314             if (factor >= 0)
15315               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15316             break;
15317           }
15318         if (event.xbutton.window == windows->pan.id)
15319           {
15320             switch (event.xbutton.button)
15321             {
15322               case Button4:
15323               {
15324                 /*
15325                   Wheel up.
15326                 */
15327                 XTranslateImage(display,windows,*image,XK_Up);
15328                 break;
15329               }
15330               case Button5:
15331               {
15332                 /*
15333                   Wheel down.
15334                 */
15335                 XTranslateImage(display,windows,*image,XK_Down);
15336                 break;
15337               }
15338               default:
15339               {
15340                 XPanImage(display,windows,&event);
15341                 break;
15342               }
15343             }
15344             break;
15345           }
15346         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15347           1L);
15348         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15349         break;
15350       }
15351       case ButtonRelease:
15352       {
15353         if (display_image->debug != MagickFalse)
15354           (void) LogMagickEvent(X11Event,GetMagickModule(),
15355             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15356             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15357         break;
15358       }
15359       case ClientMessage:
15360       {
15361         if (display_image->debug != MagickFalse)
15362           (void) LogMagickEvent(X11Event,GetMagickModule(),
15363             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15364             event.xclient.message_type,event.xclient.format,(unsigned long)
15365             event.xclient.data.l[0]);
15366         if (event.xclient.message_type == windows->im_protocols)
15367           {
15368             if (*event.xclient.data.l == (long) windows->im_update_widget)
15369               {
15370                 (void) CloneString(&windows->command.name,MagickTitle);
15371                 windows->command.data=MagickMenus;
15372                 (void) XCommandWidget(display,windows,CommandMenu,
15373                   (XEvent *) NULL);
15374                 break;
15375               }
15376             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15377               {
15378                 /*
15379                   Update graphic context and window colormap.
15380                 */
15381                 for (i=0; i < (int) number_windows; i++)
15382                 {
15383                   if (magick_windows[i]->id == windows->icon.id)
15384                     continue;
15385                   context_values.background=pixel->background_color.pixel;
15386                   context_values.foreground=pixel->foreground_color.pixel;
15387                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15388                     context_mask,&context_values);
15389                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15390                     context_mask,&context_values);
15391                   context_values.background=pixel->foreground_color.pixel;
15392                   context_values.foreground=pixel->background_color.pixel;
15393                   context_values.plane_mask=context_values.background ^
15394                     context_values.foreground;
15395                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15396                     (size_t) (context_mask | GCPlaneMask),
15397                     &context_values);
15398                   magick_windows[i]->attributes.background_pixel=
15399                     pixel->background_color.pixel;
15400                   magick_windows[i]->attributes.border_pixel=
15401                     pixel->border_color.pixel;
15402                   magick_windows[i]->attributes.colormap=map_info->colormap;
15403                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15404                     (unsigned long) magick_windows[i]->mask,
15405                     &magick_windows[i]->attributes);
15406                 }
15407                 if (windows->pan.mapped != MagickFalse)
15408                   {
15409                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15410                       windows->pan.pixmap);
15411                     (void) XClearWindow(display,windows->pan.id);
15412                     XDrawPanRectangle(display,windows);
15413                   }
15414                 if (windows->backdrop.id != (Window) NULL)
15415                   (void) XInstallColormap(display,map_info->colormap);
15416                 break;
15417               }
15418             if (*event.xclient.data.l == (long) windows->im_former_image)
15419               {
15420                 *state|=FormerImageState | ExitState;
15421                 break;
15422               }
15423             if (*event.xclient.data.l == (long) windows->im_next_image)
15424               {
15425                 *state|=NextImageState | ExitState;
15426                 break;
15427               }
15428             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15429               {
15430                 *state|=RetainColorsState;
15431                 break;
15432               }
15433             if (*event.xclient.data.l == (long) windows->im_exit)
15434               {
15435                 *state|=ExitState;
15436                 break;
15437               }
15438             break;
15439           }
15440         if (event.xclient.message_type == windows->dnd_protocols)
15441           {
15442             Atom
15443               selection,
15444               type;
15445
15446             int
15447               format,
15448               status;
15449
15450             unsigned char
15451               *data;
15452
15453             unsigned long
15454               after,
15455               length;
15456
15457             /*
15458               Display image named by the Drag-and-Drop selection.
15459             */
15460             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15461               break;
15462             selection=XInternAtom(display,"DndSelection",MagickFalse);
15463             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15464               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15465               &length,&after,&data);
15466             if ((status != Success) || (length == 0))
15467               break;
15468             if (*event.xclient.data.l == 2)
15469               {
15470                 /*
15471                   Offix DND.
15472                 */
15473                 (void) CopyMagickString(resource_info->image_info->filename,
15474                   (char *) data,MaxTextExtent);
15475               }
15476             else
15477               {
15478                 /*
15479                   XDND.
15480                 */
15481                 if (strncmp((char *) data, "file:", 5) != 0)
15482                   {
15483                     (void) XFree((void *) data);
15484                     break;
15485                   }
15486                 (void) CopyMagickString(resource_info->image_info->filename,
15487                   ((char *) data)+5,MaxTextExtent);
15488               }
15489             nexus=ReadImage(resource_info->image_info,
15490               &display_image->exception);
15491             CatchException(&display_image->exception);
15492             if (nexus != (Image *) NULL)
15493               *state|=NextImageState | ExitState;
15494             (void) XFree((void *) data);
15495             break;
15496           }
15497         /*
15498           If client window delete message, exit.
15499         */
15500         if (event.xclient.message_type != windows->wm_protocols)
15501           break;
15502         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15503           break;
15504         (void) XWithdrawWindow(display,event.xclient.window,
15505           visual_info->screen);
15506         if (event.xclient.window == windows->image.id)
15507           {
15508             *state|=ExitState;
15509             break;
15510           }
15511         if (event.xclient.window == windows->pan.id)
15512           {
15513             /*
15514               Restore original image size when pan window is deleted.
15515             */
15516             windows->image.window_changes.width=windows->image.ximage->width;
15517             windows->image.window_changes.height=windows->image.ximage->height;
15518             (void) XConfigureImage(display,resource_info,windows,
15519               display_image,exception);
15520           }
15521         break;
15522       }
15523       case ConfigureNotify:
15524       {
15525         if (display_image->debug != MagickFalse)
15526           (void) LogMagickEvent(X11Event,GetMagickModule(),
15527             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15528             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15529             event.xconfigure.y,event.xconfigure.send_event);
15530         if (event.xconfigure.window == windows->image.id)
15531           {
15532             /*
15533               Image window has a new configuration.
15534             */
15535             if (event.xconfigure.send_event != 0)
15536               {
15537                 XWindowChanges
15538                   window_changes;
15539
15540                 /*
15541                   Position the transient windows relative of the Image window.
15542                 */
15543                 if (windows->command.geometry == (char *) NULL)
15544                   if (windows->command.mapped == MagickFalse)
15545                     {
15546                       windows->command.x=event.xconfigure.x-
15547                         windows->command.width-25;
15548                       windows->command.y=event.xconfigure.y;
15549                       XConstrainWindowPosition(display,&windows->command);
15550                       window_changes.x=windows->command.x;
15551                       window_changes.y=windows->command.y;
15552                       (void) XReconfigureWMWindow(display,windows->command.id,
15553                         windows->command.screen,(unsigned int) (CWX | CWY),
15554                         &window_changes);
15555                     }
15556                 if (windows->widget.geometry == (char *) NULL)
15557                   if (windows->widget.mapped == MagickFalse)
15558                     {
15559                       windows->widget.x=event.xconfigure.x+
15560                         event.xconfigure.width/10;
15561                       windows->widget.y=event.xconfigure.y+
15562                         event.xconfigure.height/10;
15563                       XConstrainWindowPosition(display,&windows->widget);
15564                       window_changes.x=windows->widget.x;
15565                       window_changes.y=windows->widget.y;
15566                       (void) XReconfigureWMWindow(display,windows->widget.id,
15567                         windows->widget.screen,(unsigned int) (CWX | CWY),
15568                         &window_changes);
15569                     }
15570                 if (windows->magnify.geometry == (char *) NULL)
15571                   if (windows->magnify.mapped == MagickFalse)
15572                     {
15573                       windows->magnify.x=event.xconfigure.x+
15574                         event.xconfigure.width+25;
15575                       windows->magnify.y=event.xconfigure.y;
15576                       XConstrainWindowPosition(display,&windows->magnify);
15577                       window_changes.x=windows->magnify.x;
15578                       window_changes.y=windows->magnify.y;
15579                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15580                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15581                         &window_changes);
15582                     }
15583                 if (windows->pan.geometry == (char *) NULL)
15584                   if (windows->pan.mapped == MagickFalse)
15585                     {
15586                       windows->pan.x=event.xconfigure.x+
15587                         event.xconfigure.width+25;
15588                       windows->pan.y=event.xconfigure.y+
15589                         windows->magnify.height+50;
15590                       XConstrainWindowPosition(display,&windows->pan);
15591                       window_changes.x=windows->pan.x;
15592                       window_changes.y=windows->pan.y;
15593                       (void) XReconfigureWMWindow(display,windows->pan.id,
15594                         windows->pan.screen,(unsigned int) (CWX | CWY),
15595                         &window_changes);
15596                     }
15597               }
15598             if ((event.xconfigure.width == (int) windows->image.width) &&
15599                 (event.xconfigure.height == (int) windows->image.height))
15600               break;
15601             windows->image.width=(unsigned int) event.xconfigure.width;
15602             windows->image.height=(unsigned int) event.xconfigure.height;
15603             windows->image.x=0;
15604             windows->image.y=0;
15605             if (display_image->montage != (char *) NULL)
15606               {
15607                 windows->image.x=vid_info.x;
15608                 windows->image.y=vid_info.y;
15609               }
15610             if ((windows->image.mapped != MagickFalse) &&
15611                 (windows->image.stasis != MagickFalse))
15612               {
15613                 /*
15614                   Update image window configuration.
15615                 */
15616                 windows->image.window_changes.width=event.xconfigure.width;
15617                 windows->image.window_changes.height=event.xconfigure.height;
15618                 (void) XConfigureImage(display,resource_info,windows,
15619                   display_image,exception);
15620               }
15621             /*
15622               Update pan window configuration.
15623             */
15624             if ((event.xconfigure.width < windows->image.ximage->width) ||
15625                 (event.xconfigure.height < windows->image.ximage->height))
15626               {
15627                 (void) XMapRaised(display,windows->pan.id);
15628                 XDrawPanRectangle(display,windows);
15629               }
15630             else
15631               if (windows->pan.mapped != MagickFalse)
15632                 (void) XWithdrawWindow(display,windows->pan.id,
15633                   windows->pan.screen);
15634             break;
15635           }
15636         if (event.xconfigure.window == windows->magnify.id)
15637           {
15638             unsigned int
15639               magnify;
15640
15641             /*
15642               Magnify window has a new configuration.
15643             */
15644             windows->magnify.width=(unsigned int) event.xconfigure.width;
15645             windows->magnify.height=(unsigned int) event.xconfigure.height;
15646             if (windows->magnify.mapped == MagickFalse)
15647               break;
15648             magnify=1;
15649             while ((int) magnify <= event.xconfigure.width)
15650               magnify<<=1;
15651             while ((int) magnify <= event.xconfigure.height)
15652               magnify<<=1;
15653             magnify>>=1;
15654             if (((int) magnify != event.xconfigure.width) ||
15655                 ((int) magnify != event.xconfigure.height))
15656               {
15657                 window_changes.width=(int) magnify;
15658                 window_changes.height=(int) magnify;
15659                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15660                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15661                   &window_changes);
15662                 break;
15663               }
15664             if ((windows->magnify.mapped != MagickFalse) &&
15665                 (windows->magnify.stasis != MagickFalse))
15666               {
15667                 status=XMakeImage(display,resource_info,&windows->magnify,
15668                   display_image,windows->magnify.width,windows->magnify.height,
15669                   exception);
15670                 XMakeMagnifyImage(display,windows);
15671               }
15672             break;
15673           }
15674         if ((windows->magnify.mapped != MagickFalse) &&
15675             (event.xconfigure.window == windows->pan.id))
15676           {
15677             /*
15678               Pan icon window has a new configuration.
15679             */
15680             if (event.xconfigure.send_event != 0)
15681               {
15682                 windows->pan.x=event.xconfigure.x;
15683                 windows->pan.y=event.xconfigure.y;
15684               }
15685             windows->pan.width=(unsigned int) event.xconfigure.width;
15686             windows->pan.height=(unsigned int) event.xconfigure.height;
15687             break;
15688           }
15689         if (event.xconfigure.window == windows->icon.id)
15690           {
15691             /*
15692               Icon window has a new configuration.
15693             */
15694             windows->icon.width=(unsigned int) event.xconfigure.width;
15695             windows->icon.height=(unsigned int) event.xconfigure.height;
15696             break;
15697           }
15698         break;
15699       }
15700       case DestroyNotify:
15701       {
15702         /*
15703           Group leader has exited.
15704         */
15705         if (display_image->debug != MagickFalse)
15706           (void) LogMagickEvent(X11Event,GetMagickModule(),
15707             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15708         if (event.xdestroywindow.window == windows->group_leader.id)
15709           {
15710             *state|=ExitState;
15711             break;
15712           }
15713         break;
15714       }
15715       case EnterNotify:
15716       {
15717         /*
15718           Selectively install colormap.
15719         */
15720         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15721           if (event.xcrossing.mode != NotifyUngrab)
15722             XInstallColormap(display,map_info->colormap);
15723         break;
15724       }
15725       case Expose:
15726       {
15727         if (display_image->debug != MagickFalse)
15728           (void) LogMagickEvent(X11Event,GetMagickModule(),
15729             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15730             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15731             event.xexpose.y);
15732         /*
15733           Refresh windows that are now exposed.
15734         */
15735         if ((event.xexpose.window == windows->image.id) &&
15736             (windows->image.mapped != MagickFalse))
15737           {
15738             XRefreshWindow(display,&windows->image,&event);
15739             delay=display_image->delay/MagickMax(
15740               display_image->ticks_per_second,1L);
15741             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15742             break;
15743           }
15744         if ((event.xexpose.window == windows->magnify.id) &&
15745             (windows->magnify.mapped != MagickFalse))
15746           {
15747             XMakeMagnifyImage(display,windows);
15748             break;
15749           }
15750         if (event.xexpose.window == windows->pan.id)
15751           {
15752             XDrawPanRectangle(display,windows);
15753             break;
15754           }
15755         if (event.xexpose.window == windows->icon.id)
15756           {
15757             XRefreshWindow(display,&windows->icon,&event);
15758             break;
15759           }
15760         break;
15761       }
15762       case KeyPress:
15763       {
15764         int
15765           length;
15766
15767         /*
15768           Respond to a user key press.
15769         */
15770         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15771           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15772         *(command+length)='\0';
15773         if (display_image->debug != MagickFalse)
15774           (void) LogMagickEvent(X11Event,GetMagickModule(),
15775             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15776             key_symbol,command);
15777         if (event.xkey.window == windows->image.id)
15778           {
15779             command_type=XImageWindowCommand(display,resource_info,windows,
15780               event.xkey.state,key_symbol,&display_image,exception);
15781             if (command_type != NullCommand)
15782               nexus=XMagickCommand(display,resource_info,windows,command_type,
15783                 &display_image,exception);
15784           }
15785         if (event.xkey.window == windows->magnify.id)
15786           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15787         if (event.xkey.window == windows->pan.id)
15788           {
15789             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15790               (void) XWithdrawWindow(display,windows->pan.id,
15791                 windows->pan.screen);
15792             else
15793               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15794                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15795                   "Help Viewer - Image Pan",ImagePanHelp);
15796               else
15797                 XTranslateImage(display,windows,*image,key_symbol);
15798           }
15799         delay=display_image->delay/MagickMax(
15800           display_image->ticks_per_second,1L);
15801         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15802         break;
15803       }
15804       case KeyRelease:
15805       {
15806         /*
15807           Respond to a user key release.
15808         */
15809         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15810           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15811         if (display_image->debug != MagickFalse)
15812           (void) LogMagickEvent(X11Event,GetMagickModule(),
15813             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15814         break;
15815       }
15816       case LeaveNotify:
15817       {
15818         /*
15819           Selectively uninstall colormap.
15820         */
15821         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15822           if (event.xcrossing.mode != NotifyUngrab)
15823             XUninstallColormap(display,map_info->colormap);
15824         break;
15825       }
15826       case MapNotify:
15827       {
15828         if (display_image->debug != MagickFalse)
15829           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15830             event.xmap.window);
15831         if (event.xmap.window == windows->backdrop.id)
15832           {
15833             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15834               CurrentTime);
15835             windows->backdrop.mapped=MagickTrue;
15836             break;
15837           }
15838         if (event.xmap.window == windows->image.id)
15839           {
15840             if (windows->backdrop.id != (Window) NULL)
15841               (void) XInstallColormap(display,map_info->colormap);
15842             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15843               {
15844                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15845                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15846               }
15847             if (((int) windows->image.width < windows->image.ximage->width) ||
15848                 ((int) windows->image.height < windows->image.ximage->height))
15849               (void) XMapRaised(display,windows->pan.id);
15850             windows->image.mapped=MagickTrue;
15851             break;
15852           }
15853         if (event.xmap.window == windows->magnify.id)
15854           {
15855             XMakeMagnifyImage(display,windows);
15856             windows->magnify.mapped=MagickTrue;
15857             (void) XWithdrawWindow(display,windows->info.id,
15858               windows->info.screen);
15859             break;
15860           }
15861         if (event.xmap.window == windows->pan.id)
15862           {
15863             XMakePanImage(display,resource_info,windows,display_image,
15864               exception);
15865             windows->pan.mapped=MagickTrue;
15866             break;
15867           }
15868         if (event.xmap.window == windows->info.id)
15869           {
15870             windows->info.mapped=MagickTrue;
15871             break;
15872           }
15873         if (event.xmap.window == windows->icon.id)
15874           {
15875             MagickBooleanType
15876               taint;
15877
15878             /*
15879               Create an icon image.
15880             */
15881             taint=display_image->taint;
15882             XMakeStandardColormap(display,icon_visual,icon_resources,
15883               display_image,icon_map,icon_pixel);
15884             (void) XMakeImage(display,icon_resources,&windows->icon,
15885               display_image,windows->icon.width,windows->icon.height,
15886               exception);
15887             display_image->taint=taint;
15888             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15889               windows->icon.pixmap);
15890             (void) XClearWindow(display,windows->icon.id);
15891             (void) XWithdrawWindow(display,windows->info.id,
15892               windows->info.screen);
15893             windows->icon.mapped=MagickTrue;
15894             break;
15895           }
15896         if (event.xmap.window == windows->command.id)
15897           {
15898             windows->command.mapped=MagickTrue;
15899             break;
15900           }
15901         if (event.xmap.window == windows->popup.id)
15902           {
15903             windows->popup.mapped=MagickTrue;
15904             break;
15905           }
15906         if (event.xmap.window == windows->widget.id)
15907           {
15908             windows->widget.mapped=MagickTrue;
15909             break;
15910           }
15911         break;
15912       }
15913       case MappingNotify:
15914       {
15915         (void) XRefreshKeyboardMapping(&event.xmapping);
15916         break;
15917       }
15918       case NoExpose:
15919         break;
15920       case PropertyNotify:
15921       {
15922         Atom
15923           type;
15924
15925         int
15926           format,
15927           status;
15928
15929         unsigned char
15930           *data;
15931
15932         unsigned long
15933           after,
15934           length;
15935
15936         if (display_image->debug != MagickFalse)
15937           (void) LogMagickEvent(X11Event,GetMagickModule(),
15938             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15939             event.xproperty.atom,event.xproperty.state);
15940         if (event.xproperty.atom != windows->im_remote_command)
15941           break;
15942         /*
15943           Display image named by the remote command protocol.
15944         */
15945         status=XGetWindowProperty(display,event.xproperty.window,
15946           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15947           AnyPropertyType,&type,&format,&length,&after,&data);
15948         if ((status != Success) || (length == 0))
15949           break;
15950         if (LocaleCompare((char *) data,"-quit") == 0)
15951           {
15952             XClientMessage(display,windows->image.id,windows->im_protocols,
15953               windows->im_exit,CurrentTime);
15954             (void) XFree((void *) data);
15955             break;
15956           }
15957         (void) CopyMagickString(resource_info->image_info->filename,
15958           (char *) data,MaxTextExtent);
15959         (void) XFree((void *) data);
15960         nexus=ReadImage(resource_info->image_info,&display_image->exception);
15961         CatchException(&display_image->exception);
15962         if (nexus != (Image *) NULL)
15963           *state|=NextImageState | ExitState;
15964         break;
15965       }
15966       case ReparentNotify:
15967       {
15968         if (display_image->debug != MagickFalse)
15969           (void) LogMagickEvent(X11Event,GetMagickModule(),
15970             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15971             event.xreparent.window);
15972         break;
15973       }
15974       case UnmapNotify:
15975       {
15976         if (display_image->debug != MagickFalse)
15977           (void) LogMagickEvent(X11Event,GetMagickModule(),
15978             "Unmap Notify: 0x%lx",event.xunmap.window);
15979         if (event.xunmap.window == windows->backdrop.id)
15980           {
15981             windows->backdrop.mapped=MagickFalse;
15982             break;
15983           }
15984         if (event.xunmap.window == windows->image.id)
15985           {
15986             windows->image.mapped=MagickFalse;
15987             break;
15988           }
15989         if (event.xunmap.window == windows->magnify.id)
15990           {
15991             windows->magnify.mapped=MagickFalse;
15992             break;
15993           }
15994         if (event.xunmap.window == windows->pan.id)
15995           {
15996             windows->pan.mapped=MagickFalse;
15997             break;
15998           }
15999         if (event.xunmap.window == windows->info.id)
16000           {
16001             windows->info.mapped=MagickFalse;
16002             break;
16003           }
16004         if (event.xunmap.window == windows->icon.id)
16005           {
16006             if (map_info->colormap == icon_map->colormap)
16007               XConfigureImageColormap(display,resource_info,windows,
16008                 display_image);
16009             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16010               icon_pixel);
16011             windows->icon.mapped=MagickFalse;
16012             break;
16013           }
16014         if (event.xunmap.window == windows->command.id)
16015           {
16016             windows->command.mapped=MagickFalse;
16017             break;
16018           }
16019         if (event.xunmap.window == windows->popup.id)
16020           {
16021             if (windows->backdrop.id != (Window) NULL)
16022               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16023                 CurrentTime);
16024             windows->popup.mapped=MagickFalse;
16025             break;
16026           }
16027         if (event.xunmap.window == windows->widget.id)
16028           {
16029             if (windows->backdrop.id != (Window) NULL)
16030               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16031                 CurrentTime);
16032             windows->widget.mapped=MagickFalse;
16033             break;
16034           }
16035         break;
16036       }
16037       default:
16038       {
16039         if (display_image->debug != MagickFalse)
16040           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16041             event.type);
16042         break;
16043       }
16044     }
16045   } while (!(*state & ExitState));
16046   if ((*state & ExitState) == 0)
16047     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16048       &display_image,exception);
16049   else
16050     if (resource_info->confirm_edit != MagickFalse)
16051       {
16052         /*
16053           Query user if image has changed.
16054         */
16055         if ((resource_info->immutable == MagickFalse) &&
16056             (display_image->taint != MagickFalse))
16057           {
16058             int
16059               status;
16060
16061             status=XConfirmWidget(display,windows,"Your image changed.",
16062               "Do you want to save it");
16063             if (status == 0)
16064               *state&=(~ExitState);
16065             else
16066               if (status > 0)
16067                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16068                   &display_image,exception);
16069           }
16070       }
16071   if ((windows->visual_info->klass == GrayScale) ||
16072       (windows->visual_info->klass == PseudoColor) ||
16073       (windows->visual_info->klass == DirectColor))
16074     {
16075       /*
16076         Withdraw pan and Magnify window.
16077       */
16078       if (windows->info.mapped != MagickFalse)
16079         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16080       if (windows->magnify.mapped != MagickFalse)
16081         (void) XWithdrawWindow(display,windows->magnify.id,
16082           windows->magnify.screen);
16083       if (windows->command.mapped != MagickFalse)
16084         (void) XWithdrawWindow(display,windows->command.id,
16085           windows->command.screen);
16086     }
16087   if (windows->pan.mapped != MagickFalse)
16088     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16089   if (resource_info->backdrop == MagickFalse)
16090     if (windows->backdrop.mapped)
16091       {
16092         (void) XWithdrawWindow(display,windows->backdrop.id,
16093           windows->backdrop.screen);
16094         (void) XDestroyWindow(display,windows->backdrop.id);
16095         windows->backdrop.id=(Window) NULL;
16096         (void) XWithdrawWindow(display,windows->image.id,
16097           windows->image.screen);
16098         (void) XDestroyWindow(display,windows->image.id);
16099         windows->image.id=(Window) NULL;
16100       }
16101   XSetCursorState(display,windows,MagickTrue);
16102   XCheckRefreshWindows(display,windows);
16103   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16104     *state&=(~ExitState);
16105   if (*state & ExitState)
16106     {
16107       /*
16108         Free Standard Colormap.
16109       */
16110       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16111       if (resource_info->map_type == (char *) NULL)
16112         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16113       /*
16114         Free X resources.
16115       */
16116       if (resource_info->copy_image != (Image *) NULL)
16117         {
16118           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16119           resource_info->copy_image=NewImageList();
16120         }
16121       DestroyXResources();
16122     }
16123   (void) XSync(display,MagickFalse);
16124   /*
16125     Restore our progress monitor and warning handlers.
16126   */
16127   (void) SetErrorHandler(warning_handler);
16128   (void) SetWarningHandler(warning_handler);
16129   /*
16130     Change to home directory.
16131   */
16132   directory=getcwd(working_directory,MaxTextExtent);
16133   (void) directory;
16134   {
16135     int
16136       status;
16137
16138     status=chdir(resource_info->home_directory);
16139     if (status == -1)
16140       (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16141         FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16142   }
16143   *image=display_image;
16144   return(nexus);
16145 }
16146 #else
16147 \f
16148 /*
16149 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16150 %                                                                             %
16151 %                                                                             %
16152 %                                                                             %
16153 +   D i s p l a y I m a g e s                                                 %
16154 %                                                                             %
16155 %                                                                             %
16156 %                                                                             %
16157 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16158 %
16159 %  DisplayImages() displays an image sequence to any X window screen.  It
16160 %  returns a value other than 0 if successful.  Check the exception member
16161 %  of image to determine the reason for any failure.
16162 %
16163 %  The format of the DisplayImages method is:
16164 %
16165 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16166 %        Image *images,ExceptionInfo *exception)
16167 %
16168 %  A description of each parameter follows:
16169 %
16170 %    o image_info: the image info.
16171 %
16172 %    o image: the image.
16173 %
16174 %    o exception: return any errors or warnings in this structure.
16175 %
16176 */
16177 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16178   Image *image,ExceptionInfo *exception)
16179 {
16180   assert(image_info != (const ImageInfo *) NULL);
16181   assert(image_info->signature == MagickSignature);
16182   assert(image != (Image *) NULL);
16183   assert(image->signature == MagickSignature);
16184   if (image->debug != MagickFalse)
16185     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16186   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16187     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16188   return(MagickFalse);
16189 }
16190 \f
16191 /*
16192 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16193 %                                                                             %
16194 %                                                                             %
16195 %                                                                             %
16196 +   R e m o t e D i s p l a y C o m m a n d                                   %
16197 %                                                                             %
16198 %                                                                             %
16199 %                                                                             %
16200 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16201 %
16202 %  RemoteDisplayCommand() encourages a remote display program to display the
16203 %  specified image filename.
16204 %
16205 %  The format of the RemoteDisplayCommand method is:
16206 %
16207 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16208 %        const char *window,const char *filename,ExceptionInfo *exception)
16209 %
16210 %  A description of each parameter follows:
16211 %
16212 %    o image_info: the image info.
16213 %
16214 %    o window: Specifies the name or id of an X window.
16215 %
16216 %    o filename: the name of the image filename to display.
16217 %
16218 %    o exception: return any errors or warnings in this structure.
16219 %
16220 */
16221 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16222   const char *window,const char *filename,ExceptionInfo *exception)
16223 {
16224   assert(image_info != (const ImageInfo *) NULL);
16225   assert(image_info->signature == MagickSignature);
16226   assert(filename != (char *) NULL);
16227   (void) window;
16228   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16229   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16230     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16231   return(MagickFalse);
16232 }
16233 #endif