]> 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-2013 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/attribute.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache.h"
47 #include "MagickCore/cache-private.h"
48 #include "MagickCore/channel.h"
49 #include "MagickCore/client.h"
50 #include "MagickCore/color.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/composite.h"
53 #include "MagickCore/constitute.h"
54 #include "MagickCore/decorate.h"
55 #include "MagickCore/delegate.h"
56 #include "MagickCore/display.h"
57 #include "MagickCore/display-private.h"
58 #include "MagickCore/distort.h"
59 #include "MagickCore/draw.h"
60 #include "MagickCore/effect.h"
61 #include "MagickCore/enhance.h"
62 #include "MagickCore/exception.h"
63 #include "MagickCore/exception-private.h"
64 #include "MagickCore/fx.h"
65 #include "MagickCore/geometry.h"
66 #include "MagickCore/image.h"
67 #include "MagickCore/image-private.h"
68 #include "MagickCore/list.h"
69 #include "MagickCore/log.h"
70 #include "MagickCore/magick.h"
71 #include "MagickCore/memory_.h"
72 #include "MagickCore/monitor.h"
73 #include "MagickCore/monitor-private.h"
74 #include "MagickCore/montage.h"
75 #include "MagickCore/option.h"
76 #include "MagickCore/paint.h"
77 #include "MagickCore/pixel.h"
78 #include "MagickCore/pixel-accessor.h"
79 #include "MagickCore/PreRvIcccm.h"
80 #include "MagickCore/property.h"
81 #include "MagickCore/quantum.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/resize.h"
84 #include "MagickCore/resource_.h"
85 #include "MagickCore/shear.h"
86 #include "MagickCore/segment.h"
87 #include "MagickCore/statistic.h"
88 #include "MagickCore/string_.h"
89 #include "MagickCore/string-private.h"
90 #include "MagickCore/transform.h"
91 #include "MagickCore/threshold.h"
92 #include "MagickCore/utility.h"
93 #include "MagickCore/utility-private.h"
94 #include "MagickCore/version.h"
95 #include "MagickCore/widget.h"
96 #include "MagickCore/widget-private.h"
97 #include "MagickCore/xwindow.h"
98 #include "MagickCore/xwindow-private.h"
99 \f
100 #if defined(MAGICKCORE_X11_DELEGATE)
101 /*
102   Define declarations.
103 */
104 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
105 \f
106 /*
107   Constant declarations.
108 */
109 static const unsigned char
110   HighlightBitmap[8] =
111   {
112     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
113   },
114   OpaqueBitmap[8] =
115   {
116     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
117   },
118   ShadowBitmap[8] =
119   {
120     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
121   };
122
123 static const char
124   *PageSizes[] =
125   {
126     "Letter",
127     "Tabloid",
128     "Ledger",
129     "Legal",
130     "Statement",
131     "Executive",
132     "A3",
133     "A4",
134     "A5",
135     "B4",
136     "B5",
137     "Folio",
138     "Quarto",
139     "10x14",
140     (char *) NULL
141   };
142 \f
143 /*
144   Help widget declarations.
145 */
146 static const char
147   *ImageAnnotateHelp[] =
148   {
149     "In annotate mode, the Command widget has these options:",
150     "",
151     "    Font Name",
152     "      fixed",
153     "      variable",
154     "      5x8",
155     "      6x10",
156     "      7x13bold",
157     "      8x13bold",
158     "      9x15bold",
159     "      10x20",
160     "      12x24",
161     "      Browser...",
162     "    Font Color",
163     "      black",
164     "      blue",
165     "      cyan",
166     "      green",
167     "      gray",
168     "      red",
169     "      magenta",
170     "      yellow",
171     "      white",
172     "      transparent",
173     "      Browser...",
174     "    Font Color",
175     "      black",
176     "      blue",
177     "      cyan",
178     "      green",
179     "      gray",
180     "      red",
181     "      magenta",
182     "      yellow",
183     "      white",
184     "      transparent",
185     "      Browser...",
186     "    Rotate Text",
187     "      -90",
188     "      -45",
189     "      -30",
190     "      0",
191     "      30",
192     "      45",
193     "      90",
194     "      180",
195     "      Dialog...",
196     "    Help",
197     "    Dismiss",
198     "",
199     "Choose a font name from the Font Name sub-menu.  Additional",
200     "font names can be specified with the font browser.  You can",
201     "change the menu names by setting the X resources font1",
202     "through font9.",
203     "",
204     "Choose a font color from the Font Color sub-menu.",
205     "Additional font colors can be specified with the color",
206     "browser.  You can change the menu colors by setting the X",
207     "resources pen1 through pen9.",
208     "",
209     "If you select the color browser and press Grab, you can",
210     "choose the font color by moving the pointer to the desired",
211     "color on the screen and press any button.",
212     "",
213     "If you choose to rotate the text, choose Rotate Text from the",
214     "menu and select an angle.  Typically you will only want to",
215     "rotate one line of text at a time.  Depending on the angle you",
216     "choose, subsequent lines may end up overwriting each other.",
217     "",
218     "Choosing a font and its color is optional.  The default font",
219     "is fixed and the default color is black.  However, you must",
220     "choose a location to begin entering text and press button 1.",
221     "An underscore character will appear at the location of the",
222     "pointer.  The cursor changes to a pencil to indicate you are",
223     "in text mode.  To exit immediately, press Dismiss.",
224     "",
225     "In text mode, any key presses will display the character at",
226     "the location of the underscore and advance the underscore",
227     "cursor.  Enter your text and once completed press Apply to",
228     "finish your image annotation.  To correct errors press BACK",
229     "SPACE.  To delete an entire line of text, press DELETE.  Any",
230     "text that exceeds the boundaries of the image window is",
231     "automagically continued onto the next line.",
232     "",
233     "The actual color you request for the font is saved in the",
234     "image.  However, the color that appears in your image window",
235     "may be different.  For example, on a monochrome screen the",
236     "text will appear black or white even if you choose the color",
237     "red as the font color.  However, the image saved to a file",
238     "with -write is written with red lettering.  To assure the",
239     "correct color text in the final image, any PseudoClass image",
240     "is promoted to DirectClass (see miff(5)).  To force a",
241     "PseudoClass image to remain PseudoClass, use -colors.",
242     (char *) NULL,
243   },
244   *ImageChopHelp[] =
245   {
246     "In chop mode, the Command widget has these options:",
247     "",
248     "    Direction",
249     "      horizontal",
250     "      vertical",
251     "    Help",
252     "    Dismiss",
253     "",
254     "If the you choose the horizontal direction (this the",
255     "default), the area of the image between the two horizontal",
256     "endpoints of the chop line is removed.  Otherwise, the area",
257     "of the image between the two vertical endpoints of the chop",
258     "line is removed.",
259     "",
260     "Select a location within the image window to begin your chop,",
261     "press and hold any button.  Next, move the pointer to",
262     "another location in the image.  As you move a line will",
263     "connect the initial location and the pointer.  When you",
264     "release the button, the area within the image to chop is",
265     "determined by which direction you choose from the Command",
266     "widget.",
267     "",
268     "To cancel the image chopping, move the pointer back to the",
269     "starting point of the line and release the button.",
270     (char *) NULL,
271   },
272   *ImageColorEditHelp[] =
273   {
274     "In color edit mode, the Command widget has these options:",
275     "",
276     "    Method",
277     "      point",
278     "      replace",
279     "      floodfill",
280     "      filltoborder",
281     "      reset",
282     "    Pixel Color",
283     "      black",
284     "      blue",
285     "      cyan",
286     "      green",
287     "      gray",
288     "      red",
289     "      magenta",
290     "      yellow",
291     "      white",
292     "      Browser...",
293     "    Border Color",
294     "      black",
295     "      blue",
296     "      cyan",
297     "      green",
298     "      gray",
299     "      red",
300     "      magenta",
301     "      yellow",
302     "      white",
303     "      Browser...",
304     "    Fuzz",
305     "      0%",
306     "      2%",
307     "      5%",
308     "      10%",
309     "      15%",
310     "      Dialog...",
311     "    Undo",
312     "    Help",
313     "    Dismiss",
314     "",
315     "Choose a color editing method from the Method sub-menu",
316     "of the Command widget.  The point method recolors any pixel",
317     "selected with the pointer until the button is released.  The",
318     "replace method recolors any pixel that matches the color of",
319     "the pixel you select with a button press.  Floodfill recolors",
320     "any pixel that matches the color of the pixel you select with",
321     "a button press and is a neighbor.  Whereas filltoborder recolors",
322     "any neighbor pixel that is not the border color.  Finally reset",
323     "changes the entire image to the designated color.",
324     "",
325     "Next, choose a pixel color from the Pixel Color sub-menu.",
326     "Additional pixel colors can be specified with the color",
327     "browser.  You can change the menu colors by setting the X",
328     "resources pen1 through pen9.",
329     "",
330     "Now press button 1 to select a pixel within the image window",
331     "to change its color.  Additional pixels may be recolored as",
332     "prescribed by the method you choose.",
333     "",
334     "If the Magnify widget is mapped, it can be helpful in positioning",
335     "your pointer within the image (refer to button 2).",
336     "",
337     "The actual color you request for the pixels is saved in the",
338     "image.  However, the color that appears in your image window",
339     "may be different.  For example, on a monochrome screen the",
340     "pixel will appear black or white even if you choose the",
341     "color red as the pixel color.  However, the image saved to a",
342     "file with -write is written with red pixels.  To assure the",
343     "correct color text in the final image, any PseudoClass image",
344     "is promoted to DirectClass (see miff(5)).  To force a",
345     "PseudoClass image to remain PseudoClass, use -colors.",
346     (char *) NULL,
347   },
348   *ImageCompositeHelp[] =
349   {
350     "First a widget window is displayed requesting you to enter an",
351     "image name. Press Composite, Grab or type a file name.",
352     "Press Cancel if you choose not to create a composite image.",
353     "When you choose Grab, move the pointer to the desired window",
354     "and press any button.",
355     "",
356     "If the Composite image does not have any matte information,",
357     "you are informed and the file browser is displayed again.",
358     "Enter the name of a mask image.  The image is typically",
359     "grayscale and the same size as the composite image.  If the",
360     "image is not grayscale, it is converted to grayscale and the",
361     "resulting intensities are used as matte information.",
362     "",
363     "A small window appears showing the location of the cursor in",
364     "the image window. You are now in composite mode.  To exit",
365     "immediately, press Dismiss.  In composite mode, the Command",
366     "widget has these options:",
367     "",
368     "    Operators",
369     "      Over",
370     "      In",
371     "      Out",
372     "      Atop",
373     "      Xor",
374     "      Plus",
375     "      Minus",
376     "      Add",
377     "      Subtract",
378     "      Difference",
379     "      Multiply",
380     "      Bumpmap",
381     "      Copy",
382     "      CopyRed",
383     "      CopyGreen",
384     "      CopyBlue",
385     "      CopyOpacity",
386     "      Clear",
387     "    Dissolve",
388     "    Displace",
389     "    Help",
390     "    Dismiss",
391     "",
392     "Choose a composite operation from the Operators sub-menu of",
393     "the Command widget.  How each operator behaves is described",
394     "below.  Image window is the image currently displayed on",
395     "your X server and image is the image obtained with the File",
396     "Browser widget.",
397     "",
398     "Over     The result is the union of the two image shapes,",
399     "         with image obscuring image window in the region of",
400     "         overlap.",
401     "",
402     "In       The result is simply image cut by the shape of",
403     "         image window.  None of the image data of image",
404     "         window is in the result.",
405     "",
406     "Out      The resulting image is image with the shape of",
407     "         image window cut out.",
408     "",
409     "Atop     The result is the same shape as image image window,",
410     "         with image obscuring image window where the image",
411     "         shapes overlap.  Note this differs from over",
412     "         because the portion of image outside image window's",
413     "         shape does not appear in the result.",
414     "",
415     "Xor      The result is the image data from both image and",
416     "         image window that is outside the overlap region.",
417     "         The overlap region is blank.",
418     "",
419     "Plus     The result is just the sum of the image data.",
420     "         Output values are cropped to QuantumRange (no overflow).",
421     "",
422     "Minus    The result of image - image window, with underflow",
423     "         cropped to zero.",
424     "",
425     "Add      The result of image + image window, with overflow",
426     "         wrapping around (mod 256).",
427     "",
428     "Subtract The result of image - image window, with underflow",
429     "         wrapping around (mod 256).  The add and subtract",
430     "         operators can be used to perform reversible",
431     "         transformations.",
432     "",
433     "Difference",
434     "         The result of abs(image - image window).  This",
435     "         useful for comparing two very similar images.",
436     "",
437     "Multiply",
438     "         The result of image * image window.  This",
439     "         useful for the creation of drop-shadows.",
440     "",
441     "Bumpmap  The result of surface normals from image * image",
442     "         window.",
443     "",
444     "Copy     The resulting image is image window replaced with",
445     "         image.  Here the matte information is ignored.",
446     "",
447     "CopyRed  The red layer of the image window is replace with",
448     "         the red layer of the image.  The other layers are",
449     "         untouched.",
450     "",
451     "CopyGreen",
452     "         The green layer of the image window is replace with",
453     "         the green layer of the image.  The other layers are",
454     "         untouched.",
455     "",
456     "CopyBlue The blue layer of the image window is replace with",
457     "         the blue layer of the image.  The other layers are",
458     "         untouched.",
459     "",
460     "CopyOpacity",
461     "         The matte layer of the image window is replace with",
462     "         the matte layer of the image.  The other layers are",
463     "         untouched.",
464     "",
465     "The image compositor requires a matte, or alpha channel in",
466     "the image for some operations.  This extra channel usually",
467     "defines a mask which represents a sort of a cookie-cutter",
468     "for the image.  This the case when matte is opaque (full",
469     "coverage) for pixels inside the shape, zero outside, and",
470     "between 0 and QuantumRange on the boundary.  If image does not",
471     "have a matte channel, it is initialized with 0 for any pixel",
472     "matching in color to pixel location (0,0), otherwise QuantumRange.",
473     "",
474     "If you choose Dissolve, the composite operator becomes Over.  The",
475     "image matte channel percent transparency is initialized to factor.",
476     "The image window is initialized to (100-factor). Where factor is the",
477     "value you specify in the Dialog widget.",
478     "",
479     "Displace shifts the image pixels as defined by a displacement",
480     "map.  With this option, image is used as a displacement map.",
481     "Black, within the displacement map, is a maximum positive",
482     "displacement.  White is a maximum negative displacement and",
483     "middle gray is neutral.  The displacement is scaled to determine",
484     "the pixel shift.  By default, the displacement applies in both the",
485     "horizontal and vertical directions.  However, if you specify a mask,",
486     "image is the horizontal X displacement and mask the vertical Y",
487     "displacement.",
488     "",
489     "Note that matte information for image window is not retained",
490     "for colormapped X server visuals (e.g. StaticColor,",
491     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
492     "behavior may require a TrueColor or DirectColor visual or a",
493     "Standard Colormap.",
494     "",
495     "Choosing a composite operator is optional.  The default",
496     "operator is replace.  However, you must choose a location to",
497     "composite your image and press button 1.  Press and hold the",
498     "button before releasing and an outline of the image will",
499     "appear to help you identify your location.",
500     "",
501     "The actual colors of the composite image is saved.  However,",
502     "the color that appears in image window may be different.",
503     "For example, on a monochrome screen image window will appear",
504     "black or white even though your composited image may have",
505     "many colors.  If the image is saved to a file it is written",
506     "with the correct colors.  To assure the correct colors are",
507     "saved in the final image, any PseudoClass image is promoted",
508     "to DirectClass (see miff(5)).  To force a PseudoClass image",
509     "to remain PseudoClass, use -colors.",
510     (char *) NULL,
511   },
512   *ImageCutHelp[] =
513   {
514     "In cut mode, the Command widget has these options:",
515     "",
516     "    Help",
517     "    Dismiss",
518     "",
519     "To define a cut region, press button 1 and drag.  The",
520     "cut region is defined by a highlighted rectangle that",
521     "expands or contracts as it follows the pointer.  Once you",
522     "are satisfied with the cut region, release the button.",
523     "You are now in rectify mode.  In rectify mode, the Command",
524     "widget has these options:",
525     "",
526     "    Cut",
527     "    Help",
528     "    Dismiss",
529     "",
530     "You can make adjustments by moving the pointer to one of the",
531     "cut rectangle corners, pressing a button, and dragging.",
532     "Finally, press Cut to commit your copy region.  To",
533     "exit without cutting the image, press Dismiss.",
534     (char *) NULL,
535   },
536   *ImageCopyHelp[] =
537   {
538     "In copy mode, the Command widget has these options:",
539     "",
540     "    Help",
541     "    Dismiss",
542     "",
543     "To define a copy region, press button 1 and drag.  The",
544     "copy region is defined by a highlighted rectangle that",
545     "expands or contracts as it follows the pointer.  Once you",
546     "are satisfied with the copy region, release the button.",
547     "You are now in rectify mode.  In rectify mode, the Command",
548     "widget has these options:",
549     "",
550     "    Copy",
551     "    Help",
552     "    Dismiss",
553     "",
554     "You can make adjustments by moving the pointer to one of the",
555     "copy rectangle corners, pressing a button, and dragging.",
556     "Finally, press Copy to commit your copy region.  To",
557     "exit without copying the image, press Dismiss.",
558     (char *) NULL,
559   },
560   *ImageCropHelp[] =
561   {
562     "In crop mode, the Command widget has these options:",
563     "",
564     "    Help",
565     "    Dismiss",
566     "",
567     "To define a cropping region, press button 1 and drag.  The",
568     "cropping region is defined by a highlighted rectangle that",
569     "expands or contracts as it follows the pointer.  Once you",
570     "are satisfied with the cropping region, release the button.",
571     "You are now in rectify mode.  In rectify mode, the Command",
572     "widget has these options:",
573     "",
574     "    Crop",
575     "    Help",
576     "    Dismiss",
577     "",
578     "You can make adjustments by moving the pointer to one of the",
579     "cropping rectangle corners, pressing a button, and dragging.",
580     "Finally, press Crop to commit your cropping region.  To",
581     "exit without cropping the image, press Dismiss.",
582     (char *) NULL,
583   },
584   *ImageDrawHelp[] =
585   {
586     "The cursor changes to a crosshair to indicate you are in",
587     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
588     "the Command widget has these options:",
589     "",
590     "    Element",
591     "      point",
592     "      line",
593     "      rectangle",
594     "      fill rectangle",
595     "      circle",
596     "      fill circle",
597     "      ellipse",
598     "      fill ellipse",
599     "      polygon",
600     "      fill polygon",
601     "    Color",
602     "      black",
603     "      blue",
604     "      cyan",
605     "      green",
606     "      gray",
607     "      red",
608     "      magenta",
609     "      yellow",
610     "      white",
611     "      transparent",
612     "      Browser...",
613     "    Stipple",
614     "      Brick",
615     "      Diagonal",
616     "      Scales",
617     "      Vertical",
618     "      Wavy",
619     "      Translucent",
620     "      Opaque",
621     "      Open...",
622     "    Width",
623     "      1",
624     "      2",
625     "      4",
626     "      8",
627     "      16",
628     "      Dialog...",
629     "    Undo",
630     "    Help",
631     "    Dismiss",
632     "",
633     "Choose a drawing primitive from the Element sub-menu.",
634     "",
635     "Choose a color from the Color sub-menu.  Additional",
636     "colors can be specified with the color browser.",
637     "",
638     "If you choose the color browser and press Grab, you can",
639     "select the color by moving the pointer to the desired",
640     "color on the screen and press any button.  The transparent",
641     "color updates the image matte channel and is useful for",
642     "image compositing.",
643     "",
644     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
645     "Additional stipples can be specified with the file browser.",
646     "Stipples obtained from the file browser must be on disk in the",
647     "X11 bitmap format.",
648     "",
649     "Choose a width, if appropriate, from the Width sub-menu.  To",
650     "choose a specific width select the Dialog widget.",
651     "",
652     "Choose a point in the Image window and press button 1 and",
653     "hold.  Next, move the pointer to another location in the",
654     "image.  As you move, a line connects the initial location and",
655     "the pointer.  When you release the button, the image is",
656     "updated with the primitive you just drew.  For polygons, the",
657     "image is updated when you press and release the button without",
658     "moving the pointer.",
659     "",
660     "To cancel image drawing, move the pointer back to the",
661     "starting point of the line and release the button.",
662     (char *) NULL,
663   },
664   *DisplayHelp[] =
665   {
666     "BUTTONS",
667     "  The effects of each button press is described below.  Three",
668     "  buttons are required.  If you have a two button mouse,",
669     "  button 1 and 3 are returned.  Press ALT and button 3 to",
670     "  simulate button 2.",
671     "",
672     "  1    Press this button to map or unmap the Command widget.",
673     "",
674     "  2    Press and drag to define a region of the image to",
675     "       magnify.",
676     "",
677     "  3    Press and drag to choose from a select set of commands.",
678     "       This button behaves differently if the image being",
679     "       displayed is a visual image directory.  Here, choose a",
680     "       particular tile of the directory and press this button and",
681     "       drag to select a command from a pop-up menu.  Choose from",
682     "       these menu items:",
683     "",
684     "           Open",
685     "           Next",
686     "           Former",
687     "           Delete",
688     "           Update",
689     "",
690     "       If you choose Open, the image represented by the tile is",
691     "       displayed.  To return to the visual image directory, choose",
692     "       Next from the Command widget.  Next and Former moves to the",
693     "       next or former image respectively.  Choose Delete to delete",
694     "       a particular image tile.  Finally, choose Update to",
695     "       synchronize all the image tiles with their respective",
696     "       images.",
697     "",
698     "COMMAND WIDGET",
699     "  The Command widget lists a number of sub-menus and commands.",
700     "  They are",
701     "",
702     "      File",
703     "        Open...",
704     "        Next",
705     "        Former",
706     "        Select...",
707     "        Save...",
708     "        Print...",
709     "        Delete...",
710     "        New...",
711     "        Visual Directory...",
712     "        Quit",
713     "      Edit",
714     "        Undo",
715     "        Redo",
716     "        Cut",
717     "        Copy",
718     "        Paste",
719     "      View",
720     "        Half Size",
721     "        Original Size",
722     "        Double Size",
723     "        Resize...",
724     "        Apply",
725     "        Refresh",
726     "        Restore",
727     "      Transform",
728     "        Crop",
729     "        Chop",
730     "        Flop",
731     "        Flip",
732     "        Rotate Right",
733     "        Rotate Left",
734     "        Rotate...",
735     "        Shear...",
736     "        Roll...",
737     "        Trim Edges",
738     "      Enhance",
739     "        Brightness...",
740     "        Saturation...",
741     "        Hue...",
742     "        Gamma...",
743     "        Sharpen...",
744     "        Dull",
745     "        Contrast Stretch...",
746     "        Sigmoidal Contrast...",
747     "        Normalize",
748     "        Equalize",
749     "        Negate",
750     "        Grayscale",
751     "        Map...",
752     "        Quantize...",
753     "      Effects",
754     "        Despeckle",
755     "        Emboss",
756     "        Reduce Noise",
757     "        Add Noise",
758     "        Sharpen...",
759     "        Blur...",
760     "        Threshold...",
761     "        Edge Detect...",
762     "        Spread...",
763     "        Shade...",
764     "        Painting...",
765     "        Segment...",
766     "      F/X",
767     "        Solarize...",
768     "        Sepia Tone...",
769     "        Swirl...",
770     "        Implode...",
771     "        Vignette...",
772     "        Wave...",
773     "        Oil Painting...",
774     "        Charcoal Drawing...",
775     "      Image Edit",
776     "        Annotate...",
777     "        Draw...",
778     "        Color...",
779     "        Matte...",
780     "        Composite...",
781     "        Add Border...",
782     "        Add Frame...",
783     "        Comment...",
784     "        Launch...",
785     "        Region of Interest...",
786     "      Miscellany",
787     "        Image Info",
788     "        Zoom Image",
789     "        Show Preview...",
790     "        Show Histogram",
791     "        Show Matte",
792     "        Background...",
793     "        Slide Show",
794     "        Preferences...",
795     "      Help",
796     "        Overview",
797     "        Browse Documentation",
798     "        About Display",
799     "",
800     "  Menu items with a indented triangle have a sub-menu.  They",
801     "  are represented above as the indented items.  To access a",
802     "  sub-menu item, move the pointer to the appropriate menu and",
803     "  press a button and drag.  When you find the desired sub-menu",
804     "  item, release the button and the command is executed.  Move",
805     "  the pointer away from the sub-menu if you decide not to",
806     "  execute a particular command.",
807     "",
808     "KEYBOARD ACCELERATORS",
809     "  Accelerators are one or two key presses that effect a",
810     "  particular command.  The keyboard accelerators that",
811     "  display(1) understands is:",
812     "",
813     "  Ctl+O     Press to open an image from a file.",
814     "",
815     "  space     Press to display the next image.",
816     "",
817     "            If the image is a multi-paged document such as a Postscript",
818     "            document, you can skip ahead several pages by preceding",
819     "            this command with a number.  For example to display the",
820     "            third page beyond the current page, press 3<space>.",
821     "",
822     "  backspace Press to display the former image.",
823     "",
824     "            If the image is a multi-paged document such as a Postscript",
825     "            document, you can skip behind several pages by preceding",
826     "            this command with a number.  For example to display the",
827     "            third page preceding the current page, press 3<backspace>.",
828     "",
829     "  Ctl+S     Press to write the image to a file.",
830     "",
831     "  Ctl+P     Press to print the image to a Postscript printer.",
832     "",
833     "  Ctl+D     Press to delete an image file.",
834     "",
835     "  Ctl+N     Press to create a blank canvas.",
836     "",
837     "  Ctl+Q     Press to discard all images and exit program.",
838     "",
839     "  Ctl+Z     Press to undo last image transformation.",
840     "",
841     "  Ctl+R     Press to redo last image transformation.",
842     "",
843     "  Ctl+X     Press to cut a region of the image.",
844     "",
845     "  Ctl+C     Press to copy a region of the image.",
846     "",
847     "  Ctl+V     Press to paste a region to the image.",
848     "",
849     "  <         Press to half the image size.",
850     "",
851     "  -         Press to return to the original image size.",
852     "",
853     "  >         Press to double the image size.",
854     "",
855     "  %         Press to resize the image to a width and height you",
856     "            specify.",
857     "",
858     "Cmd-A       Press to make any image transformations permanent."
859     "",
860     "            By default, any image size transformations are applied",
861     "            to the original image to create the image displayed on",
862     "            the X server.  However, the transformations are not",
863     "            permanent (i.e. the original image does not change",
864     "            size only the X image does).  For example, if you",
865     "            press > the X image will appear to double in size,",
866     "            but the original image will in fact remain the same size.",
867     "            To force the original image to double in size, press >",
868     "            followed by Cmd-A.",
869     "",
870     "  @         Press to refresh the image window.",
871     "",
872     "  C         Press to cut out a rectangular region of the image.",
873     "",
874     "  [         Press to chop the image.",
875     "",
876     "  H         Press to flop image in the horizontal direction.",
877     "",
878     "  V         Press to flip image in the vertical direction.",
879     "",
880     "  /         Press to rotate the image 90 degrees clockwise.",
881     "",
882     " \\         Press to rotate the image 90 degrees counter-clockwise.",
883     "",
884     "  *         Press to rotate the image the number of degrees you",
885     "            specify.",
886     "",
887     "  S         Press to shear the image the number of degrees you",
888     "            specify.",
889     "",
890     "  R         Press to roll the image.",
891     "",
892     "  T         Press to trim the image edges.",
893     "",
894     "  Shft-H    Press to vary the image hue.",
895     "",
896     "  Shft-S    Press to vary the color saturation.",
897     "",
898     "  Shft-L    Press to vary the color brightness.",
899     "",
900     "  Shft-G    Press to gamma correct the image.",
901     "",
902     "  Shft-C    Press to sharpen the image contrast.",
903     "",
904     "  Shft-Z    Press to dull the image contrast.",
905     "",
906     "  =         Press to perform histogram equalization on the image.",
907     "",
908     "  Shft-N    Press to perform histogram normalization on the image.",
909     "",
910     "  Shft-~    Press to negate the colors of the image.",
911     "",
912     "  .         Press to convert the image colors to gray.",
913     "",
914     "  Shft-#    Press to set the maximum number of unique colors in the",
915     "            image.",
916     "",
917     "  F2        Press to reduce the speckles in an image.",
918     "",
919     "  F3        Press to eliminate peak noise from an image.",
920     "",
921     "  F4        Press to add noise to an image.",
922     "",
923     "  F5        Press to sharpen an image.",
924     "",
925     "  F6        Press to delete an image file.",
926     "",
927     "  F7        Press to threshold the image.",
928     "",
929     "  F8        Press to detect edges within an image.",
930     "",
931     "  F9        Press to emboss an image.",
932     "",
933     "  F10       Press to displace pixels by a random amount.",
934     "",
935     "  F11       Press to negate all pixels above the threshold level.",
936     "",
937     "  F12       Press to shade the image using a distant light source.",
938     "",
939     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
940     "",
941     "  F14       Press to segment the image by color.",
942     "",
943     "  Meta-S    Press to swirl image pixels about the center.",
944     "",
945     "  Meta-I    Press to implode image pixels about the center.",
946     "",
947     "  Meta-W    Press to alter an image along a sine wave.",
948     "",
949     "  Meta-P    Press to simulate an oil painting.",
950     "",
951     "  Meta-C    Press to simulate a charcoal drawing.",
952     "",
953     "  Alt-A     Press to annotate the image with text.",
954     "",
955     "  Alt-D     Press to draw on an image.",
956     "",
957     "  Alt-P     Press to edit an image pixel color.",
958     "",
959     "  Alt-M     Press to edit the image matte information.",
960     "",
961     "  Alt-V     Press to composite the image with another.",
962     "",
963     "  Alt-B     Press to add a border to the image.",
964     "",
965     "  Alt-F     Press to add an ornamental border to the image.",
966     "",
967     "  Alt-Shft-!",
968     "            Press to add an image comment.",
969     "",
970     "  Ctl-A     Press to apply image processing techniques to a region",
971     "            of interest.",
972     "",
973     "  Shft-?    Press to display information about the image.",
974     "",
975     "  Shft-+    Press to map the zoom image window.",
976     "",
977     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
978     "",
979     "  F1        Press to display helpful information about display(1).",
980     "",
981     "  Find      Press to browse documentation about ImageMagick.",
982     "",
983     "  1-9       Press to change the level of magnification.",
984     "",
985     "  Use the arrow keys to move the image one pixel up, down,",
986     "  left, or right within the magnify window.  Be sure to first",
987     "  map the magnify window by pressing button 2.",
988     "",
989     "  Press ALT and one of the arrow keys to trim off one pixel",
990     "  from any side of the image.",
991     (char *) NULL,
992   },
993   *ImageMatteEditHelp[] =
994   {
995     "Matte information within an image is useful for some",
996     "operations such as image compositing (See IMAGE",
997     "COMPOSITING).  This extra channel usually defines a mask",
998     "which represents a sort of a cookie-cutter for the image.",
999     "This the case when matte is opaque (full coverage) for",
1000     "pixels inside the shape, zero outside, and between 0 and",
1001     "QuantumRange on the boundary.",
1002     "",
1003     "A small window appears showing the location of the cursor in",
1004     "the image window. You are now in matte edit mode.  To exit",
1005     "immediately, press Dismiss.  In matte edit mode, the Command",
1006     "widget has these options:",
1007     "",
1008     "    Method",
1009     "      point",
1010     "      replace",
1011     "      floodfill",
1012     "      filltoborder",
1013     "      reset",
1014     "    Border Color",
1015     "      black",
1016     "      blue",
1017     "      cyan",
1018     "      green",
1019     "      gray",
1020     "      red",
1021     "      magenta",
1022     "      yellow",
1023     "      white",
1024     "      Browser...",
1025     "    Fuzz",
1026     "      0%",
1027     "      2%",
1028     "      5%",
1029     "      10%",
1030     "      15%",
1031     "      Dialog...",
1032     "    Matte",
1033     "      Opaque",
1034     "      Transparent",
1035     "      Dialog...",
1036     "    Undo",
1037     "    Help",
1038     "    Dismiss",
1039     "",
1040     "Choose a matte editing method from the Method sub-menu of",
1041     "the Command widget.  The point method changes the matte value",
1042     "of any pixel selected with the pointer until the button is",
1043     "is released.  The replace method changes the matte value of",
1044     "any pixel that matches the color of the pixel you select with",
1045     "a button press.  Floodfill changes the matte value of any pixel",
1046     "that matches the color of the pixel you select with a button",
1047     "press and is a neighbor.  Whereas filltoborder changes the matte",
1048     "value any neighbor pixel that is not the border color.  Finally",
1049     "reset changes the entire image to the designated matte value.",
1050     "",
1051     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1052     "select the Dialog entry.  Here a dialog appears requesting a matte",
1053     "value.  The value you select is assigned as the opacity value of the",
1054     "selected pixel or pixels.",
1055     "",
1056     "Now, press any button to select a pixel within the image",
1057     "window to change its matte value.",
1058     "",
1059     "If the Magnify widget is mapped, it can be helpful in positioning",
1060     "your pointer within the image (refer to button 2).",
1061     "",
1062     "Matte information is only valid in a DirectClass image.",
1063     "Therefore, any PseudoClass image is promoted to DirectClass",
1064     "(see miff(5)).  Note that matte information for PseudoClass",
1065     "is not retained for colormapped X server visuals (e.g.",
1066     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1067     "immediately save your image to a file (refer to Write).",
1068     "Correct matte editing behavior may require a TrueColor or",
1069     "DirectColor visual or a Standard Colormap.",
1070     (char *) NULL,
1071   },
1072   *ImagePanHelp[] =
1073   {
1074     "When an image exceeds the width or height of the X server",
1075     "screen, display maps a small panning icon.  The rectangle",
1076     "within the panning icon shows the area that is currently",
1077     "displayed in the image window.  To pan about the image,",
1078     "press any button and drag the pointer within the panning",
1079     "icon.  The pan rectangle moves with the pointer and the",
1080     "image window is updated to reflect the location of the",
1081     "rectangle within the panning icon.  When you have selected",
1082     "the area of the image you wish to view, release the button.",
1083     "",
1084     "Use the arrow keys to pan the image one pixel up, down,",
1085     "left, or right within the image window.",
1086     "",
1087     "The panning icon is withdrawn if the image becomes smaller",
1088     "than the dimensions of the X server screen.",
1089     (char *) NULL,
1090   },
1091   *ImagePasteHelp[] =
1092   {
1093     "A small window appears showing the location of the cursor in",
1094     "the image window. You are now in paste mode.  To exit",
1095     "immediately, press Dismiss.  In paste mode, the Command",
1096     "widget has these options:",
1097     "",
1098     "    Operators",
1099     "      over",
1100     "      in",
1101     "      out",
1102     "      atop",
1103     "      xor",
1104     "      plus",
1105     "      minus",
1106     "      add",
1107     "      subtract",
1108     "      difference",
1109     "      replace",
1110     "    Help",
1111     "    Dismiss",
1112     "",
1113     "Choose a composite operation from the Operators sub-menu of",
1114     "the Command widget.  How each operator behaves is described",
1115     "below.  Image window is the image currently displayed on",
1116     "your X server and image is the image obtained with the File",
1117     "Browser widget.",
1118     "",
1119     "Over     The result is the union of the two image shapes,",
1120     "         with image obscuring image window in the region of",
1121     "         overlap.",
1122     "",
1123     "In       The result is simply image cut by the shape of",
1124     "         image window.  None of the image data of image",
1125     "         window is in the result.",
1126     "",
1127     "Out      The resulting image is image with the shape of",
1128     "         image window cut out.",
1129     "",
1130     "Atop     The result is the same shape as image image window,",
1131     "         with image obscuring image window where the image",
1132     "         shapes overlap.  Note this differs from over",
1133     "         because the portion of image outside image window's",
1134     "         shape does not appear in the result.",
1135     "",
1136     "Xor      The result is the image data from both image and",
1137     "         image window that is outside the overlap region.",
1138     "         The overlap region is blank.",
1139     "",
1140     "Plus     The result is just the sum of the image data.",
1141     "         Output values are cropped to QuantumRange (no overflow).",
1142     "         This operation is independent of the matte",
1143     "         channels.",
1144     "",
1145     "Minus    The result of image - image window, with underflow",
1146     "         cropped to zero.",
1147     "",
1148     "Add      The result of image + image window, with overflow",
1149     "         wrapping around (mod 256).",
1150     "",
1151     "Subtract The result of image - image window, with underflow",
1152     "         wrapping around (mod 256).  The add and subtract",
1153     "         operators can be used to perform reversible",
1154     "         transformations.",
1155     "",
1156     "Difference",
1157     "         The result of abs(image - image window).  This",
1158     "         useful for comparing two very similar images.",
1159     "",
1160     "Copy     The resulting image is image window replaced with",
1161     "         image.  Here the matte information is ignored.",
1162     "",
1163     "CopyRed  The red layer of the image window is replace with",
1164     "         the red layer of the image.  The other layers are",
1165     "         untouched.",
1166     "",
1167     "CopyGreen",
1168     "         The green layer of the image window is replace with",
1169     "         the green layer of the image.  The other layers are",
1170     "         untouched.",
1171     "",
1172     "CopyBlue The blue layer of the image window is replace with",
1173     "         the blue layer of the image.  The other layers are",
1174     "         untouched.",
1175     "",
1176     "CopyOpacity",
1177     "         The matte layer of the image window is replace with",
1178     "         the matte layer of the image.  The other layers are",
1179     "         untouched.",
1180     "",
1181     "The image compositor requires a matte, or alpha channel in",
1182     "the image for some operations.  This extra channel usually",
1183     "defines a mask which represents a sort of a cookie-cutter",
1184     "for the image.  This the case when matte is opaque (full",
1185     "coverage) for pixels inside the shape, zero outside, and",
1186     "between 0 and QuantumRange on the boundary.  If image does not",
1187     "have a matte channel, it is initialized with 0 for any pixel",
1188     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1189     "",
1190     "Note that matte information for image window is not retained",
1191     "for colormapped X server visuals (e.g. StaticColor,",
1192     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1193     "behavior may require a TrueColor or DirectColor visual or a",
1194     "Standard Colormap.",
1195     "",
1196     "Choosing a composite operator is optional.  The default",
1197     "operator is replace.  However, you must choose a location to",
1198     "paste your image and press button 1.  Press and hold the",
1199     "button before releasing and an outline of the image will",
1200     "appear to help you identify your location.",
1201     "",
1202     "The actual colors of the pasted image is saved.  However,",
1203     "the color that appears in image window may be different.",
1204     "For example, on a monochrome screen image window will appear",
1205     "black or white even though your pasted image may have",
1206     "many colors.  If the image is saved to a file it is written",
1207     "with the correct colors.  To assure the correct colors are",
1208     "saved in the final image, any PseudoClass image is promoted",
1209     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1210     "to remain PseudoClass, use -colors.",
1211     (char *) NULL,
1212   },
1213   *ImageROIHelp[] =
1214   {
1215     "In region of interest mode, the Command widget has these",
1216     "options:",
1217     "",
1218     "    Help",
1219     "    Dismiss",
1220     "",
1221     "To define a region of interest, press button 1 and drag.",
1222     "The region of interest is defined by a highlighted rectangle",
1223     "that expands or contracts as it follows the pointer.  Once",
1224     "you are satisfied with the region of interest, release the",
1225     "button.  You are now in apply mode.  In apply mode the",
1226     "Command widget has these options:",
1227     "",
1228     "      File",
1229     "        Save...",
1230     "        Print...",
1231     "      Edit",
1232     "        Undo",
1233     "        Redo",
1234     "      Transform",
1235     "        Flop",
1236     "        Flip",
1237     "        Rotate Right",
1238     "        Rotate Left",
1239     "      Enhance",
1240     "        Hue...",
1241     "        Saturation...",
1242     "        Brightness...",
1243     "        Gamma...",
1244     "        Spiff",
1245     "        Dull",
1246     "        Contrast Stretch",
1247     "        Sigmoidal Contrast...",
1248     "        Normalize",
1249     "        Equalize",
1250     "        Negate",
1251     "        Grayscale",
1252     "        Map...",
1253     "        Quantize...",
1254     "      Effects",
1255     "        Despeckle",
1256     "        Emboss",
1257     "        Reduce Noise",
1258     "        Sharpen...",
1259     "        Blur...",
1260     "        Threshold...",
1261     "        Edge Detect...",
1262     "        Spread...",
1263     "        Shade...",
1264     "        Raise...",
1265     "        Segment...",
1266     "      F/X",
1267     "        Solarize...",
1268     "        Sepia Tone...",
1269     "        Swirl...",
1270     "        Implode...",
1271     "        Vignette...",
1272     "        Wave...",
1273     "        Oil Painting...",
1274     "        Charcoal Drawing...",
1275     "      Miscellany",
1276     "        Image Info",
1277     "        Zoom Image",
1278     "        Show Preview...",
1279     "        Show Histogram",
1280     "        Show Matte",
1281     "      Help",
1282     "      Dismiss",
1283     "",
1284     "You can make adjustments to the region of interest by moving",
1285     "the pointer to one of the rectangle corners, pressing a",
1286     "button, and dragging.  Finally, choose an image processing",
1287     "technique from the Command widget.  You can choose more than",
1288     "one image processing technique to apply to an area.",
1289     "Alternatively, you can move the region of interest before",
1290     "applying another image processing technique.  To exit, press",
1291     "Dismiss.",
1292     (char *) NULL,
1293   },
1294   *ImageRotateHelp[] =
1295   {
1296     "In rotate mode, the Command widget has these options:",
1297     "",
1298     "    Pixel Color",
1299     "      black",
1300     "      blue",
1301     "      cyan",
1302     "      green",
1303     "      gray",
1304     "      red",
1305     "      magenta",
1306     "      yellow",
1307     "      white",
1308     "      Browser...",
1309     "    Direction",
1310     "      horizontal",
1311     "      vertical",
1312     "    Help",
1313     "    Dismiss",
1314     "",
1315     "Choose a background color from the Pixel Color sub-menu.",
1316     "Additional background colors can be specified with the color",
1317     "browser.  You can change the menu colors by setting the X",
1318     "resources pen1 through pen9.",
1319     "",
1320     "If you choose the color browser and press Grab, you can",
1321     "select the background color by moving the pointer to the",
1322     "desired color on the screen and press any button.",
1323     "",
1324     "Choose a point in the image window and press this button and",
1325     "hold.  Next, move the pointer to another location in the",
1326     "image.  As you move a line connects the initial location and",
1327     "the pointer.  When you release the button, the degree of",
1328     "image rotation is determined by the slope of the line you",
1329     "just drew.  The slope is relative to the direction you",
1330     "choose from the Direction sub-menu of the Command widget.",
1331     "",
1332     "To cancel the image rotation, move the pointer back to the",
1333     "starting point of the line and release the button.",
1334     (char *) NULL,
1335   };
1336 \f
1337 /*
1338   Enumeration declarations.
1339 */
1340 typedef enum
1341 {
1342   CopyMode,
1343   CropMode,
1344   CutMode
1345 } ClipboardMode;
1346
1347 typedef enum
1348 {
1349   OpenCommand,
1350   NextCommand,
1351   FormerCommand,
1352   SelectCommand,
1353   SaveCommand,
1354   PrintCommand,
1355   DeleteCommand,
1356   NewCommand,
1357   VisualDirectoryCommand,
1358   QuitCommand,
1359   UndoCommand,
1360   RedoCommand,
1361   CutCommand,
1362   CopyCommand,
1363   PasteCommand,
1364   HalfSizeCommand,
1365   OriginalSizeCommand,
1366   DoubleSizeCommand,
1367   ResizeCommand,
1368   ApplyCommand,
1369   RefreshCommand,
1370   RestoreCommand,
1371   CropCommand,
1372   ChopCommand,
1373   FlopCommand,
1374   FlipCommand,
1375   RotateRightCommand,
1376   RotateLeftCommand,
1377   RotateCommand,
1378   ShearCommand,
1379   RollCommand,
1380   TrimCommand,
1381   HueCommand,
1382   SaturationCommand,
1383   BrightnessCommand,
1384   GammaCommand,
1385   SpiffCommand,
1386   DullCommand,
1387   ContrastStretchCommand,
1388   SigmoidalContrastCommand,
1389   NormalizeCommand,
1390   EqualizeCommand,
1391   NegateCommand,
1392   GrayscaleCommand,
1393   MapCommand,
1394   QuantizeCommand,
1395   DespeckleCommand,
1396   EmbossCommand,
1397   ReduceNoiseCommand,
1398   AddNoiseCommand,
1399   SharpenCommand,
1400   BlurCommand,
1401   ThresholdCommand,
1402   EdgeDetectCommand,
1403   SpreadCommand,
1404   ShadeCommand,
1405   RaiseCommand,
1406   SegmentCommand,
1407   SolarizeCommand,
1408   SepiaToneCommand,
1409   SwirlCommand,
1410   ImplodeCommand,
1411   VignetteCommand,
1412   WaveCommand,
1413   OilPaintCommand,
1414   CharcoalDrawCommand,
1415   AnnotateCommand,
1416   DrawCommand,
1417   ColorCommand,
1418   MatteCommand,
1419   CompositeCommand,
1420   AddBorderCommand,
1421   AddFrameCommand,
1422   CommentCommand,
1423   LaunchCommand,
1424   RegionofInterestCommand,
1425   ROIHelpCommand,
1426   ROIDismissCommand,
1427   InfoCommand,
1428   ZoomCommand,
1429   ShowPreviewCommand,
1430   ShowHistogramCommand,
1431   ShowMatteCommand,
1432   BackgroundCommand,
1433   SlideShowCommand,
1434   PreferencesCommand,
1435   HelpCommand,
1436   BrowseDocumentationCommand,
1437   VersionCommand,
1438   SaveToUndoBufferCommand,
1439   FreeBuffersCommand,
1440   NullCommand
1441 } CommandType;
1442
1443 typedef enum
1444 {
1445   AnnotateNameCommand,
1446   AnnotateFontColorCommand,
1447   AnnotateBackgroundColorCommand,
1448   AnnotateRotateCommand,
1449   AnnotateHelpCommand,
1450   AnnotateDismissCommand,
1451   TextHelpCommand,
1452   TextApplyCommand,
1453   ChopDirectionCommand,
1454   ChopHelpCommand,
1455   ChopDismissCommand,
1456   HorizontalChopCommand,
1457   VerticalChopCommand,
1458   ColorEditMethodCommand,
1459   ColorEditColorCommand,
1460   ColorEditBorderCommand,
1461   ColorEditFuzzCommand,
1462   ColorEditUndoCommand,
1463   ColorEditHelpCommand,
1464   ColorEditDismissCommand,
1465   CompositeOperatorsCommand,
1466   CompositeDissolveCommand,
1467   CompositeDisplaceCommand,
1468   CompositeHelpCommand,
1469   CompositeDismissCommand,
1470   CropHelpCommand,
1471   CropDismissCommand,
1472   RectifyCopyCommand,
1473   RectifyHelpCommand,
1474   RectifyDismissCommand,
1475   DrawElementCommand,
1476   DrawColorCommand,
1477   DrawStippleCommand,
1478   DrawWidthCommand,
1479   DrawUndoCommand,
1480   DrawHelpCommand,
1481   DrawDismissCommand,
1482   MatteEditMethod,
1483   MatteEditBorderCommand,
1484   MatteEditFuzzCommand,
1485   MatteEditValueCommand,
1486   MatteEditUndoCommand,
1487   MatteEditHelpCommand,
1488   MatteEditDismissCommand,
1489   PasteOperatorsCommand,
1490   PasteHelpCommand,
1491   PasteDismissCommand,
1492   RotateColorCommand,
1493   RotateDirectionCommand,
1494   RotateCropCommand,
1495   RotateSharpenCommand,
1496   RotateHelpCommand,
1497   RotateDismissCommand,
1498   HorizontalRotateCommand,
1499   VerticalRotateCommand,
1500   TileLoadCommand,
1501   TileNextCommand,
1502   TileFormerCommand,
1503   TileDeleteCommand,
1504   TileUpdateCommand
1505 } ModeType;
1506 \f
1507 /*
1508   Stipples.
1509 */
1510 #define BricksWidth  20
1511 #define BricksHeight  20
1512 #define DiagonalWidth  16
1513 #define DiagonalHeight  16
1514 #define HighlightWidth  8
1515 #define HighlightHeight  8
1516 #define OpaqueWidth  8
1517 #define OpaqueHeight  8
1518 #define ScalesWidth  16
1519 #define ScalesHeight  16
1520 #define ShadowWidth  8
1521 #define ShadowHeight  8
1522 #define VerticalWidth  16
1523 #define VerticalHeight  16
1524 #define WavyWidth  16
1525 #define WavyHeight  16
1526 \f
1527 /*
1528   Constant declaration.
1529 */
1530 static const int
1531   RoiDelta = 8;
1532
1533 static const unsigned char
1534   BricksBitmap[] =
1535   {
1536     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1537     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1538     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1539     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1540     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1541   },
1542   DiagonalBitmap[] =
1543   {
1544     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1545     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1546     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1547   },
1548   ScalesBitmap[] =
1549   {
1550     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1551     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1552     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1553   },
1554   VerticalBitmap[] =
1555   {
1556     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1557     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1558     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1559   },
1560   WavyBitmap[] =
1561   {
1562     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1563     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1564     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1565   };
1566 \f
1567 /*
1568   Function prototypes.
1569 */
1570 static CommandType
1571   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1572     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1573
1574 static Image
1575   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1576     Image **,ExceptionInfo *),
1577   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1578   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1579     ExceptionInfo *),
1580   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1581     ExceptionInfo *);
1582
1583 static MagickBooleanType
1584   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1585     ExceptionInfo *),
1586   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1587     ExceptionInfo *),
1588   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1589     ExceptionInfo *),
1590   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1591     ExceptionInfo *),
1592   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1593     ExceptionInfo *),
1594   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1595     ExceptionInfo *),
1596   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1597   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1598     ExceptionInfo *),
1599   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1600     ExceptionInfo *),
1601   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1603   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1604     ExceptionInfo *),
1605   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1606   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1607   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1608
1609 static void
1610   XDrawPanRectangle(Display *,XWindows *),
1611   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1612     ExceptionInfo *),
1613   XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1614   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1615   XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1616   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1617     const KeySym,ExceptionInfo *),
1618   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1619   XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1620   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1621 \f
1622 /*
1623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1624 %                                                                             %
1625 %                                                                             %
1626 %                                                                             %
1627 %   D i s p l a y I m a g e s                                                 %
1628 %                                                                             %
1629 %                                                                             %
1630 %                                                                             %
1631 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1632 %
1633 %  DisplayImages() displays an image sequence to any X window screen.  It
1634 %  returns a value other than 0 if successful.  Check the exception member
1635 %  of image to determine the reason for any failure.
1636 %
1637 %  The format of the DisplayImages method is:
1638 %
1639 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1640 %        Image *images,ExceptionInfo *exception)
1641 %
1642 %  A description of each parameter follows:
1643 %
1644 %    o image_info: the image info.
1645 %
1646 %    o image: the image.
1647 %
1648 %    o exception: return any errors or warnings in this structure.
1649 %
1650 */
1651 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1652   Image *images,ExceptionInfo *exception)
1653 {
1654   char
1655     *argv[1];
1656
1657   Display
1658     *display;
1659
1660   Image
1661     *image;
1662
1663   register ssize_t
1664     i;
1665
1666   size_t
1667     state;
1668
1669   XrmDatabase
1670     resource_database;
1671
1672   XResourceInfo
1673     resource_info;
1674
1675   assert(image_info != (const ImageInfo *) NULL);
1676   assert(image_info->signature == MagickSignature);
1677   assert(images != (Image *) NULL);
1678   assert(images->signature == MagickSignature);
1679   if (IfMagickTrue(images->debug) )
1680     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1681   display=XOpenDisplay(image_info->server_name);
1682   if (display == (Display *) NULL)
1683     {
1684       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1685         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1686       return(MagickFalse);
1687     }
1688   if (exception->severity != UndefinedException)
1689     CatchException(exception);
1690   (void) XSetErrorHandler(XError);
1691   resource_database=XGetResourceDatabase(display,GetClientName());
1692   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1693   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1694   if (image_info->page != (char *) NULL)
1695     resource_info.image_geometry=AcquireString(image_info->page);
1696   resource_info.immutable=MagickTrue;
1697   argv[0]=AcquireString(GetClientName());
1698   state=DefaultState;
1699   for (i=0; (state & ExitState) == 0; i++)
1700   {
1701     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1702       break;
1703     image=GetImageFromList(images,i % GetImageListLength(images));
1704     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1705   }
1706   (void) SetErrorHandler((ErrorHandler) NULL);
1707   (void) SetWarningHandler((WarningHandler) NULL);
1708   argv[0]=DestroyString(argv[0]);
1709   (void) XCloseDisplay(display);
1710   XDestroyResourceInfo(&resource_info);
1711   if (exception->severity != UndefinedException)
1712     return(MagickFalse);
1713   return(MagickTrue);
1714 }
1715 \f
1716 /*
1717 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1718 %                                                                             %
1719 %                                                                             %
1720 %                                                                             %
1721 %   R e m o t e D i s p l a y C o m m a n d                                   %
1722 %                                                                             %
1723 %                                                                             %
1724 %                                                                             %
1725 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1726 %
1727 %  RemoteDisplayCommand() encourages a remote display program to display the
1728 %  specified image filename.
1729 %
1730 %  The format of the RemoteDisplayCommand method is:
1731 %
1732 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1733 %        const char *window,const char *filename,ExceptionInfo *exception)
1734 %
1735 %  A description of each parameter follows:
1736 %
1737 %    o image_info: the image info.
1738 %
1739 %    o window: Specifies the name or id of an X window.
1740 %
1741 %    o filename: the name of the image filename to display.
1742 %
1743 %    o exception: return any errors or warnings in this structure.
1744 %
1745 */
1746 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1747   const char *window,const char *filename,ExceptionInfo *exception)
1748 {
1749   Display
1750     *display;
1751
1752   MagickStatusType
1753     status;
1754
1755   assert(image_info != (const ImageInfo *) NULL);
1756   assert(image_info->signature == MagickSignature);
1757   assert(filename != (char *) NULL);
1758   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1759   display=XOpenDisplay(image_info->server_name);
1760   if (display == (Display *) NULL)
1761     {
1762       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1763         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1764       return(MagickFalse);
1765     }
1766   (void) XSetErrorHandler(XError);
1767   status=XRemoteCommand(display,window,filename);
1768   (void) XCloseDisplay(display);
1769   return(IsMagickTrue(status));
1770 }
1771 \f
1772 /*
1773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1774 %                                                                             %
1775 %                                                                             %
1776 %                                                                             %
1777 +   X A n n o t a t e E d i t I m a g e                                       %
1778 %                                                                             %
1779 %                                                                             %
1780 %                                                                             %
1781 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1782 %
1783 %  XAnnotateEditImage() annotates the image with text.
1784 %
1785 %  The format of the XAnnotateEditImage method is:
1786 %
1787 %      MagickBooleanType XAnnotateEditImage(Display *display,
1788 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1789 %        ExceptionInfo *exception)
1790 %
1791 %  A description of each parameter follows:
1792 %
1793 %    o display: Specifies a connection to an X server;  returned from
1794 %      XOpenDisplay.
1795 %
1796 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1797 %
1798 %    o windows: Specifies a pointer to a XWindows structure.
1799 %
1800 %    o image: the image; returned from ReadImage.
1801 %
1802 */
1803
1804 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1805 {
1806   if (x > y)
1807     return(x);
1808   return(y);
1809 }
1810
1811 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1812 {
1813   if (x < y)
1814     return(x);
1815   return(y);
1816 }
1817
1818 static MagickBooleanType XAnnotateEditImage(Display *display,
1819   XResourceInfo *resource_info,XWindows *windows,Image *image,
1820   ExceptionInfo *exception)
1821 {
1822   static const char
1823     *AnnotateMenu[] =
1824     {
1825       "Font Name",
1826       "Font Color",
1827       "Box Color",
1828       "Rotate Text",
1829       "Help",
1830       "Dismiss",
1831       (char *) NULL
1832     },
1833     *TextMenu[] =
1834     {
1835       "Help",
1836       "Apply",
1837       (char *) NULL
1838     };
1839
1840   static const ModeType
1841     AnnotateCommands[] =
1842     {
1843       AnnotateNameCommand,
1844       AnnotateFontColorCommand,
1845       AnnotateBackgroundColorCommand,
1846       AnnotateRotateCommand,
1847       AnnotateHelpCommand,
1848       AnnotateDismissCommand
1849     },
1850     TextCommands[] =
1851     {
1852       TextHelpCommand,
1853       TextApplyCommand
1854     };
1855
1856   static MagickBooleanType
1857     transparent_box = MagickTrue,
1858     transparent_pen = MagickFalse;
1859
1860   static double
1861     degrees = 0.0;
1862
1863   static unsigned int
1864     box_id = MaxNumberPens-2,
1865     font_id = 0,
1866     pen_id = 0;
1867
1868   char
1869     command[MaxTextExtent],
1870     text[MaxTextExtent];
1871
1872   const char
1873     *ColorMenu[MaxNumberPens+1];
1874
1875   Cursor
1876     cursor;
1877
1878   GC
1879     annotate_context;
1880
1881   int
1882     id,
1883     pen_number,
1884     status,
1885     x,
1886     y;
1887
1888   KeySym
1889     key_symbol;
1890
1891   register char
1892     *p;
1893
1894   register ssize_t
1895     i;
1896
1897   unsigned int
1898     height,
1899     width;
1900
1901   size_t
1902     state;
1903
1904   XAnnotateInfo
1905     *annotate_info,
1906     *previous_info;
1907
1908   XColor
1909     color;
1910
1911   XFontStruct
1912     *font_info;
1913
1914   XEvent
1915     event,
1916     text_event;
1917
1918   /*
1919     Map Command widget.
1920   */
1921   (void) CloneString(&windows->command.name,"Annotate");
1922   windows->command.data=4;
1923   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1924   (void) XMapRaised(display,windows->command.id);
1925   XClientMessage(display,windows->image.id,windows->im_protocols,
1926     windows->im_update_widget,CurrentTime);
1927   /*
1928     Track pointer until button 1 is pressed.
1929   */
1930   XQueryPosition(display,windows->image.id,&x,&y);
1931   (void) XSelectInput(display,windows->image.id,
1932     windows->image.attributes.event_mask | PointerMotionMask);
1933   cursor=XCreateFontCursor(display,XC_left_side);
1934   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1935   state=DefaultState;
1936   do
1937   {
1938     if (IfMagickTrue(windows->info.mapped) )
1939       {
1940         /*
1941           Display pointer position.
1942         */
1943         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1944           x+windows->image.x,y+windows->image.y);
1945         XInfoWidget(display,windows,text);
1946       }
1947     /*
1948       Wait for next event.
1949     */
1950     XScreenEvent(display,windows,&event,exception);
1951     if (event.xany.window == windows->command.id)
1952       {
1953         /*
1954           Select a command from the Command widget.
1955         */
1956         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1957         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1958         if (id < 0)
1959           continue;
1960         switch (AnnotateCommands[id])
1961         {
1962           case AnnotateNameCommand:
1963           {
1964             const char
1965               *FontMenu[MaxNumberFonts];
1966
1967             int
1968               font_number;
1969
1970             /*
1971               Initialize menu selections.
1972             */
1973             for (i=0; i < MaxNumberFonts; i++)
1974               FontMenu[i]=resource_info->font_name[i];
1975             FontMenu[MaxNumberFonts-2]="Browser...";
1976             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1977             /*
1978               Select a font name from the pop-up menu.
1979             */
1980             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1981               (const char **) FontMenu,command);
1982             if (font_number < 0)
1983               break;
1984             if (font_number == (MaxNumberFonts-2))
1985               {
1986                 static char
1987                   font_name[MaxTextExtent] = "fixed";
1988
1989                 /*
1990                   Select a font name from a browser.
1991                 */
1992                 resource_info->font_name[font_number]=font_name;
1993                 XFontBrowserWidget(display,windows,"Select",font_name);
1994                 if (*font_name == '\0')
1995                   break;
1996               }
1997             /*
1998               Initialize font info.
1999             */
2000             font_info=XLoadQueryFont(display,resource_info->font_name[
2001               font_number]);
2002             if (font_info == (XFontStruct *) NULL)
2003               {
2004                 XNoticeWidget(display,windows,"Unable to load font:",
2005                   resource_info->font_name[font_number]);
2006                 break;
2007               }
2008             font_id=(unsigned int) font_number;
2009             (void) XFreeFont(display,font_info);
2010             break;
2011           }
2012           case AnnotateFontColorCommand:
2013           {
2014             /*
2015               Initialize menu selections.
2016             */
2017             for (i=0; i < (int) (MaxNumberPens-2); i++)
2018               ColorMenu[i]=resource_info->pen_colors[i];
2019             ColorMenu[MaxNumberPens-2]="transparent";
2020             ColorMenu[MaxNumberPens-1]="Browser...";
2021             ColorMenu[MaxNumberPens]=(const char *) NULL;
2022             /*
2023               Select a pen color from the pop-up menu.
2024             */
2025             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2026               (const char **) ColorMenu,command);
2027             if (pen_number < 0)
2028               break;
2029             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2030               MagickFalse;
2031             if (IfMagickTrue(transparent_pen) )
2032               break;
2033             if (pen_number == (MaxNumberPens-1))
2034               {
2035                 static char
2036                   color_name[MaxTextExtent] = "gray";
2037
2038                 /*
2039                   Select a pen color from a dialog.
2040                 */
2041                 resource_info->pen_colors[pen_number]=color_name;
2042                 XColorBrowserWidget(display,windows,"Select",color_name);
2043                 if (*color_name == '\0')
2044                   break;
2045               }
2046             /*
2047               Set pen color.
2048             */
2049             (void) XParseColor(display,windows->map_info->colormap,
2050               resource_info->pen_colors[pen_number],&color);
2051             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2052               (unsigned int) MaxColors,&color);
2053             windows->pixel_info->pen_colors[pen_number]=color;
2054             pen_id=(unsigned int) pen_number;
2055             break;
2056           }
2057           case AnnotateBackgroundColorCommand:
2058           {
2059             /*
2060               Initialize menu selections.
2061             */
2062             for (i=0; i < (int) (MaxNumberPens-2); i++)
2063               ColorMenu[i]=resource_info->pen_colors[i];
2064             ColorMenu[MaxNumberPens-2]="transparent";
2065             ColorMenu[MaxNumberPens-1]="Browser...";
2066             ColorMenu[MaxNumberPens]=(const char *) NULL;
2067             /*
2068               Select a pen color from the pop-up menu.
2069             */
2070             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2071               (const char **) ColorMenu,command);
2072             if (pen_number < 0)
2073               break;
2074             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2075               MagickFalse;
2076             if (IfMagickTrue(transparent_box) )
2077               break;
2078             if (pen_number == (MaxNumberPens-1))
2079               {
2080                 static char
2081                   color_name[MaxTextExtent] = "gray";
2082
2083                 /*
2084                   Select a pen color from a dialog.
2085                 */
2086                 resource_info->pen_colors[pen_number]=color_name;
2087                 XColorBrowserWidget(display,windows,"Select",color_name);
2088                 if (*color_name == '\0')
2089                   break;
2090               }
2091             /*
2092               Set pen color.
2093             */
2094             (void) XParseColor(display,windows->map_info->colormap,
2095               resource_info->pen_colors[pen_number],&color);
2096             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2097               (unsigned int) MaxColors,&color);
2098             windows->pixel_info->pen_colors[pen_number]=color;
2099             box_id=(unsigned int) pen_number;
2100             break;
2101           }
2102           case AnnotateRotateCommand:
2103           {
2104             int
2105               entry;
2106
2107             static char
2108               angle[MaxTextExtent] = "30.0";
2109
2110             static const char
2111               *RotateMenu[] =
2112               {
2113                 "-90",
2114                 "-45",
2115                 "-30",
2116                 "0",
2117                 "30",
2118                 "45",
2119                 "90",
2120                 "180",
2121                 "Dialog...",
2122                 (char *) NULL,
2123               };
2124
2125             /*
2126               Select a command from the pop-up menu.
2127             */
2128             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2129               command);
2130             if (entry < 0)
2131               break;
2132             if (entry != 8)
2133               {
2134                 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2135                 break;
2136               }
2137             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2138               angle);
2139             if (*angle == '\0')
2140               break;
2141             degrees=StringToDouble(angle,(char **) NULL);
2142             break;
2143           }
2144           case AnnotateHelpCommand:
2145           {
2146             XTextViewWidget(display,resource_info,windows,MagickFalse,
2147               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2148             break;
2149           }
2150           case AnnotateDismissCommand:
2151           {
2152             /*
2153               Prematurely exit.
2154             */
2155             state|=EscapeState;
2156             state|=ExitState;
2157             break;
2158           }
2159           default:
2160             break;
2161         }
2162         continue;
2163       }
2164     switch (event.type)
2165     {
2166       case ButtonPress:
2167       {
2168         if (event.xbutton.button != Button1)
2169           break;
2170         if (event.xbutton.window != windows->image.id)
2171           break;
2172         /*
2173           Change to text entering mode.
2174         */
2175         x=event.xbutton.x;
2176         y=event.xbutton.y;
2177         state|=ExitState;
2178         break;
2179       }
2180       case ButtonRelease:
2181         break;
2182       case Expose:
2183         break;
2184       case KeyPress:
2185       {
2186         if (event.xkey.window != windows->image.id)
2187           break;
2188         /*
2189           Respond to a user key press.
2190         */
2191         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2192           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2193         switch ((int) key_symbol)
2194         {
2195           case XK_Escape:
2196           case XK_F20:
2197           {
2198             /*
2199               Prematurely exit.
2200             */
2201             state|=EscapeState;
2202             state|=ExitState;
2203             break;
2204           }
2205           case XK_F1:
2206           case XK_Help:
2207           {
2208             XTextViewWidget(display,resource_info,windows,MagickFalse,
2209               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2210             break;
2211           }
2212           default:
2213           {
2214             (void) XBell(display,0);
2215             break;
2216           }
2217         }
2218         break;
2219       }
2220       case MotionNotify:
2221       {
2222         /*
2223           Map and unmap Info widget as cursor crosses its boundaries.
2224         */
2225         x=event.xmotion.x;
2226         y=event.xmotion.y;
2227         if (IfMagickTrue(windows->info.mapped) )
2228           {
2229             if ((x < (int) (windows->info.x+windows->info.width)) &&
2230                 (y < (int) (windows->info.y+windows->info.height)))
2231               (void) XWithdrawWindow(display,windows->info.id,
2232                 windows->info.screen);
2233           }
2234         else
2235           if ((x > (int) (windows->info.x+windows->info.width)) ||
2236               (y > (int) (windows->info.y+windows->info.height)))
2237             (void) XMapWindow(display,windows->info.id);
2238         break;
2239       }
2240       default:
2241         break;
2242     }
2243   } while ((state & ExitState) == 0);
2244   (void) XSelectInput(display,windows->image.id,
2245     windows->image.attributes.event_mask);
2246   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2247   if ((state & EscapeState) != 0)
2248     return(MagickTrue);
2249   /*
2250     Set font info and check boundary conditions.
2251   */
2252   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2253   if (font_info == (XFontStruct *) NULL)
2254     {
2255       XNoticeWidget(display,windows,"Unable to load font:",
2256         resource_info->font_name[font_id]);
2257       font_info=windows->font_info;
2258     }
2259   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2260     x=(int) windows->image.width-font_info->max_bounds.width;
2261   if (y < (int) (font_info->ascent+font_info->descent))
2262     y=(int) font_info->ascent+font_info->descent;
2263   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2264       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2265     return(MagickFalse);
2266   /*
2267     Initialize annotate structure.
2268   */
2269   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2270   if (annotate_info == (XAnnotateInfo *) NULL)
2271     return(MagickFalse);
2272   XGetAnnotateInfo(annotate_info);
2273   annotate_info->x=x;
2274   annotate_info->y=y;
2275   if (IfMagickFalse(transparent_box) && IfMagickFalse(transparent_pen))
2276     annotate_info->stencil=OpaqueStencil;
2277   else
2278     if (IfMagickFalse(transparent_box) )
2279       annotate_info->stencil=BackgroundStencil;
2280     else
2281       annotate_info->stencil=ForegroundStencil;
2282   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2283   annotate_info->degrees=degrees;
2284   annotate_info->font_info=font_info;
2285   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2286     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2287     sizeof(*annotate_info->text));
2288   if (annotate_info->text == (char *) NULL)
2289     return(MagickFalse);
2290   /*
2291     Create cursor and set graphic context.
2292   */
2293   cursor=XCreateFontCursor(display,XC_pencil);
2294   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2295   annotate_context=windows->image.annotate_context;
2296   (void) XSetFont(display,annotate_context,font_info->fid);
2297   (void) XSetBackground(display,annotate_context,
2298     windows->pixel_info->pen_colors[box_id].pixel);
2299   (void) XSetForeground(display,annotate_context,
2300     windows->pixel_info->pen_colors[pen_id].pixel);
2301   /*
2302     Begin annotating the image with text.
2303   */
2304   (void) CloneString(&windows->command.name,"Text");
2305   windows->command.data=0;
2306   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2307   state=DefaultState;
2308   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2309   text_event.xexpose.width=(int) font_info->max_bounds.width;
2310   text_event.xexpose.height=font_info->max_bounds.ascent+
2311     font_info->max_bounds.descent;
2312   p=annotate_info->text;
2313   do
2314   {
2315     /*
2316       Display text cursor.
2317     */
2318     *p='\0';
2319     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2320     /*
2321       Wait for next event.
2322     */
2323     XScreenEvent(display,windows,&event,exception);
2324     if (event.xany.window == windows->command.id)
2325       {
2326         /*
2327           Select a command from the Command widget.
2328         */
2329         (void) XSetBackground(display,annotate_context,
2330           windows->pixel_info->background_color.pixel);
2331         (void) XSetForeground(display,annotate_context,
2332           windows->pixel_info->foreground_color.pixel);
2333         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2334         (void) XSetBackground(display,annotate_context,
2335           windows->pixel_info->pen_colors[box_id].pixel);
2336         (void) XSetForeground(display,annotate_context,
2337           windows->pixel_info->pen_colors[pen_id].pixel);
2338         if (id < 0)
2339           continue;
2340         switch (TextCommands[id])
2341         {
2342           case TextHelpCommand:
2343           {
2344             XTextViewWidget(display,resource_info,windows,MagickFalse,
2345               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2346             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2347             break;
2348           }
2349           case TextApplyCommand:
2350           {
2351             /*
2352               Finished annotating.
2353             */
2354             annotate_info->width=(unsigned int) XTextWidth(font_info,
2355               annotate_info->text,(int) strlen(annotate_info->text));
2356             XRefreshWindow(display,&windows->image,&text_event);
2357             state|=ExitState;
2358             break;
2359           }
2360           default:
2361             break;
2362         }
2363         continue;
2364       }
2365     /*
2366       Erase text cursor.
2367     */
2368     text_event.xexpose.x=x;
2369     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2370     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2371       (unsigned int) text_event.xexpose.width,(unsigned int)
2372       text_event.xexpose.height,MagickFalse);
2373     XRefreshWindow(display,&windows->image,&text_event);
2374     switch (event.type)
2375     {
2376       case ButtonPress:
2377       {
2378         if (event.xbutton.window != windows->image.id)
2379           break;
2380         if (event.xbutton.button == Button2)
2381           {
2382             /*
2383               Request primary selection.
2384             */
2385             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2386               windows->image.id,CurrentTime);
2387             break;
2388           }
2389         break;
2390       }
2391       case Expose:
2392       {
2393         if (event.xexpose.count == 0)
2394           {
2395             XAnnotateInfo
2396               *text_info;
2397
2398             /*
2399               Refresh Image window.
2400             */
2401             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2402             text_info=annotate_info;
2403             while (text_info != (XAnnotateInfo *) NULL)
2404             {
2405               if (annotate_info->stencil == ForegroundStencil)
2406                 (void) XDrawString(display,windows->image.id,annotate_context,
2407                   text_info->x,text_info->y,text_info->text,
2408                   (int) strlen(text_info->text));
2409               else
2410                 (void) XDrawImageString(display,windows->image.id,
2411                   annotate_context,text_info->x,text_info->y,text_info->text,
2412                   (int) strlen(text_info->text));
2413               text_info=text_info->previous;
2414             }
2415             (void) XDrawString(display,windows->image.id,annotate_context,
2416               x,y,"_",1);
2417           }
2418         break;
2419       }
2420       case KeyPress:
2421       {
2422         int
2423           length;
2424
2425         if (event.xkey.window != windows->image.id)
2426           break;
2427         /*
2428           Respond to a user key press.
2429         */
2430         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2431           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2432         *(command+length)='\0';
2433         if (((event.xkey.state & ControlMask) != 0) ||
2434             ((event.xkey.state & Mod1Mask) != 0))
2435           state|=ModifierState;
2436         if ((state & ModifierState) != 0)
2437           switch ((int) key_symbol)
2438           {
2439             case XK_u:
2440             case XK_U:
2441             {
2442               key_symbol=DeleteCommand;
2443               break;
2444             }
2445             default:
2446               break;
2447           }
2448         switch ((int) key_symbol)
2449         {
2450           case XK_BackSpace:
2451           {
2452             /*
2453               Erase one character.
2454             */
2455             if (p == annotate_info->text)
2456               {
2457                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2458                   break;
2459                 else
2460                   {
2461                     /*
2462                       Go to end of the previous line of text.
2463                     */
2464                     annotate_info=annotate_info->previous;
2465                     p=annotate_info->text;
2466                     x=annotate_info->x+annotate_info->width;
2467                     y=annotate_info->y;
2468                     if (annotate_info->width != 0)
2469                       p+=strlen(annotate_info->text);
2470                     break;
2471                   }
2472               }
2473             p--;
2474             x-=XTextWidth(font_info,p,1);
2475             text_event.xexpose.x=x;
2476             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2477             XRefreshWindow(display,&windows->image,&text_event);
2478             break;
2479           }
2480           case XK_bracketleft:
2481           {
2482             key_symbol=XK_Escape;
2483             break;
2484           }
2485           case DeleteCommand:
2486           {
2487             /*
2488               Erase the entire line of text.
2489             */
2490             while (p != annotate_info->text)
2491             {
2492               p--;
2493               x-=XTextWidth(font_info,p,1);
2494               text_event.xexpose.x=x;
2495               XRefreshWindow(display,&windows->image,&text_event);
2496             }
2497             break;
2498           }
2499           case XK_Escape:
2500           case XK_F20:
2501           {
2502             /*
2503               Finished annotating.
2504             */
2505             annotate_info->width=(unsigned int) XTextWidth(font_info,
2506               annotate_info->text,(int) strlen(annotate_info->text));
2507             XRefreshWindow(display,&windows->image,&text_event);
2508             state|=ExitState;
2509             break;
2510           }
2511           default:
2512           {
2513             /*
2514               Draw a single character on the Image window.
2515             */
2516             if ((state & ModifierState) != 0)
2517               break;
2518             if (*command == '\0')
2519               break;
2520             *p=(*command);
2521             if (annotate_info->stencil == ForegroundStencil)
2522               (void) XDrawString(display,windows->image.id,annotate_context,
2523                 x,y,p,1);
2524             else
2525               (void) XDrawImageString(display,windows->image.id,
2526                 annotate_context,x,y,p,1);
2527             x+=XTextWidth(font_info,p,1);
2528             p++;
2529             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2530               break;
2531           }
2532           case XK_Return:
2533           case XK_KP_Enter:
2534           {
2535             /*
2536               Advance to the next line of text.
2537             */
2538             *p='\0';
2539             annotate_info->width=(unsigned int) XTextWidth(font_info,
2540               annotate_info->text,(int) strlen(annotate_info->text));
2541             if (annotate_info->next != (XAnnotateInfo *) NULL)
2542               {
2543                 /*
2544                   Line of text already exists.
2545                 */
2546                 annotate_info=annotate_info->next;
2547                 x=annotate_info->x;
2548                 y=annotate_info->y;
2549                 p=annotate_info->text;
2550                 break;
2551               }
2552             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2553               sizeof(*annotate_info->next));
2554             if (annotate_info->next == (XAnnotateInfo *) NULL)
2555               return(MagickFalse);
2556             *annotate_info->next=(*annotate_info);
2557             annotate_info->next->previous=annotate_info;
2558             annotate_info=annotate_info->next;
2559             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2560               windows->image.width/MagickMax((ssize_t)
2561               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2562             if (annotate_info->text == (char *) NULL)
2563               return(MagickFalse);
2564             annotate_info->y+=annotate_info->height;
2565             if (annotate_info->y > (int) windows->image.height)
2566               annotate_info->y=(int) annotate_info->height;
2567             annotate_info->next=(XAnnotateInfo *) NULL;
2568             x=annotate_info->x;
2569             y=annotate_info->y;
2570             p=annotate_info->text;
2571             break;
2572           }
2573         }
2574         break;
2575       }
2576       case KeyRelease:
2577       {
2578         /*
2579           Respond to a user key release.
2580         */
2581         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2582           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2583         state&=(~ModifierState);
2584         break;
2585       }
2586       case SelectionNotify:
2587       {
2588         Atom
2589           type;
2590
2591         int
2592           format;
2593
2594         unsigned char
2595           *data;
2596
2597         unsigned long
2598           after,
2599           length;
2600
2601         /*
2602           Obtain response from primary selection.
2603         */
2604         if (event.xselection.property == (Atom) None)
2605           break;
2606         status=XGetWindowProperty(display,event.xselection.requestor,
2607           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2608           &type,&format,&length,&after,&data);
2609         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2610             (length == 0))
2611           break;
2612         /*
2613           Annotate Image window with primary selection.
2614         */
2615         for (i=0; i < (ssize_t) length; i++)
2616         {
2617           if ((char) data[i] != '\n')
2618             {
2619               /*
2620                 Draw a single character on the Image window.
2621               */
2622               *p=(char) data[i];
2623               (void) XDrawString(display,windows->image.id,annotate_context,
2624                 x,y,p,1);
2625               x+=XTextWidth(font_info,p,1);
2626               p++;
2627               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2628                 continue;
2629             }
2630           /*
2631             Advance to the next line of text.
2632           */
2633           *p='\0';
2634           annotate_info->width=(unsigned int) XTextWidth(font_info,
2635             annotate_info->text,(int) strlen(annotate_info->text));
2636           if (annotate_info->next != (XAnnotateInfo *) NULL)
2637             {
2638               /*
2639                 Line of text already exists.
2640               */
2641               annotate_info=annotate_info->next;
2642               x=annotate_info->x;
2643               y=annotate_info->y;
2644               p=annotate_info->text;
2645               continue;
2646             }
2647           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2648             sizeof(*annotate_info->next));
2649           if (annotate_info->next == (XAnnotateInfo *) NULL)
2650             return(MagickFalse);
2651           *annotate_info->next=(*annotate_info);
2652           annotate_info->next->previous=annotate_info;
2653           annotate_info=annotate_info->next;
2654           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2655             windows->image.width/MagickMax((ssize_t)
2656             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2657           if (annotate_info->text == (char *) NULL)
2658             return(MagickFalse);
2659           annotate_info->y+=annotate_info->height;
2660           if (annotate_info->y > (int) windows->image.height)
2661             annotate_info->y=(int) annotate_info->height;
2662           annotate_info->next=(XAnnotateInfo *) NULL;
2663           x=annotate_info->x;
2664           y=annotate_info->y;
2665           p=annotate_info->text;
2666         }
2667         (void) XFree((void *) data);
2668         break;
2669       }
2670       default:
2671         break;
2672     }
2673   } while ((state & ExitState) == 0);
2674   (void) XFreeCursor(display,cursor);
2675   /*
2676     Annotation is relative to image configuration.
2677   */
2678   width=(unsigned int) image->columns;
2679   height=(unsigned int) image->rows;
2680   x=0;
2681   y=0;
2682   if (windows->image.crop_geometry != (char *) NULL)
2683     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2684   /*
2685     Initialize annotated image.
2686   */
2687   XSetCursorState(display,windows,MagickTrue);
2688   XCheckRefreshWindows(display,windows);
2689   while (annotate_info != (XAnnotateInfo *) NULL)
2690   {
2691     if (annotate_info->width == 0)
2692       {
2693         /*
2694           No text on this line--  go to the next line of text.
2695         */
2696         previous_info=annotate_info->previous;
2697         annotate_info->text=(char *)
2698           RelinquishMagickMemory(annotate_info->text);
2699         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2700         annotate_info=previous_info;
2701         continue;
2702       }
2703     /*
2704       Determine pixel index for box and pen color.
2705     */
2706     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2707     if (windows->pixel_info->colors != 0)
2708       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2709         if (windows->pixel_info->pixels[i] ==
2710             windows->pixel_info->pen_colors[box_id].pixel)
2711           {
2712             windows->pixel_info->box_index=(unsigned short) i;
2713             break;
2714           }
2715     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2716     if (windows->pixel_info->colors != 0)
2717       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2718         if (windows->pixel_info->pixels[i] ==
2719             windows->pixel_info->pen_colors[pen_id].pixel)
2720           {
2721             windows->pixel_info->pen_index=(unsigned short) i;
2722             break;
2723           }
2724     /*
2725       Define the annotate geometry string.
2726     */
2727     annotate_info->x=(int)
2728       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2729     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2730       windows->image.y)/windows->image.ximage->height;
2731     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2732       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2733       height*annotate_info->height/windows->image.ximage->height,
2734       annotate_info->x+x,annotate_info->y+y);
2735     /*
2736       Annotate image with text.
2737     */
2738     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2739       exception);
2740     if (status == 0)
2741       return(MagickFalse);
2742     /*
2743       Free up memory.
2744     */
2745     previous_info=annotate_info->previous;
2746     annotate_info->text=DestroyString(annotate_info->text);
2747     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2748     annotate_info=previous_info;
2749   }
2750   (void) XSetForeground(display,annotate_context,
2751     windows->pixel_info->foreground_color.pixel);
2752   (void) XSetBackground(display,annotate_context,
2753     windows->pixel_info->background_color.pixel);
2754   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2755   XSetCursorState(display,windows,MagickFalse);
2756   (void) XFreeFont(display,font_info);
2757   /*
2758     Update image configuration.
2759   */
2760   XConfigureImageColormap(display,resource_info,windows,image,exception);
2761   (void) XConfigureImage(display,resource_info,windows,image,exception);
2762   return(MagickTrue);
2763 }
2764 \f
2765 /*
2766 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2767 %                                                                             %
2768 %                                                                             %
2769 %                                                                             %
2770 +   X B a c k g r o u n d I m a g e                                           %
2771 %                                                                             %
2772 %                                                                             %
2773 %                                                                             %
2774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2775 %
2776 %  XBackgroundImage() displays the image in the background of a window.
2777 %
2778 %  The format of the XBackgroundImage method is:
2779 %
2780 %      MagickBooleanType XBackgroundImage(Display *display,
2781 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2782 %        ExceptionInfo *exception)
2783 %
2784 %  A description of each parameter follows:
2785 %
2786 %    o display: Specifies a connection to an X server; returned from
2787 %      XOpenDisplay.
2788 %
2789 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2790 %
2791 %    o windows: Specifies a pointer to a XWindows structure.
2792 %
2793 %    o image: the image.
2794 %
2795 %    o exception: return any errors or warnings in this structure.
2796 %
2797 */
2798 static MagickBooleanType XBackgroundImage(Display *display,
2799   XResourceInfo *resource_info,XWindows *windows,Image **image,
2800   ExceptionInfo *exception)
2801 {
2802 #define BackgroundImageTag  "Background/Image"
2803
2804   int
2805     status;
2806
2807   static char
2808     window_id[MaxTextExtent] = "root";
2809
2810   XResourceInfo
2811     background_resources;
2812
2813   /*
2814     Put image in background.
2815   */
2816   status=XDialogWidget(display,windows,"Background",
2817     "Enter window id (id 0x00 selects window with pointer):",window_id);
2818   if (*window_id == '\0')
2819     return(MagickFalse);
2820   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2821     exception);
2822   XInfoWidget(display,windows,BackgroundImageTag);
2823   XSetCursorState(display,windows,MagickTrue);
2824   XCheckRefreshWindows(display,windows);
2825   background_resources=(*resource_info);
2826   background_resources.window_id=window_id;
2827   background_resources.backdrop=IsMagickTrue(status);
2828   status=XDisplayBackgroundImage(display,&background_resources,*image,
2829     exception);
2830   if (IfMagickTrue(status))
2831     XClientMessage(display,windows->image.id,windows->im_protocols,
2832       windows->im_retain_colors,CurrentTime);
2833   XSetCursorState(display,windows,MagickFalse);
2834   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2835     exception);
2836   return(MagickTrue);
2837 }
2838 \f
2839 /*
2840 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2841 %                                                                             %
2842 %                                                                             %
2843 %                                                                             %
2844 +   X C h o p I m a g e                                                       %
2845 %                                                                             %
2846 %                                                                             %
2847 %                                                                             %
2848 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2849 %
2850 %  XChopImage() chops the X image.
2851 %
2852 %  The format of the XChopImage method is:
2853 %
2854 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2855 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2856 %
2857 %  A description of each parameter follows:
2858 %
2859 %    o display: Specifies a connection to an X server; returned from
2860 %      XOpenDisplay.
2861 %
2862 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2863 %
2864 %    o windows: Specifies a pointer to a XWindows structure.
2865 %
2866 %    o image: the image.
2867 %
2868 %    o exception: return any errors or warnings in this structure.
2869 %
2870 */
2871 static MagickBooleanType XChopImage(Display *display,
2872   XResourceInfo *resource_info,XWindows *windows,Image **image,
2873   ExceptionInfo *exception)
2874 {
2875   static const char
2876     *ChopMenu[] =
2877     {
2878       "Direction",
2879       "Help",
2880       "Dismiss",
2881       (char *) NULL
2882     };
2883
2884   static ModeType
2885     direction = HorizontalChopCommand;
2886
2887   static const ModeType
2888     ChopCommands[] =
2889     {
2890       ChopDirectionCommand,
2891       ChopHelpCommand,
2892       ChopDismissCommand
2893     },
2894     DirectionCommands[] =
2895     {
2896       HorizontalChopCommand,
2897       VerticalChopCommand
2898     };
2899
2900   char
2901     text[MaxTextExtent];
2902
2903   Image
2904     *chop_image;
2905
2906   int
2907     id,
2908     x,
2909     y;
2910
2911   double
2912     scale_factor;
2913
2914   RectangleInfo
2915     chop_info;
2916
2917   unsigned int
2918     distance,
2919     height,
2920     width;
2921
2922   size_t
2923     state;
2924
2925   XEvent
2926     event;
2927
2928   XSegment
2929     segment_info;
2930
2931   /*
2932     Map Command widget.
2933   */
2934   (void) CloneString(&windows->command.name,"Chop");
2935   windows->command.data=1;
2936   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2937   (void) XMapRaised(display,windows->command.id);
2938   XClientMessage(display,windows->image.id,windows->im_protocols,
2939     windows->im_update_widget,CurrentTime);
2940   /*
2941     Track pointer until button 1 is pressed.
2942   */
2943   XQueryPosition(display,windows->image.id,&x,&y);
2944   (void) XSelectInput(display,windows->image.id,
2945     windows->image.attributes.event_mask | PointerMotionMask);
2946   state=DefaultState;
2947   do
2948   {
2949     if (IfMagickTrue(windows->info.mapped) )
2950       {
2951         /*
2952           Display pointer position.
2953         */
2954         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2955           x+windows->image.x,y+windows->image.y);
2956         XInfoWidget(display,windows,text);
2957       }
2958     /*
2959       Wait for next event.
2960     */
2961     XScreenEvent(display,windows,&event,exception);
2962     if (event.xany.window == windows->command.id)
2963       {
2964         /*
2965           Select a command from the Command widget.
2966         */
2967         id=XCommandWidget(display,windows,ChopMenu,&event);
2968         if (id < 0)
2969           continue;
2970         switch (ChopCommands[id])
2971         {
2972           case ChopDirectionCommand:
2973           {
2974             char
2975               command[MaxTextExtent];
2976
2977             static const char
2978               *Directions[] =
2979               {
2980                 "horizontal",
2981                 "vertical",
2982                 (char *) NULL,
2983               };
2984
2985             /*
2986               Select a command from the pop-up menu.
2987             */
2988             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2989             if (id >= 0)
2990               direction=DirectionCommands[id];
2991             break;
2992           }
2993           case ChopHelpCommand:
2994           {
2995             XTextViewWidget(display,resource_info,windows,MagickFalse,
2996               "Help Viewer - Image Chop",ImageChopHelp);
2997             break;
2998           }
2999           case ChopDismissCommand:
3000           {
3001             /*
3002               Prematurely exit.
3003             */
3004             state|=EscapeState;
3005             state|=ExitState;
3006             break;
3007           }
3008           default:
3009             break;
3010         }
3011         continue;
3012       }
3013     switch (event.type)
3014     {
3015       case ButtonPress:
3016       {
3017         if (event.xbutton.button != Button1)
3018           break;
3019         if (event.xbutton.window != windows->image.id)
3020           break;
3021         /*
3022           User has committed to start point of chopping line.
3023         */
3024         segment_info.x1=(short int) event.xbutton.x;
3025         segment_info.x2=(short int) event.xbutton.x;
3026         segment_info.y1=(short int) event.xbutton.y;
3027         segment_info.y2=(short int) event.xbutton.y;
3028         state|=ExitState;
3029         break;
3030       }
3031       case ButtonRelease:
3032         break;
3033       case Expose:
3034         break;
3035       case KeyPress:
3036       {
3037         char
3038           command[MaxTextExtent];
3039
3040         KeySym
3041           key_symbol;
3042
3043         if (event.xkey.window != windows->image.id)
3044           break;
3045         /*
3046           Respond to a user key press.
3047         */
3048         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3049           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3050         switch ((int) key_symbol)
3051         {
3052           case XK_Escape:
3053           case XK_F20:
3054           {
3055             /*
3056               Prematurely exit.
3057             */
3058             state|=EscapeState;
3059             state|=ExitState;
3060             break;
3061           }
3062           case XK_F1:
3063           case XK_Help:
3064           {
3065             (void) XSetFunction(display,windows->image.highlight_context,
3066               GXcopy);
3067             XTextViewWidget(display,resource_info,windows,MagickFalse,
3068               "Help Viewer - Image Chop",ImageChopHelp);
3069             (void) XSetFunction(display,windows->image.highlight_context,
3070               GXinvert);
3071             break;
3072           }
3073           default:
3074           {
3075             (void) XBell(display,0);
3076             break;
3077           }
3078         }
3079         break;
3080       }
3081       case MotionNotify:
3082       {
3083         /*
3084           Map and unmap Info widget as text cursor crosses its boundaries.
3085         */
3086         x=event.xmotion.x;
3087         y=event.xmotion.y;
3088         if (IfMagickTrue(windows->info.mapped) )
3089           {
3090             if ((x < (int) (windows->info.x+windows->info.width)) &&
3091                 (y < (int) (windows->info.y+windows->info.height)))
3092               (void) XWithdrawWindow(display,windows->info.id,
3093                 windows->info.screen);
3094           }
3095         else
3096           if ((x > (int) (windows->info.x+windows->info.width)) ||
3097               (y > (int) (windows->info.y+windows->info.height)))
3098             (void) XMapWindow(display,windows->info.id);
3099       }
3100     }
3101   } while ((state & ExitState) == 0);
3102   (void) XSelectInput(display,windows->image.id,
3103     windows->image.attributes.event_mask);
3104   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3105   if ((state & EscapeState) != 0)
3106     return(MagickTrue);
3107   /*
3108     Draw line as pointer moves until the mouse button is released.
3109   */
3110   chop_info.width=0;
3111   chop_info.height=0;
3112   chop_info.x=0;
3113   chop_info.y=0;
3114   distance=0;
3115   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3116   state=DefaultState;
3117   do
3118   {
3119     if (distance > 9)
3120       {
3121         /*
3122           Display info and draw chopping line.
3123         */
3124         if (IfMagickFalse(windows->info.mapped) )
3125           (void) XMapWindow(display,windows->info.id);
3126         (void) FormatLocaleString(text,MaxTextExtent,
3127           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3128           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3129         XInfoWidget(display,windows,text);
3130         XHighlightLine(display,windows->image.id,
3131           windows->image.highlight_context,&segment_info);
3132       }
3133     else
3134       if (IfMagickTrue(windows->info.mapped) )
3135         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3136     /*
3137       Wait for next event.
3138     */
3139     XScreenEvent(display,windows,&event,exception);
3140     if (distance > 9)
3141       XHighlightLine(display,windows->image.id,
3142         windows->image.highlight_context,&segment_info);
3143     switch (event.type)
3144     {
3145       case ButtonPress:
3146       {
3147         segment_info.x2=(short int) event.xmotion.x;
3148         segment_info.y2=(short int) event.xmotion.y;
3149         break;
3150       }
3151       case ButtonRelease:
3152       {
3153         /*
3154           User has committed to chopping line.
3155         */
3156         segment_info.x2=(short int) event.xbutton.x;
3157         segment_info.y2=(short int) event.xbutton.y;
3158         state|=ExitState;
3159         break;
3160       }
3161       case Expose:
3162         break;
3163       case MotionNotify:
3164       {
3165         segment_info.x2=(short int) event.xmotion.x;
3166         segment_info.y2=(short int) event.xmotion.y;
3167       }
3168       default:
3169         break;
3170     }
3171     /*
3172       Check boundary conditions.
3173     */
3174     if (segment_info.x2 < 0)
3175       segment_info.x2=0;
3176     else
3177       if (segment_info.x2 > windows->image.ximage->width)
3178         segment_info.x2=windows->image.ximage->width;
3179     if (segment_info.y2 < 0)
3180       segment_info.y2=0;
3181     else
3182       if (segment_info.y2 > windows->image.ximage->height)
3183         segment_info.y2=windows->image.ximage->height;
3184     distance=(unsigned int)
3185       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3186        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3187     /*
3188       Compute chopping geometry.
3189     */
3190     if (direction == HorizontalChopCommand)
3191       {
3192         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3193         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3194         chop_info.height=0;
3195         chop_info.y=0;
3196         if (segment_info.x1 > (int) segment_info.x2)
3197           {
3198             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3199             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3200           }
3201       }
3202     else
3203       {
3204         chop_info.width=0;
3205         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3206         chop_info.x=0;
3207         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3208         if (segment_info.y1 > segment_info.y2)
3209           {
3210             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3211             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3212           }
3213       }
3214   } while ((state & ExitState) == 0);
3215   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3216   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3217   if (distance <= 9)
3218     return(MagickTrue);
3219   /*
3220     Image chopping is relative to image configuration.
3221   */
3222   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3223     exception);
3224   XSetCursorState(display,windows,MagickTrue);
3225   XCheckRefreshWindows(display,windows);
3226   windows->image.window_changes.width=windows->image.ximage->width-
3227     (unsigned int) chop_info.width;
3228   windows->image.window_changes.height=windows->image.ximage->height-
3229     (unsigned int) chop_info.height;
3230   width=(unsigned int) (*image)->columns;
3231   height=(unsigned int) (*image)->rows;
3232   x=0;
3233   y=0;
3234   if (windows->image.crop_geometry != (char *) NULL)
3235     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3236   scale_factor=(double) width/windows->image.ximage->width;
3237   chop_info.x+=x;
3238   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3239   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3240   scale_factor=(double) height/windows->image.ximage->height;
3241   chop_info.y+=y;
3242   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3243   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3244   /*
3245     Chop image.
3246   */
3247   chop_image=ChopImage(*image,&chop_info,exception);
3248   XSetCursorState(display,windows,MagickFalse);
3249   if (chop_image == (Image *) NULL)
3250     return(MagickFalse);
3251   *image=DestroyImage(*image);
3252   *image=chop_image;
3253   /*
3254     Update image configuration.
3255   */
3256   XConfigureImageColormap(display,resource_info,windows,*image,exception);
3257   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3258   return(MagickTrue);
3259 }
3260 \f
3261 /*
3262 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3263 %                                                                             %
3264 %                                                                             %
3265 %                                                                             %
3266 +   X C o l o r E d i t I m a g e                                             %
3267 %                                                                             %
3268 %                                                                             %
3269 %                                                                             %
3270 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3271 %
3272 %  XColorEditImage() allows the user to interactively change the color of one
3273 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3274 %
3275 %  The format of the XColorEditImage method is:
3276 %
3277 %      MagickBooleanType XColorEditImage(Display *display,
3278 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3279 %          ExceptionInfo *exception)
3280 %
3281 %  A description of each parameter follows:
3282 %
3283 %    o display: Specifies a connection to an X server;  returned from
3284 %      XOpenDisplay.
3285 %
3286 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3287 %
3288 %    o windows: Specifies a pointer to a XWindows structure.
3289 %
3290 %    o image: the image; returned from ReadImage.
3291 %
3292 %    o exception: return any errors or warnings in this structure.
3293 %
3294 */
3295 static MagickBooleanType XColorEditImage(Display *display,
3296   XResourceInfo *resource_info,XWindows *windows,Image **image,
3297   ExceptionInfo *exception)
3298 {
3299   static const char
3300     *ColorEditMenu[] =
3301     {
3302       "Method",
3303       "Pixel Color",
3304       "Border Color",
3305       "Fuzz",
3306       "Undo",
3307       "Help",
3308       "Dismiss",
3309       (char *) NULL
3310     };
3311
3312   static const ModeType
3313     ColorEditCommands[] =
3314     {
3315       ColorEditMethodCommand,
3316       ColorEditColorCommand,
3317       ColorEditBorderCommand,
3318       ColorEditFuzzCommand,
3319       ColorEditUndoCommand,
3320       ColorEditHelpCommand,
3321       ColorEditDismissCommand
3322     };
3323
3324   static PaintMethod
3325     method = PointMethod;
3326
3327   static unsigned int
3328     pen_id = 0;
3329
3330   static XColor
3331     border_color = { 0, 0, 0, 0, 0, 0 };
3332
3333   char
3334     command[MaxTextExtent],
3335     text[MaxTextExtent];
3336
3337   Cursor
3338     cursor;
3339
3340   int
3341     entry,
3342     id,
3343     x,
3344     x_offset,
3345     y,
3346     y_offset;
3347
3348   register Quantum
3349     *q;
3350
3351   register ssize_t
3352     i;
3353
3354   unsigned int
3355     height,
3356     width;
3357
3358   size_t
3359     state;
3360
3361   XColor
3362     color;
3363
3364   XEvent
3365     event;
3366
3367   /*
3368     Map Command widget.
3369   */
3370   (void) CloneString(&windows->command.name,"Color Edit");
3371   windows->command.data=4;
3372   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3373   (void) XMapRaised(display,windows->command.id);
3374   XClientMessage(display,windows->image.id,windows->im_protocols,
3375     windows->im_update_widget,CurrentTime);
3376   /*
3377     Make cursor.
3378   */
3379   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3380     resource_info->background_color,resource_info->foreground_color);
3381   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3382   /*
3383     Track pointer until button 1 is pressed.
3384   */
3385   XQueryPosition(display,windows->image.id,&x,&y);
3386   (void) XSelectInput(display,windows->image.id,
3387     windows->image.attributes.event_mask | PointerMotionMask);
3388   state=DefaultState;
3389   do
3390   {
3391     if (IfMagickTrue(windows->info.mapped) )
3392       {
3393         /*
3394           Display pointer position.
3395         */
3396         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3397           x+windows->image.x,y+windows->image.y);
3398         XInfoWidget(display,windows,text);
3399       }
3400     /*
3401       Wait for next event.
3402     */
3403     XScreenEvent(display,windows,&event,exception);
3404     if (event.xany.window == windows->command.id)
3405       {
3406         /*
3407           Select a command from the Command widget.
3408         */
3409         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3410         if (id < 0)
3411           {
3412             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3413             continue;
3414           }
3415         switch (ColorEditCommands[id])
3416         {
3417           case ColorEditMethodCommand:
3418           {
3419             char
3420               **methods;
3421
3422             /*
3423               Select a method from the pop-up menu.
3424             */
3425             methods=(char **) GetCommandOptions(MagickMethodOptions);
3426             if (methods == (char **) NULL)
3427               break;
3428             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3429               (const char **) methods,command);
3430             if (entry >= 0)
3431               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3432                 MagickFalse,methods[entry]);
3433             methods=DestroyStringList(methods);
3434             break;
3435           }
3436           case ColorEditColorCommand:
3437           {
3438             const char
3439               *ColorMenu[MaxNumberPens];
3440
3441             int
3442               pen_number;
3443
3444             /*
3445               Initialize menu selections.
3446             */
3447             for (i=0; i < (int) (MaxNumberPens-2); i++)
3448               ColorMenu[i]=resource_info->pen_colors[i];
3449             ColorMenu[MaxNumberPens-2]="Browser...";
3450             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3451             /*
3452               Select a pen color from the pop-up menu.
3453             */
3454             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3455               (const char **) ColorMenu,command);
3456             if (pen_number < 0)
3457               break;
3458             if (pen_number == (MaxNumberPens-2))
3459               {
3460                 static char
3461                   color_name[MaxTextExtent] = "gray";
3462
3463                 /*
3464                   Select a pen color from a dialog.
3465                 */
3466                 resource_info->pen_colors[pen_number]=color_name;
3467                 XColorBrowserWidget(display,windows,"Select",color_name);
3468                 if (*color_name == '\0')
3469                   break;
3470               }
3471             /*
3472               Set pen color.
3473             */
3474             (void) XParseColor(display,windows->map_info->colormap,
3475               resource_info->pen_colors[pen_number],&color);
3476             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3477               (unsigned int) MaxColors,&color);
3478             windows->pixel_info->pen_colors[pen_number]=color;
3479             pen_id=(unsigned int) pen_number;
3480             break;
3481           }
3482           case ColorEditBorderCommand:
3483           {
3484             const char
3485               *ColorMenu[MaxNumberPens];
3486
3487             int
3488               pen_number;
3489
3490             /*
3491               Initialize menu selections.
3492             */
3493             for (i=0; i < (int) (MaxNumberPens-2); i++)
3494               ColorMenu[i]=resource_info->pen_colors[i];
3495             ColorMenu[MaxNumberPens-2]="Browser...";
3496             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3497             /*
3498               Select a pen color from the pop-up menu.
3499             */
3500             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3501               (const char **) ColorMenu,command);
3502             if (pen_number < 0)
3503               break;
3504             if (pen_number == (MaxNumberPens-2))
3505               {
3506                 static char
3507                   color_name[MaxTextExtent] = "gray";
3508
3509                 /*
3510                   Select a pen color from a dialog.
3511                 */
3512                 resource_info->pen_colors[pen_number]=color_name;
3513                 XColorBrowserWidget(display,windows,"Select",color_name);
3514                 if (*color_name == '\0')
3515                   break;
3516               }
3517             /*
3518               Set border color.
3519             */
3520             (void) XParseColor(display,windows->map_info->colormap,
3521               resource_info->pen_colors[pen_number],&border_color);
3522             break;
3523           }
3524           case ColorEditFuzzCommand:
3525           {
3526             static char
3527               fuzz[MaxTextExtent];
3528
3529             static const char
3530               *FuzzMenu[] =
3531               {
3532                 "0%",
3533                 "2%",
3534                 "5%",
3535                 "10%",
3536                 "15%",
3537                 "Dialog...",
3538                 (char *) NULL,
3539               };
3540
3541             /*
3542               Select a command from the pop-up menu.
3543             */
3544             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3545               command);
3546             if (entry < 0)
3547               break;
3548             if (entry != 5)
3549               {
3550                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3551                   QuantumRange+1.0);
3552                 break;
3553               }
3554             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3555             (void) XDialogWidget(display,windows,"Ok",
3556               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3557             if (*fuzz == '\0')
3558               break;
3559             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3560             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3561               1.0);
3562             break;
3563           }
3564           case ColorEditUndoCommand:
3565           {
3566             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3567               image,exception);
3568             break;
3569           }
3570           case ColorEditHelpCommand:
3571           default:
3572           {
3573             XTextViewWidget(display,resource_info,windows,MagickFalse,
3574               "Help Viewer - Image Annotation",ImageColorEditHelp);
3575             break;
3576           }
3577           case ColorEditDismissCommand:
3578           {
3579             /*
3580               Prematurely exit.
3581             */
3582             state|=EscapeState;
3583             state|=ExitState;
3584             break;
3585           }
3586         }
3587         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3588         continue;
3589       }
3590     switch (event.type)
3591     {
3592       case ButtonPress:
3593       {
3594         if (event.xbutton.button != Button1)
3595           break;
3596         if ((event.xbutton.window != windows->image.id) &&
3597             (event.xbutton.window != windows->magnify.id))
3598           break;
3599         /*
3600           exit loop.
3601         */
3602         x=event.xbutton.x;
3603         y=event.xbutton.y;
3604         (void) XMagickCommand(display,resource_info,windows,
3605           SaveToUndoBufferCommand,image,exception);
3606         state|=UpdateConfigurationState;
3607         break;
3608       }
3609       case ButtonRelease:
3610       {
3611         if (event.xbutton.button != Button1)
3612           break;
3613         if ((event.xbutton.window != windows->image.id) &&
3614             (event.xbutton.window != windows->magnify.id))
3615           break;
3616         /*
3617           Update colormap information.
3618         */
3619         x=event.xbutton.x;
3620         y=event.xbutton.y;
3621         XConfigureImageColormap(display,resource_info,windows,*image,exception);
3622         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3623         XInfoWidget(display,windows,text);
3624         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3625         state&=(~UpdateConfigurationState);
3626         break;
3627       }
3628       case Expose:
3629         break;
3630       case KeyPress:
3631       {
3632         KeySym
3633           key_symbol;
3634
3635         if (event.xkey.window == windows->magnify.id)
3636           {
3637             Window
3638               window;
3639
3640             window=windows->magnify.id;
3641             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3642           }
3643         if (event.xkey.window != windows->image.id)
3644           break;
3645         /*
3646           Respond to a user key press.
3647         */
3648         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3649           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3650         switch ((int) key_symbol)
3651         {
3652           case XK_Escape:
3653           case XK_F20:
3654           {
3655             /*
3656               Prematurely exit.
3657             */
3658             state|=ExitState;
3659             break;
3660           }
3661           case XK_F1:
3662           case XK_Help:
3663           {
3664             XTextViewWidget(display,resource_info,windows,MagickFalse,
3665               "Help Viewer - Image Annotation",ImageColorEditHelp);
3666             break;
3667           }
3668           default:
3669           {
3670             (void) XBell(display,0);
3671             break;
3672           }
3673         }
3674         break;
3675       }
3676       case MotionNotify:
3677       {
3678         /*
3679           Map and unmap Info widget as cursor crosses its boundaries.
3680         */
3681         x=event.xmotion.x;
3682         y=event.xmotion.y;
3683         if (IfMagickTrue(windows->info.mapped) )
3684           {
3685             if ((x < (int) (windows->info.x+windows->info.width)) &&
3686                 (y < (int) (windows->info.y+windows->info.height)))
3687               (void) XWithdrawWindow(display,windows->info.id,
3688                 windows->info.screen);
3689           }
3690         else
3691           if ((x > (int) (windows->info.x+windows->info.width)) ||
3692               (y > (int) (windows->info.y+windows->info.height)))
3693             (void) XMapWindow(display,windows->info.id);
3694         break;
3695       }
3696       default:
3697         break;
3698     }
3699     if (event.xany.window == windows->magnify.id)
3700       {
3701         x=windows->magnify.x-windows->image.x;
3702         y=windows->magnify.y-windows->image.y;
3703       }
3704     x_offset=x;
3705     y_offset=y;
3706     if ((state & UpdateConfigurationState) != 0)
3707       {
3708         CacheView
3709           *image_view;
3710
3711         int
3712           x,
3713           y;
3714
3715         /*
3716           Pixel edit is relative to image configuration.
3717         */
3718         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3719           MagickTrue);
3720         color=windows->pixel_info->pen_colors[pen_id];
3721         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3722         width=(unsigned int) (*image)->columns;
3723         height=(unsigned int) (*image)->rows;
3724         x=0;
3725         y=0;
3726         if (windows->image.crop_geometry != (char *) NULL)
3727           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3728             &width,&height);
3729         x_offset=(int)
3730           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3731         y_offset=(int)
3732           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3733         if ((x_offset < 0) || (y_offset < 0))
3734           continue;
3735         if ((x_offset >= (int) (*image)->columns) ||
3736             (y_offset >= (int) (*image)->rows))
3737           continue;
3738         image_view=AcquireAuthenticCacheView(*image,exception);
3739         switch (method)
3740         {
3741           case PointMethod:
3742           default:
3743           {
3744             /*
3745               Update color information using point algorithm.
3746             */
3747             if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3748               return(MagickFalse);
3749             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3750               (ssize_t) y_offset,1,1,exception);
3751             if (q == (Quantum *) NULL)
3752               break;
3753             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3754             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3755             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3756             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3757             break;
3758           }
3759           case ReplaceMethod:
3760           {
3761             PixelInfo
3762               pixel,
3763               target;
3764
3765             /*
3766               Update color information using replace algorithm.
3767             */
3768             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3769               x_offset,(ssize_t) y_offset,&target,exception);
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                     GetPixelInfoPixel(*image,q,&pixel);
3781                     if (IsFuzzyEquivalencePixelInfo(&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 (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3793                     break;
3794                 }
3795               }
3796             else
3797               {
3798                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3799                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3800                     {
3801                       (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3802                         color.red);
3803                       (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3804                         color.green);
3805                       (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3806                         color.blue);
3807                     }
3808                 (void) SyncImage(*image,exception);
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) GetOneVirtualPixelInfo(*image,
3825               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3826               y_offset,&target,exception);
3827             if (method == FillToBorderMethod)
3828               {
3829                 target.red=(double)
3830                   ScaleShortToQuantum(border_color.red);
3831                 target.green=(double)
3832                   ScaleShortToQuantum(border_color.green);
3833                 target.blue=(double)
3834                   ScaleShortToQuantum(border_color.blue);
3835               }
3836             draw_info=CloneDrawInfo(resource_info->image_info,
3837               (DrawInfo *) NULL);
3838             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3839               AllCompliance,&draw_info->fill,exception);
3840             (void) FloodfillPaintImage(*image,draw_info,&target,
3841               (ssize_t)x_offset,(ssize_t)y_offset,
3842               IsMagickFalse(method == FloodfillMethod),exception);
3843             draw_info=DestroyDrawInfo(draw_info);
3844             break;
3845           }
3846           case ResetMethod:
3847           {
3848             /*
3849               Update color information using reset algorithm.
3850             */
3851             if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3852               return(MagickFalse);
3853             for (y=0; y < (int) (*image)->rows; y++)
3854             {
3855               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3856                 (*image)->columns,1,exception);
3857               if (q == (Quantum *) NULL)
3858                 break;
3859               for (x=0; x < (int) (*image)->columns; x++)
3860               {
3861                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3862                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3863                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3864                 q+=GetPixelChannels(*image);
3865               }
3866               if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3867                 break;
3868             }
3869             break;
3870           }
3871         }
3872         image_view=DestroyCacheView(image_view);
3873         state&=(~UpdateConfigurationState);
3874       }
3875   } while ((state & ExitState) == 0);
3876   (void) XSelectInput(display,windows->image.id,
3877     windows->image.attributes.event_mask);
3878   XSetCursorState(display,windows,MagickFalse);
3879   (void) XFreeCursor(display,cursor);
3880   return(MagickTrue);
3881 }
3882 \f
3883 /*
3884 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3885 %                                                                             %
3886 %                                                                             %
3887 %                                                                             %
3888 +   X C o m p o s i t e I m a g e                                             %
3889 %                                                                             %
3890 %                                                                             %
3891 %                                                                             %
3892 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3893 %
3894 %  XCompositeImage() requests an image name from the user, reads the image and
3895 %  composites it with the X window image at a location the user chooses with
3896 %  the pointer.
3897 %
3898 %  The format of the XCompositeImage method is:
3899 %
3900 %      MagickBooleanType XCompositeImage(Display *display,
3901 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3902 %        ExceptionInfo *exception)
3903 %
3904 %  A description of each parameter follows:
3905 %
3906 %    o display: Specifies a connection to an X server;  returned from
3907 %      XOpenDisplay.
3908 %
3909 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3910 %
3911 %    o windows: Specifies a pointer to a XWindows structure.
3912 %
3913 %    o image: the image; returned from ReadImage.
3914 %
3915 %    o exception: return any errors or warnings in this structure.
3916 %
3917 */
3918 static MagickBooleanType XCompositeImage(Display *display,
3919   XResourceInfo *resource_info,XWindows *windows,Image *image,
3920   ExceptionInfo *exception)
3921 {
3922   static char
3923     displacement_geometry[MaxTextExtent] = "30x30",
3924     filename[MaxTextExtent] = "\0";
3925
3926   static const char
3927     *CompositeMenu[] =
3928     {
3929       "Operators",
3930       "Dissolve",
3931       "Displace",
3932       "Help",
3933       "Dismiss",
3934       (char *) NULL
3935     };
3936
3937   static CompositeOperator
3938     compose = CopyCompositeOp;
3939
3940   static const ModeType
3941     CompositeCommands[] =
3942     {
3943       CompositeOperatorsCommand,
3944       CompositeDissolveCommand,
3945       CompositeDisplaceCommand,
3946       CompositeHelpCommand,
3947       CompositeDismissCommand
3948     };
3949
3950   char
3951     text[MaxTextExtent];
3952
3953   Cursor
3954     cursor;
3955
3956   Image
3957     *composite_image;
3958
3959   int
3960     entry,
3961     id,
3962     x,
3963     y;
3964
3965   double
3966     blend,
3967     scale_factor;
3968
3969   RectangleInfo
3970     highlight_info,
3971     composite_info;
3972
3973   unsigned int
3974     height,
3975     width;
3976
3977   size_t
3978     state;
3979
3980   XEvent
3981     event;
3982
3983   /*
3984     Request image file name from user.
3985   */
3986   XFileBrowserWidget(display,windows,"Composite",filename);
3987   if (*filename == '\0')
3988     return(MagickTrue);
3989   /*
3990     Read image.
3991   */
3992   XSetCursorState(display,windows,MagickTrue);
3993   XCheckRefreshWindows(display,windows);
3994   (void) CopyMagickString(resource_info->image_info->filename,filename,
3995     MaxTextExtent);
3996   composite_image=ReadImage(resource_info->image_info,exception);
3997   CatchException(exception);
3998   XSetCursorState(display,windows,MagickFalse);
3999   if (composite_image == (Image *) NULL)
4000     return(MagickFalse);
4001   /*
4002     Map Command widget.
4003   */
4004   (void) CloneString(&windows->command.name,"Composite");
4005   windows->command.data=1;
4006   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4007   (void) XMapRaised(display,windows->command.id);
4008   XClientMessage(display,windows->image.id,windows->im_protocols,
4009     windows->im_update_widget,CurrentTime);
4010   /*
4011     Track pointer until button 1 is pressed.
4012   */
4013   XQueryPosition(display,windows->image.id,&x,&y);
4014   (void) XSelectInput(display,windows->image.id,
4015     windows->image.attributes.event_mask | PointerMotionMask);
4016   composite_info.x=(ssize_t) windows->image.x+x;
4017   composite_info.y=(ssize_t) windows->image.y+y;
4018   composite_info.width=0;
4019   composite_info.height=0;
4020   cursor=XCreateFontCursor(display,XC_ul_angle);
4021   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4022   blend=0.0;
4023   state=DefaultState;
4024   do
4025   {
4026     if (IfMagickTrue(windows->info.mapped) )
4027       {
4028         /*
4029           Display pointer position.
4030         */
4031         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4032           (long) composite_info.x,(long) composite_info.y);
4033         XInfoWidget(display,windows,text);
4034       }
4035     highlight_info=composite_info;
4036     highlight_info.x=composite_info.x-windows->image.x;
4037     highlight_info.y=composite_info.y-windows->image.y;
4038     XHighlightRectangle(display,windows->image.id,
4039       windows->image.highlight_context,&highlight_info);
4040     /*
4041       Wait for next event.
4042     */
4043     XScreenEvent(display,windows,&event,exception);
4044     XHighlightRectangle(display,windows->image.id,
4045       windows->image.highlight_context,&highlight_info);
4046     if (event.xany.window == windows->command.id)
4047       {
4048         /*
4049           Select a command from the Command widget.
4050         */
4051         id=XCommandWidget(display,windows,CompositeMenu,&event);
4052         if (id < 0)
4053           continue;
4054         switch (CompositeCommands[id])
4055         {
4056           case CompositeOperatorsCommand:
4057           {
4058             char
4059               command[MaxTextExtent],
4060               **operators;
4061
4062             /*
4063               Select a command from the pop-up menu.
4064             */
4065             operators=GetCommandOptions(MagickComposeOptions);
4066             if (operators == (char **) NULL)
4067               break;
4068             entry=XMenuWidget(display,windows,CompositeMenu[id],
4069               (const char **) operators,command);
4070             if (entry >= 0)
4071               compose=(CompositeOperator) ParseCommandOption(
4072                 MagickComposeOptions,MagickFalse,operators[entry]);
4073             operators=DestroyStringList(operators);
4074             break;
4075           }
4076           case CompositeDissolveCommand:
4077           {
4078             static char
4079               factor[MaxTextExtent] = "20.0";
4080
4081             /*
4082               Dissolve the two images a given percent.
4083             */
4084             (void) XSetFunction(display,windows->image.highlight_context,
4085               GXcopy);
4086             (void) XDialogWidget(display,windows,"Dissolve",
4087               "Enter the blend factor (0.0 - 99.9%):",factor);
4088             (void) XSetFunction(display,windows->image.highlight_context,
4089               GXinvert);
4090             if (*factor == '\0')
4091               break;
4092             blend=StringToDouble(factor,(char **) NULL);
4093             compose=DissolveCompositeOp;
4094             break;
4095           }
4096           case CompositeDisplaceCommand:
4097           {
4098             /*
4099               Get horizontal and vertical scale displacement geometry.
4100             */
4101             (void) XSetFunction(display,windows->image.highlight_context,
4102               GXcopy);
4103             (void) XDialogWidget(display,windows,"Displace",
4104               "Enter the horizontal and vertical scale:",displacement_geometry);
4105             (void) XSetFunction(display,windows->image.highlight_context,
4106               GXinvert);
4107             if (*displacement_geometry == '\0')
4108               break;
4109             compose=DisplaceCompositeOp;
4110             break;
4111           }
4112           case CompositeHelpCommand:
4113           {
4114             (void) XSetFunction(display,windows->image.highlight_context,
4115               GXcopy);
4116             XTextViewWidget(display,resource_info,windows,MagickFalse,
4117               "Help Viewer - Image Composite",ImageCompositeHelp);
4118             (void) XSetFunction(display,windows->image.highlight_context,
4119               GXinvert);
4120             break;
4121           }
4122           case CompositeDismissCommand:
4123           {
4124             /*
4125               Prematurely exit.
4126             */
4127             state|=EscapeState;
4128             state|=ExitState;
4129             break;
4130           }
4131           default:
4132             break;
4133         }
4134         continue;
4135       }
4136     switch (event.type)
4137     {
4138       case ButtonPress:
4139       {
4140         if (IfMagickTrue(image->debug) )
4141           (void) LogMagickEvent(X11Event,GetMagickModule(),
4142             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4143             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4144         if (event.xbutton.button != Button1)
4145           break;
4146         if (event.xbutton.window != windows->image.id)
4147           break;
4148         /*
4149           Change cursor.
4150         */
4151         composite_info.width=composite_image->columns;
4152         composite_info.height=composite_image->rows;
4153         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4154         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4155         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4156         break;
4157       }
4158       case ButtonRelease:
4159       {
4160         if (IfMagickTrue(image->debug) )
4161           (void) LogMagickEvent(X11Event,GetMagickModule(),
4162             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4163             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4164         if (event.xbutton.button != Button1)
4165           break;
4166         if (event.xbutton.window != windows->image.id)
4167           break;
4168         if ((composite_info.width != 0) && (composite_info.height != 0))
4169           {
4170             /*
4171               User has selected the location of the composite image.
4172             */
4173             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4174             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4175             state|=ExitState;
4176           }
4177         break;
4178       }
4179       case Expose:
4180         break;
4181       case KeyPress:
4182       {
4183         char
4184           command[MaxTextExtent];
4185
4186         KeySym
4187           key_symbol;
4188
4189         int
4190           length;
4191
4192         if (event.xkey.window != windows->image.id)
4193           break;
4194         /*
4195           Respond to a user key press.
4196         */
4197         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4198           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4199         *(command+length)='\0';
4200         if (IfMagickTrue(image->debug) )
4201           (void) LogMagickEvent(X11Event,GetMagickModule(),
4202             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4203         switch ((int) key_symbol)
4204         {
4205           case XK_Escape:
4206           case XK_F20:
4207           {
4208             /*
4209               Prematurely exit.
4210             */
4211             composite_image=DestroyImage(composite_image);
4212             state|=EscapeState;
4213             state|=ExitState;
4214             break;
4215           }
4216           case XK_F1:
4217           case XK_Help:
4218           {
4219             (void) XSetFunction(display,windows->image.highlight_context,
4220               GXcopy);
4221             XTextViewWidget(display,resource_info,windows,MagickFalse,
4222               "Help Viewer - Image Composite",ImageCompositeHelp);
4223             (void) XSetFunction(display,windows->image.highlight_context,
4224               GXinvert);
4225             break;
4226           }
4227           default:
4228           {
4229             (void) XBell(display,0);
4230             break;
4231           }
4232         }
4233         break;
4234       }
4235       case MotionNotify:
4236       {
4237         /*
4238           Map and unmap Info widget as text cursor crosses its boundaries.
4239         */
4240         x=event.xmotion.x;
4241         y=event.xmotion.y;
4242         if (IfMagickTrue(windows->info.mapped) )
4243           {
4244             if ((x < (int) (windows->info.x+windows->info.width)) &&
4245                 (y < (int) (windows->info.y+windows->info.height)))
4246               (void) XWithdrawWindow(display,windows->info.id,
4247                 windows->info.screen);
4248           }
4249         else
4250           if ((x > (int) (windows->info.x+windows->info.width)) ||
4251               (y > (int) (windows->info.y+windows->info.height)))
4252             (void) XMapWindow(display,windows->info.id);
4253         composite_info.x=(ssize_t) windows->image.x+x;
4254         composite_info.y=(ssize_t) windows->image.y+y;
4255         break;
4256       }
4257       default:
4258       {
4259         if (IfMagickTrue(image->debug) )
4260           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4261             event.type);
4262         break;
4263       }
4264     }
4265   } while ((state & ExitState) == 0);
4266   (void) XSelectInput(display,windows->image.id,
4267     windows->image.attributes.event_mask);
4268   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4269   XSetCursorState(display,windows,MagickFalse);
4270   (void) XFreeCursor(display,cursor);
4271   if ((state & EscapeState) != 0)
4272     return(MagickTrue);
4273   /*
4274     Image compositing is relative to image configuration.
4275   */
4276   XSetCursorState(display,windows,MagickTrue);
4277   XCheckRefreshWindows(display,windows);
4278   width=(unsigned int) image->columns;
4279   height=(unsigned int) image->rows;
4280   x=0;
4281   y=0;
4282   if (windows->image.crop_geometry != (char *) NULL)
4283     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4284   scale_factor=(double) width/windows->image.ximage->width;
4285   composite_info.x+=x;
4286   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4287   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4288   scale_factor=(double) height/windows->image.ximage->height;
4289   composite_info.y+=y;
4290   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4291   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4292   if ((composite_info.width != composite_image->columns) ||
4293       (composite_info.height != composite_image->rows))
4294     {
4295       Image
4296         *resize_image;
4297
4298       /*
4299         Scale composite image.
4300       */
4301       resize_image=ResizeImage(composite_image,composite_info.width,
4302         composite_info.height,composite_image->filter,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(QuantumRange)-
4336         ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4337       if (IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
4338         return(MagickFalse);
4339       image->alpha_trait=BlendPixelTrait;
4340       image_view=AcquireAuthenticCacheView(image,exception);
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 (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
4353           break;
4354       }
4355       image_view=DestroyCacheView(image_view);
4356     }
4357   /*
4358     Composite image with X Image window.
4359   */
4360   (void) CompositeImage(image,composite_image,compose,MagickTrue,
4361     composite_info.x,composite_info.y,exception);
4362   composite_image=DestroyImage(composite_image);
4363   XSetCursorState(display,windows,MagickFalse);
4364   /*
4365     Update image configuration.
4366   */
4367   XConfigureImageColormap(display,resource_info,windows,image,exception);
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 (IfMagickTrue(image->debug) )
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 (IfMagickFalse(status) )
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 (IfMagickTrue(windows->magnify.mapped) )
4500     XMakeMagnifyImage(display,windows,exception);
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(IsMagickTrue(status));
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   double
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 (IfMagickTrue(windows->info.mapped) )
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,exception);
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 (IfMagickTrue(windows->info.mapped) )
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 (IfMagickFalse(windows->info.mapped) )
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 (IfMagickTrue(windows->info.mapped) )
4906           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4907       /*
4908         Wait for next event.
4909       */
4910       XScreenEvent(display,windows,&event,exception);
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 (IfMagickTrue(windows->info.mapped) )
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,exception);
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 (IfMagickTrue(windows->info.mapped) )
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=(double) 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=(double) 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 (IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
5359     return(MagickFalse);
5360   image->alpha_trait=BlendPixelTrait;
5361   image_view=AcquireAuthenticCacheView(image,exception);
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 (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
5374       break;
5375   }
5376   image_view=DestroyCacheView(image_view);
5377   /*
5378     Update image configuration.
5379   */
5380   XConfigureImageColormap(display,resource_info,windows,image,exception);
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   double
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 (IfMagickTrue(windows->info.mapped) )
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,exception);
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 (IfMagickTrue(transparent) )
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 (IfMagickTrue(windows->info.mapped) )
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 (IfMagickTrue(windows->info.mapped) )
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 (IfMagickTrue(windows->info.mapped) )
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 (IfMagickTrue(windows->info.mapped) )
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 (IfMagickTrue(windows->info.mapped) )
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,exception);
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,exception);
6334     XSetCursorState(display,windows,MagickFalse);
6335     /*
6336       Update image colormap and return to image drawing.
6337     */
6338     XConfigureImageColormap(display,resource_info,windows,*image,exception);
6339     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6340   }
6341   XSetCursorState(display,windows,MagickFalse);
6342   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6343   return(IsMagickTrue(status));
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   double
6376     scale_factor;
6377
6378   RectangleInfo
6379     highlight_info;
6380
6381   /*
6382     Determine dimensions of the panning rectangle.
6383   */
6384   scale_factor=(double) 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=(double)
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         exception);
6488       if (windows->image.crop_geometry != (char *) NULL)
6489         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6490           windows->image.crop_geometry);
6491       windows->image.crop_geometry=cache_image->geometry;
6492       if (redo_image != (Image *) NULL)
6493         redo_image=DestroyImage(redo_image);
6494       redo_image=(*image);
6495       *image=cache_image->list;
6496       cache_image=DestroyImage(cache_image);
6497       if (IfMagickTrue(windows->image.orphan) )
6498         return;
6499       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6500       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6501       return;
6502     }
6503     case CutCommand:
6504     case PasteCommand:
6505     case ApplyCommand:
6506     case HalfSizeCommand:
6507     case OriginalSizeCommand:
6508     case DoubleSizeCommand:
6509     case ResizeCommand:
6510     case TrimCommand:
6511     case CropCommand:
6512     case ChopCommand:
6513     case FlipCommand:
6514     case FlopCommand:
6515     case RotateRightCommand:
6516     case RotateLeftCommand:
6517     case RotateCommand:
6518     case ShearCommand:
6519     case RollCommand:
6520     case NegateCommand:
6521     case ContrastStretchCommand:
6522     case SigmoidalContrastCommand:
6523     case NormalizeCommand:
6524     case EqualizeCommand:
6525     case HueCommand:
6526     case SaturationCommand:
6527     case BrightnessCommand:
6528     case GammaCommand:
6529     case SpiffCommand:
6530     case DullCommand:
6531     case GrayscaleCommand:
6532     case MapCommand:
6533     case QuantizeCommand:
6534     case DespeckleCommand:
6535     case EmbossCommand:
6536     case ReduceNoiseCommand:
6537     case AddNoiseCommand:
6538     case SharpenCommand:
6539     case BlurCommand:
6540     case ThresholdCommand:
6541     case EdgeDetectCommand:
6542     case SpreadCommand:
6543     case ShadeCommand:
6544     case RaiseCommand:
6545     case SegmentCommand:
6546     case SolarizeCommand:
6547     case SepiaToneCommand:
6548     case SwirlCommand:
6549     case ImplodeCommand:
6550     case VignetteCommand:
6551     case WaveCommand:
6552     case OilPaintCommand:
6553     case CharcoalDrawCommand:
6554     case AnnotateCommand:
6555     case AddBorderCommand:
6556     case AddFrameCommand:
6557     case CompositeCommand:
6558     case CommentCommand:
6559     case LaunchCommand:
6560     case RegionofInterestCommand:
6561     case SaveToUndoBufferCommand:
6562     case RedoCommand:
6563     {
6564       Image
6565         *previous_image;
6566
6567       ssize_t
6568         bytes;
6569
6570       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6571       if (undo_image != (Image *) NULL)
6572         {
6573           /*
6574             Ensure the undo cache has enough memory available.
6575           */
6576           previous_image=undo_image;
6577           while (previous_image != (Image *) NULL)
6578           {
6579             bytes+=previous_image->list->columns*previous_image->list->rows*
6580               sizeof(PixelInfo);
6581             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6582               {
6583                 previous_image=GetPreviousImageInList(previous_image);
6584                 continue;
6585               }
6586             bytes-=previous_image->list->columns*previous_image->list->rows*
6587               sizeof(PixelInfo);
6588             if (previous_image == undo_image)
6589               undo_image=NewImageList();
6590             else
6591               previous_image->next->previous=NewImageList();
6592             break;
6593           }
6594           while (previous_image != (Image *) NULL)
6595           {
6596             /*
6597               Delete any excess memory from undo cache.
6598             */
6599             cache_image=previous_image;
6600             previous_image=GetPreviousImageInList(previous_image);
6601             cache_image->list=DestroyImage(cache_image->list);
6602             cache_image=DestroyImage(cache_image);
6603           }
6604         }
6605       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6606         break;
6607       /*
6608         Save image before transformations are applied.
6609       */
6610       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6611       if (cache_image == (Image *) NULL)
6612         break;
6613       XSetCursorState(display,windows,MagickTrue);
6614       XCheckRefreshWindows(display,windows);
6615       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6616       XSetCursorState(display,windows,MagickFalse);
6617       if (cache_image->list == (Image *) NULL)
6618         {
6619           cache_image=DestroyImage(cache_image);
6620           break;
6621         }
6622       cache_image->columns=(size_t) windows->image.ximage->width;
6623       cache_image->rows=(size_t) windows->image.ximage->height;
6624       cache_image->geometry=windows->image.crop_geometry;
6625       if (windows->image.crop_geometry != (char *) NULL)
6626         {
6627           cache_image->geometry=AcquireString((char *) NULL);
6628           (void) CopyMagickString(cache_image->geometry,
6629             windows->image.crop_geometry,MaxTextExtent);
6630         }
6631       if (undo_image == (Image *) NULL)
6632         {
6633           undo_image=cache_image;
6634           break;
6635         }
6636       undo_image->next=cache_image;
6637       undo_image->next->previous=undo_image;
6638       undo_image=undo_image->next;
6639       break;
6640     }
6641     default:
6642       break;
6643   }
6644   if (command == RedoCommand)
6645     {
6646       /*
6647         Redo the last image transformation.
6648       */
6649       if (redo_image == (Image *) NULL)
6650         {
6651           (void) XBell(display,0);
6652           return;
6653         }
6654       windows->image.window_changes.width=(int) redo_image->columns;
6655       windows->image.window_changes.height=(int) redo_image->rows;
6656       if (windows->image.crop_geometry != (char *) NULL)
6657         windows->image.crop_geometry=(char *)
6658           RelinquishMagickMemory(windows->image.crop_geometry);
6659       windows->image.crop_geometry=redo_image->geometry;
6660       *image=DestroyImage(*image);
6661       *image=redo_image;
6662       redo_image=NewImageList();
6663       if (IfMagickTrue(windows->image.orphan) )
6664         return;
6665       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6666       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6667       return;
6668     }
6669   if (command != InfoCommand)
6670     return;
6671   /*
6672     Display image info.
6673   */
6674   XSetCursorState(display,windows,MagickTrue);
6675   XCheckRefreshWindows(display,windows);
6676   XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6677   XSetCursorState(display,windows,MagickFalse);
6678 }
6679 \f
6680 /*
6681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6682 %                                                                             %
6683 %                                                                             %
6684 %                                                                             %
6685 +   X I m a g e W i n d o w C o m m a n d                                     %
6686 %                                                                             %
6687 %                                                                             %
6688 %                                                                             %
6689 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6690 %
6691 %  XImageWindowCommand() makes a transform to the image or Image window as
6692 %  specified by a user menu button or keyboard command.
6693 %
6694 %  The format of the XImageWindowCommand method is:
6695 %
6696 %      CommandType XImageWindowCommand(Display *display,
6697 %        XResourceInfo *resource_info,XWindows *windows,
6698 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6699 %        ExceptionInfo *exception)
6700 %
6701 %  A description of each parameter follows:
6702 %
6703 %    o nexus:  Method XImageWindowCommand returns an image when the
6704 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6705 %      image is returned.
6706 %
6707 %    o display: Specifies a connection to an X server; returned from
6708 %      XOpenDisplay.
6709 %
6710 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6711 %
6712 %    o windows: Specifies a pointer to a XWindows structure.
6713 %
6714 %    o state: key mask.
6715 %
6716 %    o key_symbol: Specifies a command to perform.
6717 %
6718 %    o image: the image;  XImageWIndowCommand may transform the image and
6719 %      return a new image pointer.
6720 %
6721 %    o exception: return any errors or warnings in this structure.
6722 %
6723 */
6724 static CommandType XImageWindowCommand(Display *display,
6725   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6726   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6727 {
6728   static char
6729     delta[MaxTextExtent] = "";
6730
6731   static const char
6732     Digits[] = "01234567890";
6733
6734   static KeySym
6735     last_symbol = XK_0;
6736
6737   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6738     {
6739       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6740         {
6741           *delta='\0';
6742           resource_info->quantum=1;
6743         }
6744       last_symbol=key_symbol;
6745       delta[strlen(delta)+1]='\0';
6746       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6747       resource_info->quantum=StringToLong(delta);
6748       return(NullCommand);
6749     }
6750   last_symbol=key_symbol;
6751   if (resource_info->immutable)
6752     {
6753       /*
6754         Virtual image window has a restricted command set.
6755       */
6756       switch (key_symbol)
6757       {
6758         case XK_question:
6759           return(InfoCommand);
6760         case XK_p:
6761         case XK_Print:
6762           return(PrintCommand);
6763         case XK_space:
6764           return(NextCommand);
6765         case XK_q:
6766         case XK_Escape:
6767           return(QuitCommand);
6768         default:
6769           break;
6770       }
6771       return(NullCommand);
6772     }
6773   switch ((int) key_symbol)
6774   {
6775     case XK_o:
6776     {
6777       if ((state & ControlMask) == 0)
6778         break;
6779       return(OpenCommand);
6780     }
6781     case XK_space:
6782       return(NextCommand);
6783     case XK_BackSpace:
6784       return(FormerCommand);
6785     case XK_s:
6786     {
6787       if ((state & Mod1Mask) != 0)
6788         return(SwirlCommand);
6789       if ((state & ControlMask) == 0)
6790         return(ShearCommand);
6791       return(SaveCommand);
6792     }
6793     case XK_p:
6794     case XK_Print:
6795     {
6796       if ((state & Mod1Mask) != 0)
6797         return(OilPaintCommand);
6798       if ((state & Mod4Mask) != 0)
6799         return(ColorCommand);
6800       if ((state & ControlMask) == 0)
6801         return(NullCommand);
6802       return(PrintCommand);
6803     }
6804     case XK_d:
6805     {
6806       if ((state & Mod4Mask) != 0)
6807         return(DrawCommand);
6808       if ((state & ControlMask) == 0)
6809         return(NullCommand);
6810       return(DeleteCommand);
6811     }
6812     case XK_Select:
6813     {
6814       if ((state & ControlMask) == 0)
6815         return(NullCommand);
6816       return(SelectCommand);
6817     }
6818     case XK_n:
6819     {
6820       if ((state & ControlMask) == 0)
6821         return(NullCommand);
6822       return(NewCommand);
6823     }
6824     case XK_q:
6825     case XK_Escape:
6826       return(QuitCommand);
6827     case XK_z:
6828     case XK_Undo:
6829     {
6830       if ((state & ControlMask) == 0)
6831         return(NullCommand);
6832       return(UndoCommand);
6833     }
6834     case XK_r:
6835     case XK_Redo:
6836     {
6837       if ((state & ControlMask) == 0)
6838         return(RollCommand);
6839       return(RedoCommand);
6840     }
6841     case XK_x:
6842     {
6843       if ((state & ControlMask) == 0)
6844         return(NullCommand);
6845       return(CutCommand);
6846     }
6847     case XK_c:
6848     {
6849       if ((state & Mod1Mask) != 0)
6850         return(CharcoalDrawCommand);
6851       if ((state & ControlMask) == 0)
6852         return(CropCommand);
6853       return(CopyCommand);
6854     }
6855     case XK_v:
6856     case XK_Insert:
6857     {
6858       if ((state & Mod4Mask) != 0)
6859         return(CompositeCommand);
6860       if ((state & ControlMask) == 0)
6861         return(FlipCommand);
6862       return(PasteCommand);
6863     }
6864     case XK_less:
6865       return(HalfSizeCommand);
6866     case XK_minus:
6867       return(OriginalSizeCommand);
6868     case XK_greater:
6869       return(DoubleSizeCommand);
6870     case XK_percent:
6871       return(ResizeCommand);
6872     case XK_at:
6873       return(RefreshCommand);
6874     case XK_bracketleft:
6875       return(ChopCommand);
6876     case XK_h:
6877       return(FlopCommand);
6878     case XK_slash:
6879       return(RotateRightCommand);
6880     case XK_backslash:
6881       return(RotateLeftCommand);
6882     case XK_asterisk:
6883       return(RotateCommand);
6884     case XK_t:
6885       return(TrimCommand);
6886     case XK_H:
6887       return(HueCommand);
6888     case XK_S:
6889       return(SaturationCommand);
6890     case XK_L:
6891       return(BrightnessCommand);
6892     case XK_G:
6893       return(GammaCommand);
6894     case XK_C:
6895       return(SpiffCommand);
6896     case XK_Z:
6897       return(DullCommand);
6898     case XK_N:
6899       return(NormalizeCommand);
6900     case XK_equal:
6901       return(EqualizeCommand);
6902     case XK_asciitilde:
6903       return(NegateCommand);
6904     case XK_period:
6905       return(GrayscaleCommand);
6906     case XK_numbersign:
6907       return(QuantizeCommand);
6908     case XK_F2:
6909       return(DespeckleCommand);
6910     case XK_F3:
6911       return(EmbossCommand);
6912     case XK_F4:
6913       return(ReduceNoiseCommand);
6914     case XK_F5:
6915       return(AddNoiseCommand);
6916     case XK_F6:
6917       return(SharpenCommand);
6918     case XK_F7:
6919       return(BlurCommand);
6920     case XK_F8:
6921       return(ThresholdCommand);
6922     case XK_F9:
6923       return(EdgeDetectCommand);
6924     case XK_F10:
6925       return(SpreadCommand);
6926     case XK_F11:
6927       return(ShadeCommand);
6928     case XK_F12:
6929       return(RaiseCommand);
6930     case XK_F13:
6931       return(SegmentCommand);
6932     case XK_i:
6933     {
6934       if ((state & Mod1Mask) == 0)
6935         return(NullCommand);
6936       return(ImplodeCommand);
6937     }
6938     case XK_w:
6939     {
6940       if ((state & Mod1Mask) == 0)
6941         return(NullCommand);
6942       return(WaveCommand);
6943     }
6944     case XK_m:
6945     {
6946       if ((state & Mod4Mask) == 0)
6947         return(NullCommand);
6948       return(MatteCommand);
6949     }
6950     case XK_b:
6951     {
6952       if ((state & Mod4Mask) == 0)
6953         return(NullCommand);
6954       return(AddBorderCommand);
6955     }
6956     case XK_f:
6957     {
6958       if ((state & Mod4Mask) == 0)
6959         return(NullCommand);
6960       return(AddFrameCommand);
6961     }
6962     case XK_exclam:
6963     {
6964       if ((state & Mod4Mask) == 0)
6965         return(NullCommand);
6966       return(CommentCommand);
6967     }
6968     case XK_a:
6969     {
6970       if ((state & Mod1Mask) != 0)
6971         return(ApplyCommand);
6972       if ((state & Mod4Mask) != 0)
6973         return(AnnotateCommand);
6974       if ((state & ControlMask) == 0)
6975         return(NullCommand);
6976       return(RegionofInterestCommand);
6977     }
6978     case XK_question:
6979       return(InfoCommand);
6980     case XK_plus:
6981       return(ZoomCommand);
6982     case XK_P:
6983     {
6984       if ((state & ShiftMask) == 0)
6985         return(NullCommand);
6986       return(ShowPreviewCommand);
6987     }
6988     case XK_Execute:
6989       return(LaunchCommand);
6990     case XK_F1:
6991       return(HelpCommand);
6992     case XK_Find:
6993       return(BrowseDocumentationCommand);
6994     case XK_Menu:
6995     {
6996       (void) XMapRaised(display,windows->command.id);
6997       return(NullCommand);
6998     }
6999     case XK_Next:
7000     case XK_Prior:
7001     case XK_Home:
7002     case XK_KP_Home:
7003     {
7004       XTranslateImage(display,windows,*image,key_symbol);
7005       return(NullCommand);
7006     }
7007     case XK_Up:
7008     case XK_KP_Up:
7009     case XK_Down:
7010     case XK_KP_Down:
7011     case XK_Left:
7012     case XK_KP_Left:
7013     case XK_Right:
7014     case XK_KP_Right:
7015     {
7016       if ((state & Mod1Mask) != 0)
7017         {
7018           RectangleInfo
7019             crop_info;
7020
7021           /*
7022             Trim one pixel from edge of image.
7023           */
7024           crop_info.x=0;
7025           crop_info.y=0;
7026           crop_info.width=(size_t) windows->image.ximage->width;
7027           crop_info.height=(size_t) windows->image.ximage->height;
7028           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7029             {
7030               if (resource_info->quantum >= (int) crop_info.height)
7031                 resource_info->quantum=(int) crop_info.height-1;
7032               crop_info.height-=resource_info->quantum;
7033             }
7034           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7035             {
7036               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7037                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7038               crop_info.y+=resource_info->quantum;
7039               crop_info.height-=resource_info->quantum;
7040             }
7041           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7042             {
7043               if (resource_info->quantum >= (int) crop_info.width)
7044                 resource_info->quantum=(int) crop_info.width-1;
7045               crop_info.width-=resource_info->quantum;
7046             }
7047           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7048             {
7049               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7050                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7051               crop_info.x+=resource_info->quantum;
7052               crop_info.width-=resource_info->quantum;
7053             }
7054           if ((int) (windows->image.x+windows->image.width) >
7055               (int) crop_info.width)
7056             windows->image.x=(int) (crop_info.width-windows->image.width);
7057           if ((int) (windows->image.y+windows->image.height) >
7058               (int) crop_info.height)
7059             windows->image.y=(int) (crop_info.height-windows->image.height);
7060           XSetCropGeometry(display,windows,&crop_info,*image);
7061           windows->image.window_changes.width=(int) crop_info.width;
7062           windows->image.window_changes.height=(int) crop_info.height;
7063           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7064           (void) XConfigureImage(display,resource_info,windows,*image,
7065             exception);
7066           return(NullCommand);
7067         }
7068       XTranslateImage(display,windows,*image,key_symbol);
7069       return(NullCommand);
7070     }
7071     default:
7072       return(NullCommand);
7073   }
7074   return(NullCommand);
7075 }
7076 \f
7077 /*
7078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7079 %                                                                             %
7080 %                                                                             %
7081 %                                                                             %
7082 +   X M a g i c k C o m m a n d                                               %
7083 %                                                                             %
7084 %                                                                             %
7085 %                                                                             %
7086 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7087 %
7088 %  XMagickCommand() makes a transform to the image or Image window as
7089 %  specified by a user menu button or keyboard command.
7090 %
7091 %  The format of the XMagickCommand method is:
7092 %
7093 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7094 %        XWindows *windows,const CommandType command,Image **image,
7095 %        ExceptionInfo *exception)
7096 %
7097 %  A description of each parameter follows:
7098 %
7099 %    o display: Specifies a connection to an X server; returned from
7100 %      XOpenDisplay.
7101 %
7102 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7103 %
7104 %    o windows: Specifies a pointer to a XWindows structure.
7105 %
7106 %    o command: Specifies a command to perform.
7107 %
7108 %    o image: the image;  XMagickCommand may transform the image and return a
7109 %      new image pointer.
7110 %
7111 %    o exception: return any errors or warnings in this structure.
7112 %
7113 */
7114 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7115   XWindows *windows,const CommandType command,Image **image,
7116   ExceptionInfo *exception)
7117 {
7118   char
7119     filename[MaxTextExtent],
7120     geometry[MaxTextExtent],
7121     modulate_factors[MaxTextExtent];
7122
7123   GeometryInfo
7124     geometry_info;
7125
7126   Image
7127     *nexus;
7128
7129   ImageInfo
7130     *image_info;
7131
7132   int
7133     x,
7134     y;
7135
7136   MagickStatusType
7137     flags,
7138     status;
7139
7140   QuantizeInfo
7141     quantize_info;
7142
7143   RectangleInfo
7144     page_geometry;
7145
7146   register int
7147     i;
7148
7149   static char
7150     color[MaxTextExtent] = "gray";
7151
7152   unsigned int
7153     height,
7154     width;
7155
7156   /*
7157     Process user command.
7158   */
7159   XCheckRefreshWindows(display,windows);
7160   XImageCache(display,resource_info,windows,command,image,exception);
7161   nexus=NewImageList();
7162   windows->image.window_changes.width=windows->image.ximage->width;
7163   windows->image.window_changes.height=windows->image.ximage->height;
7164   image_info=CloneImageInfo(resource_info->image_info);
7165   SetGeometryInfo(&geometry_info);
7166   GetQuantizeInfo(&quantize_info);
7167   switch (command)
7168   {
7169     case OpenCommand:
7170     {
7171       /*
7172         Load image.
7173       */
7174       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7175       break;
7176     }
7177     case NextCommand:
7178     {
7179       /*
7180         Display next image.
7181       */
7182       for (i=0; i < resource_info->quantum; i++)
7183         XClientMessage(display,windows->image.id,windows->im_protocols,
7184           windows->im_next_image,CurrentTime);
7185       break;
7186     }
7187     case FormerCommand:
7188     {
7189       /*
7190         Display former image.
7191       */
7192       for (i=0; i < resource_info->quantum; i++)
7193         XClientMessage(display,windows->image.id,windows->im_protocols,
7194           windows->im_former_image,CurrentTime);
7195       break;
7196     }
7197     case SelectCommand:
7198     {
7199       int
7200         status;
7201
7202       /*
7203         Select image.
7204       */
7205       if (*resource_info->home_directory == '\0')
7206         (void) CopyMagickString(resource_info->home_directory,".",
7207           MaxTextExtent);
7208       status=chdir(resource_info->home_directory);
7209       if (status == -1)
7210         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7211           "UnableToOpenFile","%s",resource_info->home_directory);
7212       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7213       break;
7214     }
7215     case SaveCommand:
7216     {
7217       /*
7218         Save image.
7219       */
7220       status=XSaveImage(display,resource_info,windows,*image,exception);
7221       if (IfMagickFalse(status) )
7222         {
7223           char
7224             message[MaxTextExtent];
7225
7226           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7227             exception->reason != (char *) NULL ? exception->reason : "",
7228             exception->description != (char *) NULL ? exception->description :
7229             "");
7230           XNoticeWidget(display,windows,"Unable to save file:",message);
7231           break;
7232         }
7233       break;
7234     }
7235     case PrintCommand:
7236     {
7237       /*
7238         Print image.
7239       */
7240       status=XPrintImage(display,resource_info,windows,*image,exception);
7241       if (IfMagickFalse(status) )
7242         {
7243           char
7244             message[MaxTextExtent];
7245
7246           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7247             exception->reason != (char *) NULL ? exception->reason : "",
7248             exception->description != (char *) NULL ? exception->description :
7249             "");
7250           XNoticeWidget(display,windows,"Unable to print file:",message);
7251           break;
7252         }
7253       break;
7254     }
7255     case DeleteCommand:
7256     {
7257       static char
7258         filename[MaxTextExtent] = "\0";
7259
7260       /*
7261         Delete image file.
7262       */
7263       XFileBrowserWidget(display,windows,"Delete",filename);
7264       if (*filename == '\0')
7265         break;
7266       status=ShredFile(filename);
7267       if (IfMagickTrue(status) )
7268         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7269       break;
7270     }
7271     case NewCommand:
7272     {
7273       int
7274         status;
7275
7276       static char
7277         color[MaxTextExtent] = "gray",
7278         geometry[MaxTextExtent] = "640x480";
7279
7280       static const char
7281         *format = "gradient";
7282
7283       /*
7284         Query user for canvas geometry.
7285       */
7286       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7287         geometry);
7288       if (*geometry == '\0')
7289         break;
7290       if (status == 0)
7291         format="xc";
7292       XColorBrowserWidget(display,windows,"Select",color);
7293       if (*color == '\0')
7294         break;
7295       /*
7296         Create canvas.
7297       */
7298       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7299         "%s:%s",format,color);
7300       (void) CloneString(&image_info->size,geometry);
7301       nexus=ReadImage(image_info,exception);
7302       CatchException(exception);
7303       XClientMessage(display,windows->image.id,windows->im_protocols,
7304         windows->im_next_image,CurrentTime);
7305       break;
7306     }
7307     case VisualDirectoryCommand:
7308     {
7309       /*
7310         Visual Image directory.
7311       */
7312       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7313       break;
7314     }
7315     case QuitCommand:
7316     {
7317       /*
7318         exit program.
7319       */
7320       if (IfMagickFalse(resource_info->confirm_exit) )
7321         XClientMessage(display,windows->image.id,windows->im_protocols,
7322           windows->im_exit,CurrentTime);
7323       else
7324         {
7325           int
7326             status;
7327
7328           /*
7329             Confirm program exit.
7330           */
7331           status=XConfirmWidget(display,windows,"Do you really want to exit",
7332             resource_info->client_name);
7333           if (status > 0)
7334             XClientMessage(display,windows->image.id,windows->im_protocols,
7335               windows->im_exit,CurrentTime);
7336         }
7337       break;
7338     }
7339     case CutCommand:
7340     {
7341       /*
7342         Cut image.
7343       */
7344       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7345       break;
7346     }
7347     case CopyCommand:
7348     {
7349       /*
7350         Copy image.
7351       */
7352       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7353         exception);
7354       break;
7355     }
7356     case PasteCommand:
7357     {
7358       /*
7359         Paste image.
7360       */
7361       status=XPasteImage(display,resource_info,windows,*image,exception);
7362       if (IfMagickFalse(status) )
7363         {
7364           XNoticeWidget(display,windows,"Unable to paste X image",
7365             (*image)->filename);
7366           break;
7367         }
7368       break;
7369     }
7370     case HalfSizeCommand:
7371     {
7372       /*
7373         Half image size.
7374       */
7375       windows->image.window_changes.width=windows->image.ximage->width/2;
7376       windows->image.window_changes.height=windows->image.ximage->height/2;
7377       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7378       break;
7379     }
7380     case OriginalSizeCommand:
7381     {
7382       /*
7383         Original image size.
7384       */
7385       windows->image.window_changes.width=(int) (*image)->columns;
7386       windows->image.window_changes.height=(int) (*image)->rows;
7387       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7388       break;
7389     }
7390     case DoubleSizeCommand:
7391     {
7392       /*
7393         Double the image size.
7394       */
7395       windows->image.window_changes.width=windows->image.ximage->width << 1;
7396       windows->image.window_changes.height=windows->image.ximage->height << 1;
7397       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7398       break;
7399     }
7400     case ResizeCommand:
7401     {
7402       int
7403         status;
7404
7405       size_t
7406         height,
7407         width;
7408
7409       ssize_t
7410         x,
7411         y;
7412
7413       /*
7414         Resize image.
7415       */
7416       width=(size_t) windows->image.ximage->width;
7417       height=(size_t) windows->image.ximage->height;
7418       x=0;
7419       y=0;
7420       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7421         (double) width,(double) height);
7422       status=XDialogWidget(display,windows,"Resize",
7423         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7424       if (*geometry == '\0')
7425         break;
7426       if (status == 0)
7427         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7428       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7429       windows->image.window_changes.width=(int) width;
7430       windows->image.window_changes.height=(int) height;
7431       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7432       break;
7433     }
7434     case ApplyCommand:
7435     {
7436       char
7437         image_geometry[MaxTextExtent];
7438
7439       if ((windows->image.crop_geometry == (char *) NULL) &&
7440           ((int) (*image)->columns == windows->image.ximage->width) &&
7441           ((int) (*image)->rows == windows->image.ximage->height))
7442         break;
7443       /*
7444         Apply size transforms to image.
7445       */
7446       XSetCursorState(display,windows,MagickTrue);
7447       XCheckRefreshWindows(display,windows);
7448       /*
7449         Crop and/or scale displayed image.
7450       */
7451       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7452         windows->image.ximage->width,windows->image.ximage->height);
7453       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7454         exception);
7455       if (windows->image.crop_geometry != (char *) NULL)
7456         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7457           windows->image.crop_geometry);
7458       windows->image.x=0;
7459       windows->image.y=0;
7460       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7461       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7462       break;
7463     }
7464     case RefreshCommand:
7465     {
7466       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7467       break;
7468     }
7469     case RestoreCommand:
7470     {
7471       /*
7472         Restore Image window to its original size.
7473       */
7474       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7475           (windows->image.height == (unsigned int) (*image)->rows) &&
7476           (windows->image.crop_geometry == (char *) NULL))
7477         {
7478           (void) XBell(display,0);
7479           break;
7480         }
7481       windows->image.window_changes.width=(int) (*image)->columns;
7482       windows->image.window_changes.height=(int) (*image)->rows;
7483       if (windows->image.crop_geometry != (char *) NULL)
7484         {
7485           windows->image.crop_geometry=(char *)
7486             RelinquishMagickMemory(windows->image.crop_geometry);
7487           windows->image.crop_geometry=(char *) NULL;
7488           windows->image.x=0;
7489           windows->image.y=0;
7490         }
7491       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7492       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7493       break;
7494     }
7495     case CropCommand:
7496     {
7497       /*
7498         Crop image.
7499       */
7500       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7501         exception);
7502       break;
7503     }
7504     case ChopCommand:
7505     {
7506       /*
7507         Chop image.
7508       */
7509       status=XChopImage(display,resource_info,windows,image,exception);
7510       if (IfMagickFalse(status) )
7511         {
7512           XNoticeWidget(display,windows,"Unable to cut X image",
7513             (*image)->filename);
7514           break;
7515         }
7516       break;
7517     }
7518     case FlopCommand:
7519     {
7520       Image
7521         *flop_image;
7522
7523       /*
7524         Flop image scanlines.
7525       */
7526       XSetCursorState(display,windows,MagickTrue);
7527       XCheckRefreshWindows(display,windows);
7528       flop_image=FlopImage(*image,exception);
7529       if (flop_image != (Image *) NULL)
7530         {
7531           *image=DestroyImage(*image);
7532           *image=flop_image;
7533         }
7534       CatchException(exception);
7535       XSetCursorState(display,windows,MagickFalse);
7536       if (windows->image.crop_geometry != (char *) NULL)
7537         {
7538           /*
7539             Flop crop geometry.
7540           */
7541           width=(unsigned int) (*image)->columns;
7542           height=(unsigned int) (*image)->rows;
7543           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7544             &width,&height);
7545           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7546             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7547         }
7548       if (IfMagickTrue(windows->image.orphan) )
7549         break;
7550       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7551       break;
7552     }
7553     case FlipCommand:
7554     {
7555       Image
7556         *flip_image;
7557
7558       /*
7559         Flip image scanlines.
7560       */
7561       XSetCursorState(display,windows,MagickTrue);
7562       XCheckRefreshWindows(display,windows);
7563       flip_image=FlipImage(*image,exception);
7564       if (flip_image != (Image *) NULL)
7565         {
7566           *image=DestroyImage(*image);
7567           *image=flip_image;
7568         }
7569       CatchException(exception);
7570       XSetCursorState(display,windows,MagickFalse);
7571       if (windows->image.crop_geometry != (char *) NULL)
7572         {
7573           /*
7574             Flip crop geometry.
7575           */
7576           width=(unsigned int) (*image)->columns;
7577           height=(unsigned int) (*image)->rows;
7578           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7579             &width,&height);
7580           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7581             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7582         }
7583       if (IfMagickTrue(windows->image.orphan) )
7584         break;
7585       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7586       break;
7587     }
7588     case RotateRightCommand:
7589     {
7590       /*
7591         Rotate image 90 degrees clockwise.
7592       */
7593       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7594       if (IfMagickFalse(status) )
7595         {
7596           XNoticeWidget(display,windows,"Unable to rotate X image",
7597             (*image)->filename);
7598           break;
7599         }
7600       break;
7601     }
7602     case RotateLeftCommand:
7603     {
7604       /*
7605         Rotate image 90 degrees counter-clockwise.
7606       */
7607       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7608       if (IfMagickFalse(status) )
7609         {
7610           XNoticeWidget(display,windows,"Unable to rotate X image",
7611             (*image)->filename);
7612           break;
7613         }
7614       break;
7615     }
7616     case RotateCommand:
7617     {
7618       /*
7619         Rotate image.
7620       */
7621       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7622       if (IfMagickFalse(status) )
7623         {
7624           XNoticeWidget(display,windows,"Unable to rotate X image",
7625             (*image)->filename);
7626           break;
7627         }
7628       break;
7629     }
7630     case ShearCommand:
7631     {
7632       Image
7633         *shear_image;
7634
7635       static char
7636         geometry[MaxTextExtent] = "45.0x45.0";
7637
7638       /*
7639         Query user for shear color and geometry.
7640       */
7641       XColorBrowserWidget(display,windows,"Select",color);
7642       if (*color == '\0')
7643         break;
7644       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7645         geometry);
7646       if (*geometry == '\0')
7647         break;
7648       /*
7649         Shear image.
7650       */
7651       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7652         exception);
7653       XSetCursorState(display,windows,MagickTrue);
7654       XCheckRefreshWindows(display,windows);
7655       (void) QueryColorCompliance(color,AllCompliance,
7656         &(*image)->background_color,exception);
7657       flags=ParseGeometry(geometry,&geometry_info);
7658       if ((flags & SigmaValue) == 0)
7659         geometry_info.sigma=geometry_info.rho;
7660       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7661         exception);
7662       if (shear_image != (Image *) NULL)
7663         {
7664           *image=DestroyImage(*image);
7665           *image=shear_image;
7666         }
7667       CatchException(exception);
7668       XSetCursorState(display,windows,MagickFalse);
7669       if (IfMagickTrue(windows->image.orphan) )
7670         break;
7671       windows->image.window_changes.width=(int) (*image)->columns;
7672       windows->image.window_changes.height=(int) (*image)->rows;
7673       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7674       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7675       break;
7676     }
7677     case RollCommand:
7678     {
7679       Image
7680         *roll_image;
7681
7682       static char
7683         geometry[MaxTextExtent] = "+2+2";
7684
7685       /*
7686         Query user for the roll geometry.
7687       */
7688       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7689         geometry);
7690       if (*geometry == '\0')
7691         break;
7692       /*
7693         Roll image.
7694       */
7695       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7696         exception);
7697       XSetCursorState(display,windows,MagickTrue);
7698       XCheckRefreshWindows(display,windows);
7699       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7700         exception);
7701       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7702         exception);
7703       if (roll_image != (Image *) NULL)
7704         {
7705           *image=DestroyImage(*image);
7706           *image=roll_image;
7707         }
7708       CatchException(exception);
7709       XSetCursorState(display,windows,MagickFalse);
7710       if (IfMagickTrue(windows->image.orphan) )
7711         break;
7712       windows->image.window_changes.width=(int) (*image)->columns;
7713       windows->image.window_changes.height=(int) (*image)->rows;
7714       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7715       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7716       break;
7717     }
7718     case TrimCommand:
7719     {
7720       static char
7721         fuzz[MaxTextExtent];
7722
7723       /*
7724         Query user for the fuzz factor.
7725       */
7726       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7727         (*image)->fuzz/(QuantumRange+1.0));
7728       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7729       if (*fuzz == '\0')
7730         break;
7731       (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7732       /*
7733         Trim image.
7734       */
7735       status=XTrimImage(display,resource_info,windows,*image,exception);
7736       if (IfMagickFalse(status) )
7737         {
7738           XNoticeWidget(display,windows,"Unable to trim X image",
7739             (*image)->filename);
7740           break;
7741         }
7742       break;
7743     }
7744     case HueCommand:
7745     {
7746       static char
7747         hue_percent[MaxTextExtent] = "110";
7748
7749       /*
7750         Query user for percent hue change.
7751       */
7752       (void) XDialogWidget(display,windows,"Apply",
7753         "Enter percent change in image hue (0-200):",hue_percent);
7754       if (*hue_percent == '\0')
7755         break;
7756       /*
7757         Vary the image hue.
7758       */
7759       XSetCursorState(display,windows,MagickTrue);
7760       XCheckRefreshWindows(display,windows);
7761       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7762       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7763         MaxTextExtent);
7764       (void) ModulateImage(*image,modulate_factors,exception);
7765       XSetCursorState(display,windows,MagickFalse);
7766       if (IfMagickTrue(windows->image.orphan) )
7767         break;
7768       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7769       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7770       break;
7771     }
7772     case SaturationCommand:
7773     {
7774       static char
7775         saturation_percent[MaxTextExtent] = "110";
7776
7777       /*
7778         Query user for percent saturation change.
7779       */
7780       (void) XDialogWidget(display,windows,"Apply",
7781         "Enter percent change in color saturation (0-200):",saturation_percent);
7782       if (*saturation_percent == '\0')
7783         break;
7784       /*
7785         Vary color saturation.
7786       */
7787       XSetCursorState(display,windows,MagickTrue);
7788       XCheckRefreshWindows(display,windows);
7789       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7790       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7791         MaxTextExtent);
7792       (void) ModulateImage(*image,modulate_factors,exception);
7793       XSetCursorState(display,windows,MagickFalse);
7794       if (IfMagickTrue(windows->image.orphan) )
7795         break;
7796       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7797       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7798       break;
7799     }
7800     case BrightnessCommand:
7801     {
7802       static char
7803         brightness_percent[MaxTextExtent] = "110";
7804
7805       /*
7806         Query user for percent brightness change.
7807       */
7808       (void) XDialogWidget(display,windows,"Apply",
7809         "Enter percent change in color brightness (0-200):",brightness_percent);
7810       if (*brightness_percent == '\0')
7811         break;
7812       /*
7813         Vary the color brightness.
7814       */
7815       XSetCursorState(display,windows,MagickTrue);
7816       XCheckRefreshWindows(display,windows);
7817       (void) CopyMagickString(modulate_factors,brightness_percent,
7818         MaxTextExtent);
7819       (void) ModulateImage(*image,modulate_factors,exception);
7820       XSetCursorState(display,windows,MagickFalse);
7821       if (IfMagickTrue(windows->image.orphan) )
7822         break;
7823       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7824       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7825       break;
7826     }
7827     case GammaCommand:
7828     {
7829       static char
7830         factor[MaxTextExtent] = "1.6";
7831
7832       /*
7833         Query user for gamma value.
7834       */
7835       (void) XDialogWidget(display,windows,"Gamma",
7836         "Enter gamma value (e.g. 1.2):",factor);
7837       if (*factor == '\0')
7838         break;
7839       /*
7840         Gamma correct image.
7841       */
7842       XSetCursorState(display,windows,MagickTrue);
7843       XCheckRefreshWindows(display,windows);
7844       (void) GammaImage(*image,atof(factor),exception);
7845       XSetCursorState(display,windows,MagickFalse);
7846       if (IfMagickTrue(windows->image.orphan) )
7847         break;
7848       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7849       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7850       break;
7851     }
7852     case SpiffCommand:
7853     {
7854       /*
7855         Sharpen the image contrast.
7856       */
7857       XSetCursorState(display,windows,MagickTrue);
7858       XCheckRefreshWindows(display,windows);
7859       (void) ContrastImage(*image,MagickTrue,exception);
7860       XSetCursorState(display,windows,MagickFalse);
7861       if (IfMagickTrue(windows->image.orphan) )
7862         break;
7863       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7864       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7865       break;
7866     }
7867     case DullCommand:
7868     {
7869       /*
7870         Dull the image contrast.
7871       */
7872       XSetCursorState(display,windows,MagickTrue);
7873       XCheckRefreshWindows(display,windows);
7874       (void) ContrastImage(*image,MagickFalse,exception);
7875       XSetCursorState(display,windows,MagickFalse);
7876       if (IfMagickTrue(windows->image.orphan) )
7877         break;
7878       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7879       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7880       break;
7881     }
7882     case ContrastStretchCommand:
7883     {
7884       double
7885         black_point,
7886         white_point;
7887
7888       static char
7889         levels[MaxTextExtent] = "1%";
7890
7891       /*
7892         Query user for gamma value.
7893       */
7894       (void) XDialogWidget(display,windows,"Contrast Stretch",
7895         "Enter black and white points:",levels);
7896       if (*levels == '\0')
7897         break;
7898       /*
7899         Contrast stretch image.
7900       */
7901       XSetCursorState(display,windows,MagickTrue);
7902       XCheckRefreshWindows(display,windows);
7903       flags=ParseGeometry(levels,&geometry_info);
7904       black_point=geometry_info.rho;
7905       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7906       if ((flags & PercentValue) != 0)
7907         {
7908           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7909           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7910         }
7911       white_point=(double) (*image)->columns*(*image)->rows-white_point;
7912       (void) ContrastStretchImage(*image,black_point,white_point,
7913         exception);
7914       XSetCursorState(display,windows,MagickFalse);
7915       if (IfMagickTrue(windows->image.orphan) )
7916         break;
7917       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7918       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7919       break;
7920     }
7921     case SigmoidalContrastCommand:
7922     {
7923       GeometryInfo
7924         geometry_info;
7925
7926       MagickStatusType
7927         flags;
7928
7929       static char
7930         levels[MaxTextExtent] = "3x50%";
7931
7932       /*
7933         Query user for gamma value.
7934       */
7935       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7936         "Enter contrast and midpoint:",levels);
7937       if (*levels == '\0')
7938         break;
7939       /*
7940         Contrast stretch image.
7941       */
7942       XSetCursorState(display,windows,MagickTrue);
7943       XCheckRefreshWindows(display,windows);
7944       flags=ParseGeometry(levels,&geometry_info);
7945       if ((flags & SigmaValue) == 0)
7946         geometry_info.sigma=1.0*QuantumRange/2.0;
7947       if ((flags & PercentValue) != 0)
7948         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7949       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7950         geometry_info.sigma,exception);
7951       XSetCursorState(display,windows,MagickFalse);
7952       if (IfMagickTrue(windows->image.orphan) )
7953         break;
7954       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7955       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7956       break;
7957     }
7958     case NormalizeCommand:
7959     {
7960       /*
7961         Perform histogram normalization on the image.
7962       */
7963       XSetCursorState(display,windows,MagickTrue);
7964       XCheckRefreshWindows(display,windows);
7965       (void) NormalizeImage(*image,exception);
7966       XSetCursorState(display,windows,MagickFalse);
7967       if (IfMagickTrue(windows->image.orphan) )
7968         break;
7969       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7970       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7971       break;
7972     }
7973     case EqualizeCommand:
7974     {
7975       /*
7976         Perform histogram equalization on the image.
7977       */
7978       XSetCursorState(display,windows,MagickTrue);
7979       XCheckRefreshWindows(display,windows);
7980       (void) EqualizeImage(*image,exception);
7981       XSetCursorState(display,windows,MagickFalse);
7982       if (IfMagickTrue(windows->image.orphan) )
7983         break;
7984       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7985       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7986       break;
7987     }
7988     case NegateCommand:
7989     {
7990       /*
7991         Negate colors in image.
7992       */
7993       XSetCursorState(display,windows,MagickTrue);
7994       XCheckRefreshWindows(display,windows);
7995       (void) NegateImage(*image,MagickFalse,exception);
7996       XSetCursorState(display,windows,MagickFalse);
7997       if (IfMagickTrue(windows->image.orphan) )
7998         break;
7999       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8000       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8001       break;
8002     }
8003     case GrayscaleCommand:
8004     {
8005       /*
8006         Convert image to grayscale.
8007       */
8008       XSetCursorState(display,windows,MagickTrue);
8009       XCheckRefreshWindows(display,windows);
8010       (void) SetImageType(*image,(*image)->alpha_trait != BlendPixelTrait ?
8011         GrayscaleType : GrayscaleMatteType,exception);
8012       XSetCursorState(display,windows,MagickFalse);
8013       if (IfMagickTrue(windows->image.orphan) )
8014         break;
8015       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8016       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8017       break;
8018     }
8019     case MapCommand:
8020     {
8021       Image
8022         *affinity_image;
8023
8024       static char
8025         filename[MaxTextExtent] = "\0";
8026
8027       /*
8028         Request image file name from user.
8029       */
8030       XFileBrowserWidget(display,windows,"Map",filename);
8031       if (*filename == '\0')
8032         break;
8033       /*
8034         Map image.
8035       */
8036       XSetCursorState(display,windows,MagickTrue);
8037       XCheckRefreshWindows(display,windows);
8038       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8039       affinity_image=ReadImage(image_info,exception);
8040       if (affinity_image != (Image *) NULL)
8041         {
8042           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8043           affinity_image=DestroyImage(affinity_image);
8044         }
8045       CatchException(exception);
8046       XSetCursorState(display,windows,MagickFalse);
8047       if (IfMagickTrue(windows->image.orphan) )
8048         break;
8049       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8050       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8051       break;
8052     }
8053     case QuantizeCommand:
8054     {
8055       int
8056         status;
8057
8058       static char
8059         colors[MaxTextExtent] = "256";
8060
8061       /*
8062         Query user for maximum number of colors.
8063       */
8064       status=XDialogWidget(display,windows,"Quantize",
8065         "Maximum number of colors:",colors);
8066       if (*colors == '\0')
8067         break;
8068       /*
8069         Color reduce the image.
8070       */
8071       XSetCursorState(display,windows,MagickTrue);
8072       XCheckRefreshWindows(display,windows);
8073       quantize_info.number_colors=StringToUnsignedLong(colors);
8074       quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8075         NoDitherMethod;
8076       (void) QuantizeImage(&quantize_info,*image,exception);
8077       XSetCursorState(display,windows,MagickFalse);
8078       if (IfMagickTrue(windows->image.orphan) )
8079         break;
8080       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8081       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8082       break;
8083     }
8084     case DespeckleCommand:
8085     {
8086       Image
8087         *despeckle_image;
8088
8089       /*
8090         Despeckle image.
8091       */
8092       XSetCursorState(display,windows,MagickTrue);
8093       XCheckRefreshWindows(display,windows);
8094       despeckle_image=DespeckleImage(*image,exception);
8095       if (despeckle_image != (Image *) NULL)
8096         {
8097           *image=DestroyImage(*image);
8098           *image=despeckle_image;
8099         }
8100       CatchException(exception);
8101       XSetCursorState(display,windows,MagickFalse);
8102       if (IfMagickTrue(windows->image.orphan) )
8103         break;
8104       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8105       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8106       break;
8107     }
8108     case EmbossCommand:
8109     {
8110       Image
8111         *emboss_image;
8112
8113       static char
8114         radius[MaxTextExtent] = "0.0x1.0";
8115
8116       /*
8117         Query user for emboss radius.
8118       */
8119       (void) XDialogWidget(display,windows,"Emboss",
8120         "Enter the emboss radius and standard deviation:",radius);
8121       if (*radius == '\0')
8122         break;
8123       /*
8124         Reduce noise in the image.
8125       */
8126       XSetCursorState(display,windows,MagickTrue);
8127       XCheckRefreshWindows(display,windows);
8128       flags=ParseGeometry(radius,&geometry_info);
8129       if ((flags & SigmaValue) == 0)
8130         geometry_info.sigma=1.0;
8131       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8132         exception);
8133       if (emboss_image != (Image *) NULL)
8134         {
8135           *image=DestroyImage(*image);
8136           *image=emboss_image;
8137         }
8138       CatchException(exception);
8139       XSetCursorState(display,windows,MagickFalse);
8140       if (IfMagickTrue(windows->image.orphan) )
8141         break;
8142       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8143       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8144       break;
8145     }
8146     case ReduceNoiseCommand:
8147     {
8148       Image
8149         *noise_image;
8150
8151       static char
8152         radius[MaxTextExtent] = "0";
8153
8154       /*
8155         Query user for noise radius.
8156       */
8157       (void) XDialogWidget(display,windows,"Reduce Noise",
8158         "Enter the noise radius:",radius);
8159       if (*radius == '\0')
8160         break;
8161       /*
8162         Reduce noise in the image.
8163       */
8164       XSetCursorState(display,windows,MagickTrue);
8165       XCheckRefreshWindows(display,windows);
8166       flags=ParseGeometry(radius,&geometry_info);
8167       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8168         geometry_info.rho,(size_t) geometry_info.rho,exception);
8169       if (noise_image != (Image *) NULL)
8170         {
8171           *image=DestroyImage(*image);
8172           *image=noise_image;
8173         }
8174       CatchException(exception);
8175       XSetCursorState(display,windows,MagickFalse);
8176       if (IfMagickTrue(windows->image.orphan) )
8177         break;
8178       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8179       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8180       break;
8181     }
8182     case AddNoiseCommand:
8183     {
8184       char
8185         **noises;
8186
8187       Image
8188         *noise_image;
8189
8190       static char
8191         noise_type[MaxTextExtent] = "Gaussian";
8192
8193       /*
8194         Add noise to the image.
8195       */
8196       noises=GetCommandOptions(MagickNoiseOptions);
8197       if (noises == (char **) NULL)
8198         break;
8199       XListBrowserWidget(display,windows,&windows->widget,
8200         (const char **) noises,"Add Noise",
8201         "Select a type of noise to add to your image:",noise_type);
8202       noises=DestroyStringList(noises);
8203       if (*noise_type == '\0')
8204         break;
8205       XSetCursorState(display,windows,MagickTrue);
8206       XCheckRefreshWindows(display,windows);
8207       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8208         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8209       if (noise_image != (Image *) NULL)
8210         {
8211           *image=DestroyImage(*image);
8212           *image=noise_image;
8213         }
8214       CatchException(exception);
8215       XSetCursorState(display,windows,MagickFalse);
8216       if (IfMagickTrue(windows->image.orphan) )
8217         break;
8218       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8219       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8220       break;
8221     }
8222     case SharpenCommand:
8223     {
8224       Image
8225         *sharp_image;
8226
8227       static char
8228         radius[MaxTextExtent] = "0.0x1.0";
8229
8230       /*
8231         Query user for sharpen radius.
8232       */
8233       (void) XDialogWidget(display,windows,"Sharpen",
8234         "Enter the sharpen radius and standard deviation:",radius);
8235       if (*radius == '\0')
8236         break;
8237       /*
8238         Sharpen image scanlines.
8239       */
8240       XSetCursorState(display,windows,MagickTrue);
8241       XCheckRefreshWindows(display,windows);
8242       flags=ParseGeometry(radius,&geometry_info);
8243       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8244         exception);
8245       if (sharp_image != (Image *) NULL)
8246         {
8247           *image=DestroyImage(*image);
8248           *image=sharp_image;
8249         }
8250       CatchException(exception);
8251       XSetCursorState(display,windows,MagickFalse);
8252       if (IfMagickTrue(windows->image.orphan) )
8253         break;
8254       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8255       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8256       break;
8257     }
8258     case BlurCommand:
8259     {
8260       Image
8261         *blur_image;
8262
8263       static char
8264         radius[MaxTextExtent] = "0.0x1.0";
8265
8266       /*
8267         Query user for blur radius.
8268       */
8269       (void) XDialogWidget(display,windows,"Blur",
8270         "Enter the blur radius and standard deviation:",radius);
8271       if (*radius == '\0')
8272         break;
8273       /*
8274         Blur an image.
8275       */
8276       XSetCursorState(display,windows,MagickTrue);
8277       XCheckRefreshWindows(display,windows);
8278       flags=ParseGeometry(radius,&geometry_info);
8279       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8280         exception);
8281       if (blur_image != (Image *) NULL)
8282         {
8283           *image=DestroyImage(*image);
8284           *image=blur_image;
8285         }
8286       CatchException(exception);
8287       XSetCursorState(display,windows,MagickFalse);
8288       if (IfMagickTrue(windows->image.orphan) )
8289         break;
8290       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8291       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8292       break;
8293     }
8294     case ThresholdCommand:
8295     {
8296       double
8297         threshold;
8298
8299       static char
8300         factor[MaxTextExtent] = "128";
8301
8302       /*
8303         Query user for threshold value.
8304       */
8305       (void) XDialogWidget(display,windows,"Threshold",
8306         "Enter threshold value:",factor);
8307       if (*factor == '\0')
8308         break;
8309       /*
8310         Gamma correct image.
8311       */
8312       XSetCursorState(display,windows,MagickTrue);
8313       XCheckRefreshWindows(display,windows);
8314       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8315       (void) BilevelImage(*image,threshold,exception);
8316       XSetCursorState(display,windows,MagickFalse);
8317       if (IfMagickTrue(windows->image.orphan) )
8318         break;
8319       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8320       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8321       break;
8322     }
8323     case EdgeDetectCommand:
8324     {
8325       Image
8326         *edge_image;
8327
8328       static char
8329         radius[MaxTextExtent] = "0";
8330
8331       /*
8332         Query user for edge factor.
8333       */
8334       (void) XDialogWidget(display,windows,"Detect Edges",
8335         "Enter the edge detect radius:",radius);
8336       if (*radius == '\0')
8337         break;
8338       /*
8339         Detect edge in image.
8340       */
8341       XSetCursorState(display,windows,MagickTrue);
8342       XCheckRefreshWindows(display,windows);
8343       flags=ParseGeometry(radius,&geometry_info);
8344       edge_image=EdgeImage(*image,geometry_info.rho,exception);
8345       if (edge_image != (Image *) NULL)
8346         {
8347           *image=DestroyImage(*image);
8348           *image=edge_image;
8349         }
8350       CatchException(exception);
8351       XSetCursorState(display,windows,MagickFalse);
8352       if (IfMagickTrue(windows->image.orphan) )
8353         break;
8354       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8355       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8356       break;
8357     }
8358     case SpreadCommand:
8359     {
8360       Image
8361         *spread_image;
8362
8363       static char
8364         amount[MaxTextExtent] = "2";
8365
8366       /*
8367         Query user for spread amount.
8368       */
8369       (void) XDialogWidget(display,windows,"Spread",
8370         "Enter the displacement amount:",amount);
8371       if (*amount == '\0')
8372         break;
8373       /*
8374         Displace image pixels by a random amount.
8375       */
8376       XSetCursorState(display,windows,MagickTrue);
8377       XCheckRefreshWindows(display,windows);
8378       flags=ParseGeometry(amount,&geometry_info);
8379       spread_image=EdgeImage(*image,geometry_info.rho,exception);
8380       if (spread_image != (Image *) NULL)
8381         {
8382           *image=DestroyImage(*image);
8383           *image=spread_image;
8384         }
8385       CatchException(exception);
8386       XSetCursorState(display,windows,MagickFalse);
8387       if (IfMagickTrue(windows->image.orphan) )
8388         break;
8389       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8390       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8391       break;
8392     }
8393     case ShadeCommand:
8394     {
8395       Image
8396         *shade_image;
8397
8398       int
8399         status;
8400
8401       static char
8402         geometry[MaxTextExtent] = "30x30";
8403
8404       /*
8405         Query user for the shade geometry.
8406       */
8407       status=XDialogWidget(display,windows,"Shade",
8408         "Enter the azimuth and elevation of the light source:",geometry);
8409       if (*geometry == '\0')
8410         break;
8411       /*
8412         Shade image pixels.
8413       */
8414       XSetCursorState(display,windows,MagickTrue);
8415       XCheckRefreshWindows(display,windows);
8416       flags=ParseGeometry(geometry,&geometry_info);
8417       if ((flags & SigmaValue) == 0)
8418         geometry_info.sigma=1.0;
8419       shade_image=ShadeImage(*image,IsMagickTrue(status),
8420         geometry_info.rho,geometry_info.sigma,exception);
8421       if (shade_image != (Image *) NULL)
8422         {
8423           *image=DestroyImage(*image);
8424           *image=shade_image;
8425         }
8426       CatchException(exception);
8427       XSetCursorState(display,windows,MagickFalse);
8428       if (IfMagickTrue(windows->image.orphan) )
8429         break;
8430       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8431       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8432       break;
8433     }
8434     case RaiseCommand:
8435     {
8436       static char
8437         bevel_width[MaxTextExtent] = "10";
8438
8439       /*
8440         Query user for bevel width.
8441       */
8442       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8443       if (*bevel_width == '\0')
8444         break;
8445       /*
8446         Raise an image.
8447       */
8448       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8449         exception);
8450       XSetCursorState(display,windows,MagickTrue);
8451       XCheckRefreshWindows(display,windows);
8452       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8453         exception);
8454       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8455       XSetCursorState(display,windows,MagickFalse);
8456       if (IfMagickTrue(windows->image.orphan) )
8457         break;
8458       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8459       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8460       break;
8461     }
8462     case SegmentCommand:
8463     {
8464       static char
8465         threshold[MaxTextExtent] = "1.0x1.5";
8466
8467       /*
8468         Query user for smoothing threshold.
8469       */
8470       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8471         threshold);
8472       if (*threshold == '\0')
8473         break;
8474       /*
8475         Segment an image.
8476       */
8477       XSetCursorState(display,windows,MagickTrue);
8478       XCheckRefreshWindows(display,windows);
8479       flags=ParseGeometry(threshold,&geometry_info);
8480       if ((flags & SigmaValue) == 0)
8481         geometry_info.sigma=1.0;
8482       (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8483         geometry_info.sigma,exception);
8484       XSetCursorState(display,windows,MagickFalse);
8485       if (IfMagickTrue(windows->image.orphan) )
8486         break;
8487       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8488       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8489       break;
8490     }
8491     case SepiaToneCommand:
8492     {
8493       double
8494         threshold;
8495
8496       Image
8497         *sepia_image;
8498
8499       static char
8500         factor[MaxTextExtent] = "80%";
8501
8502       /*
8503         Query user for sepia-tone factor.
8504       */
8505       (void) XDialogWidget(display,windows,"Sepia Tone",
8506         "Enter the sepia tone factor (0 - 99.9%):",factor);
8507       if (*factor == '\0')
8508         break;
8509       /*
8510         Sepia tone image pixels.
8511       */
8512       XSetCursorState(display,windows,MagickTrue);
8513       XCheckRefreshWindows(display,windows);
8514       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8515       sepia_image=SepiaToneImage(*image,threshold,exception);
8516       if (sepia_image != (Image *) NULL)
8517         {
8518           *image=DestroyImage(*image);
8519           *image=sepia_image;
8520         }
8521       CatchException(exception);
8522       XSetCursorState(display,windows,MagickFalse);
8523       if (IfMagickTrue(windows->image.orphan) )
8524         break;
8525       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8526       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8527       break;
8528     }
8529     case SolarizeCommand:
8530     {
8531       double
8532         threshold;
8533
8534       static char
8535         factor[MaxTextExtent] = "60%";
8536
8537       /*
8538         Query user for solarize factor.
8539       */
8540       (void) XDialogWidget(display,windows,"Solarize",
8541         "Enter the solarize factor (0 - 99.9%):",factor);
8542       if (*factor == '\0')
8543         break;
8544       /*
8545         Solarize image pixels.
8546       */
8547       XSetCursorState(display,windows,MagickTrue);
8548       XCheckRefreshWindows(display,windows);
8549       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8550       (void) SolarizeImage(*image,threshold,exception);
8551       XSetCursorState(display,windows,MagickFalse);
8552       if (IfMagickTrue(windows->image.orphan) )
8553         break;
8554       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8555       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8556       break;
8557     }
8558     case SwirlCommand:
8559     {
8560       Image
8561         *swirl_image;
8562
8563       static char
8564         degrees[MaxTextExtent] = "60";
8565
8566       /*
8567         Query user for swirl angle.
8568       */
8569       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8570         degrees);
8571       if (*degrees == '\0')
8572         break;
8573       /*
8574         Swirl image pixels about the center.
8575       */
8576       XSetCursorState(display,windows,MagickTrue);
8577       XCheckRefreshWindows(display,windows);
8578       flags=ParseGeometry(degrees,&geometry_info);
8579       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8580         exception);
8581       if (swirl_image != (Image *) NULL)
8582         {
8583           *image=DestroyImage(*image);
8584           *image=swirl_image;
8585         }
8586       CatchException(exception);
8587       XSetCursorState(display,windows,MagickFalse);
8588       if (IfMagickTrue(windows->image.orphan) )
8589         break;
8590       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8591       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8592       break;
8593     }
8594     case ImplodeCommand:
8595     {
8596       Image
8597         *implode_image;
8598
8599       static char
8600         factor[MaxTextExtent] = "0.3";
8601
8602       /*
8603         Query user for implode factor.
8604       */
8605       (void) XDialogWidget(display,windows,"Implode",
8606         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8607       if (*factor == '\0')
8608         break;
8609       /*
8610         Implode image pixels about the center.
8611       */
8612       XSetCursorState(display,windows,MagickTrue);
8613       XCheckRefreshWindows(display,windows);
8614       flags=ParseGeometry(factor,&geometry_info);
8615       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8616         exception);
8617       if (implode_image != (Image *) NULL)
8618         {
8619           *image=DestroyImage(*image);
8620           *image=implode_image;
8621         }
8622       CatchException(exception);
8623       XSetCursorState(display,windows,MagickFalse);
8624       if (IfMagickTrue(windows->image.orphan) )
8625         break;
8626       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8627       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8628       break;
8629     }
8630     case VignetteCommand:
8631     {
8632       Image
8633         *vignette_image;
8634
8635       static char
8636         geometry[MaxTextExtent] = "0x20";
8637
8638       /*
8639         Query user for the vignette geometry.
8640       */
8641       (void) XDialogWidget(display,windows,"Vignette",
8642         "Enter the radius, sigma, and x and y offsets:",geometry);
8643       if (*geometry == '\0')
8644         break;
8645       /*
8646         Soften the edges of the image in vignette style
8647       */
8648       XSetCursorState(display,windows,MagickTrue);
8649       XCheckRefreshWindows(display,windows);
8650       flags=ParseGeometry(geometry,&geometry_info);
8651       if ((flags & SigmaValue) == 0)
8652         geometry_info.sigma=1.0;
8653       if ((flags & XiValue) == 0)
8654         geometry_info.xi=0.1*(*image)->columns;
8655       if ((flags & PsiValue) == 0)
8656         geometry_info.psi=0.1*(*image)->rows;
8657       vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8658         ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8659         exception);
8660       if (vignette_image != (Image *) NULL)
8661         {
8662           *image=DestroyImage(*image);
8663           *image=vignette_image;
8664         }
8665       CatchException(exception);
8666       XSetCursorState(display,windows,MagickFalse);
8667       if (IfMagickTrue(windows->image.orphan) )
8668         break;
8669       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8670       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8671       break;
8672     }
8673     case WaveCommand:
8674     {
8675       Image
8676         *wave_image;
8677
8678       static char
8679         geometry[MaxTextExtent] = "25x150";
8680
8681       /*
8682         Query user for the wave geometry.
8683       */
8684       (void) XDialogWidget(display,windows,"Wave",
8685         "Enter the amplitude and length of the wave:",geometry);
8686       if (*geometry == '\0')
8687         break;
8688       /*
8689         Alter an image along a sine wave.
8690       */
8691       XSetCursorState(display,windows,MagickTrue);
8692       XCheckRefreshWindows(display,windows);
8693       flags=ParseGeometry(geometry,&geometry_info);
8694       if ((flags & SigmaValue) == 0)
8695         geometry_info.sigma=1.0;
8696       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8697         (*image)->interpolate,exception);
8698       if (wave_image != (Image *) NULL)
8699         {
8700           *image=DestroyImage(*image);
8701           *image=wave_image;
8702         }
8703       CatchException(exception);
8704       XSetCursorState(display,windows,MagickFalse);
8705       if (IfMagickTrue(windows->image.orphan) )
8706         break;
8707       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8708       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8709       break;
8710     }
8711     case OilPaintCommand:
8712     {
8713       Image
8714         *paint_image;
8715
8716       static char
8717         radius[MaxTextExtent] = "0";
8718
8719       /*
8720         Query user for circular neighborhood radius.
8721       */
8722       (void) XDialogWidget(display,windows,"Oil Paint",
8723         "Enter the mask radius:",radius);
8724       if (*radius == '\0')
8725         break;
8726       /*
8727         OilPaint image scanlines.
8728       */
8729       XSetCursorState(display,windows,MagickTrue);
8730       XCheckRefreshWindows(display,windows);
8731       flags=ParseGeometry(radius,&geometry_info);
8732       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8733         exception);
8734       if (paint_image != (Image *) NULL)
8735         {
8736           *image=DestroyImage(*image);
8737           *image=paint_image;
8738         }
8739       CatchException(exception);
8740       XSetCursorState(display,windows,MagickFalse);
8741       if (IfMagickTrue(windows->image.orphan) )
8742         break;
8743       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8744       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8745       break;
8746     }
8747     case CharcoalDrawCommand:
8748     {
8749       Image
8750         *charcoal_image;
8751
8752       static char
8753         radius[MaxTextExtent] = "0x1";
8754
8755       /*
8756         Query user for charcoal radius.
8757       */
8758       (void) XDialogWidget(display,windows,"Charcoal Draw",
8759         "Enter the charcoal radius and sigma:",radius);
8760       if (*radius == '\0')
8761         break;
8762       /*
8763         Charcoal the image.
8764       */
8765       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8766         exception);
8767       XSetCursorState(display,windows,MagickTrue);
8768       XCheckRefreshWindows(display,windows);
8769       flags=ParseGeometry(radius,&geometry_info);
8770       if ((flags & SigmaValue) == 0)
8771         geometry_info.sigma=geometry_info.rho;
8772       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8773         exception);
8774       if (charcoal_image != (Image *) NULL)
8775         {
8776           *image=DestroyImage(*image);
8777           *image=charcoal_image;
8778         }
8779       CatchException(exception);
8780       XSetCursorState(display,windows,MagickFalse);
8781       if (IfMagickTrue(windows->image.orphan) )
8782         break;
8783       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8784       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8785       break;
8786     }
8787     case AnnotateCommand:
8788     {
8789       /*
8790         Annotate the image with text.
8791       */
8792       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8793       if (IfMagickFalse(status) )
8794         {
8795           XNoticeWidget(display,windows,"Unable to annotate X image",
8796             (*image)->filename);
8797           break;
8798         }
8799       break;
8800     }
8801     case DrawCommand:
8802     {
8803       /*
8804         Draw image.
8805       */
8806       status=XDrawEditImage(display,resource_info,windows,image,exception);
8807       if (IfMagickFalse(status) )
8808         {
8809           XNoticeWidget(display,windows,"Unable to draw on the X image",
8810             (*image)->filename);
8811           break;
8812         }
8813       break;
8814     }
8815     case ColorCommand:
8816     {
8817       /*
8818         Color edit.
8819       */
8820       status=XColorEditImage(display,resource_info,windows,image,exception);
8821       if (IfMagickFalse(status) )
8822         {
8823           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8824             (*image)->filename);
8825           break;
8826         }
8827       break;
8828     }
8829     case MatteCommand:
8830     {
8831       /*
8832         Matte edit.
8833       */
8834       status=XMatteEditImage(display,resource_info,windows,image,exception);
8835       if (IfMagickFalse(status) )
8836         {
8837           XNoticeWidget(display,windows,"Unable to matte edit X image",
8838             (*image)->filename);
8839           break;
8840         }
8841       break;
8842     }
8843     case CompositeCommand:
8844     {
8845       /*
8846         Composite image.
8847       */
8848       status=XCompositeImage(display,resource_info,windows,*image,
8849         exception);
8850       if (IfMagickFalse(status) )
8851         {
8852           XNoticeWidget(display,windows,"Unable to composite X image",
8853             (*image)->filename);
8854           break;
8855         }
8856       break;
8857     }
8858     case AddBorderCommand:
8859     {
8860       Image
8861         *border_image;
8862
8863       static char
8864         geometry[MaxTextExtent] = "6x6";
8865
8866       /*
8867         Query user for border color and geometry.
8868       */
8869       XColorBrowserWidget(display,windows,"Select",color);
8870       if (*color == '\0')
8871         break;
8872       (void) XDialogWidget(display,windows,"Add Border",
8873         "Enter border geometry:",geometry);
8874       if (*geometry == '\0')
8875         break;
8876       /*
8877         Add a border to the image.
8878       */
8879       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8880         exception);
8881       XSetCursorState(display,windows,MagickTrue);
8882       XCheckRefreshWindows(display,windows);
8883       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8884         exception);
8885       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8886         exception);
8887       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8888         exception);
8889       if (border_image != (Image *) NULL)
8890         {
8891           *image=DestroyImage(*image);
8892           *image=border_image;
8893         }
8894       CatchException(exception);
8895       XSetCursorState(display,windows,MagickFalse);
8896       if (IfMagickTrue(windows->image.orphan) )
8897         break;
8898       windows->image.window_changes.width=(int) (*image)->columns;
8899       windows->image.window_changes.height=(int) (*image)->rows;
8900       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8901       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8902       break;
8903     }
8904     case AddFrameCommand:
8905     {
8906       FrameInfo
8907         frame_info;
8908
8909       Image
8910         *frame_image;
8911
8912       static char
8913         geometry[MaxTextExtent] = "6x6";
8914
8915       /*
8916         Query user for frame color and geometry.
8917       */
8918       XColorBrowserWidget(display,windows,"Select",color);
8919       if (*color == '\0')
8920         break;
8921       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8922         geometry);
8923       if (*geometry == '\0')
8924         break;
8925       /*
8926         Surround image with an ornamental border.
8927       */
8928       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8929         exception);
8930       XSetCursorState(display,windows,MagickTrue);
8931       XCheckRefreshWindows(display,windows);
8932       (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8933         exception);
8934       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8935         exception);
8936       frame_info.width=page_geometry.width;
8937       frame_info.height=page_geometry.height;
8938       frame_info.outer_bevel=page_geometry.x;
8939       frame_info.inner_bevel=page_geometry.y;
8940       frame_info.x=(ssize_t) frame_info.width;
8941       frame_info.y=(ssize_t) frame_info.height;
8942       frame_info.width=(*image)->columns+2*frame_info.width;
8943       frame_info.height=(*image)->rows+2*frame_info.height;
8944       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8945       if (frame_image != (Image *) NULL)
8946         {
8947           *image=DestroyImage(*image);
8948           *image=frame_image;
8949         }
8950       CatchException(exception);
8951       XSetCursorState(display,windows,MagickFalse);
8952       if (IfMagickTrue(windows->image.orphan) )
8953         break;
8954       windows->image.window_changes.width=(int) (*image)->columns;
8955       windows->image.window_changes.height=(int) (*image)->rows;
8956       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8957       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8958       break;
8959     }
8960     case CommentCommand:
8961     {
8962       const char
8963         *value;
8964
8965       FILE
8966         *file;
8967
8968       int
8969         unique_file;
8970
8971       /*
8972         Edit image comment.
8973       */
8974       unique_file=AcquireUniqueFileResource(image_info->filename);
8975       if (unique_file == -1)
8976         XNoticeWidget(display,windows,"Unable to edit image comment",
8977           image_info->filename);
8978       value=GetImageProperty(*image,"comment",exception);
8979       if (value == (char *) NULL)
8980         unique_file=close(unique_file)-1;
8981       else
8982         {
8983           register const char
8984             *p;
8985
8986           file=fdopen(unique_file,"w");
8987           if (file == (FILE *) NULL)
8988             {
8989               XNoticeWidget(display,windows,"Unable to edit image comment",
8990                 image_info->filename);
8991               break;
8992             }
8993           for (p=value; *p != '\0'; p++)
8994             (void) fputc((int) *p,file);
8995           (void) fputc('\n',file);
8996           (void) fclose(file);
8997         }
8998       XSetCursorState(display,windows,MagickTrue);
8999       XCheckRefreshWindows(display,windows);
9000       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
9001         exception);
9002       if (IfMagickFalse(status) )
9003         XNoticeWidget(display,windows,"Unable to edit image comment",
9004           (char *) NULL);
9005       else
9006         {
9007           char
9008             *comment;
9009
9010           comment=FileToString(image_info->filename,~0UL,exception);
9011           if (comment != (char *) NULL)
9012             {
9013               (void) SetImageProperty(*image,"comment",comment,exception);
9014               (*image)->taint=MagickTrue;
9015             }
9016         }
9017       (void) RelinquishUniqueFileResource(image_info->filename);
9018       XSetCursorState(display,windows,MagickFalse);
9019       break;
9020     }
9021     case LaunchCommand:
9022     {
9023       /*
9024         Launch program.
9025       */
9026       XSetCursorState(display,windows,MagickTrue);
9027       XCheckRefreshWindows(display,windows);
9028       (void) AcquireUniqueFilename(filename);
9029       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9030         filename);
9031       status=WriteImage(image_info,*image,exception);
9032       if (IfMagickFalse(status) )
9033         XNoticeWidget(display,windows,"Unable to launch image editor",
9034           (char *) NULL);
9035       else
9036         {
9037           nexus=ReadImage(resource_info->image_info,exception);
9038           CatchException(exception);
9039           XClientMessage(display,windows->image.id,windows->im_protocols,
9040             windows->im_next_image,CurrentTime);
9041         }
9042       (void) RelinquishUniqueFileResource(filename);
9043       XSetCursorState(display,windows,MagickFalse);
9044       break;
9045     }
9046     case RegionofInterestCommand:
9047     {
9048       /*
9049         Apply an image processing technique to a region of interest.
9050       */
9051       (void) XROIImage(display,resource_info,windows,image,exception);
9052       break;
9053     }
9054     case InfoCommand:
9055       break;
9056     case ZoomCommand:
9057     {
9058       /*
9059         Zoom image.
9060       */
9061       if (IfMagickTrue(windows->magnify.mapped) )
9062         (void) XRaiseWindow(display,windows->magnify.id);
9063       else
9064         {
9065           /*
9066             Make magnify image.
9067           */
9068           XSetCursorState(display,windows,MagickTrue);
9069           (void) XMapRaised(display,windows->magnify.id);
9070           XSetCursorState(display,windows,MagickFalse);
9071         }
9072       break;
9073     }
9074     case ShowPreviewCommand:
9075     {
9076       char
9077         **previews;
9078
9079       Image
9080         *preview_image;
9081
9082       static char
9083         preview_type[MaxTextExtent] = "Gamma";
9084
9085       /*
9086         Select preview type from menu.
9087       */
9088       previews=GetCommandOptions(MagickPreviewOptions);
9089       if (previews == (char **) NULL)
9090         break;
9091       XListBrowserWidget(display,windows,&windows->widget,
9092         (const char **) previews,"Preview",
9093         "Select an enhancement, effect, or F/X:",preview_type);
9094       previews=DestroyStringList(previews);
9095       if (*preview_type == '\0')
9096         break;
9097       /*
9098         Show image preview.
9099       */
9100       XSetCursorState(display,windows,MagickTrue);
9101       XCheckRefreshWindows(display,windows);
9102       image_info->preview_type=(PreviewType)
9103         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9104       image_info->group=(ssize_t) windows->image.id;
9105       (void) DeleteImageProperty(*image,"label");
9106       (void) SetImageProperty(*image,"label","Preview",exception);
9107       (void) AcquireUniqueFilename(filename);
9108       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9109         filename);
9110       status=WriteImage(image_info,*image,exception);
9111       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9112       preview_image=ReadImage(image_info,exception);
9113       (void) RelinquishUniqueFileResource(filename);
9114       if (preview_image == (Image *) NULL)
9115         break;
9116       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9117         filename);
9118       status=WriteImage(image_info,preview_image,exception);
9119       preview_image=DestroyImage(preview_image);
9120       if (IfMagickFalse(status) )
9121         XNoticeWidget(display,windows,"Unable to show image preview",
9122           (*image)->filename);
9123       XDelay(display,1500);
9124       XSetCursorState(display,windows,MagickFalse);
9125       break;
9126     }
9127     case ShowHistogramCommand:
9128     {
9129       Image
9130         *histogram_image;
9131
9132       /*
9133         Show image histogram.
9134       */
9135       XSetCursorState(display,windows,MagickTrue);
9136       XCheckRefreshWindows(display,windows);
9137       image_info->group=(ssize_t) windows->image.id;
9138       (void) DeleteImageProperty(*image,"label");
9139       (void) SetImageProperty(*image,"label","Histogram",exception);
9140       (void) AcquireUniqueFilename(filename);
9141       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9142         filename);
9143       status=WriteImage(image_info,*image,exception);
9144       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9145       histogram_image=ReadImage(image_info,exception);
9146       (void) RelinquishUniqueFileResource(filename);
9147       if (histogram_image == (Image *) NULL)
9148         break;
9149       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9150         "show:%s",filename);
9151       status=WriteImage(image_info,histogram_image,exception);
9152       histogram_image=DestroyImage(histogram_image);
9153       if (IfMagickFalse(status) )
9154         XNoticeWidget(display,windows,"Unable to show histogram",
9155           (*image)->filename);
9156       XDelay(display,1500);
9157       XSetCursorState(display,windows,MagickFalse);
9158       break;
9159     }
9160     case ShowMatteCommand:
9161     {
9162       Image
9163         *matte_image;
9164
9165       if ((*image)->alpha_trait != BlendPixelTrait)
9166         {
9167           XNoticeWidget(display,windows,
9168             "Image does not have any matte information",(*image)->filename);
9169           break;
9170         }
9171       /*
9172         Show image matte.
9173       */
9174       XSetCursorState(display,windows,MagickTrue);
9175       XCheckRefreshWindows(display,windows);
9176       image_info->group=(ssize_t) windows->image.id;
9177       (void) DeleteImageProperty(*image,"label");
9178       (void) SetImageProperty(*image,"label","Matte",exception);
9179       (void) AcquireUniqueFilename(filename);
9180       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9181         filename);
9182       status=WriteImage(image_info,*image,exception);
9183       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9184       matte_image=ReadImage(image_info,exception);
9185       (void) RelinquishUniqueFileResource(filename);
9186       if (matte_image == (Image *) NULL)
9187         break;
9188       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9189         filename);
9190       status=WriteImage(image_info,matte_image,exception);
9191       matte_image=DestroyImage(matte_image);
9192       if (IfMagickFalse(status) )
9193         XNoticeWidget(display,windows,"Unable to show matte",
9194           (*image)->filename);
9195       XDelay(display,1500);
9196       XSetCursorState(display,windows,MagickFalse);
9197       break;
9198     }
9199     case BackgroundCommand:
9200     {
9201       /*
9202         Background image.
9203       */
9204       status=XBackgroundImage(display,resource_info,windows,image,exception);
9205       if (IfMagickFalse(status) )
9206         break;
9207       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9208       if (nexus != (Image *) NULL)
9209         XClientMessage(display,windows->image.id,windows->im_protocols,
9210           windows->im_next_image,CurrentTime);
9211       break;
9212     }
9213     case SlideShowCommand:
9214     {
9215       static char
9216         delay[MaxTextExtent] = "5";
9217
9218       /*
9219         Display next image after pausing.
9220       */
9221       (void) XDialogWidget(display,windows,"Slide Show",
9222         "Pause how many 1/100ths of a second between images:",delay);
9223       if (*delay == '\0')
9224         break;
9225       resource_info->delay=StringToUnsignedLong(delay);
9226       XClientMessage(display,windows->image.id,windows->im_protocols,
9227         windows->im_next_image,CurrentTime);
9228       break;
9229     }
9230     case PreferencesCommand:
9231     {
9232       /*
9233         Set user preferences.
9234       */
9235       status=XPreferencesWidget(display,resource_info,windows);
9236       if (IfMagickFalse(status) )
9237         break;
9238       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9239       if (nexus != (Image *) NULL)
9240         XClientMessage(display,windows->image.id,windows->im_protocols,
9241           windows->im_next_image,CurrentTime);
9242       break;
9243     }
9244     case HelpCommand:
9245     {
9246       /*
9247         User requested help.
9248       */
9249       XTextViewWidget(display,resource_info,windows,MagickFalse,
9250         "Help Viewer - Display",DisplayHelp);
9251       break;
9252     }
9253     case BrowseDocumentationCommand:
9254     {
9255       Atom
9256         mozilla_atom;
9257
9258       Window
9259         mozilla_window,
9260         root_window;
9261
9262       /*
9263         Browse the ImageMagick documentation.
9264       */
9265       root_window=XRootWindow(display,XDefaultScreen(display));
9266       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9267       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9268       if (mozilla_window != (Window) NULL)
9269         {
9270           char
9271             command[MaxTextExtent],
9272             *url;
9273
9274           /*
9275             Display documentation using Netscape remote control.
9276           */
9277           url=GetMagickHomeURL();
9278           (void) FormatLocaleString(command,MaxTextExtent,
9279             "openurl(%s,new-tab)",url);
9280           url=DestroyString(url);
9281           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9282           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9283             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9284           XSetCursorState(display,windows,MagickFalse);
9285           break;
9286         }
9287       XSetCursorState(display,windows,MagickTrue);
9288       XCheckRefreshWindows(display,windows);
9289       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9290         exception);
9291       if (IfMagickFalse(status) )
9292         XNoticeWidget(display,windows,"Unable to browse documentation",
9293           (char *) NULL);
9294       XDelay(display,1500);
9295       XSetCursorState(display,windows,MagickFalse);
9296       break;
9297     }
9298     case VersionCommand:
9299     {
9300       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9301         GetMagickCopyright());
9302       break;
9303     }
9304     case SaveToUndoBufferCommand:
9305       break;
9306     default:
9307     {
9308       (void) XBell(display,0);
9309       break;
9310     }
9311   }
9312   image_info=DestroyImageInfo(image_info);
9313   return(nexus);
9314 }
9315 \f
9316 /*
9317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9318 %                                                                             %
9319 %                                                                             %
9320 %                                                                             %
9321 +   X M a g n i f y I m a g e                                                 %
9322 %                                                                             %
9323 %                                                                             %
9324 %                                                                             %
9325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9326 %
9327 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9328 %  The magnified portion is displayed in a separate window.
9329 %
9330 %  The format of the XMagnifyImage method is:
9331 %
9332 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9333 %        ExceptionInfo *exception)
9334 %
9335 %  A description of each parameter follows:
9336 %
9337 %    o display: Specifies a connection to an X server;  returned from
9338 %      XOpenDisplay.
9339 %
9340 %    o windows: Specifies a pointer to a XWindows structure.
9341 %
9342 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9343 %      the entire image is refreshed.
9344 %
9345 %    o exception: return any errors or warnings in this structure.
9346 %
9347 */
9348 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9349   ExceptionInfo *exception)
9350 {
9351   char
9352     text[MaxTextExtent];
9353
9354   register int
9355     x,
9356     y;
9357
9358   size_t
9359     state;
9360
9361   /*
9362     Update magnified image until the mouse button is released.
9363   */
9364   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9365   state=DefaultState;
9366   x=event->xbutton.x;
9367   y=event->xbutton.y;
9368   windows->magnify.x=(int) windows->image.x+x;
9369   windows->magnify.y=(int) windows->image.y+y;
9370   do
9371   {
9372     /*
9373       Map and unmap Info widget as text cursor crosses its boundaries.
9374     */
9375     if (IfMagickTrue(windows->info.mapped) )
9376       {
9377         if ((x < (int) (windows->info.x+windows->info.width)) &&
9378             (y < (int) (windows->info.y+windows->info.height)))
9379           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9380       }
9381     else
9382       if ((x > (int) (windows->info.x+windows->info.width)) ||
9383           (y > (int) (windows->info.y+windows->info.height)))
9384         (void) XMapWindow(display,windows->info.id);
9385     if (IfMagickTrue(windows->info.mapped) )
9386       {
9387         /*
9388           Display pointer position.
9389         */
9390         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9391           windows->magnify.x,windows->magnify.y);
9392         XInfoWidget(display,windows,text);
9393       }
9394     /*
9395       Wait for next event.
9396     */
9397     XScreenEvent(display,windows,event,exception);
9398     switch (event->type)
9399     {
9400       case ButtonPress:
9401         break;
9402       case ButtonRelease:
9403       {
9404         /*
9405           User has finished magnifying image.
9406         */
9407         x=event->xbutton.x;
9408         y=event->xbutton.y;
9409         state|=ExitState;
9410         break;
9411       }
9412       case Expose:
9413         break;
9414       case MotionNotify:
9415       {
9416         x=event->xmotion.x;
9417         y=event->xmotion.y;
9418         break;
9419       }
9420       default:
9421         break;
9422     }
9423     /*
9424       Check boundary conditions.
9425     */
9426     if (x < 0)
9427       x=0;
9428     else
9429       if (x >= (int) windows->image.width)
9430         x=(int) windows->image.width-1;
9431     if (y < 0)
9432       y=0;
9433     else
9434      if (y >= (int) windows->image.height)
9435        y=(int) windows->image.height-1;
9436   } while ((state & ExitState) == 0);
9437   /*
9438     Display magnified image.
9439   */
9440   XSetCursorState(display,windows,MagickFalse);
9441 }
9442 \f
9443 /*
9444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9445 %                                                                             %
9446 %                                                                             %
9447 %                                                                             %
9448 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9449 %                                                                             %
9450 %                                                                             %
9451 %                                                                             %
9452 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9453 %
9454 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9455 %  pixel as specified by the key symbol.
9456 %
9457 %  The format of the XMagnifyWindowCommand method is:
9458 %
9459 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9460 %        const MagickStatusType state,const KeySym key_symbol,
9461 %        ExceptionInfo *exception)
9462 %
9463 %  A description of each parameter follows:
9464 %
9465 %    o display: Specifies a connection to an X server; returned from
9466 %      XOpenDisplay.
9467 %
9468 %    o windows: Specifies a pointer to a XWindows structure.
9469 %
9470 %    o state: key mask.
9471 %
9472 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9473 %      to trim.
9474 %
9475 %    o exception: return any errors or warnings in this structure.
9476 %
9477 */
9478 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9479   const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9480 {
9481   unsigned int
9482     quantum;
9483
9484   /*
9485     User specified a magnify factor or position.
9486   */
9487   quantum=1;
9488   if ((state & Mod1Mask) != 0)
9489     quantum=10;
9490   switch ((int) key_symbol)
9491   {
9492     case QuitCommand:
9493     {
9494       (void) XWithdrawWindow(display,windows->magnify.id,
9495         windows->magnify.screen);
9496       break;
9497     }
9498     case XK_Home:
9499     case XK_KP_Home:
9500     {
9501       windows->magnify.x=(int) windows->image.width/2;
9502       windows->magnify.y=(int) windows->image.height/2;
9503       break;
9504     }
9505     case XK_Left:
9506     case XK_KP_Left:
9507     {
9508       if (windows->magnify.x > 0)
9509         windows->magnify.x-=quantum;
9510       break;
9511     }
9512     case XK_Up:
9513     case XK_KP_Up:
9514     {
9515       if (windows->magnify.y > 0)
9516         windows->magnify.y-=quantum;
9517       break;
9518     }
9519     case XK_Right:
9520     case XK_KP_Right:
9521     {
9522       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9523         windows->magnify.x+=quantum;
9524       break;
9525     }
9526     case XK_Down:
9527     case XK_KP_Down:
9528     {
9529       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9530         windows->magnify.y+=quantum;
9531       break;
9532     }
9533     case XK_0:
9534     case XK_1:
9535     case XK_2:
9536     case XK_3:
9537     case XK_4:
9538     case XK_5:
9539     case XK_6:
9540     case XK_7:
9541     case XK_8:
9542     case XK_9:
9543     {
9544       windows->magnify.data=(key_symbol-XK_0);
9545       break;
9546     }
9547     case XK_KP_0:
9548     case XK_KP_1:
9549     case XK_KP_2:
9550     case XK_KP_3:
9551     case XK_KP_4:
9552     case XK_KP_5:
9553     case XK_KP_6:
9554     case XK_KP_7:
9555     case XK_KP_8:
9556     case XK_KP_9:
9557     {
9558       windows->magnify.data=(key_symbol-XK_KP_0);
9559       break;
9560     }
9561     default:
9562       break;
9563   }
9564   XMakeMagnifyImage(display,windows,exception);
9565 }
9566 \f
9567 /*
9568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9569 %                                                                             %
9570 %                                                                             %
9571 %                                                                             %
9572 +   X M a k e P a n I m a g e                                                 %
9573 %                                                                             %
9574 %                                                                             %
9575 %                                                                             %
9576 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9577 %
9578 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9579 %  icon window.
9580 %
9581 %  The format of the XMakePanImage method is:
9582 %
9583 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9584 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9585 %
9586 %  A description of each parameter follows:
9587 %
9588 %    o display: Specifies a connection to an X server;  returned from
9589 %      XOpenDisplay.
9590 %
9591 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9592 %
9593 %    o windows: Specifies a pointer to a XWindows structure.
9594 %
9595 %    o image: the image.
9596 %
9597 %    o exception: return any errors or warnings in this structure.
9598 %
9599 */
9600 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9601   XWindows *windows,Image *image,ExceptionInfo *exception)
9602 {
9603   MagickStatusType
9604     status;
9605
9606   /*
9607     Create and display image for panning icon.
9608   */
9609   XSetCursorState(display,windows,MagickTrue);
9610   XCheckRefreshWindows(display,windows);
9611   windows->pan.x=(int) windows->image.x;
9612   windows->pan.y=(int) windows->image.y;
9613   status=XMakeImage(display,resource_info,&windows->pan,image,
9614     windows->pan.width,windows->pan.height,exception);
9615   if (IfMagickFalse(status) )
9616     ThrowXWindowException(ResourceLimitError,
9617      "MemoryAllocationFailed",image->filename);
9618   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9619     windows->pan.pixmap);
9620   (void) XClearWindow(display,windows->pan.id);
9621   XDrawPanRectangle(display,windows);
9622   XSetCursorState(display,windows,MagickFalse);
9623 }
9624 \f
9625 /*
9626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9627 %                                                                             %
9628 %                                                                             %
9629 %                                                                             %
9630 +   X M a t t a E d i t I m a g e                                             %
9631 %                                                                             %
9632 %                                                                             %
9633 %                                                                             %
9634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9635 %
9636 %  XMatteEditImage() allows the user to interactively change the Matte channel
9637 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9638 %  before the matte information is stored.
9639 %
9640 %  The format of the XMatteEditImage method is:
9641 %
9642 %      MagickBooleanType XMatteEditImage(Display *display,
9643 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9644 %        ExceptionInfo *exception)
9645 %
9646 %  A description of each parameter follows:
9647 %
9648 %    o display: Specifies a connection to an X server;  returned from
9649 %      XOpenDisplay.
9650 %
9651 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9652 %
9653 %    o windows: Specifies a pointer to a XWindows structure.
9654 %
9655 %    o image: the image; returned from ReadImage.
9656 %
9657 %    o exception: return any errors or warnings in this structure.
9658 %
9659 */
9660 static MagickBooleanType XMatteEditImage(Display *display,
9661   XResourceInfo *resource_info,XWindows *windows,Image **image,
9662   ExceptionInfo *exception)
9663 {
9664   static char
9665     matte[MaxTextExtent] = "0";
9666
9667   static const char
9668     *MatteEditMenu[] =
9669     {
9670       "Method",
9671       "Border Color",
9672       "Fuzz",
9673       "Matte Value",
9674       "Undo",
9675       "Help",
9676       "Dismiss",
9677       (char *) NULL
9678     };
9679
9680   static const ModeType
9681     MatteEditCommands[] =
9682     {
9683       MatteEditMethod,
9684       MatteEditBorderCommand,
9685       MatteEditFuzzCommand,
9686       MatteEditValueCommand,
9687       MatteEditUndoCommand,
9688       MatteEditHelpCommand,
9689       MatteEditDismissCommand
9690     };
9691
9692   static PaintMethod
9693     method = PointMethod;
9694
9695   static XColor
9696     border_color = { 0, 0, 0, 0, 0, 0 };
9697
9698   char
9699     command[MaxTextExtent],
9700     text[MaxTextExtent];
9701
9702   Cursor
9703     cursor;
9704
9705   int
9706     entry,
9707     id,
9708     x,
9709     x_offset,
9710     y,
9711     y_offset;
9712
9713   register int
9714     i;
9715
9716   register Quantum
9717     *q;
9718
9719   unsigned int
9720     height,
9721     width;
9722
9723   size_t
9724     state;
9725
9726   XEvent
9727     event;
9728
9729   /*
9730     Map Command widget.
9731   */
9732   (void) CloneString(&windows->command.name,"Matte Edit");
9733   windows->command.data=4;
9734   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9735   (void) XMapRaised(display,windows->command.id);
9736   XClientMessage(display,windows->image.id,windows->im_protocols,
9737     windows->im_update_widget,CurrentTime);
9738   /*
9739     Make cursor.
9740   */
9741   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9742     resource_info->background_color,resource_info->foreground_color);
9743   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9744   /*
9745     Track pointer until button 1 is pressed.
9746   */
9747   XQueryPosition(display,windows->image.id,&x,&y);
9748   (void) XSelectInput(display,windows->image.id,
9749     windows->image.attributes.event_mask | PointerMotionMask);
9750   state=DefaultState;
9751   do
9752   {
9753     if (IfMagickTrue(windows->info.mapped) )
9754       {
9755         /*
9756           Display pointer position.
9757         */
9758         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9759           x+windows->image.x,y+windows->image.y);
9760         XInfoWidget(display,windows,text);
9761       }
9762     /*
9763       Wait for next event.
9764     */
9765     XScreenEvent(display,windows,&event,exception);
9766     if (event.xany.window == windows->command.id)
9767       {
9768         /*
9769           Select a command from the Command widget.
9770         */
9771         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9772         if (id < 0)
9773           {
9774             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9775             continue;
9776           }
9777         switch (MatteEditCommands[id])
9778         {
9779           case MatteEditMethod:
9780           {
9781             char
9782               **methods;
9783
9784             /*
9785               Select a method from the pop-up menu.
9786             */
9787             methods=GetCommandOptions(MagickMethodOptions);
9788             if (methods == (char **) NULL)
9789               break;
9790             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9791               (const char **) methods,command);
9792             if (entry >= 0)
9793               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9794                 MagickFalse,methods[entry]);
9795             methods=DestroyStringList(methods);
9796             break;
9797           }
9798           case MatteEditBorderCommand:
9799           {
9800             const char
9801               *ColorMenu[MaxNumberPens];
9802
9803             int
9804               pen_number;
9805
9806             /*
9807               Initialize menu selections.
9808             */
9809             for (i=0; i < (int) (MaxNumberPens-2); i++)
9810               ColorMenu[i]=resource_info->pen_colors[i];
9811             ColorMenu[MaxNumberPens-2]="Browser...";
9812             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9813             /*
9814               Select a pen color from the pop-up menu.
9815             */
9816             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9817               (const char **) ColorMenu,command);
9818             if (pen_number < 0)
9819               break;
9820             if (pen_number == (MaxNumberPens-2))
9821               {
9822                 static char
9823                   color_name[MaxTextExtent] = "gray";
9824
9825                 /*
9826                   Select a pen color from a dialog.
9827                 */
9828                 resource_info->pen_colors[pen_number]=color_name;
9829                 XColorBrowserWidget(display,windows,"Select",color_name);
9830                 if (*color_name == '\0')
9831                   break;
9832               }
9833             /*
9834               Set border color.
9835             */
9836             (void) XParseColor(display,windows->map_info->colormap,
9837               resource_info->pen_colors[pen_number],&border_color);
9838             break;
9839           }
9840           case MatteEditFuzzCommand:
9841           {
9842             static char
9843               fuzz[MaxTextExtent];
9844
9845             static const char
9846               *FuzzMenu[] =
9847               {
9848                 "0%",
9849                 "2%",
9850                 "5%",
9851                 "10%",
9852                 "15%",
9853                 "Dialog...",
9854                 (char *) NULL,
9855               };
9856
9857             /*
9858               Select a command from the pop-up menu.
9859             */
9860             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9861               command);
9862             if (entry < 0)
9863               break;
9864             if (entry != 5)
9865               {
9866                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9867                   QuantumRange+1.0);
9868                 break;
9869               }
9870             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9871             (void) XDialogWidget(display,windows,"Ok",
9872               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9873             if (*fuzz == '\0')
9874               break;
9875             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9876             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9877               1.0);
9878             break;
9879           }
9880           case MatteEditValueCommand:
9881           {
9882             static char
9883               message[MaxTextExtent];
9884
9885             static const char
9886               *MatteMenu[] =
9887               {
9888                 "Opaque",
9889                 "Transparent",
9890                 "Dialog...",
9891                 (char *) NULL,
9892               };
9893
9894             /*
9895               Select a command from the pop-up menu.
9896             */
9897             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9898               command);
9899             if (entry < 0)
9900               break;
9901             if (entry != 2)
9902               {
9903                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9904                   OpaqueAlpha);
9905                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9906                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9907                     (Quantum) TransparentAlpha);
9908                 break;
9909               }
9910             (void) FormatLocaleString(message,MaxTextExtent,
9911               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9912               QuantumRange);
9913             (void) XDialogWidget(display,windows,"Matte",message,matte);
9914             if (*matte == '\0')
9915               break;
9916             break;
9917           }
9918           case MatteEditUndoCommand:
9919           {
9920             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9921               image,exception);
9922             break;
9923           }
9924           case MatteEditHelpCommand:
9925           {
9926             XTextViewWidget(display,resource_info,windows,MagickFalse,
9927               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9928             break;
9929           }
9930           case MatteEditDismissCommand:
9931           {
9932             /*
9933               Prematurely exit.
9934             */
9935             state|=EscapeState;
9936             state|=ExitState;
9937             break;
9938           }
9939           default:
9940             break;
9941         }
9942         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9943         continue;
9944       }
9945     switch (event.type)
9946     {
9947       case ButtonPress:
9948       {
9949         if (event.xbutton.button != Button1)
9950           break;
9951         if ((event.xbutton.window != windows->image.id) &&
9952             (event.xbutton.window != windows->magnify.id))
9953           break;
9954         /*
9955           Update matte data.
9956         */
9957         x=event.xbutton.x;
9958         y=event.xbutton.y;
9959         (void) XMagickCommand(display,resource_info,windows,
9960           SaveToUndoBufferCommand,image,exception);
9961         state|=UpdateConfigurationState;
9962         break;
9963       }
9964       case ButtonRelease:
9965       {
9966         if (event.xbutton.button != Button1)
9967           break;
9968         if ((event.xbutton.window != windows->image.id) &&
9969             (event.xbutton.window != windows->magnify.id))
9970           break;
9971         /*
9972           Update colormap information.
9973         */
9974         x=event.xbutton.x;
9975         y=event.xbutton.y;
9976         XConfigureImageColormap(display,resource_info,windows,*image,exception);
9977         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9978         XInfoWidget(display,windows,text);
9979         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9980         state&=(~UpdateConfigurationState);
9981         break;
9982       }
9983       case Expose:
9984         break;
9985       case KeyPress:
9986       {
9987         char
9988           command[MaxTextExtent];
9989
9990         KeySym
9991           key_symbol;
9992
9993         if (event.xkey.window == windows->magnify.id)
9994           {
9995             Window
9996               window;
9997
9998             window=windows->magnify.id;
9999             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
10000           }
10001         if (event.xkey.window != windows->image.id)
10002           break;
10003         /*
10004           Respond to a user key press.
10005         */
10006         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10007           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10008         switch ((int) key_symbol)
10009         {
10010           case XK_Escape:
10011           case XK_F20:
10012           {
10013             /*
10014               Prematurely exit.
10015             */
10016             state|=ExitState;
10017             break;
10018           }
10019           case XK_F1:
10020           case XK_Help:
10021           {
10022             XTextViewWidget(display,resource_info,windows,MagickFalse,
10023               "Help Viewer - Matte Edit",ImageMatteEditHelp);
10024             break;
10025           }
10026           default:
10027           {
10028             (void) XBell(display,0);
10029             break;
10030           }
10031         }
10032         break;
10033       }
10034       case MotionNotify:
10035       {
10036         /*
10037           Map and unmap Info widget as cursor crosses its boundaries.
10038         */
10039         x=event.xmotion.x;
10040         y=event.xmotion.y;
10041         if (IfMagickTrue(windows->info.mapped) )
10042           {
10043             if ((x < (int) (windows->info.x+windows->info.width)) &&
10044                 (y < (int) (windows->info.y+windows->info.height)))
10045               (void) XWithdrawWindow(display,windows->info.id,
10046                 windows->info.screen);
10047           }
10048         else
10049           if ((x > (int) (windows->info.x+windows->info.width)) ||
10050               (y > (int) (windows->info.y+windows->info.height)))
10051             (void) XMapWindow(display,windows->info.id);
10052         break;
10053       }
10054       default:
10055         break;
10056     }
10057     if (event.xany.window == windows->magnify.id)
10058       {
10059         x=windows->magnify.x-windows->image.x;
10060         y=windows->magnify.y-windows->image.y;
10061       }
10062     x_offset=x;
10063     y_offset=y;
10064     if ((state & UpdateConfigurationState) != 0)
10065       {
10066         CacheView
10067           *image_view;
10068
10069         int
10070           x,
10071           y;
10072
10073         /*
10074           Matte edit is relative to image configuration.
10075         */
10076         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10077           MagickTrue);
10078         XPutPixel(windows->image.ximage,x_offset,y_offset,
10079           windows->pixel_info->background_color.pixel);
10080         width=(unsigned int) (*image)->columns;
10081         height=(unsigned int) (*image)->rows;
10082         x=0;
10083         y=0;
10084         if (windows->image.crop_geometry != (char *) NULL)
10085           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10086             &height);
10087         x_offset=(int) (width*(windows->image.x+x_offset)/
10088           windows->image.ximage->width+x);
10089         y_offset=(int) (height*(windows->image.y+y_offset)/
10090           windows->image.ximage->height+y);
10091         if ((x_offset < 0) || (y_offset < 0))
10092           continue;
10093         if ((x_offset >= (int) (*image)->columns) ||
10094             (y_offset >= (int) (*image)->rows))
10095           continue;
10096         if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10097           return(MagickFalse);
10098         if ((*image)->alpha_trait != BlendPixelTrait)
10099           (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10100         image_view=AcquireAuthenticCacheView(*image,exception);
10101         switch (method)
10102         {
10103           case PointMethod:
10104           default:
10105           {
10106             /*
10107               Update matte information using point algorithm.
10108             */
10109             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10110               (ssize_t) y_offset,1,1,exception);
10111             if (q == (Quantum *) NULL)
10112               break;
10113             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10114             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10115             break;
10116           }
10117           case ReplaceMethod:
10118           {
10119             PixelInfo
10120               pixel,
10121               target;
10122
10123             /*
10124               Update matte information using replace algorithm.
10125             */
10126             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10127               x_offset,(ssize_t) y_offset,&target,exception);
10128             for (y=0; y < (int) (*image)->rows; y++)
10129             {
10130               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10131                 (*image)->columns,1,exception);
10132               if (q == (Quantum *) NULL)
10133                 break;
10134               for (x=0; x < (int) (*image)->columns; x++)
10135               {
10136                 GetPixelInfoPixel(*image,q,&pixel);
10137                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10138                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10139                 q+=GetPixelChannels(*image);
10140               }
10141               if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10142                 break;
10143             }
10144             break;
10145           }
10146           case FloodfillMethod:
10147           case FillToBorderMethod:
10148           {
10149             ChannelType
10150               channel_mask;
10151
10152             DrawInfo
10153               *draw_info;
10154
10155             PixelInfo
10156               target;
10157
10158             /*
10159               Update matte information using floodfill algorithm.
10160             */
10161             (void) GetOneVirtualPixelInfo(*image,
10162               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10163               y_offset,&target,exception);
10164             if (method == FillToBorderMethod)
10165               {
10166                 target.red=(double) ScaleShortToQuantum(
10167                   border_color.red);
10168                 target.green=(double) ScaleShortToQuantum(
10169                   border_color.green);
10170                 target.blue=(double) ScaleShortToQuantum(
10171                   border_color.blue);
10172               }
10173             draw_info=CloneDrawInfo(resource_info->image_info,
10174               (DrawInfo *) NULL);
10175             draw_info->fill.alpha=(double) ClampToQuantum(
10176               StringToDouble(matte,(char **) NULL));
10177             channel_mask=SetImageChannelMask(*image,AlphaChannel);
10178             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10179               x_offset,(ssize_t) y_offset,
10180               IsMagickFalse(method == FloodfillMethod),exception);
10181             (void) SetPixelChannelMask(*image,channel_mask);
10182             draw_info=DestroyDrawInfo(draw_info);
10183             break;
10184           }
10185           case ResetMethod:
10186           {
10187             /*
10188               Update matte information using reset algorithm.
10189             */
10190             if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10191               return(MagickFalse);
10192             for (y=0; y < (int) (*image)->rows; y++)
10193             {
10194               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10195                 (*image)->columns,1,exception);
10196               if (q == (Quantum *) NULL)
10197                 break;
10198               for (x=0; x < (int) (*image)->columns; x++)
10199               {
10200                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10201                 q+=GetPixelChannels(*image);
10202               }
10203               if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10204                 break;
10205             }
10206             if (StringToLong(matte) == (long) OpaqueAlpha)
10207               (*image)->alpha_trait=UndefinedPixelTrait;
10208             break;
10209           }
10210         }
10211         image_view=DestroyCacheView(image_view);
10212         state&=(~UpdateConfigurationState);
10213       }
10214   } while ((state & ExitState) == 0);
10215   (void) XSelectInput(display,windows->image.id,
10216     windows->image.attributes.event_mask);
10217   XSetCursorState(display,windows,MagickFalse);
10218   (void) XFreeCursor(display,cursor);
10219   return(MagickTrue);
10220 }
10221 \f
10222 /*
10223 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10224 %                                                                             %
10225 %                                                                             %
10226 %                                                                             %
10227 +   X O p e n I m a g e                                                       %
10228 %                                                                             %
10229 %                                                                             %
10230 %                                                                             %
10231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10232 %
10233 %  XOpenImage() loads an image from a file.
10234 %
10235 %  The format of the XOpenImage method is:
10236 %
10237 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10238 %       XWindows *windows,const unsigned int command)
10239 %
10240 %  A description of each parameter follows:
10241 %
10242 %    o display: Specifies a connection to an X server; returned from
10243 %      XOpenDisplay.
10244 %
10245 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10246 %
10247 %    o windows: Specifies a pointer to a XWindows structure.
10248 %
10249 %    o command: A value other than zero indicates that the file is selected
10250 %      from the command line argument list.
10251 %
10252 */
10253 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10254   XWindows *windows,const MagickBooleanType command)
10255 {
10256   const MagickInfo
10257     *magick_info;
10258
10259   ExceptionInfo
10260     *exception;
10261
10262   Image
10263     *nexus;
10264
10265   ImageInfo
10266     *image_info;
10267
10268   static char
10269     filename[MaxTextExtent] = "\0";
10270
10271   /*
10272     Request file name from user.
10273   */
10274   if (IfMagickFalse(command) )
10275     XFileBrowserWidget(display,windows,"Open",filename);
10276   else
10277     {
10278       char
10279         **filelist,
10280         **files;
10281
10282       int
10283         count,
10284         status;
10285
10286       register int
10287         i,
10288         j;
10289
10290       /*
10291         Select next image from the command line.
10292       */
10293       status=XGetCommand(display,windows->image.id,&files,&count);
10294       if (status == 0)
10295         {
10296           ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10297           return((Image *) NULL);
10298         }
10299       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10300       if (filelist == (char **) NULL)
10301         {
10302           ThrowXWindowException(ResourceLimitError,
10303             "MemoryAllocationFailed","...");
10304           (void) XFreeStringList(files);
10305           return((Image *) NULL);
10306         }
10307       j=0;
10308       for (i=1; i < count; i++)
10309         if (*files[i] != '-')
10310           filelist[j++]=files[i];
10311       filelist[j]=(char *) NULL;
10312       XListBrowserWidget(display,windows,&windows->widget,
10313         (const char **) filelist,"Load","Select Image to Load:",filename);
10314       filelist=(char **) RelinquishMagickMemory(filelist);
10315       (void) XFreeStringList(files);
10316     }
10317   if (*filename == '\0')
10318     return((Image *) NULL);
10319   image_info=CloneImageInfo(resource_info->image_info);
10320   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10321     (void *) NULL);
10322   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10323   exception=AcquireExceptionInfo();
10324   (void) SetImageInfo(image_info,0,exception);
10325   if (LocaleCompare(image_info->magick,"X") == 0)
10326     {
10327       char
10328         seconds[MaxTextExtent];
10329
10330       /*
10331         User may want to delay the X server screen grab.
10332       */
10333       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10334       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10335         seconds);
10336       if (*seconds == '\0')
10337         return((Image *) NULL);
10338       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10339     }
10340   magick_info=GetMagickInfo(image_info->magick,exception);
10341   if ((magick_info != (const MagickInfo *) NULL) &&
10342       IfMagickTrue(magick_info->raw))
10343     {
10344       char
10345         geometry[MaxTextExtent];
10346
10347       /*
10348         Request image size from the user.
10349       */
10350       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10351       if (image_info->size != (char *) NULL)
10352         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10353       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10354         geometry);
10355       (void) CloneString(&image_info->size,geometry);
10356     }
10357   /*
10358     Load the image.
10359   */
10360   XSetCursorState(display,windows,MagickTrue);
10361   XCheckRefreshWindows(display,windows);
10362   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10363   nexus=ReadImage(image_info,exception);
10364   CatchException(exception);
10365   XSetCursorState(display,windows,MagickFalse);
10366   if (nexus != (Image *) NULL)
10367     XClientMessage(display,windows->image.id,windows->im_protocols,
10368       windows->im_next_image,CurrentTime);
10369   else
10370     {
10371       char
10372         *text,
10373         **textlist;
10374
10375       /*
10376         Unknown image format.
10377       */
10378       text=FileToString(filename,~0UL,exception);
10379       if (text == (char *) NULL)
10380         return((Image *) NULL);
10381       textlist=StringToList(text);
10382       if (textlist != (char **) NULL)
10383         {
10384           char
10385             title[MaxTextExtent];
10386
10387           register int
10388             i;
10389
10390           (void) FormatLocaleString(title,MaxTextExtent,
10391             "Unknown format: %s",filename);
10392           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10393             (const char **) textlist);
10394           for (i=0; textlist[i] != (char *) NULL; i++)
10395             textlist[i]=DestroyString(textlist[i]);
10396           textlist=(char **) RelinquishMagickMemory(textlist);
10397         }
10398       text=DestroyString(text);
10399     }
10400   exception=DestroyExceptionInfo(exception);
10401   image_info=DestroyImageInfo(image_info);
10402   return(nexus);
10403 }
10404 \f
10405 /*
10406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10407 %                                                                             %
10408 %                                                                             %
10409 %                                                                             %
10410 +   X P a n I m a g e                                                         %
10411 %                                                                             %
10412 %                                                                             %
10413 %                                                                             %
10414 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10415 %
10416 %  XPanImage() pans the image until the mouse button is released.
10417 %
10418 %  The format of the XPanImage method is:
10419 %
10420 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10421 %        ExceptionInfo *exception)
10422 %
10423 %  A description of each parameter follows:
10424 %
10425 %    o display: Specifies a connection to an X server;  returned from
10426 %      XOpenDisplay.
10427 %
10428 %    o windows: Specifies a pointer to a XWindows structure.
10429 %
10430 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10431 %      the entire image is refreshed.
10432 %
10433 %    o exception: return any errors or warnings in this structure.
10434 %
10435 */
10436 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10437   ExceptionInfo *exception)
10438 {
10439   char
10440     text[MaxTextExtent];
10441
10442   Cursor
10443     cursor;
10444
10445   double
10446     x_factor,
10447     y_factor;
10448
10449   RectangleInfo
10450     pan_info;
10451
10452   size_t
10453     state;
10454
10455   /*
10456     Define cursor.
10457   */
10458   if ((windows->image.ximage->width > (int) windows->image.width) &&
10459       (windows->image.ximage->height > (int) windows->image.height))
10460     cursor=XCreateFontCursor(display,XC_fleur);
10461   else
10462     if (windows->image.ximage->width > (int) windows->image.width)
10463       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10464     else
10465       if (windows->image.ximage->height > (int) windows->image.height)
10466         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10467       else
10468         cursor=XCreateFontCursor(display,XC_arrow);
10469   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10470   /*
10471     Pan image as pointer moves until the mouse button is released.
10472   */
10473   x_factor=(double) windows->image.ximage->width/windows->pan.width;
10474   y_factor=(double) windows->image.ximage->height/windows->pan.height;
10475   pan_info.width=windows->pan.width*windows->image.width/
10476     windows->image.ximage->width;
10477   pan_info.height=windows->pan.height*windows->image.height/
10478     windows->image.ximage->height;
10479   pan_info.x=0;
10480   pan_info.y=0;
10481   state=UpdateConfigurationState;
10482   do
10483   {
10484     switch (event->type)
10485     {
10486       case ButtonPress:
10487       {
10488         /*
10489           User choose an initial pan location.
10490         */
10491         pan_info.x=(ssize_t) event->xbutton.x;
10492         pan_info.y=(ssize_t) event->xbutton.y;
10493         state|=UpdateConfigurationState;
10494         break;
10495       }
10496       case ButtonRelease:
10497       {
10498         /*
10499           User has finished panning the image.
10500         */
10501         pan_info.x=(ssize_t) event->xbutton.x;
10502         pan_info.y=(ssize_t) event->xbutton.y;
10503         state|=UpdateConfigurationState | ExitState;
10504         break;
10505       }
10506       case MotionNotify:
10507       {
10508         pan_info.x=(ssize_t) event->xmotion.x;
10509         pan_info.y=(ssize_t) event->xmotion.y;
10510         state|=UpdateConfigurationState;
10511       }
10512       default:
10513         break;
10514     }
10515     if ((state & UpdateConfigurationState) != 0)
10516       {
10517         /*
10518           Check boundary conditions.
10519         */
10520         if (pan_info.x < (ssize_t) (pan_info.width/2))
10521           pan_info.x=0;
10522         else
10523           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10524         if (pan_info.x < 0)
10525           pan_info.x=0;
10526         else
10527           if ((int) (pan_info.x+windows->image.width) >
10528               windows->image.ximage->width)
10529             pan_info.x=(ssize_t)
10530               (windows->image.ximage->width-windows->image.width);
10531         if (pan_info.y < (ssize_t) (pan_info.height/2))
10532           pan_info.y=0;
10533         else
10534           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10535         if (pan_info.y < 0)
10536           pan_info.y=0;
10537         else
10538           if ((int) (pan_info.y+windows->image.height) >
10539               windows->image.ximage->height)
10540             pan_info.y=(ssize_t)
10541               (windows->image.ximage->height-windows->image.height);
10542         if ((windows->image.x != (int) pan_info.x) ||
10543             (windows->image.y != (int) pan_info.y))
10544           {
10545             /*
10546               Display image pan offset.
10547             */
10548             windows->image.x=(int) pan_info.x;
10549             windows->image.y=(int) pan_info.y;
10550             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10551               windows->image.width,windows->image.height,windows->image.x,
10552               windows->image.y);
10553             XInfoWidget(display,windows,text);
10554             /*
10555               Refresh Image window.
10556             */
10557             XDrawPanRectangle(display,windows);
10558             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10559           }
10560         state&=(~UpdateConfigurationState);
10561       }
10562     /*
10563       Wait for next event.
10564     */
10565     if ((state & ExitState) == 0)
10566       XScreenEvent(display,windows,event,exception);
10567   } while ((state & ExitState) == 0);
10568   /*
10569     Restore cursor.
10570   */
10571   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10572   (void) XFreeCursor(display,cursor);
10573   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10574 }
10575 \f
10576 /*
10577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10578 %                                                                             %
10579 %                                                                             %
10580 %                                                                             %
10581 +   X P a s t e I m a g e                                                     %
10582 %                                                                             %
10583 %                                                                             %
10584 %                                                                             %
10585 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10586 %
10587 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10588 %  window image at a location the user chooses with the pointer.
10589 %
10590 %  The format of the XPasteImage method is:
10591 %
10592 %      MagickBooleanType XPasteImage(Display *display,
10593 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10594 %        ExceptionInfo *exception)
10595 %
10596 %  A description of each parameter follows:
10597 %
10598 %    o display: Specifies a connection to an X server;  returned from
10599 %      XOpenDisplay.
10600 %
10601 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10602 %
10603 %    o windows: Specifies a pointer to a XWindows structure.
10604 %
10605 %    o image: the image; returned from ReadImage.
10606 %
10607 %    o exception: return any errors or warnings in this structure.
10608 %
10609 */
10610 static MagickBooleanType XPasteImage(Display *display,
10611   XResourceInfo *resource_info,XWindows *windows,Image *image,
10612   ExceptionInfo *exception)
10613 {
10614   static const char
10615     *PasteMenu[] =
10616     {
10617       "Operator",
10618       "Help",
10619       "Dismiss",
10620       (char *) NULL
10621     };
10622
10623   static const ModeType
10624     PasteCommands[] =
10625     {
10626       PasteOperatorsCommand,
10627       PasteHelpCommand,
10628       PasteDismissCommand
10629     };
10630
10631   static CompositeOperator
10632     compose = CopyCompositeOp;
10633
10634   char
10635     text[MaxTextExtent];
10636
10637   Cursor
10638     cursor;
10639
10640   Image
10641     *paste_image;
10642
10643   int
10644     entry,
10645     id,
10646     x,
10647     y;
10648
10649   double
10650     scale_factor;
10651
10652   RectangleInfo
10653     highlight_info,
10654     paste_info;
10655
10656   unsigned int
10657     height,
10658     width;
10659
10660   size_t
10661     state;
10662
10663   XEvent
10664     event;
10665
10666   /*
10667     Copy image.
10668   */
10669   if (resource_info->copy_image == (Image *) NULL)
10670     return(MagickFalse);
10671   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10672   /*
10673     Map Command widget.
10674   */
10675   (void) CloneString(&windows->command.name,"Paste");
10676   windows->command.data=1;
10677   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10678   (void) XMapRaised(display,windows->command.id);
10679   XClientMessage(display,windows->image.id,windows->im_protocols,
10680     windows->im_update_widget,CurrentTime);
10681   /*
10682     Track pointer until button 1 is pressed.
10683   */
10684   XSetCursorState(display,windows,MagickFalse);
10685   XQueryPosition(display,windows->image.id,&x,&y);
10686   (void) XSelectInput(display,windows->image.id,
10687     windows->image.attributes.event_mask | PointerMotionMask);
10688   paste_info.x=(ssize_t) windows->image.x+x;
10689   paste_info.y=(ssize_t) windows->image.y+y;
10690   paste_info.width=0;
10691   paste_info.height=0;
10692   cursor=XCreateFontCursor(display,XC_ul_angle);
10693   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10694   state=DefaultState;
10695   do
10696   {
10697     if (IfMagickTrue(windows->info.mapped) )
10698       {
10699         /*
10700           Display pointer position.
10701         */
10702         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10703           (long) paste_info.x,(long) paste_info.y);
10704         XInfoWidget(display,windows,text);
10705       }
10706     highlight_info=paste_info;
10707     highlight_info.x=paste_info.x-windows->image.x;
10708     highlight_info.y=paste_info.y-windows->image.y;
10709     XHighlightRectangle(display,windows->image.id,
10710       windows->image.highlight_context,&highlight_info);
10711     /*
10712       Wait for next event.
10713     */
10714     XScreenEvent(display,windows,&event,exception);
10715     XHighlightRectangle(display,windows->image.id,
10716       windows->image.highlight_context,&highlight_info);
10717     if (event.xany.window == windows->command.id)
10718       {
10719         /*
10720           Select a command from the Command widget.
10721         */
10722         id=XCommandWidget(display,windows,PasteMenu,&event);
10723         if (id < 0)
10724           continue;
10725         switch (PasteCommands[id])
10726         {
10727           case PasteOperatorsCommand:
10728           {
10729             char
10730               command[MaxTextExtent],
10731               **operators;
10732
10733             /*
10734               Select a command from the pop-up menu.
10735             */
10736             operators=GetCommandOptions(MagickComposeOptions);
10737             if (operators == (char **) NULL)
10738               break;
10739             entry=XMenuWidget(display,windows,PasteMenu[id],
10740               (const char **) operators,command);
10741             if (entry >= 0)
10742               compose=(CompositeOperator) ParseCommandOption(
10743                 MagickComposeOptions,MagickFalse,operators[entry]);
10744             operators=DestroyStringList(operators);
10745             break;
10746           }
10747           case PasteHelpCommand:
10748           {
10749             XTextViewWidget(display,resource_info,windows,MagickFalse,
10750               "Help Viewer - Image Composite",ImagePasteHelp);
10751             break;
10752           }
10753           case PasteDismissCommand:
10754           {
10755             /*
10756               Prematurely exit.
10757             */
10758             state|=EscapeState;
10759             state|=ExitState;
10760             break;
10761           }
10762           default:
10763             break;
10764         }
10765         continue;
10766       }
10767     switch (event.type)
10768     {
10769       case ButtonPress:
10770       {
10771         if (IfMagickTrue(image->debug) )
10772           (void) LogMagickEvent(X11Event,GetMagickModule(),
10773             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10774             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10775         if (event.xbutton.button != Button1)
10776           break;
10777         if (event.xbutton.window != windows->image.id)
10778           break;
10779         /*
10780           Paste rectangle is relative to image configuration.
10781         */
10782         width=(unsigned int) image->columns;
10783         height=(unsigned int) image->rows;
10784         x=0;
10785         y=0;
10786         if (windows->image.crop_geometry != (char *) NULL)
10787           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10788             &width,&height);
10789         scale_factor=(double) windows->image.ximage->width/width;
10790         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10791         scale_factor=(double) windows->image.ximage->height/height;
10792         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10793         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10794         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10795         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10796         break;
10797       }
10798       case ButtonRelease:
10799       {
10800         if (IfMagickTrue(image->debug) )
10801           (void) LogMagickEvent(X11Event,GetMagickModule(),
10802             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10803             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10804         if (event.xbutton.button != Button1)
10805           break;
10806         if (event.xbutton.window != windows->image.id)
10807           break;
10808         if ((paste_info.width != 0) && (paste_info.height != 0))
10809           {
10810             /*
10811               User has selected the location of the paste image.
10812             */
10813             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10814             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10815             state|=ExitState;
10816           }
10817         break;
10818       }
10819       case Expose:
10820         break;
10821       case KeyPress:
10822       {
10823         char
10824           command[MaxTextExtent];
10825
10826         KeySym
10827           key_symbol;
10828
10829         int
10830           length;
10831
10832         if (event.xkey.window != windows->image.id)
10833           break;
10834         /*
10835           Respond to a user key press.
10836         */
10837         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10838           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10839         *(command+length)='\0';
10840         if (IfMagickTrue(image->debug) )
10841           (void) LogMagickEvent(X11Event,GetMagickModule(),
10842             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10843         switch ((int) key_symbol)
10844         {
10845           case XK_Escape:
10846           case XK_F20:
10847           {
10848             /*
10849               Prematurely exit.
10850             */
10851             paste_image=DestroyImage(paste_image);
10852             state|=EscapeState;
10853             state|=ExitState;
10854             break;
10855           }
10856           case XK_F1:
10857           case XK_Help:
10858           {
10859             (void) XSetFunction(display,windows->image.highlight_context,
10860               GXcopy);
10861             XTextViewWidget(display,resource_info,windows,MagickFalse,
10862               "Help Viewer - Image Composite",ImagePasteHelp);
10863             (void) XSetFunction(display,windows->image.highlight_context,
10864               GXinvert);
10865             break;
10866           }
10867           default:
10868           {
10869             (void) XBell(display,0);
10870             break;
10871           }
10872         }
10873         break;
10874       }
10875       case MotionNotify:
10876       {
10877         /*
10878           Map and unmap Info widget as text cursor crosses its boundaries.
10879         */
10880         x=event.xmotion.x;
10881         y=event.xmotion.y;
10882         if (IfMagickTrue(windows->info.mapped) )
10883           {
10884             if ((x < (int) (windows->info.x+windows->info.width)) &&
10885                 (y < (int) (windows->info.y+windows->info.height)))
10886               (void) XWithdrawWindow(display,windows->info.id,
10887                 windows->info.screen);
10888           }
10889         else
10890           if ((x > (int) (windows->info.x+windows->info.width)) ||
10891               (y > (int) (windows->info.y+windows->info.height)))
10892             (void) XMapWindow(display,windows->info.id);
10893         paste_info.x=(ssize_t) windows->image.x+x;
10894         paste_info.y=(ssize_t) windows->image.y+y;
10895         break;
10896       }
10897       default:
10898       {
10899         if (IfMagickTrue(image->debug) )
10900           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10901             event.type);
10902         break;
10903       }
10904     }
10905   } while ((state & ExitState) == 0);
10906   (void) XSelectInput(display,windows->image.id,
10907     windows->image.attributes.event_mask);
10908   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10909   XSetCursorState(display,windows,MagickFalse);
10910   (void) XFreeCursor(display,cursor);
10911   if ((state & EscapeState) != 0)
10912     return(MagickTrue);
10913   /*
10914     Image pasting is relative to image configuration.
10915   */
10916   XSetCursorState(display,windows,MagickTrue);
10917   XCheckRefreshWindows(display,windows);
10918   width=(unsigned int) image->columns;
10919   height=(unsigned int) image->rows;
10920   x=0;
10921   y=0;
10922   if (windows->image.crop_geometry != (char *) NULL)
10923     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10924   scale_factor=(double) width/windows->image.ximage->width;
10925   paste_info.x+=x;
10926   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10927   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10928   scale_factor=(double) height/windows->image.ximage->height;
10929   paste_info.y+=y;
10930   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10931   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10932   /*
10933     Paste image with X Image window.
10934   */
10935   (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10936     paste_info.y,exception);
10937   paste_image=DestroyImage(paste_image);
10938   XSetCursorState(display,windows,MagickFalse);
10939   /*
10940     Update image colormap.
10941   */
10942   XConfigureImageColormap(display,resource_info,windows,image,exception);
10943   (void) XConfigureImage(display,resource_info,windows,image,exception);
10944   return(MagickTrue);
10945 }
10946 \f
10947 /*
10948 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10949 %                                                                             %
10950 %                                                                             %
10951 %                                                                             %
10952 +   X P r i n t I m a g e                                                     %
10953 %                                                                             %
10954 %                                                                             %
10955 %                                                                             %
10956 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10957 %
10958 %  XPrintImage() prints an image to a Postscript printer.
10959 %
10960 %  The format of the XPrintImage method is:
10961 %
10962 %      MagickBooleanType XPrintImage(Display *display,
10963 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10964 %        ExceptionInfo *exception)
10965 %
10966 %  A description of each parameter follows:
10967 %
10968 %    o display: Specifies a connection to an X server; returned from
10969 %      XOpenDisplay.
10970 %
10971 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10972 %
10973 %    o windows: Specifies a pointer to a XWindows structure.
10974 %
10975 %    o image: the image.
10976 %
10977 %    o exception: return any errors or warnings in this structure.
10978 %
10979 */
10980 static MagickBooleanType XPrintImage(Display *display,
10981   XResourceInfo *resource_info,XWindows *windows,Image *image,
10982   ExceptionInfo *exception)
10983 {
10984   char
10985     filename[MaxTextExtent],
10986     geometry[MaxTextExtent];
10987
10988   Image
10989     *print_image;
10990
10991   ImageInfo
10992     *image_info;
10993
10994   MagickStatusType
10995     status;
10996
10997   /*
10998     Request Postscript page geometry from user.
10999   */
11000   image_info=CloneImageInfo(resource_info->image_info);
11001   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
11002   if (image_info->page != (char *) NULL)
11003     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11004   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11005     "Select Postscript Page Geometry:",geometry);
11006   if (*geometry == '\0')
11007     return(MagickTrue);
11008   image_info->page=GetPageGeometry(geometry);
11009   /*
11010     Apply image transforms.
11011   */
11012   XSetCursorState(display,windows,MagickTrue);
11013   XCheckRefreshWindows(display,windows);
11014   print_image=CloneImage(image,0,0,MagickTrue,exception);
11015   if (print_image == (Image *) NULL)
11016     return(MagickFalse);
11017   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11018     windows->image.ximage->width,windows->image.ximage->height);
11019   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11020     exception);
11021   /*
11022     Print image.
11023   */
11024   (void) AcquireUniqueFilename(filename);
11025   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11026     filename);
11027   status=WriteImage(image_info,print_image,exception);
11028   (void) RelinquishUniqueFileResource(filename);
11029   print_image=DestroyImage(print_image);
11030   image_info=DestroyImageInfo(image_info);
11031   XSetCursorState(display,windows,MagickFalse);
11032   return(IsMagickTrue(status));
11033 }
11034 \f
11035 /*
11036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11037 %                                                                             %
11038 %                                                                             %
11039 %                                                                             %
11040 +   X R O I I m a g e                                                         %
11041 %                                                                             %
11042 %                                                                             %
11043 %                                                                             %
11044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11045 %
11046 %  XROIImage() applies an image processing technique to a region of interest.
11047 %
11048 %  The format of the XROIImage method is:
11049 %
11050 %      MagickBooleanType XROIImage(Display *display,
11051 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11052 %        ExceptionInfo *exception)
11053 %
11054 %  A description of each parameter follows:
11055 %
11056 %    o display: Specifies a connection to an X server; returned from
11057 %      XOpenDisplay.
11058 %
11059 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11060 %
11061 %    o windows: Specifies a pointer to a XWindows structure.
11062 %
11063 %    o image: the image; returned from ReadImage.
11064 %
11065 %    o exception: return any errors or warnings in this structure.
11066 %
11067 */
11068 static MagickBooleanType XROIImage(Display *display,
11069   XResourceInfo *resource_info,XWindows *windows,Image **image,
11070   ExceptionInfo *exception)
11071 {
11072 #define ApplyMenus  7
11073
11074   static const char
11075     *ROIMenu[] =
11076     {
11077       "Help",
11078       "Dismiss",
11079       (char *) NULL
11080     },
11081     *ApplyMenu[] =
11082     {
11083       "File",
11084       "Edit",
11085       "Transform",
11086       "Enhance",
11087       "Effects",
11088       "F/X",
11089       "Miscellany",
11090       "Help",
11091       "Dismiss",
11092       (char *) NULL
11093     },
11094     *FileMenu[] =
11095     {
11096       "Save...",
11097       "Print...",
11098       (char *) NULL
11099     },
11100     *EditMenu[] =
11101     {
11102       "Undo",
11103       "Redo",
11104       (char *) NULL
11105     },
11106     *TransformMenu[] =
11107     {
11108       "Flop",
11109       "Flip",
11110       "Rotate Right",
11111       "Rotate Left",
11112       (char *) NULL
11113     },
11114     *EnhanceMenu[] =
11115     {
11116       "Hue...",
11117       "Saturation...",
11118       "Brightness...",
11119       "Gamma...",
11120       "Spiff",
11121       "Dull",
11122       "Contrast Stretch...",
11123       "Sigmoidal Contrast...",
11124       "Normalize",
11125       "Equalize",
11126       "Negate",
11127       "Grayscale",
11128       "Map...",
11129       "Quantize...",
11130       (char *) NULL
11131     },
11132     *EffectsMenu[] =
11133     {
11134       "Despeckle",
11135       "Emboss",
11136       "Reduce Noise",
11137       "Add Noise",
11138       "Sharpen...",
11139       "Blur...",
11140       "Threshold...",
11141       "Edge Detect...",
11142       "Spread...",
11143       "Shade...",
11144       "Raise...",
11145       "Segment...",
11146       (char *) NULL
11147     },
11148     *FXMenu[] =
11149     {
11150       "Solarize...",
11151       "Sepia Tone...",
11152       "Swirl...",
11153       "Implode...",
11154       "Vignette...",
11155       "Wave...",
11156       "Oil Paint...",
11157       "Charcoal Draw...",
11158       (char *) NULL
11159     },
11160     *MiscellanyMenu[] =
11161     {
11162       "Image Info",
11163       "Zoom Image",
11164       "Show Preview...",
11165       "Show Histogram",
11166       "Show Matte",
11167       (char *) NULL
11168     };
11169
11170   static const char
11171     **Menus[ApplyMenus] =
11172     {
11173       FileMenu,
11174       EditMenu,
11175       TransformMenu,
11176       EnhanceMenu,
11177       EffectsMenu,
11178       FXMenu,
11179       MiscellanyMenu
11180     };
11181
11182   static const CommandType
11183     ApplyCommands[] =
11184     {
11185       NullCommand,
11186       NullCommand,
11187       NullCommand,
11188       NullCommand,
11189       NullCommand,
11190       NullCommand,
11191       NullCommand,
11192       HelpCommand,
11193       QuitCommand
11194     },
11195     FileCommands[] =
11196     {
11197       SaveCommand,
11198       PrintCommand
11199     },
11200     EditCommands[] =
11201     {
11202       UndoCommand,
11203       RedoCommand
11204     },
11205     TransformCommands[] =
11206     {
11207       FlopCommand,
11208       FlipCommand,
11209       RotateRightCommand,
11210       RotateLeftCommand
11211     },
11212     EnhanceCommands[] =
11213     {
11214       HueCommand,
11215       SaturationCommand,
11216       BrightnessCommand,
11217       GammaCommand,
11218       SpiffCommand,
11219       DullCommand,
11220       ContrastStretchCommand,
11221       SigmoidalContrastCommand,
11222       NormalizeCommand,
11223       EqualizeCommand,
11224       NegateCommand,
11225       GrayscaleCommand,
11226       MapCommand,
11227       QuantizeCommand
11228     },
11229     EffectsCommands[] =
11230     {
11231       DespeckleCommand,
11232       EmbossCommand,
11233       ReduceNoiseCommand,
11234       AddNoiseCommand,
11235       SharpenCommand,
11236       BlurCommand,
11237       EdgeDetectCommand,
11238       SpreadCommand,
11239       ShadeCommand,
11240       RaiseCommand,
11241       SegmentCommand
11242     },
11243     FXCommands[] =
11244     {
11245       SolarizeCommand,
11246       SepiaToneCommand,
11247       SwirlCommand,
11248       ImplodeCommand,
11249       VignetteCommand,
11250       WaveCommand,
11251       OilPaintCommand,
11252       CharcoalDrawCommand
11253     },
11254     MiscellanyCommands[] =
11255     {
11256       InfoCommand,
11257       ZoomCommand,
11258       ShowPreviewCommand,
11259       ShowHistogramCommand,
11260       ShowMatteCommand
11261     },
11262     ROICommands[] =
11263     {
11264       ROIHelpCommand,
11265       ROIDismissCommand
11266     };
11267
11268   static const CommandType
11269     *Commands[ApplyMenus] =
11270     {
11271       FileCommands,
11272       EditCommands,
11273       TransformCommands,
11274       EnhanceCommands,
11275       EffectsCommands,
11276       FXCommands,
11277       MiscellanyCommands
11278     };
11279
11280   char
11281     command[MaxTextExtent],
11282     text[MaxTextExtent];
11283
11284   CommandType
11285     command_type;
11286
11287   Cursor
11288     cursor;
11289
11290   Image
11291     *roi_image;
11292
11293   int
11294     entry,
11295     id,
11296     x,
11297     y;
11298
11299   double
11300     scale_factor;
11301
11302   MagickProgressMonitor
11303     progress_monitor;
11304
11305   RectangleInfo
11306     crop_info,
11307     highlight_info,
11308     roi_info;
11309
11310   unsigned int
11311     height,
11312     width;
11313
11314   size_t
11315     state;
11316
11317   XEvent
11318     event;
11319
11320   /*
11321     Map Command widget.
11322   */
11323   (void) CloneString(&windows->command.name,"ROI");
11324   windows->command.data=0;
11325   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11326   (void) XMapRaised(display,windows->command.id);
11327   XClientMessage(display,windows->image.id,windows->im_protocols,
11328     windows->im_update_widget,CurrentTime);
11329   /*
11330     Track pointer until button 1 is pressed.
11331   */
11332   XQueryPosition(display,windows->image.id,&x,&y);
11333   (void) XSelectInput(display,windows->image.id,
11334     windows->image.attributes.event_mask | PointerMotionMask);
11335   roi_info.x=(ssize_t) windows->image.x+x;
11336   roi_info.y=(ssize_t) windows->image.y+y;
11337   roi_info.width=0;
11338   roi_info.height=0;
11339   cursor=XCreateFontCursor(display,XC_fleur);
11340   state=DefaultState;
11341   do
11342   {
11343     if (IfMagickTrue(windows->info.mapped) )
11344       {
11345         /*
11346           Display pointer position.
11347         */
11348         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11349           (long) roi_info.x,(long) roi_info.y);
11350         XInfoWidget(display,windows,text);
11351       }
11352     /*
11353       Wait for next event.
11354     */
11355     XScreenEvent(display,windows,&event,exception);
11356     if (event.xany.window == windows->command.id)
11357       {
11358         /*
11359           Select a command from the Command widget.
11360         */
11361         id=XCommandWidget(display,windows,ROIMenu,&event);
11362         if (id < 0)
11363           continue;
11364         switch (ROICommands[id])
11365         {
11366           case ROIHelpCommand:
11367           {
11368             XTextViewWidget(display,resource_info,windows,MagickFalse,
11369               "Help Viewer - Region of Interest",ImageROIHelp);
11370             break;
11371           }
11372           case ROIDismissCommand:
11373           {
11374             /*
11375               Prematurely exit.
11376             */
11377             state|=EscapeState;
11378             state|=ExitState;
11379             break;
11380           }
11381           default:
11382             break;
11383         }
11384         continue;
11385       }
11386     switch (event.type)
11387     {
11388       case ButtonPress:
11389       {
11390         if (event.xbutton.button != Button1)
11391           break;
11392         if (event.xbutton.window != windows->image.id)
11393           break;
11394         /*
11395           Note first corner of region of interest rectangle-- exit loop.
11396         */
11397         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11398         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11399         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11400         state|=ExitState;
11401         break;
11402       }
11403       case ButtonRelease:
11404         break;
11405       case Expose:
11406         break;
11407       case KeyPress:
11408       {
11409         KeySym
11410           key_symbol;
11411
11412         if (event.xkey.window != windows->image.id)
11413           break;
11414         /*
11415           Respond to a user key press.
11416         */
11417         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11418           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11419         switch ((int) key_symbol)
11420         {
11421           case XK_Escape:
11422           case XK_F20:
11423           {
11424             /*
11425               Prematurely exit.
11426             */
11427             state|=EscapeState;
11428             state|=ExitState;
11429             break;
11430           }
11431           case XK_F1:
11432           case XK_Help:
11433           {
11434             XTextViewWidget(display,resource_info,windows,MagickFalse,
11435               "Help Viewer - Region of Interest",ImageROIHelp);
11436             break;
11437           }
11438           default:
11439           {
11440             (void) XBell(display,0);
11441             break;
11442           }
11443         }
11444         break;
11445       }
11446       case MotionNotify:
11447       {
11448         /*
11449           Map and unmap Info widget as text cursor crosses its boundaries.
11450         */
11451         x=event.xmotion.x;
11452         y=event.xmotion.y;
11453         if (IfMagickTrue(windows->info.mapped) )
11454           {
11455             if ((x < (int) (windows->info.x+windows->info.width)) &&
11456                 (y < (int) (windows->info.y+windows->info.height)))
11457               (void) XWithdrawWindow(display,windows->info.id,
11458                 windows->info.screen);
11459           }
11460         else
11461           if ((x > (int) (windows->info.x+windows->info.width)) ||
11462               (y > (int) (windows->info.y+windows->info.height)))
11463             (void) XMapWindow(display,windows->info.id);
11464         roi_info.x=(ssize_t) windows->image.x+x;
11465         roi_info.y=(ssize_t) windows->image.y+y;
11466         break;
11467       }
11468       default:
11469         break;
11470     }
11471   } while ((state & ExitState) == 0);
11472   (void) XSelectInput(display,windows->image.id,
11473     windows->image.attributes.event_mask);
11474   if ((state & EscapeState) != 0)
11475     {
11476       /*
11477         User want to exit without region of interest.
11478       */
11479       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11480       (void) XFreeCursor(display,cursor);
11481       return(MagickTrue);
11482     }
11483   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11484   do
11485   {
11486     /*
11487       Size rectangle as pointer moves until the mouse button is released.
11488     */
11489     x=(int) roi_info.x;
11490     y=(int) roi_info.y;
11491     roi_info.width=0;
11492     roi_info.height=0;
11493     state=DefaultState;
11494     do
11495     {
11496       highlight_info=roi_info;
11497       highlight_info.x=roi_info.x-windows->image.x;
11498       highlight_info.y=roi_info.y-windows->image.y;
11499       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11500         {
11501           /*
11502             Display info and draw region of interest rectangle.
11503           */
11504           if (IfMagickFalse(windows->info.mapped) )
11505             (void) XMapWindow(display,windows->info.id);
11506           (void) FormatLocaleString(text,MaxTextExtent,
11507             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11508             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11509           XInfoWidget(display,windows,text);
11510           XHighlightRectangle(display,windows->image.id,
11511             windows->image.highlight_context,&highlight_info);
11512         }
11513       else
11514         if (IfMagickTrue(windows->info.mapped) )
11515           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11516       /*
11517         Wait for next event.
11518       */
11519       XScreenEvent(display,windows,&event,exception);
11520       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11521         XHighlightRectangle(display,windows->image.id,
11522           windows->image.highlight_context,&highlight_info);
11523       switch (event.type)
11524       {
11525         case ButtonPress:
11526         {
11527           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11528           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11529           break;
11530         }
11531         case ButtonRelease:
11532         {
11533           /*
11534             User has committed to region of interest rectangle.
11535           */
11536           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11537           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11538           XSetCursorState(display,windows,MagickFalse);
11539           state|=ExitState;
11540           if (LocaleCompare(windows->command.name,"Apply") == 0)
11541             break;
11542           (void) CloneString(&windows->command.name,"Apply");
11543           windows->command.data=ApplyMenus;
11544           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11545           break;
11546         }
11547         case Expose:
11548           break;
11549         case MotionNotify:
11550         {
11551           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11552           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11553         }
11554         default:
11555           break;
11556       }
11557       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11558           ((state & ExitState) != 0))
11559         {
11560           /*
11561             Check boundary conditions.
11562           */
11563           if (roi_info.x < 0)
11564             roi_info.x=0;
11565           else
11566             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11567               roi_info.x=(ssize_t) windows->image.ximage->width;
11568           if ((int) roi_info.x < x)
11569             roi_info.width=(unsigned int) (x-roi_info.x);
11570           else
11571             {
11572               roi_info.width=(unsigned int) (roi_info.x-x);
11573               roi_info.x=(ssize_t) x;
11574             }
11575           if (roi_info.y < 0)
11576             roi_info.y=0;
11577           else
11578             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11579               roi_info.y=(ssize_t) windows->image.ximage->height;
11580           if ((int) roi_info.y < y)
11581             roi_info.height=(unsigned int) (y-roi_info.y);
11582           else
11583             {
11584               roi_info.height=(unsigned int) (roi_info.y-y);
11585               roi_info.y=(ssize_t) y;
11586             }
11587         }
11588     } while ((state & ExitState) == 0);
11589     /*
11590       Wait for user to grab a corner of the rectangle or press return.
11591     */
11592     state=DefaultState;
11593     command_type=NullCommand;
11594     (void) XMapWindow(display,windows->info.id);
11595     do
11596     {
11597       if (IfMagickTrue(windows->info.mapped) )
11598         {
11599           /*
11600             Display pointer position.
11601           */
11602           (void) FormatLocaleString(text,MaxTextExtent,
11603             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11604             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11605           XInfoWidget(display,windows,text);
11606         }
11607       highlight_info=roi_info;
11608       highlight_info.x=roi_info.x-windows->image.x;
11609       highlight_info.y=roi_info.y-windows->image.y;
11610       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11611         {
11612           state|=EscapeState;
11613           state|=ExitState;
11614           break;
11615         }
11616       if ((state & UpdateRegionState) != 0)
11617         {
11618           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11619           switch (command_type)
11620           {
11621             case UndoCommand:
11622             case RedoCommand:
11623             {
11624               (void) XMagickCommand(display,resource_info,windows,command_type,
11625                 image,exception);
11626               break;
11627             }
11628             default:
11629             {
11630               /*
11631                 Region of interest is relative to image configuration.
11632               */
11633               progress_monitor=SetImageProgressMonitor(*image,
11634                 (MagickProgressMonitor) NULL,(*image)->client_data);
11635               crop_info=roi_info;
11636               width=(unsigned int) (*image)->columns;
11637               height=(unsigned int) (*image)->rows;
11638               x=0;
11639               y=0;
11640               if (windows->image.crop_geometry != (char *) NULL)
11641                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11642                   &width,&height);
11643               scale_factor=(double) width/windows->image.ximage->width;
11644               crop_info.x+=x;
11645               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11646               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11647               scale_factor=(double)
11648                 height/windows->image.ximage->height;
11649               crop_info.y+=y;
11650               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11651               crop_info.height=(unsigned int)
11652                 (scale_factor*crop_info.height+0.5);
11653               roi_image=CropImage(*image,&crop_info,exception);
11654               (void) SetImageProgressMonitor(*image,progress_monitor,
11655                 (*image)->client_data);
11656               if (roi_image == (Image *) NULL)
11657                 continue;
11658               /*
11659                 Apply image processing technique to the region of interest.
11660               */
11661               windows->image.orphan=MagickTrue;
11662               (void) XMagickCommand(display,resource_info,windows,command_type,
11663                 &roi_image,exception);
11664               progress_monitor=SetImageProgressMonitor(*image,
11665                 (MagickProgressMonitor) NULL,(*image)->client_data);
11666               (void) XMagickCommand(display,resource_info,windows,
11667                 SaveToUndoBufferCommand,image,exception);
11668               windows->image.orphan=MagickFalse;
11669               (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11670                 MagickTrue,crop_info.x,crop_info.y,exception);
11671               roi_image=DestroyImage(roi_image);
11672               (void) SetImageProgressMonitor(*image,progress_monitor,
11673                 (*image)->client_data);
11674               break;
11675             }
11676           }
11677           if (command_type != InfoCommand)
11678             {
11679               XConfigureImageColormap(display,resource_info,windows,*image,
11680                 exception);
11681               (void) XConfigureImage(display,resource_info,windows,*image,
11682                 exception);
11683             }
11684           XCheckRefreshWindows(display,windows);
11685           XInfoWidget(display,windows,text);
11686           (void) XSetFunction(display,windows->image.highlight_context,
11687             GXinvert);
11688           state&=(~UpdateRegionState);
11689         }
11690       XHighlightRectangle(display,windows->image.id,
11691         windows->image.highlight_context,&highlight_info);
11692       XScreenEvent(display,windows,&event,exception);
11693       if (event.xany.window == windows->command.id)
11694         {
11695           /*
11696             Select a command from the Command widget.
11697           */
11698           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11699           command_type=NullCommand;
11700           id=XCommandWidget(display,windows,ApplyMenu,&event);
11701           if (id >= 0)
11702             {
11703               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11704               command_type=ApplyCommands[id];
11705               if (id < ApplyMenus)
11706                 {
11707                   /*
11708                     Select a command from a pop-up menu.
11709                   */
11710                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11711                     (const char **) Menus[id],command);
11712                   if (entry >= 0)
11713                     {
11714                       (void) CopyMagickString(command,Menus[id][entry],
11715                         MaxTextExtent);
11716                       command_type=Commands[id][entry];
11717                     }
11718                 }
11719             }
11720           (void) XSetFunction(display,windows->image.highlight_context,
11721             GXinvert);
11722           XHighlightRectangle(display,windows->image.id,
11723             windows->image.highlight_context,&highlight_info);
11724           if (command_type == HelpCommand)
11725             {
11726               (void) XSetFunction(display,windows->image.highlight_context,
11727                 GXcopy);
11728               XTextViewWidget(display,resource_info,windows,MagickFalse,
11729                 "Help Viewer - Region of Interest",ImageROIHelp);
11730               (void) XSetFunction(display,windows->image.highlight_context,
11731                 GXinvert);
11732               continue;
11733             }
11734           if (command_type == QuitCommand)
11735             {
11736               /*
11737                 exit.
11738               */
11739               state|=EscapeState;
11740               state|=ExitState;
11741               continue;
11742             }
11743           if (command_type != NullCommand)
11744             state|=UpdateRegionState;
11745           continue;
11746         }
11747       XHighlightRectangle(display,windows->image.id,
11748         windows->image.highlight_context,&highlight_info);
11749       switch (event.type)
11750       {
11751         case ButtonPress:
11752         {
11753           x=windows->image.x;
11754           y=windows->image.y;
11755           if (event.xbutton.button != Button1)
11756             break;
11757           if (event.xbutton.window != windows->image.id)
11758             break;
11759           x=windows->image.x+event.xbutton.x;
11760           y=windows->image.y+event.xbutton.y;
11761           if ((x < (int) (roi_info.x+RoiDelta)) &&
11762               (x > (int) (roi_info.x-RoiDelta)) &&
11763               (y < (int) (roi_info.y+RoiDelta)) &&
11764               (y > (int) (roi_info.y-RoiDelta)))
11765             {
11766               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11767               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11768               state|=UpdateConfigurationState;
11769               break;
11770             }
11771           if ((x < (int) (roi_info.x+RoiDelta)) &&
11772               (x > (int) (roi_info.x-RoiDelta)) &&
11773               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11774               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11775             {
11776               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11777               state|=UpdateConfigurationState;
11778               break;
11779             }
11780           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11781               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11782               (y < (int) (roi_info.y+RoiDelta)) &&
11783               (y > (int) (roi_info.y-RoiDelta)))
11784             {
11785               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11786               state|=UpdateConfigurationState;
11787               break;
11788             }
11789           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11790               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11791               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11792               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11793             {
11794               state|=UpdateConfigurationState;
11795               break;
11796             }
11797         }
11798         case ButtonRelease:
11799         {
11800           if (event.xbutton.window == windows->pan.id)
11801             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11802                 (highlight_info.y != crop_info.y-windows->image.y))
11803               XHighlightRectangle(display,windows->image.id,
11804                 windows->image.highlight_context,&highlight_info);
11805           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11806             event.xbutton.time);
11807           break;
11808         }
11809         case Expose:
11810         {
11811           if (event.xexpose.window == windows->image.id)
11812             if (event.xexpose.count == 0)
11813               {
11814                 event.xexpose.x=(int) highlight_info.x;
11815                 event.xexpose.y=(int) highlight_info.y;
11816                 event.xexpose.width=(int) highlight_info.width;
11817                 event.xexpose.height=(int) highlight_info.height;
11818                 XRefreshWindow(display,&windows->image,&event);
11819               }
11820           if (event.xexpose.window == windows->info.id)
11821             if (event.xexpose.count == 0)
11822               XInfoWidget(display,windows,text);
11823           break;
11824         }
11825         case KeyPress:
11826         {
11827           KeySym
11828             key_symbol;
11829
11830           if (event.xkey.window != windows->image.id)
11831             break;
11832           /*
11833             Respond to a user key press.
11834           */
11835           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11836             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11837           switch ((int) key_symbol)
11838           {
11839             case XK_Shift_L:
11840             case XK_Shift_R:
11841               break;
11842             case XK_Escape:
11843             case XK_F20:
11844               state|=EscapeState;
11845             case XK_Return:
11846             {
11847               state|=ExitState;
11848               break;
11849             }
11850             case XK_Home:
11851             case XK_KP_Home:
11852             {
11853               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11854               roi_info.y=(ssize_t) (windows->image.height/2L-
11855                 roi_info.height/2L);
11856               break;
11857             }
11858             case XK_Left:
11859             case XK_KP_Left:
11860             {
11861               roi_info.x--;
11862               break;
11863             }
11864             case XK_Up:
11865             case XK_KP_Up:
11866             case XK_Next:
11867             {
11868               roi_info.y--;
11869               break;
11870             }
11871             case XK_Right:
11872             case XK_KP_Right:
11873             {
11874               roi_info.x++;
11875               break;
11876             }
11877             case XK_Prior:
11878             case XK_Down:
11879             case XK_KP_Down:
11880             {
11881               roi_info.y++;
11882               break;
11883             }
11884             case XK_F1:
11885             case XK_Help:
11886             {
11887               (void) XSetFunction(display,windows->image.highlight_context,
11888                 GXcopy);
11889               XTextViewWidget(display,resource_info,windows,MagickFalse,
11890                 "Help Viewer - Region of Interest",ImageROIHelp);
11891               (void) XSetFunction(display,windows->image.highlight_context,
11892                 GXinvert);
11893               break;
11894             }
11895             default:
11896             {
11897               command_type=XImageWindowCommand(display,resource_info,windows,
11898                 event.xkey.state,key_symbol,image,exception);
11899               if (command_type != NullCommand)
11900                 state|=UpdateRegionState;
11901               break;
11902             }
11903           }
11904           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11905             event.xkey.time);
11906           break;
11907         }
11908         case KeyRelease:
11909           break;
11910         case MotionNotify:
11911         {
11912           if (event.xbutton.window != windows->image.id)
11913             break;
11914           /*
11915             Map and unmap Info widget as text cursor crosses its boundaries.
11916           */
11917           x=event.xmotion.x;
11918           y=event.xmotion.y;
11919           if (IfMagickTrue(windows->info.mapped) )
11920             {
11921               if ((x < (int) (windows->info.x+windows->info.width)) &&
11922                   (y < (int) (windows->info.y+windows->info.height)))
11923                 (void) XWithdrawWindow(display,windows->info.id,
11924                   windows->info.screen);
11925             }
11926           else
11927             if ((x > (int) (windows->info.x+windows->info.width)) ||
11928                 (y > (int) (windows->info.y+windows->info.height)))
11929               (void) XMapWindow(display,windows->info.id);
11930           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11931           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11932           break;
11933         }
11934         case SelectionRequest:
11935         {
11936           XSelectionEvent
11937             notify;
11938
11939           XSelectionRequestEvent
11940             *request;
11941
11942           /*
11943             Set primary selection.
11944           */
11945           (void) FormatLocaleString(text,MaxTextExtent,
11946             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11947             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11948           request=(&(event.xselectionrequest));
11949           (void) XChangeProperty(request->display,request->requestor,
11950             request->property,request->target,8,PropModeReplace,
11951             (unsigned char *) text,(int) strlen(text));
11952           notify.type=SelectionNotify;
11953           notify.display=request->display;
11954           notify.requestor=request->requestor;
11955           notify.selection=request->selection;
11956           notify.target=request->target;
11957           notify.time=request->time;
11958           if (request->property == None)
11959             notify.property=request->target;
11960           else
11961             notify.property=request->property;
11962           (void) XSendEvent(request->display,request->requestor,False,0,
11963             (XEvent *) &notify);
11964         }
11965         default:
11966           break;
11967       }
11968       if ((state & UpdateConfigurationState) != 0)
11969         {
11970           (void) XPutBackEvent(display,&event);
11971           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11972           break;
11973         }
11974     } while ((state & ExitState) == 0);
11975   } while ((state & ExitState) == 0);
11976   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11977   XSetCursorState(display,windows,MagickFalse);
11978   if ((state & EscapeState) != 0)
11979     return(MagickTrue);
11980   return(MagickTrue);
11981 }
11982 \f
11983 /*
11984 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11985 %                                                                             %
11986 %                                                                             %
11987 %                                                                             %
11988 +   X R o t a t e I m a g e                                                   %
11989 %                                                                             %
11990 %                                                                             %
11991 %                                                                             %
11992 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11993 %
11994 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11995 %  rotation angle is computed from the slope of a line drawn by the user.
11996 %
11997 %  The format of the XRotateImage method is:
11998 %
11999 %      MagickBooleanType XRotateImage(Display *display,
12000 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
12001 %        Image **image,ExceptionInfo *exception)
12002 %
12003 %  A description of each parameter follows:
12004 %
12005 %    o display: Specifies a connection to an X server; returned from
12006 %      XOpenDisplay.
12007 %
12008 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12009 %
12010 %    o windows: Specifies a pointer to a XWindows structure.
12011 %
12012 %    o degrees: Specifies the number of degrees to rotate the image.
12013 %
12014 %    o image: the image.
12015 %
12016 %    o exception: return any errors or warnings in this structure.
12017 %
12018 */
12019 static MagickBooleanType XRotateImage(Display *display,
12020   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12021   ExceptionInfo *exception)
12022 {
12023   static const char
12024     *RotateMenu[] =
12025     {
12026       "Pixel Color",
12027       "Direction",
12028       "Help",
12029       "Dismiss",
12030       (char *) NULL
12031     };
12032
12033   static ModeType
12034     direction = HorizontalRotateCommand;
12035
12036   static const ModeType
12037     DirectionCommands[] =
12038     {
12039       HorizontalRotateCommand,
12040       VerticalRotateCommand
12041     },
12042     RotateCommands[] =
12043     {
12044       RotateColorCommand,
12045       RotateDirectionCommand,
12046       RotateHelpCommand,
12047       RotateDismissCommand
12048     };
12049
12050   static unsigned int
12051     pen_id = 0;
12052
12053   char
12054     command[MaxTextExtent],
12055     text[MaxTextExtent];
12056
12057   Image
12058     *rotate_image;
12059
12060   int
12061     id,
12062     x,
12063     y;
12064
12065   double
12066     normalized_degrees;
12067
12068   register int
12069     i;
12070
12071   unsigned int
12072     height,
12073     rotations,
12074     width;
12075
12076   if (degrees == 0.0)
12077     {
12078       unsigned int
12079         distance;
12080
12081       size_t
12082         state;
12083
12084       XEvent
12085         event;
12086
12087       XSegment
12088         rotate_info;
12089
12090       /*
12091         Map Command widget.
12092       */
12093       (void) CloneString(&windows->command.name,"Rotate");
12094       windows->command.data=2;
12095       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12096       (void) XMapRaised(display,windows->command.id);
12097       XClientMessage(display,windows->image.id,windows->im_protocols,
12098         windows->im_update_widget,CurrentTime);
12099       /*
12100         Wait for first button press.
12101       */
12102       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12103       XQueryPosition(display,windows->image.id,&x,&y);
12104       rotate_info.x1=x;
12105       rotate_info.y1=y;
12106       rotate_info.x2=x;
12107       rotate_info.y2=y;
12108       state=DefaultState;
12109       do
12110       {
12111         XHighlightLine(display,windows->image.id,
12112           windows->image.highlight_context,&rotate_info);
12113         /*
12114           Wait for next event.
12115         */
12116         XScreenEvent(display,windows,&event,exception);
12117         XHighlightLine(display,windows->image.id,
12118           windows->image.highlight_context,&rotate_info);
12119         if (event.xany.window == windows->command.id)
12120           {
12121             /*
12122               Select a command from the Command widget.
12123             */
12124             id=XCommandWidget(display,windows,RotateMenu,&event);
12125             if (id < 0)
12126               continue;
12127             (void) XSetFunction(display,windows->image.highlight_context,
12128               GXcopy);
12129             switch (RotateCommands[id])
12130             {
12131               case RotateColorCommand:
12132               {
12133                 const char
12134                   *ColorMenu[MaxNumberPens];
12135
12136                 int
12137                   pen_number;
12138
12139                 XColor
12140                   color;
12141
12142                 /*
12143                   Initialize menu selections.
12144                 */
12145                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12146                   ColorMenu[i]=resource_info->pen_colors[i];
12147                 ColorMenu[MaxNumberPens-2]="Browser...";
12148                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12149                 /*
12150                   Select a pen color from the pop-up menu.
12151                 */
12152                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12153                   (const char **) ColorMenu,command);
12154                 if (pen_number < 0)
12155                   break;
12156                 if (pen_number == (MaxNumberPens-2))
12157                   {
12158                     static char
12159                       color_name[MaxTextExtent] = "gray";
12160
12161                     /*
12162                       Select a pen color from a dialog.
12163                     */
12164                     resource_info->pen_colors[pen_number]=color_name;
12165                     XColorBrowserWidget(display,windows,"Select",color_name);
12166                     if (*color_name == '\0')
12167                       break;
12168                   }
12169                 /*
12170                   Set pen color.
12171                 */
12172                 (void) XParseColor(display,windows->map_info->colormap,
12173                   resource_info->pen_colors[pen_number],&color);
12174                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12175                   (unsigned int) MaxColors,&color);
12176                 windows->pixel_info->pen_colors[pen_number]=color;
12177                 pen_id=(unsigned int) pen_number;
12178                 break;
12179               }
12180               case RotateDirectionCommand:
12181               {
12182                 static const char
12183                   *Directions[] =
12184                   {
12185                     "horizontal",
12186                     "vertical",
12187                     (char *) NULL,
12188                   };
12189
12190                 /*
12191                   Select a command from the pop-up menu.
12192                 */
12193                 id=XMenuWidget(display,windows,RotateMenu[id],
12194                   Directions,command);
12195                 if (id >= 0)
12196                   direction=DirectionCommands[id];
12197                 break;
12198               }
12199               case RotateHelpCommand:
12200               {
12201                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12202                   "Help Viewer - Image Rotation",ImageRotateHelp);
12203                 break;
12204               }
12205               case RotateDismissCommand:
12206               {
12207                 /*
12208                   Prematurely exit.
12209                 */
12210                 state|=EscapeState;
12211                 state|=ExitState;
12212                 break;
12213               }
12214               default:
12215                 break;
12216             }
12217             (void) XSetFunction(display,windows->image.highlight_context,
12218               GXinvert);
12219             continue;
12220           }
12221         switch (event.type)
12222         {
12223           case ButtonPress:
12224           {
12225             if (event.xbutton.button != Button1)
12226               break;
12227             if (event.xbutton.window != windows->image.id)
12228               break;
12229             /*
12230               exit loop.
12231             */
12232             (void) XSetFunction(display,windows->image.highlight_context,
12233               GXcopy);
12234             rotate_info.x1=event.xbutton.x;
12235             rotate_info.y1=event.xbutton.y;
12236             state|=ExitState;
12237             break;
12238           }
12239           case ButtonRelease:
12240             break;
12241           case Expose:
12242             break;
12243           case KeyPress:
12244           {
12245             char
12246               command[MaxTextExtent];
12247
12248             KeySym
12249               key_symbol;
12250
12251             if (event.xkey.window != windows->image.id)
12252               break;
12253             /*
12254               Respond to a user key press.
12255             */
12256             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12257               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12258             switch ((int) key_symbol)
12259             {
12260               case XK_Escape:
12261               case XK_F20:
12262               {
12263                 /*
12264                   Prematurely exit.
12265                 */
12266                 state|=EscapeState;
12267                 state|=ExitState;
12268                 break;
12269               }
12270               case XK_F1:
12271               case XK_Help:
12272               {
12273                 (void) XSetFunction(display,windows->image.highlight_context,
12274                   GXcopy);
12275                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12276                   "Help Viewer - Image Rotation",ImageRotateHelp);
12277                 (void) XSetFunction(display,windows->image.highlight_context,
12278                   GXinvert);
12279                 break;
12280               }
12281               default:
12282               {
12283                 (void) XBell(display,0);
12284                 break;
12285               }
12286             }
12287             break;
12288           }
12289           case MotionNotify:
12290           {
12291             rotate_info.x1=event.xmotion.x;
12292             rotate_info.y1=event.xmotion.y;
12293           }
12294         }
12295         rotate_info.x2=rotate_info.x1;
12296         rotate_info.y2=rotate_info.y1;
12297         if (direction == HorizontalRotateCommand)
12298           rotate_info.x2+=32;
12299         else
12300           rotate_info.y2-=32;
12301       } while ((state & ExitState) == 0);
12302       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12303       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12304       if ((state & EscapeState) != 0)
12305         return(MagickTrue);
12306       /*
12307         Draw line as pointer moves until the mouse button is released.
12308       */
12309       distance=0;
12310       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12311       state=DefaultState;
12312       do
12313       {
12314         if (distance > 9)
12315           {
12316             /*
12317               Display info and draw rotation line.
12318             */
12319             if (IfMagickFalse(windows->info.mapped) )
12320               (void) XMapWindow(display,windows->info.id);
12321             (void) FormatLocaleString(text,MaxTextExtent," %g",
12322               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12323             XInfoWidget(display,windows,text);
12324             XHighlightLine(display,windows->image.id,
12325               windows->image.highlight_context,&rotate_info);
12326           }
12327         else
12328           if (IfMagickTrue(windows->info.mapped) )
12329             (void) XWithdrawWindow(display,windows->info.id,
12330               windows->info.screen);
12331         /*
12332           Wait for next event.
12333         */
12334         XScreenEvent(display,windows,&event,exception);
12335         if (distance > 9)
12336           XHighlightLine(display,windows->image.id,
12337             windows->image.highlight_context,&rotate_info);
12338         switch (event.type)
12339         {
12340           case ButtonPress:
12341             break;
12342           case ButtonRelease:
12343           {
12344             /*
12345               User has committed to rotation line.
12346             */
12347             rotate_info.x2=event.xbutton.x;
12348             rotate_info.y2=event.xbutton.y;
12349             state|=ExitState;
12350             break;
12351           }
12352           case Expose:
12353             break;
12354           case MotionNotify:
12355           {
12356             rotate_info.x2=event.xmotion.x;
12357             rotate_info.y2=event.xmotion.y;
12358           }
12359           default:
12360             break;
12361         }
12362         /*
12363           Check boundary conditions.
12364         */
12365         if (rotate_info.x2 < 0)
12366           rotate_info.x2=0;
12367         else
12368           if (rotate_info.x2 > (int) windows->image.width)
12369             rotate_info.x2=(short) windows->image.width;
12370         if (rotate_info.y2 < 0)
12371           rotate_info.y2=0;
12372         else
12373           if (rotate_info.y2 > (int) windows->image.height)
12374             rotate_info.y2=(short) windows->image.height;
12375         /*
12376           Compute rotation angle from the slope of the line.
12377         */
12378         degrees=0.0;
12379         distance=(unsigned int)
12380           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12381           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12382         if (distance > 9)
12383           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12384             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12385       } while ((state & ExitState) == 0);
12386       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12387       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12388       if (distance <= 9)
12389         return(MagickTrue);
12390     }
12391   if (direction == VerticalRotateCommand)
12392     degrees-=90.0;
12393   if (degrees == 0.0)
12394     return(MagickTrue);
12395   /*
12396     Rotate image.
12397   */
12398   normalized_degrees=degrees;
12399   while (normalized_degrees < -45.0)
12400     normalized_degrees+=360.0;
12401   for (rotations=0; normalized_degrees > 45.0; rotations++)
12402     normalized_degrees-=90.0;
12403   if (normalized_degrees != 0.0)
12404     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12405       exception);
12406   XSetCursorState(display,windows,MagickTrue);
12407   XCheckRefreshWindows(display,windows);
12408   (*image)->background_color.red=(double) ScaleShortToQuantum(
12409     windows->pixel_info->pen_colors[pen_id].red);
12410   (*image)->background_color.green=(double) ScaleShortToQuantum(
12411     windows->pixel_info->pen_colors[pen_id].green);
12412   (*image)->background_color.blue=(double) ScaleShortToQuantum(
12413     windows->pixel_info->pen_colors[pen_id].blue);
12414   rotate_image=RotateImage(*image,degrees,exception);
12415   XSetCursorState(display,windows,MagickFalse);
12416   if (rotate_image == (Image *) NULL)
12417     return(MagickFalse);
12418   *image=DestroyImage(*image);
12419   *image=rotate_image;
12420   if (windows->image.crop_geometry != (char *) NULL)
12421     {
12422       /*
12423         Rotate crop geometry.
12424       */
12425       width=(unsigned int) (*image)->columns;
12426       height=(unsigned int) (*image)->rows;
12427       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12428       switch (rotations % 4)
12429       {
12430         default:
12431         case 0:
12432           break;
12433         case 1:
12434         {
12435           /*
12436             Rotate 90 degrees.
12437           */
12438           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12439             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12440             (int) height-y,x);
12441           break;
12442         }
12443         case 2:
12444         {
12445           /*
12446             Rotate 180 degrees.
12447           */
12448           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12449             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12450           break;
12451         }
12452         case 3:
12453         {
12454           /*
12455             Rotate 270 degrees.
12456           */
12457           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12458             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12459           break;
12460         }
12461       }
12462     }
12463   if (IfMagickTrue(windows->image.orphan) )
12464     return(MagickTrue);
12465   if (normalized_degrees != 0.0)
12466     {
12467       /*
12468         Update image colormap.
12469       */
12470       windows->image.window_changes.width=(int) (*image)->columns;
12471       windows->image.window_changes.height=(int) (*image)->rows;
12472       if (windows->image.crop_geometry != (char *) NULL)
12473         {
12474           /*
12475             Obtain dimensions of image from crop geometry.
12476           */
12477           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12478             &width,&height);
12479           windows->image.window_changes.width=(int) width;
12480           windows->image.window_changes.height=(int) height;
12481         }
12482       XConfigureImageColormap(display,resource_info,windows,*image,exception);
12483     }
12484   else
12485     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12486       {
12487         windows->image.window_changes.width=windows->image.ximage->height;
12488         windows->image.window_changes.height=windows->image.ximage->width;
12489       }
12490   /*
12491     Update image configuration.
12492   */
12493   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12494   return(MagickTrue);
12495 }
12496 \f
12497 /*
12498 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12499 %                                                                             %
12500 %                                                                             %
12501 %                                                                             %
12502 +   X S a v e I m a g e                                                       %
12503 %                                                                             %
12504 %                                                                             %
12505 %                                                                             %
12506 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12507 %
12508 %  XSaveImage() saves an image to a file.
12509 %
12510 %  The format of the XSaveImage method is:
12511 %
12512 %      MagickBooleanType XSaveImage(Display *display,
12513 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12514 %        ExceptionInfo *exception)
12515 %
12516 %  A description of each parameter follows:
12517 %
12518 %    o display: Specifies a connection to an X server; returned from
12519 %      XOpenDisplay.
12520 %
12521 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12522 %
12523 %    o windows: Specifies a pointer to a XWindows structure.
12524 %
12525 %    o image: the image.
12526 %
12527 %    o exception: return any errors or warnings in this structure.
12528 %
12529 */
12530 static MagickBooleanType XSaveImage(Display *display,
12531   XResourceInfo *resource_info,XWindows *windows,Image *image,
12532   ExceptionInfo *exception)
12533 {
12534   char
12535     filename[MaxTextExtent],
12536     geometry[MaxTextExtent];
12537
12538   Image
12539     *save_image;
12540
12541   ImageInfo
12542     *image_info;
12543
12544   MagickStatusType
12545     status;
12546
12547   /*
12548     Request file name from user.
12549   */
12550   if (resource_info->write_filename != (char *) NULL)
12551     (void) CopyMagickString(filename,resource_info->write_filename,
12552       MaxTextExtent);
12553   else
12554     {
12555       char
12556         path[MaxTextExtent];
12557
12558       int
12559         status;
12560
12561       GetPathComponent(image->filename,HeadPath,path);
12562       GetPathComponent(image->filename,TailPath,filename);
12563       if (*path != '\0')
12564         {
12565           status=chdir(path);
12566           if (status == -1)
12567             (void) ThrowMagickException(exception,GetMagickModule(),
12568               FileOpenError,"UnableToOpenFile","%s",path);
12569         }
12570     }
12571   XFileBrowserWidget(display,windows,"Save",filename);
12572   if (*filename == '\0')
12573     return(MagickTrue);
12574   if (IfMagickTrue(IsPathAccessible(filename)) )
12575     {
12576       int
12577         status;
12578
12579       /*
12580         File exists-- seek user's permission before overwriting.
12581       */
12582       status=XConfirmWidget(display,windows,"Overwrite",filename);
12583       if (status <= 0)
12584         return(MagickTrue);
12585     }
12586   image_info=CloneImageInfo(resource_info->image_info);
12587   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12588   (void) SetImageInfo(image_info,1,exception);
12589   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12590       (LocaleCompare(image_info->magick,"JPG") == 0))
12591     {
12592       char
12593         quality[MaxTextExtent];
12594
12595       int
12596         status;
12597
12598       /*
12599         Request JPEG quality from user.
12600       */
12601       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12602         image->quality);
12603       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12604         quality);
12605       if (*quality == '\0')
12606         return(MagickTrue);
12607       image->quality=StringToUnsignedLong(quality);
12608       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12609     }
12610   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12611       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12612       (LocaleCompare(image_info->magick,"PS") == 0) ||
12613       (LocaleCompare(image_info->magick,"PS2") == 0))
12614     {
12615       char
12616         geometry[MaxTextExtent];
12617
12618       /*
12619         Request page geometry from user.
12620       */
12621       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12622       if (LocaleCompare(image_info->magick,"PDF") == 0)
12623         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12624       if (image_info->page != (char *) NULL)
12625         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12626       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12627         "Select page geometry:",geometry);
12628       if (*geometry != '\0')
12629         image_info->page=GetPageGeometry(geometry);
12630     }
12631   /*
12632     Apply image transforms.
12633   */
12634   XSetCursorState(display,windows,MagickTrue);
12635   XCheckRefreshWindows(display,windows);
12636   save_image=CloneImage(image,0,0,MagickTrue,exception);
12637   if (save_image == (Image *) NULL)
12638     return(MagickFalse);
12639   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12640     windows->image.ximage->width,windows->image.ximage->height);
12641   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12642     exception);
12643   /*
12644     Write image.
12645   */
12646   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12647   status=WriteImage(image_info,save_image,exception);
12648   if (IfMagickTrue(status) )
12649     image->taint=MagickFalse;
12650   save_image=DestroyImage(save_image);
12651   image_info=DestroyImageInfo(image_info);
12652   XSetCursorState(display,windows,MagickFalse);
12653   return(IsMagickTrue(status));
12654 }
12655 \f
12656 /*
12657 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12658 %                                                                             %
12659 %                                                                             %
12660 %                                                                             %
12661 +   X S c r e e n E v e n t                                                   %
12662 %                                                                             %
12663 %                                                                             %
12664 %                                                                             %
12665 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12666 %
12667 %  XScreenEvent() handles global events associated with the Pan and Magnify
12668 %  windows.
12669 %
12670 %  The format of the XScreenEvent function is:
12671 %
12672 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12673 %        ExceptionInfo *exception)
12674 %
12675 %  A description of each parameter follows:
12676 %
12677 %    o display: Specifies a pointer to the Display structure;  returned from
12678 %      XOpenDisplay.
12679 %
12680 %    o windows: Specifies a pointer to a XWindows structure.
12681 %
12682 %    o event: Specifies a pointer to a X11 XEvent structure.
12683 %
12684 %    o exception: return any errors or warnings in this structure.
12685 %
12686 */
12687
12688 #if defined(__cplusplus) || defined(c_plusplus)
12689 extern "C" {
12690 #endif
12691
12692 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12693 {
12694   register XWindows
12695     *windows;
12696
12697   windows=(XWindows *) data;
12698   if ((event->type == ClientMessage) &&
12699       (event->xclient.window == windows->image.id))
12700     return(MagickFalse);
12701   return(MagickTrue);
12702 }
12703
12704 #if defined(__cplusplus) || defined(c_plusplus)
12705 }
12706 #endif
12707
12708 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12709   ExceptionInfo *exception)
12710 {
12711   register int
12712     x,
12713     y;
12714
12715   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12716   if (event->xany.window == windows->command.id)
12717     return;
12718   switch (event->type)
12719   {
12720     case ButtonPress:
12721     case ButtonRelease:
12722     {
12723       if ((event->xbutton.button == Button3) &&
12724           (event->xbutton.state & Mod1Mask))
12725         {
12726           /*
12727             Convert Alt-Button3 to Button2.
12728           */
12729           event->xbutton.button=Button2;
12730           event->xbutton.state&=(~Mod1Mask);
12731         }
12732       if (event->xbutton.window == windows->backdrop.id)
12733         {
12734           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12735             event->xbutton.time);
12736           break;
12737         }
12738       if (event->xbutton.window == windows->pan.id)
12739         {
12740           XPanImage(display,windows,event,exception);
12741           break;
12742         }
12743       if (event->xbutton.window == windows->image.id)
12744         if (event->xbutton.button == Button2)
12745           {
12746             /*
12747               Update magnified image.
12748             */
12749             x=event->xbutton.x;
12750             y=event->xbutton.y;
12751             if (x < 0)
12752               x=0;
12753             else
12754               if (x >= (int) windows->image.width)
12755                 x=(int) (windows->image.width-1);
12756             windows->magnify.x=(int) windows->image.x+x;
12757             if (y < 0)
12758               y=0;
12759             else
12760              if (y >= (int) windows->image.height)
12761                y=(int) (windows->image.height-1);
12762             windows->magnify.y=windows->image.y+y;
12763             if (IfMagickFalse(windows->magnify.mapped) )
12764               (void) XMapRaised(display,windows->magnify.id);
12765             XMakeMagnifyImage(display,windows,exception);
12766             if (event->type == ButtonRelease)
12767               (void) XWithdrawWindow(display,windows->info.id,
12768                 windows->info.screen);
12769             break;
12770           }
12771       break;
12772     }
12773     case ClientMessage:
12774     {
12775       /*
12776         If client window delete message, exit.
12777       */
12778       if (event->xclient.message_type != windows->wm_protocols)
12779         break;
12780       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12781         break;
12782       if (event->xclient.window == windows->magnify.id)
12783         {
12784           (void) XWithdrawWindow(display,windows->magnify.id,
12785             windows->magnify.screen);
12786           break;
12787         }
12788       break;
12789     }
12790     case ConfigureNotify:
12791     {
12792       if (event->xconfigure.window == windows->magnify.id)
12793         {
12794           unsigned int
12795             magnify;
12796
12797           /*
12798             Magnify window has a new configuration.
12799           */
12800           windows->magnify.width=(unsigned int) event->xconfigure.width;
12801           windows->magnify.height=(unsigned int) event->xconfigure.height;
12802           if (IfMagickFalse(windows->magnify.mapped) )
12803             break;
12804           magnify=1;
12805           while ((int) magnify <= event->xconfigure.width)
12806             magnify<<=1;
12807           while ((int) magnify <= event->xconfigure.height)
12808             magnify<<=1;
12809           magnify>>=1;
12810           if (((int) magnify != event->xconfigure.width) ||
12811               ((int) magnify != event->xconfigure.height))
12812             {
12813               XWindowChanges
12814                 window_changes;
12815
12816               window_changes.width=(int) magnify;
12817               window_changes.height=(int) magnify;
12818               (void) XReconfigureWMWindow(display,windows->magnify.id,
12819                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12820                 &window_changes);
12821               break;
12822             }
12823           XMakeMagnifyImage(display,windows,exception);
12824           break;
12825         }
12826       break;
12827     }
12828     case Expose:
12829     {
12830       if (event->xexpose.window == windows->image.id)
12831         {
12832           XRefreshWindow(display,&windows->image,event);
12833           break;
12834         }
12835       if (event->xexpose.window == windows->pan.id)
12836         if (event->xexpose.count == 0)
12837           {
12838             XDrawPanRectangle(display,windows);
12839             break;
12840           }
12841       if (event->xexpose.window == windows->magnify.id)
12842         if (event->xexpose.count == 0)
12843           {
12844             XMakeMagnifyImage(display,windows,exception);
12845             break;
12846           }
12847       break;
12848     }
12849     case KeyPress:
12850     {
12851       char
12852         command[MaxTextExtent];
12853
12854       KeySym
12855         key_symbol;
12856
12857       if (event->xkey.window != windows->magnify.id)
12858         break;
12859       /*
12860         Respond to a user key press.
12861       */
12862       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12863         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12864       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12865         exception);
12866       break;
12867     }
12868     case MapNotify:
12869     {
12870       if (event->xmap.window == windows->magnify.id)
12871         {
12872           windows->magnify.mapped=MagickTrue;
12873           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12874           break;
12875         }
12876       if (event->xmap.window == windows->info.id)
12877         {
12878           windows->info.mapped=MagickTrue;
12879           break;
12880         }
12881       break;
12882     }
12883     case MotionNotify:
12884     {
12885       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12886       if (event->xmotion.window == windows->image.id)
12887         if (IfMagickTrue(windows->magnify.mapped) )
12888           {
12889             /*
12890               Update magnified image.
12891             */
12892             x=event->xmotion.x;
12893             y=event->xmotion.y;
12894             if (x < 0)
12895               x=0;
12896             else
12897               if (x >= (int) windows->image.width)
12898                 x=(int) (windows->image.width-1);
12899             windows->magnify.x=(int) windows->image.x+x;
12900             if (y < 0)
12901               y=0;
12902             else
12903              if (y >= (int) windows->image.height)
12904                y=(int) (windows->image.height-1);
12905             windows->magnify.y=windows->image.y+y;
12906             XMakeMagnifyImage(display,windows,exception);
12907           }
12908       break;
12909     }
12910     case UnmapNotify:
12911     {
12912       if (event->xunmap.window == windows->magnify.id)
12913         {
12914           windows->magnify.mapped=MagickFalse;
12915           break;
12916         }
12917       if (event->xunmap.window == windows->info.id)
12918         {
12919           windows->info.mapped=MagickFalse;
12920           break;
12921         }
12922       break;
12923     }
12924     default:
12925       break;
12926   }
12927 }
12928 \f
12929 /*
12930 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12931 %                                                                             %
12932 %                                                                             %
12933 %                                                                             %
12934 +   X S e t C r o p G e o m e t r y                                           %
12935 %                                                                             %
12936 %                                                                             %
12937 %                                                                             %
12938 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12939 %
12940 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12941 %  and translates it to a cropping geometry relative to the image.
12942 %
12943 %  The format of the XSetCropGeometry method is:
12944 %
12945 %      void XSetCropGeometry(Display *display,XWindows *windows,
12946 %        RectangleInfo *crop_info,Image *image)
12947 %
12948 %  A description of each parameter follows:
12949 %
12950 %    o display: Specifies a connection to an X server; returned from
12951 %      XOpenDisplay.
12952 %
12953 %    o windows: Specifies a pointer to a XWindows structure.
12954 %
12955 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12956 %      Image window to crop.
12957 %
12958 %    o image: the image.
12959 %
12960 */
12961 static void XSetCropGeometry(Display *display,XWindows *windows,
12962   RectangleInfo *crop_info,Image *image)
12963 {
12964   char
12965     text[MaxTextExtent];
12966
12967   int
12968     x,
12969     y;
12970
12971   double
12972     scale_factor;
12973
12974   unsigned int
12975     height,
12976     width;
12977
12978   if (IfMagickTrue(windows->info.mapped) )
12979     {
12980       /*
12981         Display info on cropping rectangle.
12982       */
12983       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12984         (double) crop_info->width,(double) crop_info->height,(double)
12985         crop_info->x,(double) crop_info->y);
12986       XInfoWidget(display,windows,text);
12987     }
12988   /*
12989     Cropping geometry is relative to any previous crop geometry.
12990   */
12991   x=0;
12992   y=0;
12993   width=(unsigned int) image->columns;
12994   height=(unsigned int) image->rows;
12995   if (windows->image.crop_geometry != (char *) NULL)
12996     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12997   else
12998     windows->image.crop_geometry=AcquireString((char *) NULL);
12999   /*
13000     Define the crop geometry string from the cropping rectangle.
13001   */
13002   scale_factor=(double) width/windows->image.ximage->width;
13003   if (crop_info->x > 0)
13004     x+=(int) (scale_factor*crop_info->x+0.5);
13005   width=(unsigned int) (scale_factor*crop_info->width+0.5);
13006   if (width == 0)
13007     width=1;
13008   scale_factor=(double) height/windows->image.ximage->height;
13009   if (crop_info->y > 0)
13010     y+=(int) (scale_factor*crop_info->y+0.5);
13011   height=(unsigned int) (scale_factor*crop_info->height+0.5);
13012   if (height == 0)
13013     height=1;
13014   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13015     "%ux%u%+d%+d",width,height,x,y);
13016 }
13017 \f
13018 /*
13019 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13020 %                                                                             %
13021 %                                                                             %
13022 %                                                                             %
13023 +   X T i l e I m a g e                                                       %
13024 %                                                                             %
13025 %                                                                             %
13026 %                                                                             %
13027 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13028 %
13029 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13030 %  The load or delete command is chosen from a menu.
13031 %
13032 %  The format of the XTileImage method is:
13033 %
13034 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13035 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13036 %
13037 %  A description of each parameter follows:
13038 %
13039 %    o tile_image:  XTileImage reads or deletes the tile image
13040 %      and returns it.  A null image is returned if an error occurs.
13041 %
13042 %    o display: Specifies a connection to an X server;  returned from
13043 %      XOpenDisplay.
13044 %
13045 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13046 %
13047 %    o windows: Specifies a pointer to a XWindows structure.
13048 %
13049 %    o image: the image; returned from ReadImage.
13050 %
13051 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13052 %      the entire image is refreshed.
13053 %
13054 %    o exception: return any errors or warnings in this structure.
13055 %
13056 */
13057 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13058   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13059 {
13060   static const char
13061     *VerbMenu[] =
13062     {
13063       "Load",
13064       "Next",
13065       "Former",
13066       "Delete",
13067       "Update",
13068       (char *) NULL,
13069     };
13070
13071   static const ModeType
13072     TileCommands[] =
13073     {
13074       TileLoadCommand,
13075       TileNextCommand,
13076       TileFormerCommand,
13077       TileDeleteCommand,
13078       TileUpdateCommand
13079     };
13080
13081   char
13082     command[MaxTextExtent],
13083     filename[MaxTextExtent];
13084
13085   Image
13086     *tile_image;
13087
13088   int
13089     id,
13090     status,
13091     tile,
13092     x,
13093     y;
13094
13095   double
13096     scale_factor;
13097
13098   register char
13099     *p,
13100     *q;
13101
13102   register int
13103     i;
13104
13105   unsigned int
13106     height,
13107     width;
13108
13109   /*
13110     Tile image is relative to montage image configuration.
13111   */
13112   x=0;
13113   y=0;
13114   width=(unsigned int) image->columns;
13115   height=(unsigned int) image->rows;
13116   if (windows->image.crop_geometry != (char *) NULL)
13117     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13118   scale_factor=(double) width/windows->image.ximage->width;
13119   event->xbutton.x+=windows->image.x;
13120   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13121   scale_factor=(double) height/windows->image.ximage->height;
13122   event->xbutton.y+=windows->image.y;
13123   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13124   /*
13125     Determine size and location of each tile in the visual image directory.
13126   */
13127   width=(unsigned int) image->columns;
13128   height=(unsigned int) image->rows;
13129   x=0;
13130   y=0;
13131   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13132   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13133     (event->xbutton.x-x)/width;
13134   if (tile < 0)
13135     {
13136       /*
13137         Button press is outside any tile.
13138       */
13139       (void) XBell(display,0);
13140       return((Image *) NULL);
13141     }
13142   /*
13143     Determine file name from the tile directory.
13144   */
13145   p=image->directory;
13146   for (i=tile; (i != 0) && (*p != '\0'); )
13147   {
13148     if (*p == '\n')
13149       i--;
13150     p++;
13151   }
13152   if (*p == '\0')
13153     {
13154       /*
13155         Button press is outside any tile.
13156       */
13157       (void) XBell(display,0);
13158       return((Image *) NULL);
13159     }
13160   /*
13161     Select a command from the pop-up menu.
13162   */
13163   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13164   if (id < 0)
13165     return((Image *) NULL);
13166   q=p;
13167   while ((*q != '\n') && (*q != '\0'))
13168     q++;
13169   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13170   /*
13171     Perform command for the selected tile.
13172   */
13173   XSetCursorState(display,windows,MagickTrue);
13174   XCheckRefreshWindows(display,windows);
13175   tile_image=NewImageList();
13176   switch (TileCommands[id])
13177   {
13178     case TileLoadCommand:
13179     {
13180       /*
13181         Load tile image.
13182       */
13183       XCheckRefreshWindows(display,windows);
13184       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13185         MaxTextExtent);
13186       (void) CopyMagickString(resource_info->image_info->filename,filename,
13187         MaxTextExtent);
13188       tile_image=ReadImage(resource_info->image_info,exception);
13189       CatchException(exception);
13190       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13191       break;
13192     }
13193     case TileNextCommand:
13194     {
13195       /*
13196         Display next image.
13197       */
13198       XClientMessage(display,windows->image.id,windows->im_protocols,
13199         windows->im_next_image,CurrentTime);
13200       break;
13201     }
13202     case TileFormerCommand:
13203     {
13204       /*
13205         Display former image.
13206       */
13207       XClientMessage(display,windows->image.id,windows->im_protocols,
13208         windows->im_former_image,CurrentTime);
13209       break;
13210     }
13211     case TileDeleteCommand:
13212     {
13213       /*
13214         Delete tile image.
13215       */
13216       if (IfMagickFalse(IsPathAccessible(filename)) )
13217         {
13218           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13219           break;
13220         }
13221       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13222       if (status <= 0)
13223         break;
13224       status=ShredFile(filename);
13225       if (IfMagickTrue(status) )
13226         {
13227           XNoticeWidget(display,windows,"Unable to delete image file:",
13228             filename);
13229           break;
13230         }
13231     }
13232     case TileUpdateCommand:
13233     {
13234       int
13235         x_offset,
13236         y_offset;
13237
13238       PixelInfo
13239         pixel;
13240
13241       register int
13242         j;
13243
13244       register Quantum
13245         *s;
13246
13247       /*
13248         Ensure all the images exist.
13249       */
13250       tile=0;
13251       GetPixelInfo(image,&pixel);
13252       for (p=image->directory; *p != '\0'; p++)
13253       {
13254         CacheView
13255           *image_view;
13256
13257         q=p;
13258         while ((*q != '\n') && (*q != '\0'))
13259           q++;
13260         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13261         p=q;
13262         if (IfMagickTrue(IsPathAccessible(filename)) )
13263           {
13264             tile++;
13265             continue;
13266           }
13267         /*
13268           Overwrite tile with background color.
13269         */
13270         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13271         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13272         image_view=AcquireAuthenticCacheView(image,exception);
13273         (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13274         for (i=0; i < (int) height; i++)
13275         {
13276           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13277             y_offset+i,width,1,exception);
13278           if (s == (Quantum *) NULL)
13279             break;
13280           for (j=0; j < (int) width; j++)
13281           {
13282             SetPixelInfoPixel(image,&pixel,s);
13283             s+=GetPixelChannels(image);
13284           }
13285           if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
13286             break;
13287         }
13288         image_view=DestroyCacheView(image_view);
13289         tile++;
13290       }
13291       windows->image.window_changes.width=(int) image->columns;
13292       windows->image.window_changes.height=(int) image->rows;
13293       XConfigureImageColormap(display,resource_info,windows,image,exception);
13294       (void) XConfigureImage(display,resource_info,windows,image,exception);
13295       break;
13296     }
13297     default:
13298       break;
13299   }
13300   XSetCursorState(display,windows,MagickFalse);
13301   return(tile_image);
13302 }
13303 \f
13304 /*
13305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13306 %                                                                             %
13307 %                                                                             %
13308 %                                                                             %
13309 +   X T r a n s l a t e I m a g e                                             %
13310 %                                                                             %
13311 %                                                                             %
13312 %                                                                             %
13313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13314 %
13315 %  XTranslateImage() translates the image within an Image window by one pixel
13316 %  as specified by the key symbol.  If the image has a montage string the
13317 %  translation is respect to the width and height contained within the string.
13318 %
13319 %  The format of the XTranslateImage method is:
13320 %
13321 %      void XTranslateImage(Display *display,XWindows *windows,
13322 %        Image *image,const KeySym key_symbol)
13323 %
13324 %  A description of each parameter follows:
13325 %
13326 %    o display: Specifies a connection to an X server; returned from
13327 %      XOpenDisplay.
13328 %
13329 %    o windows: Specifies a pointer to a XWindows structure.
13330 %
13331 %    o image: the image.
13332 %
13333 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13334 %      to trim.
13335 %
13336 */
13337 static void XTranslateImage(Display *display,XWindows *windows,
13338   Image *image,const KeySym key_symbol)
13339 {
13340   char
13341     text[MaxTextExtent];
13342
13343   int
13344     x,
13345     y;
13346
13347   unsigned int
13348     x_offset,
13349     y_offset;
13350
13351   /*
13352     User specified a pan position offset.
13353   */
13354   x_offset=windows->image.width;
13355   y_offset=windows->image.height;
13356   if (image->montage != (char *) NULL)
13357     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13358   switch ((int) key_symbol)
13359   {
13360     case XK_Home:
13361     case XK_KP_Home:
13362     {
13363       windows->image.x=(int) windows->image.width/2;
13364       windows->image.y=(int) windows->image.height/2;
13365       break;
13366     }
13367     case XK_Left:
13368     case XK_KP_Left:
13369     {
13370       windows->image.x-=x_offset;
13371       break;
13372     }
13373     case XK_Next:
13374     case XK_Up:
13375     case XK_KP_Up:
13376     {
13377       windows->image.y-=y_offset;
13378       break;
13379     }
13380     case XK_Right:
13381     case XK_KP_Right:
13382     {
13383       windows->image.x+=x_offset;
13384       break;
13385     }
13386     case XK_Prior:
13387     case XK_Down:
13388     case XK_KP_Down:
13389     {
13390       windows->image.y+=y_offset;
13391       break;
13392     }
13393     default:
13394       return;
13395   }
13396   /*
13397     Check boundary conditions.
13398   */
13399   if (windows->image.x < 0)
13400     windows->image.x=0;
13401   else
13402     if ((int) (windows->image.x+windows->image.width) >
13403         windows->image.ximage->width)
13404       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13405   if (windows->image.y < 0)
13406     windows->image.y=0;
13407   else
13408     if ((int) (windows->image.y+windows->image.height) >
13409         windows->image.ximage->height)
13410       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13411   /*
13412     Refresh Image window.
13413   */
13414   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13415     windows->image.width,windows->image.height,windows->image.x,
13416     windows->image.y);
13417   XInfoWidget(display,windows,text);
13418   XCheckRefreshWindows(display,windows);
13419   XDrawPanRectangle(display,windows);
13420   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13421   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13422 }
13423 \f
13424 /*
13425 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13426 %                                                                             %
13427 %                                                                             %
13428 %                                                                             %
13429 +   X T r i m I m a g e                                                       %
13430 %                                                                             %
13431 %                                                                             %
13432 %                                                                             %
13433 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13434 %
13435 %  XTrimImage() trims the edges from the Image window.
13436 %
13437 %  The format of the XTrimImage method is:
13438 %
13439 %      MagickBooleanType XTrimImage(Display *display,
13440 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13441 %        ExceptionInfo *exception)
13442 %
13443 %  A description of each parameter follows:
13444 %
13445 %    o display: Specifies a connection to an X server; returned from
13446 %      XOpenDisplay.
13447 %
13448 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13449 %
13450 %    o windows: Specifies a pointer to a XWindows structure.
13451 %
13452 %    o image: the image.
13453 %
13454 %    o exception: return any errors or warnings in this structure.
13455 %
13456 */
13457 static MagickBooleanType XTrimImage(Display *display,
13458   XResourceInfo *resource_info,XWindows *windows,Image *image,
13459   ExceptionInfo *exception)
13460 {
13461   RectangleInfo
13462     trim_info;
13463
13464   register int
13465     x,
13466     y;
13467
13468   size_t
13469     background,
13470     pixel;
13471
13472   /*
13473     Trim edges from image.
13474   */
13475   XSetCursorState(display,windows,MagickTrue);
13476   XCheckRefreshWindows(display,windows);
13477   /*
13478     Crop the left edge.
13479   */
13480   background=XGetPixel(windows->image.ximage,0,0);
13481   trim_info.width=(size_t) windows->image.ximage->width;
13482   for (x=0; x < windows->image.ximage->width; x++)
13483   {
13484     for (y=0; y < windows->image.ximage->height; y++)
13485     {
13486       pixel=XGetPixel(windows->image.ximage,x,y);
13487       if (pixel != background)
13488         break;
13489     }
13490     if (y < windows->image.ximage->height)
13491       break;
13492   }
13493   trim_info.x=(ssize_t) x;
13494   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13495     {
13496       XSetCursorState(display,windows,MagickFalse);
13497       return(MagickFalse);
13498     }
13499   /*
13500     Crop the right edge.
13501   */
13502   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13503   for (x=windows->image.ximage->width-1; x != 0; x--)
13504   {
13505     for (y=0; y < windows->image.ximage->height; y++)
13506     {
13507       pixel=XGetPixel(windows->image.ximage,x,y);
13508       if (pixel != background)
13509         break;
13510     }
13511     if (y < windows->image.ximage->height)
13512       break;
13513   }
13514   trim_info.width=(size_t) (x-trim_info.x+1);
13515   /*
13516     Crop the top edge.
13517   */
13518   background=XGetPixel(windows->image.ximage,0,0);
13519   trim_info.height=(size_t) windows->image.ximage->height;
13520   for (y=0; y < windows->image.ximage->height; y++)
13521   {
13522     for (x=0; x < windows->image.ximage->width; x++)
13523     {
13524       pixel=XGetPixel(windows->image.ximage,x,y);
13525       if (pixel != background)
13526         break;
13527     }
13528     if (x < windows->image.ximage->width)
13529       break;
13530   }
13531   trim_info.y=(ssize_t) y;
13532   /*
13533     Crop the bottom edge.
13534   */
13535   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13536   for (y=windows->image.ximage->height-1; y != 0; y--)
13537   {
13538     for (x=0; x < windows->image.ximage->width; x++)
13539     {
13540       pixel=XGetPixel(windows->image.ximage,x,y);
13541       if (pixel != background)
13542         break;
13543     }
13544     if (x < windows->image.ximage->width)
13545       break;
13546   }
13547   trim_info.height=(size_t) y-trim_info.y+1;
13548   if (((unsigned int) trim_info.width != windows->image.width) ||
13549       ((unsigned int) trim_info.height != windows->image.height))
13550     {
13551       /*
13552         Reconfigure Image window as defined by the trimming rectangle.
13553       */
13554       XSetCropGeometry(display,windows,&trim_info,image);
13555       windows->image.window_changes.width=(int) trim_info.width;
13556       windows->image.window_changes.height=(int) trim_info.height;
13557       (void) XConfigureImage(display,resource_info,windows,image,exception);
13558     }
13559   XSetCursorState(display,windows,MagickFalse);
13560   return(MagickTrue);
13561 }
13562 \f
13563 /*
13564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13565 %                                                                             %
13566 %                                                                             %
13567 %                                                                             %
13568 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13569 %                                                                             %
13570 %                                                                             %
13571 %                                                                             %
13572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13573 %
13574 %  XVisualDirectoryImage() creates a Visual Image Directory.
13575 %
13576 %  The format of the XVisualDirectoryImage method is:
13577 %
13578 %      Image *XVisualDirectoryImage(Display *display,
13579 %        XResourceInfo *resource_info,XWindows *windows,
13580 %        ExceptionInfo *exception)
13581 %
13582 %  A description of each parameter follows:
13583 %
13584 %    o display: Specifies a connection to an X server; returned from
13585 %      XOpenDisplay.
13586 %
13587 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13588 %
13589 %    o windows: Specifies a pointer to a XWindows structure.
13590 %
13591 %    o exception: return any errors or warnings in this structure.
13592 %
13593 */
13594 static Image *XVisualDirectoryImage(Display *display,
13595   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13596 {
13597 #define TileImageTag  "Scale/Image"
13598 #define XClientName  "montage"
13599
13600   char
13601     **filelist;
13602
13603   Image
13604     *images,
13605     *montage_image,
13606     *next_image,
13607     *thumbnail_image;
13608
13609   ImageInfo
13610     *read_info;
13611
13612   int
13613     number_files;
13614
13615   MagickBooleanType
13616     backdrop;
13617
13618   MagickStatusType
13619     status;
13620
13621   MontageInfo
13622     *montage_info;
13623
13624   RectangleInfo
13625     geometry;
13626
13627   register int
13628     i;
13629
13630   static char
13631     filename[MaxTextExtent] = "\0",
13632     filenames[MaxTextExtent] = "*";
13633
13634   XResourceInfo
13635     background_resources;
13636
13637   /*
13638     Request file name from user.
13639   */
13640   XFileBrowserWidget(display,windows,"Directory",filenames);
13641   if (*filenames == '\0')
13642     return((Image *) NULL);
13643   /*
13644     Expand the filenames.
13645   */
13646   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13647   if (filelist == (char **) NULL)
13648     {
13649       ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13650         filenames);
13651       return((Image *) NULL);
13652     }
13653   number_files=1;
13654   filelist[0]=filenames;
13655   status=ExpandFilenames(&number_files,&filelist);
13656   if (IfMagickFalse(status) || (number_files == 0))
13657     {
13658       if (number_files == 0)
13659         ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13660       else
13661         ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13662           filenames);
13663       return((Image *) NULL);
13664     }
13665   /*
13666     Set image background resources.
13667   */
13668   background_resources=(*resource_info);
13669   background_resources.window_id=AcquireString("");
13670   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13671     "0x%lx",windows->image.id);
13672   background_resources.backdrop=MagickTrue;
13673   /*
13674     Read each image and convert them to a tile.
13675   */
13676   backdrop=IsMagickTrue( (windows->visual_info->klass == TrueColor) ||
13677     (windows->visual_info->klass == DirectColor) );
13678   read_info=CloneImageInfo(resource_info->image_info);
13679   (void) SetImageOption(read_info,"jpeg:size","120x120");
13680   (void) CloneString(&read_info->size,DefaultTileGeometry);
13681   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13682     (void *) NULL);
13683   images=NewImageList();
13684   XSetCursorState(display,windows,MagickTrue);
13685   XCheckRefreshWindows(display,windows);
13686   for (i=0; i < (int) number_files; i++)
13687   {
13688     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13689     filelist[i]=DestroyString(filelist[i]);
13690     *read_info->magick='\0';
13691     next_image=ReadImage(read_info,exception);
13692     CatchException(exception);
13693     if (next_image != (Image *) NULL)
13694       {
13695         (void) DeleteImageProperty(next_image,"label");
13696         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13697           read_info,next_image,DefaultTileLabel,exception),exception);
13698         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13699           exception);
13700         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13701           geometry.height,exception);
13702         if (thumbnail_image != (Image *) NULL)
13703           {
13704             next_image=DestroyImage(next_image);
13705             next_image=thumbnail_image;
13706           }
13707         if (backdrop)
13708           {
13709             (void) XDisplayBackgroundImage(display,&background_resources,
13710               next_image,exception);
13711             XSetCursorState(display,windows,MagickTrue);
13712           }
13713         AppendImageToList(&images,next_image);
13714         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13715           {
13716             MagickBooleanType
13717               proceed;
13718
13719             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13720               (MagickSizeType) number_files);
13721             if (IfMagickFalse(proceed) )
13722               break;
13723           }
13724       }
13725   }
13726   filelist=(char **) RelinquishMagickMemory(filelist);
13727   if (images == (Image *) NULL)
13728     {
13729       read_info=DestroyImageInfo(read_info);
13730       XSetCursorState(display,windows,MagickFalse);
13731       ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13732       return((Image *) NULL);
13733     }
13734   /*
13735     Create the Visual Image Directory.
13736   */
13737   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13738   montage_info->pointsize=10;
13739   if (resource_info->font != (char *) NULL)
13740     (void) CloneString(&montage_info->font,resource_info->font);
13741   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13742   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13743     images),exception);
13744   images=DestroyImageList(images);
13745   montage_info=DestroyMontageInfo(montage_info);
13746   read_info=DestroyImageInfo(read_info);
13747   XSetCursorState(display,windows,MagickFalse);
13748   if (montage_image == (Image *) NULL)
13749     return(montage_image);
13750   XClientMessage(display,windows->image.id,windows->im_protocols,
13751     windows->im_next_image,CurrentTime);
13752   return(montage_image);
13753 }
13754 \f
13755 /*
13756 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13757 %                                                                             %
13758 %                                                                             %
13759 %                                                                             %
13760 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13761 %                                                                             %
13762 %                                                                             %
13763 %                                                                             %
13764 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13765 %
13766 %  XDisplayBackgroundImage() displays an image in the background of a window.
13767 %
13768 %  The format of the XDisplayBackgroundImage method is:
13769 %
13770 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13771 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13772 %
13773 %  A description of each parameter follows:
13774 %
13775 %    o display: Specifies a connection to an X server;  returned from
13776 %      XOpenDisplay.
13777 %
13778 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13779 %
13780 %    o image: the image.
13781 %
13782 %    o exception: return any errors or warnings in this structure.
13783 %
13784 */
13785 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13786   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13787 {
13788   char
13789     geometry[MaxTextExtent],
13790     visual_type[MaxTextExtent];
13791
13792   int
13793     height,
13794     status,
13795     width;
13796
13797   RectangleInfo
13798     geometry_info;
13799
13800   static XPixelInfo
13801     pixel;
13802
13803   static XStandardColormap
13804     *map_info;
13805
13806   static XVisualInfo
13807     *visual_info = (XVisualInfo *) NULL;
13808
13809   static XWindowInfo
13810     window_info;
13811
13812   size_t
13813     delay;
13814
13815   Window
13816     root_window;
13817
13818   XGCValues
13819     context_values;
13820
13821   XResourceInfo
13822     resources;
13823
13824   XWindowAttributes
13825     window_attributes;
13826
13827   /*
13828     Determine target window.
13829   */
13830   assert(image != (Image *) NULL);
13831   assert(image->signature == MagickSignature);
13832   if (IfMagickTrue(image->debug) )
13833     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13834   resources=(*resource_info);
13835   window_info.id=(Window) NULL;
13836   root_window=XRootWindow(display,XDefaultScreen(display));
13837   if (LocaleCompare(resources.window_id,"root") == 0)
13838     window_info.id=root_window;
13839   else
13840     {
13841       if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13842         window_info.id=XWindowByID(display,root_window,
13843           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13844       if (window_info.id == (Window) NULL)
13845         window_info.id=XWindowByName(display,root_window,resources.window_id);
13846     }
13847   if (window_info.id == (Window) NULL)
13848     {
13849       ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13850         resources.window_id);
13851       return(MagickFalse);
13852     }
13853   /*
13854     Determine window visual id.
13855   */
13856   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13857   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13858   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13859   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13860   if (status != 0)
13861     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13862       XVisualIDFromVisual(window_attributes.visual));
13863   if (visual_info == (XVisualInfo *) NULL)
13864     {
13865       /*
13866         Allocate standard colormap.
13867       */
13868       map_info=XAllocStandardColormap();
13869       if (map_info == (XStandardColormap *) NULL)
13870         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13871           image->filename);
13872       map_info->colormap=(Colormap) NULL;
13873       pixel.pixels=(unsigned long *) NULL;
13874       /*
13875         Initialize visual info.
13876       */
13877       resources.map_type=(char *) NULL;
13878       resources.visual_type=visual_type;
13879       visual_info=XBestVisualInfo(display,map_info,&resources);
13880       if (visual_info == (XVisualInfo *) NULL)
13881         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13882           resources.visual_type);
13883       /*
13884         Initialize window info.
13885       */
13886       window_info.ximage=(XImage *) NULL;
13887       window_info.matte_image=(XImage *) NULL;
13888       window_info.pixmap=(Pixmap) NULL;
13889       window_info.matte_pixmap=(Pixmap) NULL;
13890     }
13891   /*
13892     Free previous root colors.
13893   */
13894   if (window_info.id == root_window)
13895     (void) XDestroyWindowColors(display,root_window);
13896   /*
13897     Initialize Standard Colormap.
13898   */
13899   resources.colormap=SharedColormap;
13900   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13901     exception);
13902   /*
13903     Graphic context superclass.
13904   */
13905   context_values.background=pixel.background_color.pixel;
13906   context_values.foreground=pixel.foreground_color.pixel;
13907   pixel.annotate_context=XCreateGC(display,window_info.id,
13908     (size_t) (GCBackground | GCForeground),&context_values);
13909   if (pixel.annotate_context == (GC) NULL)
13910     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13911       image->filename);
13912   /*
13913     Initialize Image window attributes.
13914   */
13915   window_info.name=AcquireString("\0");
13916   window_info.icon_name=AcquireString("\0");
13917   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13918     &resources,&window_info);
13919   /*
13920     Create the X image.
13921   */
13922   window_info.width=(unsigned int) image->columns;
13923   window_info.height=(unsigned int) image->rows;
13924   if ((image->columns != window_info.width) ||
13925       (image->rows != window_info.height))
13926     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13927       image->filename);
13928   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13929     window_attributes.width,window_attributes.height);
13930   geometry_info.width=window_info.width;
13931   geometry_info.height=window_info.height;
13932   geometry_info.x=(ssize_t) window_info.x;
13933   geometry_info.y=(ssize_t) window_info.y;
13934   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13935     &geometry_info.width,&geometry_info.height);
13936   window_info.width=(unsigned int) geometry_info.width;
13937   window_info.height=(unsigned int) geometry_info.height;
13938   window_info.x=(int) geometry_info.x;
13939   window_info.y=(int) geometry_info.y;
13940   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13941     window_info.height,exception);
13942   if (IfMagickFalse(status) )
13943     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13944       image->filename);
13945   window_info.x=0;
13946   window_info.y=0;
13947   if (IfMagickTrue(image->debug) )
13948     {
13949       (void) LogMagickEvent(X11Event,GetMagickModule(),
13950         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13951         (double) image->columns,(double) image->rows);
13952       if (image->colors != 0)
13953         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13954           image->colors);
13955       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13956     }
13957   /*
13958     Adjust image dimensions as specified by backdrop or geometry options.
13959   */
13960   width=(int) window_info.width;
13961   height=(int) window_info.height;
13962   if (IfMagickTrue(resources.backdrop) )
13963     {
13964       /*
13965         Center image on window.
13966       */
13967       window_info.x=(window_attributes.width/2)-
13968         (window_info.ximage->width/2);
13969       window_info.y=(window_attributes.height/2)-
13970         (window_info.ximage->height/2);
13971       width=window_attributes.width;
13972       height=window_attributes.height;
13973     }
13974   if ((resources.image_geometry != (char *) NULL) &&
13975       (*resources.image_geometry != '\0'))
13976     {
13977       char
13978         default_geometry[MaxTextExtent];
13979
13980       int
13981         flags,
13982         gravity;
13983
13984       XSizeHints
13985         *size_hints;
13986
13987       /*
13988         User specified geometry.
13989       */
13990       size_hints=XAllocSizeHints();
13991       if (size_hints == (XSizeHints *) NULL)
13992         ThrowXWindowFatalException(ResourceLimitFatalError,
13993           "MemoryAllocationFailed",image->filename);
13994       size_hints->flags=0L;
13995       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13996         width,height);
13997       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13998         default_geometry,window_info.border_width,size_hints,&window_info.x,
13999         &window_info.y,&width,&height,&gravity);
14000       if (flags & (XValue | YValue))
14001         {
14002           width=window_attributes.width;
14003           height=window_attributes.height;
14004         }
14005       (void) XFree((void *) size_hints);
14006     }
14007   /*
14008     Create the X pixmap.
14009   */
14010   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14011     (unsigned int) height,window_info.depth);
14012   if (window_info.pixmap == (Pixmap) NULL)
14013     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14014       image->filename);
14015   /*
14016     Display pixmap on the window.
14017   */
14018   if (((unsigned int) width > window_info.width) ||
14019       ((unsigned int) height > window_info.height))
14020     (void) XFillRectangle(display,window_info.pixmap,
14021       window_info.annotate_context,0,0,(unsigned int) width,
14022       (unsigned int) height);
14023   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14024     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14025     window_info.width,(unsigned int) window_info.height);
14026   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14027   (void) XClearWindow(display,window_info.id);
14028   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14029   XDelay(display,delay == 0UL ? 10UL : delay);
14030   (void) XSync(display,MagickFalse);
14031   return(IsMagickTrue(window_info.id == root_window));
14032 }
14033 \f
14034 /*
14035 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14036 %                                                                             %
14037 %                                                                             %
14038 %                                                                             %
14039 +   X D i s p l a y I m a g e                                                 %
14040 %                                                                             %
14041 %                                                                             %
14042 %                                                                             %
14043 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14044 %
14045 %  XDisplayImage() displays an image via X11.  A new image is created and
14046 %  returned if the user interactively transforms the displayed image.
14047 %
14048 %  The format of the XDisplayImage method is:
14049 %
14050 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14051 %        char **argv,int argc,Image **image,size_t *state,
14052 %        ExceptionInfo *exception)
14053 %
14054 %  A description of each parameter follows:
14055 %
14056 %    o nexus:  Method XDisplayImage returns an image when the
14057 %      user chooses 'Open Image' from the command menu or picks a tile
14058 %      from the image directory.  Otherwise a null image is returned.
14059 %
14060 %    o display: Specifies a connection to an X server;  returned from
14061 %      XOpenDisplay.
14062 %
14063 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14064 %
14065 %    o argv: Specifies the application's argument list.
14066 %
14067 %    o argc: Specifies the number of arguments.
14068 %
14069 %    o image: Specifies an address to an address of an Image structure;
14070 %
14071 %    o exception: return any errors or warnings in this structure.
14072 %
14073 */
14074 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14075   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14076 {
14077 #define MagnifySize  256  /* must be a power of 2 */
14078 #define MagickMenus  10
14079 #define MagickTitle  "Commands"
14080
14081   static const char
14082     *CommandMenu[] =
14083     {
14084       "File",
14085       "Edit",
14086       "View",
14087       "Transform",
14088       "Enhance",
14089       "Effects",
14090       "F/X",
14091       "Image Edit",
14092       "Miscellany",
14093       "Help",
14094       (char *) NULL
14095     },
14096     *FileMenu[] =
14097     {
14098       "Open...",
14099       "Next",
14100       "Former",
14101       "Select...",
14102       "Save...",
14103       "Print...",
14104       "Delete...",
14105       "New...",
14106       "Visual Directory...",
14107       "Quit",
14108       (char *) NULL
14109     },
14110     *EditMenu[] =
14111     {
14112       "Undo",
14113       "Redo",
14114       "Cut",
14115       "Copy",
14116       "Paste",
14117       (char *) NULL
14118     },
14119     *ViewMenu[] =
14120     {
14121       "Half Size",
14122       "Original Size",
14123       "Double Size",
14124       "Resize...",
14125       "Apply",
14126       "Refresh",
14127       "Restore",
14128       (char *) NULL
14129     },
14130     *TransformMenu[] =
14131     {
14132       "Crop",
14133       "Chop",
14134       "Flop",
14135       "Flip",
14136       "Rotate Right",
14137       "Rotate Left",
14138       "Rotate...",
14139       "Shear...",
14140       "Roll...",
14141       "Trim Edges",
14142       (char *) NULL
14143     },
14144     *EnhanceMenu[] =
14145     {
14146       "Hue...",
14147       "Saturation...",
14148       "Brightness...",
14149       "Gamma...",
14150       "Spiff",
14151       "Dull",
14152       "Contrast Stretch...",
14153       "Sigmoidal Contrast...",
14154       "Normalize",
14155       "Equalize",
14156       "Negate",
14157       "Grayscale",
14158       "Map...",
14159       "Quantize...",
14160       (char *) NULL
14161     },
14162     *EffectsMenu[] =
14163     {
14164       "Despeckle",
14165       "Emboss",
14166       "Reduce Noise",
14167       "Add Noise...",
14168       "Sharpen...",
14169       "Blur...",
14170       "Threshold...",
14171       "Edge Detect...",
14172       "Spread...",
14173       "Shade...",
14174       "Raise...",
14175       "Segment...",
14176       (char *) NULL
14177     },
14178     *FXMenu[] =
14179     {
14180       "Solarize...",
14181       "Sepia Tone...",
14182       "Swirl...",
14183       "Implode...",
14184       "Vignette...",
14185       "Wave...",
14186       "Oil Paint...",
14187       "Charcoal Draw...",
14188       (char *) NULL
14189     },
14190     *ImageEditMenu[] =
14191     {
14192       "Annotate...",
14193       "Draw...",
14194       "Color...",
14195       "Matte...",
14196       "Composite...",
14197       "Add Border...",
14198       "Add Frame...",
14199       "Comment...",
14200       "Launch...",
14201       "Region of Interest...",
14202       (char *) NULL
14203     },
14204     *MiscellanyMenu[] =
14205     {
14206       "Image Info",
14207       "Zoom Image",
14208       "Show Preview...",
14209       "Show Histogram",
14210       "Show Matte",
14211       "Background...",
14212       "Slide Show...",
14213       "Preferences...",
14214       (char *) NULL
14215     },
14216     *HelpMenu[] =
14217     {
14218       "Overview",
14219       "Browse Documentation",
14220       "About Display",
14221       (char *) NULL
14222     },
14223     *ShortCutsMenu[] =
14224     {
14225       "Next",
14226       "Former",
14227       "Open...",
14228       "Save...",
14229       "Print...",
14230       "Undo",
14231       "Restore",
14232       "Image Info",
14233       "Quit",
14234       (char *) NULL
14235     },
14236     *VirtualMenu[] =
14237     {
14238       "Image Info",
14239       "Print",
14240       "Next",
14241       "Quit",
14242       (char *) NULL
14243     };
14244
14245   static const char
14246     **Menus[MagickMenus] =
14247     {
14248       FileMenu,
14249       EditMenu,
14250       ViewMenu,
14251       TransformMenu,
14252       EnhanceMenu,
14253       EffectsMenu,
14254       FXMenu,
14255       ImageEditMenu,
14256       MiscellanyMenu,
14257       HelpMenu
14258     };
14259
14260   static CommandType
14261     CommandMenus[] =
14262     {
14263       NullCommand,
14264       NullCommand,
14265       NullCommand,
14266       NullCommand,
14267       NullCommand,
14268       NullCommand,
14269       NullCommand,
14270       NullCommand,
14271       NullCommand,
14272       NullCommand,
14273     },
14274     FileCommands[] =
14275     {
14276       OpenCommand,
14277       NextCommand,
14278       FormerCommand,
14279       SelectCommand,
14280       SaveCommand,
14281       PrintCommand,
14282       DeleteCommand,
14283       NewCommand,
14284       VisualDirectoryCommand,
14285       QuitCommand
14286     },
14287     EditCommands[] =
14288     {
14289       UndoCommand,
14290       RedoCommand,
14291       CutCommand,
14292       CopyCommand,
14293       PasteCommand
14294     },
14295     ViewCommands[] =
14296     {
14297       HalfSizeCommand,
14298       OriginalSizeCommand,
14299       DoubleSizeCommand,
14300       ResizeCommand,
14301       ApplyCommand,
14302       RefreshCommand,
14303       RestoreCommand
14304     },
14305     TransformCommands[] =
14306     {
14307       CropCommand,
14308       ChopCommand,
14309       FlopCommand,
14310       FlipCommand,
14311       RotateRightCommand,
14312       RotateLeftCommand,
14313       RotateCommand,
14314       ShearCommand,
14315       RollCommand,
14316       TrimCommand
14317     },
14318     EnhanceCommands[] =
14319     {
14320       HueCommand,
14321       SaturationCommand,
14322       BrightnessCommand,
14323       GammaCommand,
14324       SpiffCommand,
14325       DullCommand,
14326       ContrastStretchCommand,
14327       SigmoidalContrastCommand,
14328       NormalizeCommand,
14329       EqualizeCommand,
14330       NegateCommand,
14331       GrayscaleCommand,
14332       MapCommand,
14333       QuantizeCommand
14334     },
14335     EffectsCommands[] =
14336     {
14337       DespeckleCommand,
14338       EmbossCommand,
14339       ReduceNoiseCommand,
14340       AddNoiseCommand,
14341       SharpenCommand,
14342       BlurCommand,
14343       ThresholdCommand,
14344       EdgeDetectCommand,
14345       SpreadCommand,
14346       ShadeCommand,
14347       RaiseCommand,
14348       SegmentCommand
14349     },
14350     FXCommands[] =
14351     {
14352       SolarizeCommand,
14353       SepiaToneCommand,
14354       SwirlCommand,
14355       ImplodeCommand,
14356       VignetteCommand,
14357       WaveCommand,
14358       OilPaintCommand,
14359       CharcoalDrawCommand
14360     },
14361     ImageEditCommands[] =
14362     {
14363       AnnotateCommand,
14364       DrawCommand,
14365       ColorCommand,
14366       MatteCommand,
14367       CompositeCommand,
14368       AddBorderCommand,
14369       AddFrameCommand,
14370       CommentCommand,
14371       LaunchCommand,
14372       RegionofInterestCommand
14373     },
14374     MiscellanyCommands[] =
14375     {
14376       InfoCommand,
14377       ZoomCommand,
14378       ShowPreviewCommand,
14379       ShowHistogramCommand,
14380       ShowMatteCommand,
14381       BackgroundCommand,
14382       SlideShowCommand,
14383       PreferencesCommand
14384     },
14385     HelpCommands[] =
14386     {
14387       HelpCommand,
14388       BrowseDocumentationCommand,
14389       VersionCommand
14390     },
14391     ShortCutsCommands[] =
14392     {
14393       NextCommand,
14394       FormerCommand,
14395       OpenCommand,
14396       SaveCommand,
14397       PrintCommand,
14398       UndoCommand,
14399       RestoreCommand,
14400       InfoCommand,
14401       QuitCommand
14402     },
14403     VirtualCommands[] =
14404     {
14405       InfoCommand,
14406       PrintCommand,
14407       NextCommand,
14408       QuitCommand
14409     };
14410
14411   static CommandType
14412     *Commands[MagickMenus] =
14413     {
14414       FileCommands,
14415       EditCommands,
14416       ViewCommands,
14417       TransformCommands,
14418       EnhanceCommands,
14419       EffectsCommands,
14420       FXCommands,
14421       ImageEditCommands,
14422       MiscellanyCommands,
14423       HelpCommands
14424     };
14425
14426   char
14427     command[MaxTextExtent],
14428     *directory,
14429     geometry[MaxTextExtent],
14430     resource_name[MaxTextExtent];
14431
14432   CommandType
14433     command_type;
14434
14435   Image
14436     *display_image,
14437     *nexus;
14438
14439   int
14440     entry,
14441     id;
14442
14443   KeySym
14444     key_symbol;
14445
14446   MagickStatusType
14447     context_mask,
14448     status;
14449
14450   RectangleInfo
14451     geometry_info;
14452
14453   register int
14454     i;
14455
14456   static char
14457     working_directory[MaxTextExtent];
14458
14459   static XPoint
14460     vid_info;
14461
14462   static XWindowInfo
14463     *magick_windows[MaxXWindows];
14464
14465   static unsigned int
14466     number_windows;
14467
14468   struct stat
14469     attributes;
14470
14471   time_t
14472     timer,
14473     timestamp,
14474     update_time;
14475
14476   unsigned int
14477     height,
14478     width;
14479
14480   size_t
14481     delay;
14482
14483   WarningHandler
14484     warning_handler;
14485
14486   Window
14487     root_window;
14488
14489   XClassHint
14490     *class_hints;
14491
14492   XEvent
14493     event;
14494
14495   XFontStruct
14496     *font_info;
14497
14498   XGCValues
14499     context_values;
14500
14501   XPixelInfo
14502     *icon_pixel,
14503     *pixel;
14504
14505   XResourceInfo
14506     *icon_resources;
14507
14508   XStandardColormap
14509     *icon_map,
14510     *map_info;
14511
14512   XVisualInfo
14513     *icon_visual,
14514     *visual_info;
14515
14516   XWindowChanges
14517     window_changes;
14518
14519   XWindows
14520     *windows;
14521
14522   XWMHints
14523     *manager_hints;
14524
14525   assert(image != (Image **) NULL);
14526   assert((*image)->signature == MagickSignature);
14527   if (IfMagickTrue((*image)->debug) )
14528     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14529   display_image=(*image);
14530   warning_handler=(WarningHandler) NULL;
14531   windows=XSetWindows((XWindows *) ~0);
14532   if (windows != (XWindows *) NULL)
14533     {
14534       int
14535         status;
14536
14537       if (*working_directory == '\0')
14538         (void) CopyMagickString(working_directory,".",MaxTextExtent);
14539       status=chdir(working_directory);
14540       if (status == -1)
14541         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14542           "UnableToOpenFile","%s",working_directory);
14543       warning_handler=resource_info->display_warnings ?
14544         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14545       warning_handler=resource_info->display_warnings ?
14546         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14547     }
14548   else
14549     {
14550       /*
14551         Allocate windows structure.
14552       */
14553       resource_info->colors=display_image->colors;
14554       windows=XSetWindows(XInitializeWindows(display,resource_info));
14555       if (windows == (XWindows *) NULL)
14556         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14557           (*image)->filename);
14558       /*
14559         Initialize window id's.
14560       */
14561       number_windows=0;
14562       magick_windows[number_windows++]=(&windows->icon);
14563       magick_windows[number_windows++]=(&windows->backdrop);
14564       magick_windows[number_windows++]=(&windows->image);
14565       magick_windows[number_windows++]=(&windows->info);
14566       magick_windows[number_windows++]=(&windows->command);
14567       magick_windows[number_windows++]=(&windows->widget);
14568       magick_windows[number_windows++]=(&windows->popup);
14569       magick_windows[number_windows++]=(&windows->magnify);
14570       magick_windows[number_windows++]=(&windows->pan);
14571       for (i=0; i < (int) number_windows; i++)
14572         magick_windows[i]->id=(Window) NULL;
14573       vid_info.x=0;
14574       vid_info.y=0;
14575     }
14576   /*
14577     Initialize font info.
14578   */
14579   if (windows->font_info != (XFontStruct *) NULL)
14580     (void) XFreeFont(display,windows->font_info);
14581   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14582   if (windows->font_info == (XFontStruct *) NULL)
14583     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14584       resource_info->font);
14585   /*
14586     Initialize Standard Colormap.
14587   */
14588   map_info=windows->map_info;
14589   icon_map=windows->icon_map;
14590   visual_info=windows->visual_info;
14591   icon_visual=windows->icon_visual;
14592   pixel=windows->pixel_info;
14593   icon_pixel=windows->icon_pixel;
14594   font_info=windows->font_info;
14595   icon_resources=windows->icon_resources;
14596   class_hints=windows->class_hints;
14597   manager_hints=windows->manager_hints;
14598   root_window=XRootWindow(display,visual_info->screen);
14599   nexus=NewImageList();
14600   if (IfMagickTrue(display_image->debug) )
14601     {
14602       (void) LogMagickEvent(X11Event,GetMagickModule(),
14603         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14604         (double) display_image->scene,(double) display_image->columns,
14605         (double) display_image->rows);
14606       if (display_image->colors != 0)
14607         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14608           display_image->colors);
14609       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14610         display_image->magick);
14611     }
14612   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14613     map_info,pixel,exception);
14614   display_image->taint=MagickFalse;
14615   /*
14616     Initialize graphic context.
14617   */
14618   windows->context.id=(Window) NULL;
14619   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14620     resource_info,&windows->context);
14621   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14622   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14623   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14624   manager_hints->flags=InputHint | StateHint;
14625   manager_hints->input=MagickFalse;
14626   manager_hints->initial_state=WithdrawnState;
14627   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14628     &windows->context);
14629   if (IfMagickTrue(display_image->debug) )
14630     (void) LogMagickEvent(X11Event,GetMagickModule(),
14631       "Window id: 0x%lx (context)",windows->context.id);
14632   context_values.background=pixel->background_color.pixel;
14633   context_values.font=font_info->fid;
14634   context_values.foreground=pixel->foreground_color.pixel;
14635   context_values.graphics_exposures=MagickFalse;
14636   context_mask=(MagickStatusType)
14637     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14638   if (pixel->annotate_context != (GC) NULL)
14639     (void) XFreeGC(display,pixel->annotate_context);
14640   pixel->annotate_context=XCreateGC(display,windows->context.id,
14641     context_mask,&context_values);
14642   if (pixel->annotate_context == (GC) NULL)
14643     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14644       display_image->filename);
14645   context_values.background=pixel->depth_color.pixel;
14646   if (pixel->widget_context != (GC) NULL)
14647     (void) XFreeGC(display,pixel->widget_context);
14648   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14649     &context_values);
14650   if (pixel->widget_context == (GC) NULL)
14651     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14652       display_image->filename);
14653   context_values.background=pixel->foreground_color.pixel;
14654   context_values.foreground=pixel->background_color.pixel;
14655   context_values.plane_mask=context_values.background ^
14656     context_values.foreground;
14657   if (pixel->highlight_context != (GC) NULL)
14658     (void) XFreeGC(display,pixel->highlight_context);
14659   pixel->highlight_context=XCreateGC(display,windows->context.id,
14660     (size_t) (context_mask | GCPlaneMask),&context_values);
14661   if (pixel->highlight_context == (GC) NULL)
14662     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14663       display_image->filename);
14664   (void) XDestroyWindow(display,windows->context.id);
14665   /*
14666     Initialize icon window.
14667   */
14668   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14669     icon_resources,&windows->icon);
14670   windows->icon.geometry=resource_info->icon_geometry;
14671   XBestIconSize(display,&windows->icon,display_image);
14672   windows->icon.attributes.colormap=XDefaultColormap(display,
14673     icon_visual->screen);
14674   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14675   manager_hints->flags=InputHint | StateHint;
14676   manager_hints->input=MagickFalse;
14677   manager_hints->initial_state=IconicState;
14678   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14679     &windows->icon);
14680   if (IfMagickTrue(display_image->debug) )
14681     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14682       windows->icon.id);
14683   /*
14684     Initialize graphic context for icon window.
14685   */
14686   if (icon_pixel->annotate_context != (GC) NULL)
14687     (void) XFreeGC(display,icon_pixel->annotate_context);
14688   context_values.background=icon_pixel->background_color.pixel;
14689   context_values.foreground=icon_pixel->foreground_color.pixel;
14690   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14691     (size_t) (GCBackground | GCForeground),&context_values);
14692   if (icon_pixel->annotate_context == (GC) NULL)
14693     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14694       display_image->filename);
14695   windows->icon.annotate_context=icon_pixel->annotate_context;
14696   /*
14697     Initialize Image window.
14698   */
14699   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14700     &windows->image);
14701   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14702   if (IfMagickFalse(resource_info->use_shared_memory) )
14703     windows->image.shared_memory=MagickFalse;
14704   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14705     {
14706       char
14707         *title;
14708
14709       title=InterpretImageProperties(resource_info->image_info,display_image,
14710         resource_info->title,exception);
14711       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14712       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14713       title=DestroyString(title);
14714     }
14715   else
14716     {
14717       char
14718         filename[MaxTextExtent];
14719
14720       /*
14721         Window name is the base of the filename.
14722       */
14723       GetPathComponent(display_image->magick_filename,TailPath,filename);
14724       if (display_image->scene == 0)
14725         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14726           "%s: %s",MagickPackageName,filename);
14727       else
14728         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14729           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14730           (double) display_image->scene,(double) GetImageListLength(
14731           display_image));
14732       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14733     }
14734   if (resource_info->immutable)
14735     windows->image.immutable=MagickTrue;
14736   windows->image.use_pixmap=resource_info->use_pixmap;
14737   windows->image.geometry=resource_info->image_geometry;
14738   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14739     XDisplayWidth(display,visual_info->screen),
14740     XDisplayHeight(display,visual_info->screen));
14741   geometry_info.width=display_image->columns;
14742   geometry_info.height=display_image->rows;
14743   geometry_info.x=0;
14744   geometry_info.y=0;
14745   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14746     &geometry_info.width,&geometry_info.height);
14747   windows->image.width=(unsigned int) geometry_info.width;
14748   windows->image.height=(unsigned int) geometry_info.height;
14749   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14750     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14751     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14752     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14753   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14754     resource_info,&windows->backdrop);
14755   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14756     {
14757       /*
14758         Initialize backdrop window.
14759       */
14760       windows->backdrop.x=0;
14761       windows->backdrop.y=0;
14762       (void) CloneString(&windows->backdrop.name,"Backdrop");
14763       windows->backdrop.flags=(size_t) (USSize | USPosition);
14764       windows->backdrop.width=(unsigned int)
14765         XDisplayWidth(display,visual_info->screen);
14766       windows->backdrop.height=(unsigned int)
14767         XDisplayHeight(display,visual_info->screen);
14768       windows->backdrop.border_width=0;
14769       windows->backdrop.immutable=MagickTrue;
14770       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14771         ButtonReleaseMask;
14772       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14773         StructureNotifyMask;
14774       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14775       manager_hints->icon_window=windows->icon.id;
14776       manager_hints->input=MagickTrue;
14777       manager_hints->initial_state=resource_info->iconic ? IconicState :
14778         NormalState;
14779       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14780         &windows->backdrop);
14781       if (IfMagickTrue(display_image->debug) )
14782         (void) LogMagickEvent(X11Event,GetMagickModule(),
14783           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14784       (void) XMapWindow(display,windows->backdrop.id);
14785       (void) XClearWindow(display,windows->backdrop.id);
14786       if (windows->image.id != (Window) NULL)
14787         {
14788           (void) XDestroyWindow(display,windows->image.id);
14789           windows->image.id=(Window) NULL;
14790         }
14791       /*
14792         Position image in the center the backdrop.
14793       */
14794       windows->image.flags|=USPosition;
14795       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14796         (windows->image.width/2);
14797       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14798         (windows->image.height/2);
14799     }
14800   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14801   manager_hints->icon_window=windows->icon.id;
14802   manager_hints->input=MagickTrue;
14803   manager_hints->initial_state=resource_info->iconic ? IconicState :
14804     NormalState;
14805   if (windows->group_leader.id != (Window) NULL)
14806     {
14807       /*
14808         Follow the leader.
14809       */
14810       manager_hints->flags|=WindowGroupHint;
14811       manager_hints->window_group=windows->group_leader.id;
14812       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14813       if (IfMagickTrue(display_image->debug) )
14814         (void) LogMagickEvent(X11Event,GetMagickModule(),
14815           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14816     }
14817   XMakeWindow(display,
14818     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14819     argv,argc,class_hints,manager_hints,&windows->image);
14820   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14821     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14822   if (windows->group_leader.id != (Window) NULL)
14823     (void) XSetTransientForHint(display,windows->image.id,
14824       windows->group_leader.id);
14825   if (IfMagickTrue(display_image->debug) )
14826     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14827       windows->image.id);
14828   /*
14829     Initialize Info widget.
14830   */
14831   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14832     &windows->info);
14833   (void) CloneString(&windows->info.name,"Info");
14834   (void) CloneString(&windows->info.icon_name,"Info");
14835   windows->info.border_width=1;
14836   windows->info.x=2;
14837   windows->info.y=2;
14838   windows->info.flags|=PPosition;
14839   windows->info.attributes.win_gravity=UnmapGravity;
14840   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14841     StructureNotifyMask;
14842   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14843   manager_hints->input=MagickFalse;
14844   manager_hints->initial_state=NormalState;
14845   manager_hints->window_group=windows->image.id;
14846   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14847     &windows->info);
14848   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14849     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14850   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14851     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14852   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14853   if (IfMagickTrue(windows->image.mapped) )
14854     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14855   if (IfMagickTrue(display_image->debug) )
14856     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14857       windows->info.id);
14858   /*
14859     Initialize Command widget.
14860   */
14861   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14862     resource_info,&windows->command);
14863   windows->command.data=MagickMenus;
14864   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14865   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14866     resource_info->client_name);
14867   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14868     resource_name,"geometry",(char *) NULL);
14869   (void) CloneString(&windows->command.name,MagickTitle);
14870   windows->command.border_width=0;
14871   windows->command.flags|=PPosition;
14872   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14873     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14874     OwnerGrabButtonMask | StructureNotifyMask;
14875   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14876   manager_hints->input=MagickTrue;
14877   manager_hints->initial_state=NormalState;
14878   manager_hints->window_group=windows->image.id;
14879   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14880     &windows->command);
14881   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14882     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14883     HighlightHeight);
14884   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14885     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14886   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14887   if (IfMagickTrue(windows->command.mapped) )
14888     (void) XMapRaised(display,windows->command.id);
14889   if (IfMagickTrue(display_image->debug) )
14890     (void) LogMagickEvent(X11Event,GetMagickModule(),
14891       "Window id: 0x%lx (command)",windows->command.id);
14892   /*
14893     Initialize Widget window.
14894   */
14895   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14896     resource_info,&windows->widget);
14897   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14898     resource_info->client_name);
14899   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14900     resource_name,"geometry",(char *) NULL);
14901   windows->widget.border_width=0;
14902   windows->widget.flags|=PPosition;
14903   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14904     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14905     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14906     StructureNotifyMask;
14907   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14908   manager_hints->input=MagickTrue;
14909   manager_hints->initial_state=NormalState;
14910   manager_hints->window_group=windows->image.id;
14911   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14912     &windows->widget);
14913   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14914     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14915   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14916     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14917   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14918   if (IfMagickTrue(display_image->debug) )
14919     (void) LogMagickEvent(X11Event,GetMagickModule(),
14920       "Window id: 0x%lx (widget)",windows->widget.id);
14921   /*
14922     Initialize popup window.
14923   */
14924   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14925     resource_info,&windows->popup);
14926   windows->popup.border_width=0;
14927   windows->popup.flags|=PPosition;
14928   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14929     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14930     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14931   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14932   manager_hints->input=MagickTrue;
14933   manager_hints->initial_state=NormalState;
14934   manager_hints->window_group=windows->image.id;
14935   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14936     &windows->popup);
14937   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14938     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14939   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14940     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14941   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14942   if (IfMagickTrue(display_image->debug) )
14943     (void) LogMagickEvent(X11Event,GetMagickModule(),
14944       "Window id: 0x%lx (pop up)",windows->popup.id);
14945   /*
14946     Initialize Magnify window and cursor.
14947   */
14948   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14949     resource_info,&windows->magnify);
14950   if (IfMagickFalse(resource_info->use_shared_memory) )
14951     windows->magnify.shared_memory=MagickFalse;
14952   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14953     resource_info->client_name);
14954   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14955     resource_name,"geometry",(char *) NULL);
14956   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14957     resource_info->magnify);
14958   if (windows->magnify.cursor != (Cursor) NULL)
14959     (void) XFreeCursor(display,windows->magnify.cursor);
14960   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14961     map_info->colormap,resource_info->background_color,
14962     resource_info->foreground_color);
14963   if (windows->magnify.cursor == (Cursor) NULL)
14964     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14965       display_image->filename);
14966   windows->magnify.width=MagnifySize;
14967   windows->magnify.height=MagnifySize;
14968   windows->magnify.flags|=PPosition;
14969   windows->magnify.min_width=MagnifySize;
14970   windows->magnify.min_height=MagnifySize;
14971   windows->magnify.width_inc=MagnifySize;
14972   windows->magnify.height_inc=MagnifySize;
14973   windows->magnify.data=resource_info->magnify;
14974   windows->magnify.attributes.cursor=windows->magnify.cursor;
14975   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14976     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14977     StructureNotifyMask;
14978   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14979   manager_hints->input=MagickTrue;
14980   manager_hints->initial_state=NormalState;
14981   manager_hints->window_group=windows->image.id;
14982   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14983     &windows->magnify);
14984   if (IfMagickTrue(display_image->debug) )
14985     (void) LogMagickEvent(X11Event,GetMagickModule(),
14986       "Window id: 0x%lx (magnify)",windows->magnify.id);
14987   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14988   /*
14989     Initialize panning window.
14990   */
14991   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14992     resource_info,&windows->pan);
14993   (void) CloneString(&windows->pan.name,"Pan Icon");
14994   windows->pan.width=windows->icon.width;
14995   windows->pan.height=windows->icon.height;
14996   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14997     resource_info->client_name);
14998   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14999     resource_name,"geometry",(char *) NULL);
15000   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15001     &windows->pan.width,&windows->pan.height);
15002   windows->pan.flags|=PPosition;
15003   windows->pan.immutable=MagickTrue;
15004   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15005     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15006     StructureNotifyMask;
15007   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15008   manager_hints->input=MagickFalse;
15009   manager_hints->initial_state=NormalState;
15010   manager_hints->window_group=windows->image.id;
15011   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15012     &windows->pan);
15013   if (IfMagickTrue(display_image->debug) )
15014     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15015       windows->pan.id);
15016   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15017   if (IfMagickTrue(windows->info.mapped) )
15018     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15019   if (IfMagickFalse(windows->image.mapped) ||
15020       (windows->backdrop.id != (Window) NULL))
15021     (void) XMapWindow(display,windows->image.id);
15022   /*
15023     Set our progress monitor and warning handlers.
15024   */
15025   if (warning_handler == (WarningHandler) NULL)
15026     {
15027       warning_handler=resource_info->display_warnings ?
15028         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15029       warning_handler=resource_info->display_warnings ?
15030         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15031     }
15032   /*
15033     Initialize Image and Magnify X images.
15034   */
15035   windows->image.x=0;
15036   windows->image.y=0;
15037   windows->magnify.shape=MagickFalse;
15038   width=(unsigned int) display_image->columns;
15039   height=(unsigned int) display_image->rows;
15040   if ((display_image->columns != width) || (display_image->rows != height))
15041     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15042       display_image->filename);
15043   status=XMakeImage(display,resource_info,&windows->image,display_image,
15044     width,height,exception);
15045   if (IfMagickFalse(status) )
15046     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15047       display_image->filename);
15048   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15049     windows->magnify.width,windows->magnify.height,exception);
15050   if (IfMagickFalse(status))
15051     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15052       display_image->filename);
15053   if (IfMagickTrue(windows->magnify.mapped) )
15054     (void) XMapRaised(display,windows->magnify.id);
15055   if (IfMagickTrue(windows->pan.mapped) )
15056     (void) XMapRaised(display,windows->pan.id);
15057   windows->image.window_changes.width=(int) display_image->columns;
15058   windows->image.window_changes.height=(int) display_image->rows;
15059   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15060   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15061   (void) XSync(display,MagickFalse);
15062   /*
15063     Respond to events.
15064   */
15065   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15066   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15067   update_time=0;
15068   if (IfMagickTrue(resource_info->update) )
15069     {
15070       MagickBooleanType
15071         status;
15072
15073       /*
15074         Determine when file data was last modified.
15075       */
15076       status=GetPathAttributes(display_image->filename,&attributes);
15077       if (IfMagickTrue(status) )
15078         update_time=attributes.st_mtime;
15079     }
15080   *state&=(~FormerImageState);
15081   *state&=(~MontageImageState);
15082   *state&=(~NextImageState);
15083   do
15084   {
15085     /*
15086       Handle a window event.
15087     */
15088     if (IfMagickTrue(windows->image.mapped) )
15089       if ((display_image->delay != 0) || (resource_info->update != 0))
15090         {
15091           if (timer < time((time_t *) NULL))
15092             {
15093               if (IfMagickFalse(resource_info->update) )
15094                 *state|=NextImageState | ExitState;
15095               else
15096                 {
15097                   MagickBooleanType
15098                     status;
15099
15100                   /*
15101                     Determine if image file was modified.
15102                   */
15103                   status=GetPathAttributes(display_image->filename,&attributes);
15104                   if (IfMagickTrue(status) )
15105                     if (update_time != attributes.st_mtime)
15106                       {
15107                         /*
15108                           Redisplay image.
15109                         */
15110                         (void) FormatLocaleString(
15111                           resource_info->image_info->filename,MaxTextExtent,
15112                           "%s:%s",display_image->magick,
15113                           display_image->filename);
15114                         nexus=ReadImage(resource_info->image_info,exception);
15115                         if (nexus != (Image *) NULL)
15116                           {
15117                             nexus=DestroyImage(nexus);
15118                             *state|=NextImageState | ExitState;
15119                           }
15120                       }
15121                   delay=display_image->delay/MagickMax(
15122                     display_image->ticks_per_second,1L);
15123                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15124                 }
15125             }
15126           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15127             {
15128               /*
15129                 Do not block if delay > 0.
15130               */
15131               XDelay(display,SuspendTime << 2);
15132               continue;
15133             }
15134         }
15135     timestamp=time((time_t *) NULL);
15136     (void) XNextEvent(display,&event);
15137     if (IfMagickFalse(windows->image.stasis) )
15138       windows->image.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15139     if (IfMagickFalse(windows->magnify.stasis) )
15140       windows->magnify.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15141     if (event.xany.window == windows->command.id)
15142       {
15143         /*
15144           Select a command from the Command widget.
15145         */
15146         id=XCommandWidget(display,windows,CommandMenu,&event);
15147         if (id < 0)
15148           continue;
15149         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15150         command_type=CommandMenus[id];
15151         if (id < MagickMenus)
15152           {
15153             /*
15154               Select a command from a pop-up menu.
15155             */
15156             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15157               command);
15158             if (entry < 0)
15159               continue;
15160             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15161             command_type=Commands[id][entry];
15162           }
15163         if (command_type != NullCommand)
15164           nexus=XMagickCommand(display,resource_info,windows,command_type,
15165             &display_image,exception);
15166         continue;
15167       }
15168     switch (event.type)
15169     {
15170       case ButtonPress:
15171       {
15172         if (IfMagickTrue(display_image->debug) )
15173           (void) LogMagickEvent(X11Event,GetMagickModule(),
15174             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15175             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15176         if ((event.xbutton.button == Button3) &&
15177             (event.xbutton.state & Mod1Mask))
15178           {
15179             /*
15180               Convert Alt-Button3 to Button2.
15181             */
15182             event.xbutton.button=Button2;
15183             event.xbutton.state&=(~Mod1Mask);
15184           }
15185         if (event.xbutton.window == windows->backdrop.id)
15186           {
15187             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15188               event.xbutton.time);
15189             break;
15190           }
15191         if (event.xbutton.window == windows->image.id)
15192           {
15193             switch (event.xbutton.button)
15194             {
15195               case Button1:
15196               {
15197                 if (resource_info->immutable)
15198                   {
15199                     /*
15200                       Select a command from the Virtual menu.
15201                     */
15202                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15203                       command);
15204                     if (entry >= 0)
15205                       nexus=XMagickCommand(display,resource_info,windows,
15206                         VirtualCommands[entry],&display_image,exception);
15207                     break;
15208                   }
15209                 /*
15210                   Map/unmap Command widget.
15211                 */
15212                 if (IfMagickTrue(windows->command.mapped) )
15213                   (void) XWithdrawWindow(display,windows->command.id,
15214                     windows->command.screen);
15215                 else
15216                   {
15217                     (void) XCommandWidget(display,windows,CommandMenu,
15218                       (XEvent *) NULL);
15219                     (void) XMapRaised(display,windows->command.id);
15220                   }
15221                 break;
15222               }
15223               case Button2:
15224               {
15225                 /*
15226                   User pressed the image magnify button.
15227                 */
15228                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15229                   &display_image,exception);
15230                 XMagnifyImage(display,windows,&event,exception);
15231                 break;
15232               }
15233               case Button3:
15234               {
15235                 if (resource_info->immutable)
15236                   {
15237                     /*
15238                       Select a command from the Virtual menu.
15239                     */
15240                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15241                       command);
15242                     if (entry >= 0)
15243                       nexus=XMagickCommand(display,resource_info,windows,
15244                         VirtualCommands[entry],&display_image,exception);
15245                     break;
15246                   }
15247                 if (display_image->montage != (char *) NULL)
15248                   {
15249                     /*
15250                       Open or delete a tile from a visual image directory.
15251                     */
15252                     nexus=XTileImage(display,resource_info,windows,
15253                       display_image,&event,exception);
15254                     if (nexus != (Image *) NULL)
15255                       *state|=MontageImageState | NextImageState | ExitState;
15256                     vid_info.x=(short int) windows->image.x;
15257                     vid_info.y=(short int) windows->image.y;
15258                     break;
15259                   }
15260                 /*
15261                   Select a command from the Short Cuts menu.
15262                 */
15263                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15264                   command);
15265                 if (entry >= 0)
15266                   nexus=XMagickCommand(display,resource_info,windows,
15267                     ShortCutsCommands[entry],&display_image,exception);
15268                 break;
15269               }
15270               case Button4:
15271               {
15272                 /*
15273                   Wheel up.
15274                 */
15275                 XTranslateImage(display,windows,*image,XK_Up);
15276                 break;
15277               }
15278               case Button5:
15279               {
15280                 /*
15281                   Wheel down.
15282                 */
15283                 XTranslateImage(display,windows,*image,XK_Down);
15284                 break;
15285               }
15286               default:
15287                 break;
15288             }
15289             break;
15290           }
15291         if (event.xbutton.window == windows->magnify.id)
15292           {
15293             int
15294               factor;
15295
15296             static const char
15297               *MagnifyMenu[] =
15298               {
15299                 "2",
15300                 "4",
15301                 "5",
15302                 "6",
15303                 "7",
15304                 "8",
15305                 "9",
15306                 "3",
15307                 (char *) NULL,
15308               };
15309
15310             static KeySym
15311               MagnifyCommands[] =
15312               {
15313                 XK_2,
15314                 XK_4,
15315                 XK_5,
15316                 XK_6,
15317                 XK_7,
15318                 XK_8,
15319                 XK_9,
15320                 XK_3
15321               };
15322
15323             /*
15324               Select a magnify factor from the pop-up menu.
15325             */
15326             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15327             if (factor >= 0)
15328               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15329                 exception);
15330             break;
15331           }
15332         if (event.xbutton.window == windows->pan.id)
15333           {
15334             switch (event.xbutton.button)
15335             {
15336               case Button4:
15337               {
15338                 /*
15339                   Wheel up.
15340                 */
15341                 XTranslateImage(display,windows,*image,XK_Up);
15342                 break;
15343               }
15344               case Button5:
15345               {
15346                 /*
15347                   Wheel down.
15348                 */
15349                 XTranslateImage(display,windows,*image,XK_Down);
15350                 break;
15351               }
15352               default:
15353               {
15354                 XPanImage(display,windows,&event,exception);
15355                 break;
15356               }
15357             }
15358             break;
15359           }
15360         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15361           1L);
15362         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15363         break;
15364       }
15365       case ButtonRelease:
15366       {
15367         if (IfMagickTrue(display_image->debug) )
15368           (void) LogMagickEvent(X11Event,GetMagickModule(),
15369             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15370             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15371         break;
15372       }
15373       case ClientMessage:
15374       {
15375         if (IfMagickTrue(display_image->debug) )
15376           (void) LogMagickEvent(X11Event,GetMagickModule(),
15377             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15378             event.xclient.message_type,event.xclient.format,(unsigned long)
15379             event.xclient.data.l[0]);
15380         if (event.xclient.message_type == windows->im_protocols)
15381           {
15382             if (*event.xclient.data.l == (long) windows->im_update_widget)
15383               {
15384                 (void) CloneString(&windows->command.name,MagickTitle);
15385                 windows->command.data=MagickMenus;
15386                 (void) XCommandWidget(display,windows,CommandMenu,
15387                   (XEvent *) NULL);
15388                 break;
15389               }
15390             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15391               {
15392                 /*
15393                   Update graphic context and window colormap.
15394                 */
15395                 for (i=0; i < (int) number_windows; i++)
15396                 {
15397                   if (magick_windows[i]->id == windows->icon.id)
15398                     continue;
15399                   context_values.background=pixel->background_color.pixel;
15400                   context_values.foreground=pixel->foreground_color.pixel;
15401                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15402                     context_mask,&context_values);
15403                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15404                     context_mask,&context_values);
15405                   context_values.background=pixel->foreground_color.pixel;
15406                   context_values.foreground=pixel->background_color.pixel;
15407                   context_values.plane_mask=context_values.background ^
15408                     context_values.foreground;
15409                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15410                     (size_t) (context_mask | GCPlaneMask),
15411                     &context_values);
15412                   magick_windows[i]->attributes.background_pixel=
15413                     pixel->background_color.pixel;
15414                   magick_windows[i]->attributes.border_pixel=
15415                     pixel->border_color.pixel;
15416                   magick_windows[i]->attributes.colormap=map_info->colormap;
15417                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15418                     (unsigned long) magick_windows[i]->mask,
15419                     &magick_windows[i]->attributes);
15420                 }
15421                 if (IfMagickTrue(windows->pan.mapped) )
15422                   {
15423                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15424                       windows->pan.pixmap);
15425                     (void) XClearWindow(display,windows->pan.id);
15426                     XDrawPanRectangle(display,windows);
15427                   }
15428                 if (windows->backdrop.id != (Window) NULL)
15429                   (void) XInstallColormap(display,map_info->colormap);
15430                 break;
15431               }
15432             if (*event.xclient.data.l == (long) windows->im_former_image)
15433               {
15434                 *state|=FormerImageState | ExitState;
15435                 break;
15436               }
15437             if (*event.xclient.data.l == (long) windows->im_next_image)
15438               {
15439                 *state|=NextImageState | ExitState;
15440                 break;
15441               }
15442             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15443               {
15444                 *state|=RetainColorsState;
15445                 break;
15446               }
15447             if (*event.xclient.data.l == (long) windows->im_exit)
15448               {
15449                 *state|=ExitState;
15450                 break;
15451               }
15452             break;
15453           }
15454         if (event.xclient.message_type == windows->dnd_protocols)
15455           {
15456             Atom
15457               selection,
15458               type;
15459
15460             int
15461               format,
15462               status;
15463
15464             unsigned char
15465               *data;
15466
15467             unsigned long
15468               after,
15469               length;
15470
15471             /*
15472               Display image named by the Drag-and-Drop selection.
15473             */
15474             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15475               break;
15476             selection=XInternAtom(display,"DndSelection",MagickFalse);
15477             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15478               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15479               &length,&after,&data);
15480             if ((status != Success) || (length == 0))
15481               break;
15482             if (*event.xclient.data.l == 2)
15483               {
15484                 /*
15485                   Offix DND.
15486                 */
15487                 (void) CopyMagickString(resource_info->image_info->filename,
15488                   (char *) data,MaxTextExtent);
15489               }
15490             else
15491               {
15492                 /*
15493                   XDND.
15494                 */
15495                 if (strncmp((char *) data, "file:", 5) != 0)
15496                   {
15497                     (void) XFree((void *) data);
15498                     break;
15499                   }
15500                 (void) CopyMagickString(resource_info->image_info->filename,
15501                   ((char *) data)+5,MaxTextExtent);
15502               }
15503             nexus=ReadImage(resource_info->image_info,exception);
15504             CatchException(exception);
15505             if (nexus != (Image *) NULL)
15506               *state|=NextImageState | ExitState;
15507             (void) XFree((void *) data);
15508             break;
15509           }
15510         /*
15511           If client window delete message, exit.
15512         */
15513         if (event.xclient.message_type != windows->wm_protocols)
15514           break;
15515         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15516           break;
15517         (void) XWithdrawWindow(display,event.xclient.window,
15518           visual_info->screen);
15519         if (event.xclient.window == windows->image.id)
15520           {
15521             *state|=ExitState;
15522             break;
15523           }
15524         if (event.xclient.window == windows->pan.id)
15525           {
15526             /*
15527               Restore original image size when pan window is deleted.
15528             */
15529             windows->image.window_changes.width=windows->image.ximage->width;
15530             windows->image.window_changes.height=windows->image.ximage->height;
15531             (void) XConfigureImage(display,resource_info,windows,
15532               display_image,exception);
15533           }
15534         break;
15535       }
15536       case ConfigureNotify:
15537       {
15538         if (IfMagickTrue(display_image->debug) )
15539           (void) LogMagickEvent(X11Event,GetMagickModule(),
15540             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15541             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15542             event.xconfigure.y,event.xconfigure.send_event);
15543         if (event.xconfigure.window == windows->image.id)
15544           {
15545             /*
15546               Image window has a new configuration.
15547             */
15548             if (event.xconfigure.send_event != 0)
15549               {
15550                 XWindowChanges
15551                   window_changes;
15552
15553                 /*
15554                   Position the transient windows relative of the Image window.
15555                 */
15556                 if (windows->command.geometry == (char *) NULL)
15557                   if (IfMagickFalse(windows->command.mapped) )
15558                     {
15559                       windows->command.x=event.xconfigure.x-
15560                         windows->command.width-25;
15561                       windows->command.y=event.xconfigure.y;
15562                       XConstrainWindowPosition(display,&windows->command);
15563                       window_changes.x=windows->command.x;
15564                       window_changes.y=windows->command.y;
15565                       (void) XReconfigureWMWindow(display,windows->command.id,
15566                         windows->command.screen,(unsigned int) (CWX | CWY),
15567                         &window_changes);
15568                     }
15569                 if (windows->widget.geometry == (char *) NULL)
15570                   if (IfMagickFalse(windows->widget.mapped) )
15571                     {
15572                       windows->widget.x=event.xconfigure.x+
15573                         event.xconfigure.width/10;
15574                       windows->widget.y=event.xconfigure.y+
15575                         event.xconfigure.height/10;
15576                       XConstrainWindowPosition(display,&windows->widget);
15577                       window_changes.x=windows->widget.x;
15578                       window_changes.y=windows->widget.y;
15579                       (void) XReconfigureWMWindow(display,windows->widget.id,
15580                         windows->widget.screen,(unsigned int) (CWX | CWY),
15581                         &window_changes);
15582                     }
15583                 if (windows->magnify.geometry == (char *) NULL)
15584                   if (IfMagickFalse(windows->magnify.mapped) )
15585                     {
15586                       windows->magnify.x=event.xconfigure.x+
15587                         event.xconfigure.width+25;
15588                       windows->magnify.y=event.xconfigure.y;
15589                       XConstrainWindowPosition(display,&windows->magnify);
15590                       window_changes.x=windows->magnify.x;
15591                       window_changes.y=windows->magnify.y;
15592                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15593                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15594                         &window_changes);
15595                     }
15596                 if (windows->pan.geometry == (char *) NULL)
15597                   if (IfMagickFalse(windows->pan.mapped) )
15598                     {
15599                       windows->pan.x=event.xconfigure.x+
15600                         event.xconfigure.width+25;
15601                       windows->pan.y=event.xconfigure.y+
15602                         windows->magnify.height+50;
15603                       XConstrainWindowPosition(display,&windows->pan);
15604                       window_changes.x=windows->pan.x;
15605                       window_changes.y=windows->pan.y;
15606                       (void) XReconfigureWMWindow(display,windows->pan.id,
15607                         windows->pan.screen,(unsigned int) (CWX | CWY),
15608                         &window_changes);
15609                     }
15610               }
15611             if ((event.xconfigure.width == (int) windows->image.width) &&
15612                 (event.xconfigure.height == (int) windows->image.height))
15613               break;
15614             windows->image.width=(unsigned int) event.xconfigure.width;
15615             windows->image.height=(unsigned int) event.xconfigure.height;
15616             windows->image.x=0;
15617             windows->image.y=0;
15618             if (display_image->montage != (char *) NULL)
15619               {
15620                 windows->image.x=vid_info.x;
15621                 windows->image.y=vid_info.y;
15622               }
15623             if (IfMagickTrue(windows->image.mapped) &&
15624                 IfMagickTrue(windows->image.stasis) )
15625               {
15626                 /*
15627                   Update image window configuration.
15628                 */
15629                 windows->image.window_changes.width=event.xconfigure.width;
15630                 windows->image.window_changes.height=event.xconfigure.height;
15631                 (void) XConfigureImage(display,resource_info,windows,
15632                   display_image,exception);
15633               }
15634             /*
15635               Update pan window configuration.
15636             */
15637             if ((event.xconfigure.width < windows->image.ximage->width) ||
15638                 (event.xconfigure.height < windows->image.ximage->height))
15639               {
15640                 (void) XMapRaised(display,windows->pan.id);
15641                 XDrawPanRectangle(display,windows);
15642               }
15643             else
15644               if (IfMagickTrue(windows->pan.mapped) )
15645                 (void) XWithdrawWindow(display,windows->pan.id,
15646                   windows->pan.screen);
15647             break;
15648           }
15649         if (event.xconfigure.window == windows->magnify.id)
15650           {
15651             unsigned int
15652               magnify;
15653
15654             /*
15655               Magnify window has a new configuration.
15656             */
15657             windows->magnify.width=(unsigned int) event.xconfigure.width;
15658             windows->magnify.height=(unsigned int) event.xconfigure.height;
15659             if (IfMagickFalse(windows->magnify.mapped) )
15660               break;
15661             magnify=1;
15662             while ((int) magnify <= event.xconfigure.width)
15663               magnify<<=1;
15664             while ((int) magnify <= event.xconfigure.height)
15665               magnify<<=1;
15666             magnify>>=1;
15667             if (((int) magnify != event.xconfigure.width) ||
15668                 ((int) magnify != event.xconfigure.height))
15669               {
15670                 window_changes.width=(int) magnify;
15671                 window_changes.height=(int) magnify;
15672                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15673                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15674                   &window_changes);
15675                 break;
15676               }
15677             if (IfMagickTrue(windows->magnify.mapped) &&
15678                 IfMagickTrue(windows->magnify.stasis) )
15679               {
15680                 status=XMakeImage(display,resource_info,&windows->magnify,
15681                   display_image,windows->magnify.width,windows->magnify.height,
15682                   exception);
15683                 XMakeMagnifyImage(display,windows,exception);
15684               }
15685             break;
15686           }
15687         if (IfMagickTrue(windows->magnify.mapped) &&
15688             (event.xconfigure.window == windows->pan.id))
15689           {
15690             /*
15691               Pan icon window has a new configuration.
15692             */
15693             if (event.xconfigure.send_event != 0)
15694               {
15695                 windows->pan.x=event.xconfigure.x;
15696                 windows->pan.y=event.xconfigure.y;
15697               }
15698             windows->pan.width=(unsigned int) event.xconfigure.width;
15699             windows->pan.height=(unsigned int) event.xconfigure.height;
15700             break;
15701           }
15702         if (event.xconfigure.window == windows->icon.id)
15703           {
15704             /*
15705               Icon window has a new configuration.
15706             */
15707             windows->icon.width=(unsigned int) event.xconfigure.width;
15708             windows->icon.height=(unsigned int) event.xconfigure.height;
15709             break;
15710           }
15711         break;
15712       }
15713       case DestroyNotify:
15714       {
15715         /*
15716           Group leader has exited.
15717         */
15718         if (IfMagickTrue(display_image->debug) )
15719           (void) LogMagickEvent(X11Event,GetMagickModule(),
15720             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15721         if (event.xdestroywindow.window == windows->group_leader.id)
15722           {
15723             *state|=ExitState;
15724             break;
15725           }
15726         break;
15727       }
15728       case EnterNotify:
15729       {
15730         /*
15731           Selectively install colormap.
15732         */
15733         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15734           if (event.xcrossing.mode != NotifyUngrab)
15735             XInstallColormap(display,map_info->colormap);
15736         break;
15737       }
15738       case Expose:
15739       {
15740         if (IfMagickTrue(display_image->debug) )
15741           (void) LogMagickEvent(X11Event,GetMagickModule(),
15742             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15743             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15744             event.xexpose.y);
15745         /*
15746           Refresh windows that are now exposed.
15747         */
15748         if ((event.xexpose.window == windows->image.id) &&
15749             IfMagickTrue(windows->image.mapped) )
15750           {
15751             XRefreshWindow(display,&windows->image,&event);
15752             delay=display_image->delay/MagickMax(
15753               display_image->ticks_per_second,1L);
15754             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15755             break;
15756           }
15757         if ((event.xexpose.window == windows->magnify.id) &&
15758             IfMagickTrue(windows->magnify.mapped))
15759           {
15760             XMakeMagnifyImage(display,windows,exception);
15761             break;
15762           }
15763         if (event.xexpose.window == windows->pan.id)
15764           {
15765             XDrawPanRectangle(display,windows);
15766             break;
15767           }
15768         if (event.xexpose.window == windows->icon.id)
15769           {
15770             XRefreshWindow(display,&windows->icon,&event);
15771             break;
15772           }
15773         break;
15774       }
15775       case KeyPress:
15776       {
15777         int
15778           length;
15779
15780         /*
15781           Respond to a user key press.
15782         */
15783         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15784           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15785         *(command+length)='\0';
15786         if (IfMagickTrue(display_image->debug) )
15787           (void) LogMagickEvent(X11Event,GetMagickModule(),
15788             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15789             key_symbol,command);
15790         if (event.xkey.window == windows->image.id)
15791           {
15792             command_type=XImageWindowCommand(display,resource_info,windows,
15793               event.xkey.state,key_symbol,&display_image,exception);
15794             if (command_type != NullCommand)
15795               nexus=XMagickCommand(display,resource_info,windows,command_type,
15796                 &display_image,exception);
15797           }
15798         if (event.xkey.window == windows->magnify.id)
15799           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15800             exception);
15801         if (event.xkey.window == windows->pan.id)
15802           {
15803             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15804               (void) XWithdrawWindow(display,windows->pan.id,
15805                 windows->pan.screen);
15806             else
15807               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15808                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15809                   "Help Viewer - Image Pan",ImagePanHelp);
15810               else
15811                 XTranslateImage(display,windows,*image,key_symbol);
15812           }
15813         delay=display_image->delay/MagickMax(
15814           display_image->ticks_per_second,1L);
15815         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15816         break;
15817       }
15818       case KeyRelease:
15819       {
15820         /*
15821           Respond to a user key release.
15822         */
15823         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15824           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15825         if (IfMagickTrue(display_image->debug) )
15826           (void) LogMagickEvent(X11Event,GetMagickModule(),
15827             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15828         break;
15829       }
15830       case LeaveNotify:
15831       {
15832         /*
15833           Selectively uninstall colormap.
15834         */
15835         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15836           if (event.xcrossing.mode != NotifyUngrab)
15837             XUninstallColormap(display,map_info->colormap);
15838         break;
15839       }
15840       case MapNotify:
15841       {
15842         if (IfMagickTrue(display_image->debug) )
15843           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15844             event.xmap.window);
15845         if (event.xmap.window == windows->backdrop.id)
15846           {
15847             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15848               CurrentTime);
15849             windows->backdrop.mapped=MagickTrue;
15850             break;
15851           }
15852         if (event.xmap.window == windows->image.id)
15853           {
15854             if (windows->backdrop.id != (Window) NULL)
15855               (void) XInstallColormap(display,map_info->colormap);
15856             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15857               {
15858                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15859                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15860               }
15861             if (((int) windows->image.width < windows->image.ximage->width) ||
15862                 ((int) windows->image.height < windows->image.ximage->height))
15863               (void) XMapRaised(display,windows->pan.id);
15864             windows->image.mapped=MagickTrue;
15865             break;
15866           }
15867         if (event.xmap.window == windows->magnify.id)
15868           {
15869             XMakeMagnifyImage(display,windows,exception);
15870             windows->magnify.mapped=MagickTrue;
15871             (void) XWithdrawWindow(display,windows->info.id,
15872               windows->info.screen);
15873             break;
15874           }
15875         if (event.xmap.window == windows->pan.id)
15876           {
15877             XMakePanImage(display,resource_info,windows,display_image,
15878               exception);
15879             windows->pan.mapped=MagickTrue;
15880             break;
15881           }
15882         if (event.xmap.window == windows->info.id)
15883           {
15884             windows->info.mapped=MagickTrue;
15885             break;
15886           }
15887         if (event.xmap.window == windows->icon.id)
15888           {
15889             MagickBooleanType
15890               taint;
15891
15892             /*
15893               Create an icon image.
15894             */
15895             taint=display_image->taint;
15896             XMakeStandardColormap(display,icon_visual,icon_resources,
15897               display_image,icon_map,icon_pixel,exception);
15898             (void) XMakeImage(display,icon_resources,&windows->icon,
15899               display_image,windows->icon.width,windows->icon.height,
15900               exception);
15901             display_image->taint=taint;
15902             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15903               windows->icon.pixmap);
15904             (void) XClearWindow(display,windows->icon.id);
15905             (void) XWithdrawWindow(display,windows->info.id,
15906               windows->info.screen);
15907             windows->icon.mapped=MagickTrue;
15908             break;
15909           }
15910         if (event.xmap.window == windows->command.id)
15911           {
15912             windows->command.mapped=MagickTrue;
15913             break;
15914           }
15915         if (event.xmap.window == windows->popup.id)
15916           {
15917             windows->popup.mapped=MagickTrue;
15918             break;
15919           }
15920         if (event.xmap.window == windows->widget.id)
15921           {
15922             windows->widget.mapped=MagickTrue;
15923             break;
15924           }
15925         break;
15926       }
15927       case MappingNotify:
15928       {
15929         (void) XRefreshKeyboardMapping(&event.xmapping);
15930         break;
15931       }
15932       case NoExpose:
15933         break;
15934       case PropertyNotify:
15935       {
15936         Atom
15937           type;
15938
15939         int
15940           format,
15941           status;
15942
15943         unsigned char
15944           *data;
15945
15946         unsigned long
15947           after,
15948           length;
15949
15950         if (IfMagickTrue(display_image->debug) )
15951           (void) LogMagickEvent(X11Event,GetMagickModule(),
15952             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15953             event.xproperty.atom,event.xproperty.state);
15954         if (event.xproperty.atom != windows->im_remote_command)
15955           break;
15956         /*
15957           Display image named by the remote command protocol.
15958         */
15959         status=XGetWindowProperty(display,event.xproperty.window,
15960           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15961           AnyPropertyType,&type,&format,&length,&after,&data);
15962         if ((status != Success) || (length == 0))
15963           break;
15964         if (LocaleCompare((char *) data,"-quit") == 0)
15965           {
15966             XClientMessage(display,windows->image.id,windows->im_protocols,
15967               windows->im_exit,CurrentTime);
15968             (void) XFree((void *) data);
15969             break;
15970           }
15971         (void) CopyMagickString(resource_info->image_info->filename,
15972           (char *) data,MaxTextExtent);
15973         (void) XFree((void *) data);
15974         nexus=ReadImage(resource_info->image_info,exception);
15975         CatchException(exception);
15976         if (nexus != (Image *) NULL)
15977           *state|=NextImageState | ExitState;
15978         break;
15979       }
15980       case ReparentNotify:
15981       {
15982         if (IfMagickTrue(display_image->debug) )
15983           (void) LogMagickEvent(X11Event,GetMagickModule(),
15984             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15985             event.xreparent.window);
15986         break;
15987       }
15988       case UnmapNotify:
15989       {
15990         if (IfMagickTrue(display_image->debug) )
15991           (void) LogMagickEvent(X11Event,GetMagickModule(),
15992             "Unmap Notify: 0x%lx",event.xunmap.window);
15993         if (event.xunmap.window == windows->backdrop.id)
15994           {
15995             windows->backdrop.mapped=MagickFalse;
15996             break;
15997           }
15998         if (event.xunmap.window == windows->image.id)
15999           {
16000             windows->image.mapped=MagickFalse;
16001             break;
16002           }
16003         if (event.xunmap.window == windows->magnify.id)
16004           {
16005             windows->magnify.mapped=MagickFalse;
16006             break;
16007           }
16008         if (event.xunmap.window == windows->pan.id)
16009           {
16010             windows->pan.mapped=MagickFalse;
16011             break;
16012           }
16013         if (event.xunmap.window == windows->info.id)
16014           {
16015             windows->info.mapped=MagickFalse;
16016             break;
16017           }
16018         if (event.xunmap.window == windows->icon.id)
16019           {
16020             if (map_info->colormap == icon_map->colormap)
16021               XConfigureImageColormap(display,resource_info,windows,
16022                 display_image,exception);
16023             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16024               icon_pixel);
16025             windows->icon.mapped=MagickFalse;
16026             break;
16027           }
16028         if (event.xunmap.window == windows->command.id)
16029           {
16030             windows->command.mapped=MagickFalse;
16031             break;
16032           }
16033         if (event.xunmap.window == windows->popup.id)
16034           {
16035             if (windows->backdrop.id != (Window) NULL)
16036               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16037                 CurrentTime);
16038             windows->popup.mapped=MagickFalse;
16039             break;
16040           }
16041         if (event.xunmap.window == windows->widget.id)
16042           {
16043             if (windows->backdrop.id != (Window) NULL)
16044               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16045                 CurrentTime);
16046             windows->widget.mapped=MagickFalse;
16047             break;
16048           }
16049         break;
16050       }
16051       default:
16052       {
16053         if (IfMagickTrue(display_image->debug) )
16054           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16055             event.type);
16056         break;
16057       }
16058     }
16059   } while (!(*state & ExitState));
16060   if ((*state & ExitState) == 0)
16061     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16062       &display_image,exception);
16063   else
16064     if (IfMagickTrue(resource_info->confirm_edit) )
16065       {
16066         /*
16067           Query user if image has changed.
16068         */
16069         if (IfMagickFalse(resource_info->immutable) &&
16070             IfMagickTrue(display_image->taint))
16071           {
16072             int
16073               status;
16074
16075             status=XConfirmWidget(display,windows,"Your image changed.",
16076               "Do you want to save it");
16077             if (status == 0)
16078               *state&=(~ExitState);
16079             else
16080               if (status > 0)
16081                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16082                   &display_image,exception);
16083           }
16084       }
16085   if ((windows->visual_info->klass == GrayScale) ||
16086       (windows->visual_info->klass == PseudoColor) ||
16087       (windows->visual_info->klass == DirectColor))
16088     {
16089       /*
16090         Withdraw pan and Magnify window.
16091       */
16092       if (IfMagickTrue(windows->info.mapped) )
16093         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16094       if (IfMagickTrue(windows->magnify.mapped) )
16095         (void) XWithdrawWindow(display,windows->magnify.id,
16096           windows->magnify.screen);
16097       if (IfMagickTrue(windows->command.mapped) )
16098         (void) XWithdrawWindow(display,windows->command.id,
16099           windows->command.screen);
16100     }
16101   if (IfMagickTrue(windows->pan.mapped) )
16102     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16103   if (IfMagickFalse(resource_info->backdrop) )
16104     if (windows->backdrop.mapped)
16105       {
16106         (void) XWithdrawWindow(display,windows->backdrop.id,
16107           windows->backdrop.screen);
16108         (void) XDestroyWindow(display,windows->backdrop.id);
16109         windows->backdrop.id=(Window) NULL;
16110         (void) XWithdrawWindow(display,windows->image.id,
16111           windows->image.screen);
16112         (void) XDestroyWindow(display,windows->image.id);
16113         windows->image.id=(Window) NULL;
16114       }
16115   XSetCursorState(display,windows,MagickTrue);
16116   XCheckRefreshWindows(display,windows);
16117   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16118     *state&=(~ExitState);
16119   if (*state & ExitState)
16120     {
16121       /*
16122         Free Standard Colormap.
16123       */
16124       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16125       if (resource_info->map_type == (char *) NULL)
16126         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16127       /*
16128         Free X resources.
16129       */
16130       if (resource_info->copy_image != (Image *) NULL)
16131         {
16132           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16133           resource_info->copy_image=NewImageList();
16134         }
16135       DestroyXResources();
16136     }
16137   (void) XSync(display,MagickFalse);
16138   /*
16139     Restore our progress monitor and warning handlers.
16140   */
16141   (void) SetErrorHandler(warning_handler);
16142   (void) SetWarningHandler(warning_handler);
16143   /*
16144     Change to home directory.
16145   */
16146   directory=getcwd(working_directory,MaxTextExtent);
16147   (void) directory;
16148   {
16149     int
16150       status;
16151
16152     if (*resource_info->home_directory == '\0')
16153       (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16154     status=chdir(resource_info->home_directory);
16155     if (status == -1)
16156       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16157         "UnableToOpenFile","%s",resource_info->home_directory);
16158   }
16159   *image=display_image;
16160   return(nexus);
16161 }
16162 #else
16163 \f
16164 /*
16165 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16166 %                                                                             %
16167 %                                                                             %
16168 %                                                                             %
16169 +   D i s p l a y I m a g e s                                                 %
16170 %                                                                             %
16171 %                                                                             %
16172 %                                                                             %
16173 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16174 %
16175 %  DisplayImages() displays an image sequence to any X window screen.  It
16176 %  returns a value other than 0 if successful.  Check the exception member
16177 %  of image to determine the reason for any failure.
16178 %
16179 %  The format of the DisplayImages method is:
16180 %
16181 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16182 %        Image *images,ExceptionInfo *exception)
16183 %
16184 %  A description of each parameter follows:
16185 %
16186 %    o image_info: the image info.
16187 %
16188 %    o image: the image.
16189 %
16190 %    o exception: return any errors or warnings in this structure.
16191 %
16192 */
16193 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16194   Image *image,ExceptionInfo *exception)
16195 {
16196   assert(image_info != (const ImageInfo *) NULL);
16197   assert(image_info->signature == MagickSignature);
16198   assert(image != (Image *) NULL);
16199   assert(image->signature == MagickSignature);
16200   if (IfMagickTrue(image->debug) )
16201     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16202   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16203     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16204   return(MagickFalse);
16205 }
16206 \f
16207 /*
16208 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16209 %                                                                             %
16210 %                                                                             %
16211 %                                                                             %
16212 +   R e m o t e D i s p l a y C o m m a n d                                   %
16213 %                                                                             %
16214 %                                                                             %
16215 %                                                                             %
16216 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16217 %
16218 %  RemoteDisplayCommand() encourages a remote display program to display the
16219 %  specified image filename.
16220 %
16221 %  The format of the RemoteDisplayCommand method is:
16222 %
16223 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16224 %        const char *window,const char *filename,ExceptionInfo *exception)
16225 %
16226 %  A description of each parameter follows:
16227 %
16228 %    o image_info: the image info.
16229 %
16230 %    o window: Specifies the name or id of an X window.
16231 %
16232 %    o filename: the name of the image filename to display.
16233 %
16234 %    o exception: return any errors or warnings in this structure.
16235 %
16236 */
16237 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16238   const char *window,const char *filename,ExceptionInfo *exception)
16239 {
16240   assert(image_info != (const ImageInfo *) NULL);
16241   assert(image_info->signature == MagickSignature);
16242   assert(filename != (char *) NULL);
16243   (void) window;
16244   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16245   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16246     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16247   return(MagickFalse);
16248 }
16249 #endif