]> granicus.if.org Git - imagemagick/blob - MagickCore/display.c
(no commit message)
[imagemagick] / MagickCore / display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7 %               D   D    I    SS     P   P  L      A   A   Y Y                %
8 %               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9 %               D   D    I       SS  P      L      A   A    Y                 %
10 %               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11 %                                                                             %
12 %                                                                             %
13 %        MagickCore Methods to Interactively Display and Edit an Image        %
14 %                                                                             %
15 %                             Software Design                                 %
16 %                               John Cristy                                   %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-private.h"
47 #include "MagickCore/client.h"
48 #include "MagickCore/color.h"
49 #include "MagickCore/colorspace.h"
50 #include "MagickCore/composite.h"
51 #include "MagickCore/constitute.h"
52 #include "MagickCore/decorate.h"
53 #include "MagickCore/delegate.h"
54 #include "MagickCore/display.h"
55 #include "MagickCore/display-private.h"
56 #include "MagickCore/draw.h"
57 #include "MagickCore/effect.h"
58 #include "MagickCore/enhance.h"
59 #include "MagickCore/exception.h"
60 #include "MagickCore/exception-private.h"
61 #include "MagickCore/fx.h"
62 #include "MagickCore/geometry.h"
63 #include "MagickCore/image.h"
64 #include "MagickCore/image-private.h"
65 #include "MagickCore/list.h"
66 #include "MagickCore/log.h"
67 #include "MagickCore/magick.h"
68 #include "MagickCore/memory_.h"
69 #include "MagickCore/monitor.h"
70 #include "MagickCore/monitor-private.h"
71 #include "MagickCore/montage.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/paint.h"
74 #include "MagickCore/pixel.h"
75 #include "MagickCore/pixel-accessor.h"
76 #include "MagickCore/PreRvIcccm.h"
77 #include "MagickCore/property.h"
78 #include "MagickCore/quantum.h"
79 #include "MagickCore/quantum-private.h"
80 #include "MagickCore/resize.h"
81 #include "MagickCore/resource_.h"
82 #include "MagickCore/shear.h"
83 #include "MagickCore/segment.h"
84 #include "MagickCore/string_.h"
85 #include "MagickCore/string-private.h"
86 #include "MagickCore/transform.h"
87 #include "MagickCore/threshold.h"
88 #include "MagickCore/utility.h"
89 #include "MagickCore/utility-private.h"
90 #include "MagickCore/version.h"
91 #include "MagickCore/widget.h"
92 #include "MagickCore/widget-private.h"
93 #include "MagickCore/xwindow.h"
94 #include "MagickCore/xwindow-private.h"
95 \f
96 #if defined(MAGICKCORE_X11_DELEGATE)
97 /*
98   Define declarations.
99 */
100 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
101 \f
102 /*
103   Constant declarations.
104 */
105 static const unsigned char
106   HighlightBitmap[8] =
107   {
108     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
109   },
110   OpaqueBitmap[8] =
111   {
112     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
113   },
114   ShadowBitmap[8] =
115   {
116     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
117   };
118
119 static const char
120   *PageSizes[] =
121   {
122     "Letter",
123     "Tabloid",
124     "Ledger",
125     "Legal",
126     "Statement",
127     "Executive",
128     "A3",
129     "A4",
130     "A5",
131     "B4",
132     "B5",
133     "Folio",
134     "Quarto",
135     "10x14",
136     (char *) NULL
137   };
138 \f
139 /*
140   Help widget declarations.
141 */
142 static const char
143   *ImageAnnotateHelp[] =
144   {
145     "In annotate mode, the Command widget has these options:",
146     "",
147     "    Font Name",
148     "      fixed",
149     "      variable",
150     "      5x8",
151     "      6x10",
152     "      7x13bold",
153     "      8x13bold",
154     "      9x15bold",
155     "      10x20",
156     "      12x24",
157     "      Browser...",
158     "    Font Color",
159     "      black",
160     "      blue",
161     "      cyan",
162     "      green",
163     "      gray",
164     "      red",
165     "      magenta",
166     "      yellow",
167     "      white",
168     "      transparent",
169     "      Browser...",
170     "    Font Color",
171     "      black",
172     "      blue",
173     "      cyan",
174     "      green",
175     "      gray",
176     "      red",
177     "      magenta",
178     "      yellow",
179     "      white",
180     "      transparent",
181     "      Browser...",
182     "    Rotate Text",
183     "      -90",
184     "      -45",
185     "      -30",
186     "      0",
187     "      30",
188     "      45",
189     "      90",
190     "      180",
191     "      Dialog...",
192     "    Help",
193     "    Dismiss",
194     "",
195     "Choose a font name from the Font Name sub-menu.  Additional",
196     "font names can be specified with the font browser.  You can",
197     "change the menu names by setting the X resources font1",
198     "through font9.",
199     "",
200     "Choose a font color from the Font Color sub-menu.",
201     "Additional font colors can be specified with the color",
202     "browser.  You can change the menu colors by setting the X",
203     "resources pen1 through pen9.",
204     "",
205     "If you select the color browser and press Grab, you can",
206     "choose the font color by moving the pointer to the desired",
207     "color on the screen and press any button.",
208     "",
209     "If you choose to rotate the text, choose Rotate Text from the",
210     "menu and select an angle.  Typically you will only want to",
211     "rotate one line of text at a time.  Depending on the angle you",
212     "choose, subsequent lines may end up overwriting each other.",
213     "",
214     "Choosing a font and its color is optional.  The default font",
215     "is fixed and the default color is black.  However, you must",
216     "choose a location to begin entering text and press button 1.",
217     "An underscore character will appear at the location of the",
218     "pointer.  The cursor changes to a pencil to indicate you are",
219     "in text mode.  To exit immediately, press Dismiss.",
220     "",
221     "In text mode, any key presses will display the character at",
222     "the location of the underscore and advance the underscore",
223     "cursor.  Enter your text and once completed press Apply to",
224     "finish your image annotation.  To correct errors press BACK",
225     "SPACE.  To delete an entire line of text, press DELETE.  Any",
226     "text that exceeds the boundaries of the image window is",
227     "automagically continued onto the next line.",
228     "",
229     "The actual color you request for the font is saved in the",
230     "image.  However, the color that appears in your image window",
231     "may be different.  For example, on a monochrome screen the",
232     "text will appear black or white even if you choose the color",
233     "red as the font color.  However, the image saved to a file",
234     "with -write is written with red lettering.  To assure the",
235     "correct color text in the final image, any PseudoClass image",
236     "is promoted to DirectClass (see miff(5)).  To force a",
237     "PseudoClass image to remain PseudoClass, use -colors.",
238     (char *) NULL,
239   },
240   *ImageChopHelp[] =
241   {
242     "In chop mode, the Command widget has these options:",
243     "",
244     "    Direction",
245     "      horizontal",
246     "      vertical",
247     "    Help",
248     "    Dismiss",
249     "",
250     "If the you choose the horizontal direction (this the",
251     "default), the area of the image between the two horizontal",
252     "endpoints of the chop line is removed.  Otherwise, the area",
253     "of the image between the two vertical endpoints of the chop",
254     "line is removed.",
255     "",
256     "Select a location within the image window to begin your chop,",
257     "press and hold any button.  Next, move the pointer to",
258     "another location in the image.  As you move a line will",
259     "connect the initial location and the pointer.  When you",
260     "release the button, the area within the image to chop is",
261     "determined by which direction you choose from the Command",
262     "widget.",
263     "",
264     "To cancel the image chopping, move the pointer back to the",
265     "starting point of the line and release the button.",
266     (char *) NULL,
267   },
268   *ImageColorEditHelp[] =
269   {
270     "In color edit mode, the Command widget has these options:",
271     "",
272     "    Method",
273     "      point",
274     "      replace",
275     "      floodfill",
276     "      filltoborder",
277     "      reset",
278     "    Pixel Color",
279     "      black",
280     "      blue",
281     "      cyan",
282     "      green",
283     "      gray",
284     "      red",
285     "      magenta",
286     "      yellow",
287     "      white",
288     "      Browser...",
289     "    Border Color",
290     "      black",
291     "      blue",
292     "      cyan",
293     "      green",
294     "      gray",
295     "      red",
296     "      magenta",
297     "      yellow",
298     "      white",
299     "      Browser...",
300     "    Fuzz",
301     "      0%",
302     "      2%",
303     "      5%",
304     "      10%",
305     "      15%",
306     "      Dialog...",
307     "    Undo",
308     "    Help",
309     "    Dismiss",
310     "",
311     "Choose a color editing method from the Method sub-menu",
312     "of the Command widget.  The point method recolors any pixel",
313     "selected with the pointer until the button is released.  The",
314     "replace method recolors any pixel that matches the color of",
315     "the pixel you select with a button press.  Floodfill recolors",
316     "any pixel that matches the color of the pixel you select with",
317     "a button press and is a neighbor.  Whereas filltoborder recolors",
318     "any neighbor pixel that is not the border color.  Finally reset",
319     "changes the entire image to the designated color.",
320     "",
321     "Next, choose a pixel color from the Pixel Color sub-menu.",
322     "Additional pixel colors can be specified with the color",
323     "browser.  You can change the menu colors by setting the X",
324     "resources pen1 through pen9.",
325     "",
326     "Now press button 1 to select a pixel within the image window",
327     "to change its color.  Additional pixels may be recolored as",
328     "prescribed by the method you choose.",
329     "",
330     "If the Magnify widget is mapped, it can be helpful in positioning",
331     "your pointer within the image (refer to button 2).",
332     "",
333     "The actual color you request for the pixels is saved in the",
334     "image.  However, the color that appears in your image window",
335     "may be different.  For example, on a monochrome screen the",
336     "pixel will appear black or white even if you choose the",
337     "color red as the pixel color.  However, the image saved to a",
338     "file with -write is written with red pixels.  To assure the",
339     "correct color text in the final image, any PseudoClass image",
340     "is promoted to DirectClass (see miff(5)).  To force a",
341     "PseudoClass image to remain PseudoClass, use -colors.",
342     (char *) NULL,
343   },
344   *ImageCompositeHelp[] =
345   {
346     "First a widget window is displayed requesting you to enter an",
347     "image name. Press Composite, Grab or type a file name.",
348     "Press Cancel if you choose not to create a composite image.",
349     "When you choose Grab, move the pointer to the desired window",
350     "and press any button.",
351     "",
352     "If the Composite image does not have any matte information,",
353     "you are informed and the file browser is displayed again.",
354     "Enter the name of a mask image.  The image is typically",
355     "grayscale and the same size as the composite image.  If the",
356     "image is not grayscale, it is converted to grayscale and the",
357     "resulting intensities are used as matte information.",
358     "",
359     "A small window appears showing the location of the cursor in",
360     "the image window. You are now in composite mode.  To exit",
361     "immediately, press Dismiss.  In composite mode, the Command",
362     "widget has these options:",
363     "",
364     "    Operators",
365     "      Over",
366     "      In",
367     "      Out",
368     "      Atop",
369     "      Xor",
370     "      Plus",
371     "      Minus",
372     "      Add",
373     "      Subtract",
374     "      Difference",
375     "      Multiply",
376     "      Bumpmap",
377     "      Copy",
378     "      CopyRed",
379     "      CopyGreen",
380     "      CopyBlue",
381     "      CopyOpacity",
382     "      Clear",
383     "    Dissolve",
384     "    Displace",
385     "    Help",
386     "    Dismiss",
387     "",
388     "Choose a composite operation from the Operators sub-menu of",
389     "the Command widget.  How each operator behaves is described",
390     "below.  Image window is the image currently displayed on",
391     "your X server and image is the image obtained with the File",
392     "Browser widget.",
393     "",
394     "Over     The result is the union of the two image shapes,",
395     "         with image obscuring image window in the region of",
396     "         overlap.",
397     "",
398     "In       The result is simply image cut by the shape of",
399     "         image window.  None of the image data of image",
400     "         window is in the result.",
401     "",
402     "Out      The resulting image is image with the shape of",
403     "         image window cut out.",
404     "",
405     "Atop     The result is the same shape as image image window,",
406     "         with image obscuring image window where the image",
407     "         shapes overlap.  Note this differs from over",
408     "         because the portion of image outside image window's",
409     "         shape does not appear in the result.",
410     "",
411     "Xor      The result is the image data from both image and",
412     "         image window that is outside the overlap region.",
413     "         The overlap region is blank.",
414     "",
415     "Plus     The result is just the sum of the image data.",
416     "         Output values are cropped to QuantumRange (no overflow).",
417     "",
418     "Minus    The result of image - image window, with underflow",
419     "         cropped to zero.",
420     "",
421     "Add      The result of image + image window, with overflow",
422     "         wrapping around (mod 256).",
423     "",
424     "Subtract The result of image - image window, with underflow",
425     "         wrapping around (mod 256).  The add and subtract",
426     "         operators can be used to perform reversible",
427     "         transformations.",
428     "",
429     "Difference",
430     "         The result of abs(image - image window).  This",
431     "         useful for comparing two very similar images.",
432     "",
433     "Multiply",
434     "         The result of image * image window.  This",
435     "         useful for the creation of drop-shadows.",
436     "",
437     "Bumpmap  The result of surface normals from image * image",
438     "         window.",
439     "",
440     "Copy     The resulting image is image window replaced with",
441     "         image.  Here the matte information is ignored.",
442     "",
443     "CopyRed  The red layer of the image window is replace with",
444     "         the red layer of the image.  The other layers are",
445     "         untouched.",
446     "",
447     "CopyGreen",
448     "         The green layer of the image window is replace with",
449     "         the green layer of the image.  The other layers are",
450     "         untouched.",
451     "",
452     "CopyBlue The blue layer of the image window is replace with",
453     "         the blue layer of the image.  The other layers are",
454     "         untouched.",
455     "",
456     "CopyOpacity",
457     "         The matte layer of the image window is replace with",
458     "         the matte layer of the image.  The other layers are",
459     "         untouched.",
460     "",
461     "The image compositor requires a matte, or alpha channel in",
462     "the image for some operations.  This extra channel usually",
463     "defines a mask which represents a sort of a cookie-cutter",
464     "for the image.  This the case when matte is opaque (full",
465     "coverage) for pixels inside the shape, zero outside, and",
466     "between 0 and QuantumRange on the boundary.  If image does not",
467     "have a matte channel, it is initialized with 0 for any pixel",
468     "matching in color to pixel location (0,0), otherwise QuantumRange.",
469     "",
470     "If you choose Dissolve, the composite operator becomes Over.  The",
471     "image matte channel percent transparency is initialized to factor.",
472     "The image window is initialized to (100-factor). Where factor is the",
473     "value you specify in the Dialog widget.",
474     "",
475     "Displace shifts the image pixels as defined by a displacement",
476     "map.  With this option, image is used as a displacement map.",
477     "Black, within the displacement map, is a maximum positive",
478     "displacement.  White is a maximum negative displacement and",
479     "middle gray is neutral.  The displacement is scaled to determine",
480     "the pixel shift.  By default, the displacement applies in both the",
481     "horizontal and vertical directions.  However, if you specify a mask,",
482     "image is the horizontal X displacement and mask the vertical Y",
483     "displacement.",
484     "",
485     "Note that matte information for image window is not retained",
486     "for colormapped X server visuals (e.g. StaticColor,",
487     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
488     "behavior may require a TrueColor or DirectColor visual or a",
489     "Standard Colormap.",
490     "",
491     "Choosing a composite operator is optional.  The default",
492     "operator is replace.  However, you must choose a location to",
493     "composite your image and press button 1.  Press and hold the",
494     "button before releasing and an outline of the image will",
495     "appear to help you identify your location.",
496     "",
497     "The actual colors of the composite image is saved.  However,",
498     "the color that appears in image window may be different.",
499     "For example, on a monochrome screen image window will appear",
500     "black or white even though your composited image may have",
501     "many colors.  If the image is saved to a file it is written",
502     "with the correct colors.  To assure the correct colors are",
503     "saved in the final image, any PseudoClass image is promoted",
504     "to DirectClass (see miff(5)).  To force a PseudoClass image",
505     "to remain PseudoClass, use -colors.",
506     (char *) NULL,
507   },
508   *ImageCutHelp[] =
509   {
510     "In cut mode, the Command widget has these options:",
511     "",
512     "    Help",
513     "    Dismiss",
514     "",
515     "To define a cut region, press button 1 and drag.  The",
516     "cut region is defined by a highlighted rectangle that",
517     "expands or contracts as it follows the pointer.  Once you",
518     "are satisfied with the cut region, release the button.",
519     "You are now in rectify mode.  In rectify mode, the Command",
520     "widget has these options:",
521     "",
522     "    Cut",
523     "    Help",
524     "    Dismiss",
525     "",
526     "You can make adjustments by moving the pointer to one of the",
527     "cut rectangle corners, pressing a button, and dragging.",
528     "Finally, press Cut to commit your copy region.  To",
529     "exit without cutting the image, press Dismiss.",
530     (char *) NULL,
531   },
532   *ImageCopyHelp[] =
533   {
534     "In copy mode, the Command widget has these options:",
535     "",
536     "    Help",
537     "    Dismiss",
538     "",
539     "To define a copy region, press button 1 and drag.  The",
540     "copy region is defined by a highlighted rectangle that",
541     "expands or contracts as it follows the pointer.  Once you",
542     "are satisfied with the copy region, release the button.",
543     "You are now in rectify mode.  In rectify mode, the Command",
544     "widget has these options:",
545     "",
546     "    Copy",
547     "    Help",
548     "    Dismiss",
549     "",
550     "You can make adjustments by moving the pointer to one of the",
551     "copy rectangle corners, pressing a button, and dragging.",
552     "Finally, press Copy to commit your copy region.  To",
553     "exit without copying the image, press Dismiss.",
554     (char *) NULL,
555   },
556   *ImageCropHelp[] =
557   {
558     "In crop mode, the Command widget has these options:",
559     "",
560     "    Help",
561     "    Dismiss",
562     "",
563     "To define a cropping region, press button 1 and drag.  The",
564     "cropping region is defined by a highlighted rectangle that",
565     "expands or contracts as it follows the pointer.  Once you",
566     "are satisfied with the cropping region, release the button.",
567     "You are now in rectify mode.  In rectify mode, the Command",
568     "widget has these options:",
569     "",
570     "    Crop",
571     "    Help",
572     "    Dismiss",
573     "",
574     "You can make adjustments by moving the pointer to one of the",
575     "cropping rectangle corners, pressing a button, and dragging.",
576     "Finally, press Crop to commit your cropping region.  To",
577     "exit without cropping the image, press Dismiss.",
578     (char *) NULL,
579   },
580   *ImageDrawHelp[] =
581   {
582     "The cursor changes to a crosshair to indicate you are in",
583     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
584     "the Command widget has these options:",
585     "",
586     "    Element",
587     "      point",
588     "      line",
589     "      rectangle",
590     "      fill rectangle",
591     "      circle",
592     "      fill circle",
593     "      ellipse",
594     "      fill ellipse",
595     "      polygon",
596     "      fill polygon",
597     "    Color",
598     "      black",
599     "      blue",
600     "      cyan",
601     "      green",
602     "      gray",
603     "      red",
604     "      magenta",
605     "      yellow",
606     "      white",
607     "      transparent",
608     "      Browser...",
609     "    Stipple",
610     "      Brick",
611     "      Diagonal",
612     "      Scales",
613     "      Vertical",
614     "      Wavy",
615     "      Translucent",
616     "      Opaque",
617     "      Open...",
618     "    Width",
619     "      1",
620     "      2",
621     "      4",
622     "      8",
623     "      16",
624     "      Dialog...",
625     "    Undo",
626     "    Help",
627     "    Dismiss",
628     "",
629     "Choose a drawing primitive from the Element sub-menu.",
630     "",
631     "Choose a color from the Color sub-menu.  Additional",
632     "colors can be specified with the color browser.",
633     "",
634     "If you choose the color browser and press Grab, you can",
635     "select the color by moving the pointer to the desired",
636     "color on the screen and press any button.  The transparent",
637     "color updates the image matte channel and is useful for",
638     "image compositing.",
639     "",
640     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
641     "Additional stipples can be specified with the file browser.",
642     "Stipples obtained from the file browser must be on disk in the",
643     "X11 bitmap format.",
644     "",
645     "Choose a width, if appropriate, from the Width sub-menu.  To",
646     "choose a specific width select the Dialog widget.",
647     "",
648     "Choose a point in the Image window and press button 1 and",
649     "hold.  Next, move the pointer to another location in the",
650     "image.  As you move, a line connects the initial location and",
651     "the pointer.  When you release the button, the image is",
652     "updated with the primitive you just drew.  For polygons, the",
653     "image is updated when you press and release the button without",
654     "moving the pointer.",
655     "",
656     "To cancel image drawing, move the pointer back to the",
657     "starting point of the line and release the button.",
658     (char *) NULL,
659   },
660   *DisplayHelp[] =
661   {
662     "BUTTONS",
663     "  The effects of each button press is described below.  Three",
664     "  buttons are required.  If you have a two button mouse,",
665     "  button 1 and 3 are returned.  Press ALT and button 3 to",
666     "  simulate button 2.",
667     "",
668     "  1    Press this button to map or unmap the Command widget.",
669     "",
670     "  2    Press and drag to define a region of the image to",
671     "       magnify.",
672     "",
673     "  3    Press and drag to choose from a select set of commands.",
674     "       This button behaves differently if the image being",
675     "       displayed is a visual image directory.  Here, choose a",
676     "       particular tile of the directory and press this button and",
677     "       drag to select a command from a pop-up menu.  Choose from",
678     "       these menu items:",
679     "",
680     "           Open",
681     "           Next",
682     "           Former",
683     "           Delete",
684     "           Update",
685     "",
686     "       If you choose Open, the image represented by the tile is",
687     "       displayed.  To return to the visual image directory, choose",
688     "       Next from the Command widget.  Next and Former moves to the",
689     "       next or former image respectively.  Choose Delete to delete",
690     "       a particular image tile.  Finally, choose Update to",
691     "       synchronize all the image tiles with their respective",
692     "       images.",
693     "",
694     "COMMAND WIDGET",
695     "  The Command widget lists a number of sub-menus and commands.",
696     "  They are",
697     "",
698     "      File",
699     "        Open...",
700     "        Next",
701     "        Former",
702     "        Select...",
703     "        Save...",
704     "        Print...",
705     "        Delete...",
706     "        New...",
707     "        Visual Directory...",
708     "        Quit",
709     "      Edit",
710     "        Undo",
711     "        Redo",
712     "        Cut",
713     "        Copy",
714     "        Paste",
715     "      View",
716     "        Half Size",
717     "        Original Size",
718     "        Double Size",
719     "        Resize...",
720     "        Apply",
721     "        Refresh",
722     "        Restore",
723     "      Transform",
724     "        Crop",
725     "        Chop",
726     "        Flop",
727     "        Flip",
728     "        Rotate Right",
729     "        Rotate Left",
730     "        Rotate...",
731     "        Shear...",
732     "        Roll...",
733     "        Trim Edges",
734     "      Enhance",
735     "        Brightness...",
736     "        Saturation...",
737     "        Hue...",
738     "        Gamma...",
739     "        Sharpen...",
740     "        Dull",
741     "        Contrast Stretch...",
742     "        Sigmoidal Contrast...",
743     "        Normalize",
744     "        Equalize",
745     "        Negate",
746     "        Grayscale",
747     "        Map...",
748     "        Quantize...",
749     "      Effects",
750     "        Despeckle",
751     "        Emboss",
752     "        Reduce Noise",
753     "        Add Noise",
754     "        Sharpen...",
755     "        Blur...",
756     "        Threshold...",
757     "        Edge Detect...",
758     "        Spread...",
759     "        Shade...",
760     "        Painting...",
761     "        Segment...",
762     "      F/X",
763     "        Solarize...",
764     "        Sepia Tone...",
765     "        Swirl...",
766     "        Implode...",
767     "        Vignette...",
768     "        Wave...",
769     "        Oil Painting...",
770     "        Charcoal Drawing...",
771     "      Image Edit",
772     "        Annotate...",
773     "        Draw...",
774     "        Color...",
775     "        Matte...",
776     "        Composite...",
777     "        Add Border...",
778     "        Add Frame...",
779     "        Comment...",
780     "        Launch...",
781     "        Region of Interest...",
782     "      Miscellany",
783     "        Image Info",
784     "        Zoom Image",
785     "        Show Preview...",
786     "        Show Histogram",
787     "        Show Matte",
788     "        Background...",
789     "        Slide Show",
790     "        Preferences...",
791     "      Help",
792     "        Overview",
793     "        Browse Documentation",
794     "        About Display",
795     "",
796     "  Menu items with a indented triangle have a sub-menu.  They",
797     "  are represented above as the indented items.  To access a",
798     "  sub-menu item, move the pointer to the appropriate menu and",
799     "  press a button and drag.  When you find the desired sub-menu",
800     "  item, release the button and the command is executed.  Move",
801     "  the pointer away from the sub-menu if you decide not to",
802     "  execute a particular command.",
803     "",
804     "KEYBOARD ACCELERATORS",
805     "  Accelerators are one or two key presses that effect a",
806     "  particular command.  The keyboard accelerators that",
807     "  display(1) understands is:",
808     "",
809     "  Ctl+O     Press to open an image from a file.",
810     "",
811     "  space     Press to display the next image.",
812     "",
813     "            If the image is a multi-paged document such as a Postscript",
814     "            document, you can skip ahead several pages by preceding",
815     "            this command with a number.  For example to display the",
816     "            third page beyond the current page, press 3<space>.",
817     "",
818     "  backspace Press to display the former image.",
819     "",
820     "            If the image is a multi-paged document such as a Postscript",
821     "            document, you can skip behind several pages by preceding",
822     "            this command with a number.  For example to display the",
823     "            third page preceding the current page, press 3<backspace>.",
824     "",
825     "  Ctl+S     Press to write the image to a file.",
826     "",
827     "  Ctl+P     Press to print the image to a Postscript printer.",
828     "",
829     "  Ctl+D     Press to delete an image file.",
830     "",
831     "  Ctl+N     Press to create a blank canvas.",
832     "",
833     "  Ctl+Q     Press to discard all images and exit program.",
834     "",
835     "  Ctl+Z     Press to undo last image transformation.",
836     "",
837     "  Ctl+R     Press to redo last image transformation.",
838     "",
839     "  Ctl+X     Press to cut a region of the image.",
840     "",
841     "  Ctl+C     Press to copy a region of the image.",
842     "",
843     "  Ctl+V     Press to paste a region to the image.",
844     "",
845     "  <         Press to half the image size.",
846     "",
847     "  -         Press to return to the original image size.",
848     "",
849     "  >         Press to double the image size.",
850     "",
851     "  %         Press to resize the image to a width and height you",
852     "            specify.",
853     "",
854     "Cmd-A       Press to make any image transformations permanent."
855     "",
856     "            By default, any image size transformations are applied",
857     "            to the original image to create the image displayed on",
858     "            the X server.  However, the transformations are not",
859     "            permanent (i.e. the original image does not change",
860     "            size only the X image does).  For example, if you",
861     "            press > the X image will appear to double in size,",
862     "            but the original image will in fact remain the same size.",
863     "            To force the original image to double in size, press >",
864     "            followed by Cmd-A.",
865     "",
866     "  @         Press to refresh the image window.",
867     "",
868     "  C         Press to cut out a rectangular region of the image.",
869     "",
870     "  [         Press to chop the image.",
871     "",
872     "  H         Press to flop image in the horizontal direction.",
873     "",
874     "  V         Press to flip image in the vertical direction.",
875     "",
876     "  /         Press to rotate the image 90 degrees clockwise.",
877     "",
878     " \\         Press to rotate the image 90 degrees counter-clockwise.",
879     "",
880     "  *         Press to rotate the image the number of degrees you",
881     "            specify.",
882     "",
883     "  S         Press to shear the image the number of degrees you",
884     "            specify.",
885     "",
886     "  R         Press to roll the image.",
887     "",
888     "  T         Press to trim the image edges.",
889     "",
890     "  Shft-H    Press to vary the image hue.",
891     "",
892     "  Shft-S    Press to vary the color saturation.",
893     "",
894     "  Shft-L    Press to vary the color brightness.",
895     "",
896     "  Shft-G    Press to gamma correct the image.",
897     "",
898     "  Shft-C    Press to sharpen the image contrast.",
899     "",
900     "  Shft-Z    Press to dull the image contrast.",
901     "",
902     "  =         Press to perform histogram equalization on the image.",
903     "",
904     "  Shft-N    Press to perform histogram normalization on the image.",
905     "",
906     "  Shft-~    Press to negate the colors of the image.",
907     "",
908     "  .         Press to convert the image colors to gray.",
909     "",
910     "  Shft-#    Press to set the maximum number of unique colors in the",
911     "            image.",
912     "",
913     "  F2        Press to reduce the speckles in an image.",
914     "",
915     "  F3        Press to eliminate peak noise from an image.",
916     "",
917     "  F4        Press to add noise to an image.",
918     "",
919     "  F5        Press to sharpen an image.",
920     "",
921     "  F6        Press to delete an image file.",
922     "",
923     "  F7        Press to threshold the image.",
924     "",
925     "  F8        Press to detect edges within an image.",
926     "",
927     "  F9        Press to emboss an image.",
928     "",
929     "  F10       Press to displace pixels by a random amount.",
930     "",
931     "  F11       Press to negate all pixels above the threshold level.",
932     "",
933     "  F12       Press to shade the image using a distant light source.",
934     "",
935     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
936     "",
937     "  F14       Press to segment the image by color.",
938     "",
939     "  Meta-S    Press to swirl image pixels about the center.",
940     "",
941     "  Meta-I    Press to implode image pixels about the center.",
942     "",
943     "  Meta-W    Press to alter an image along a sine wave.",
944     "",
945     "  Meta-P    Press to simulate an oil painting.",
946     "",
947     "  Meta-C    Press to simulate a charcoal drawing.",
948     "",
949     "  Alt-A     Press to annotate the image with text.",
950     "",
951     "  Alt-D     Press to draw on an image.",
952     "",
953     "  Alt-P     Press to edit an image pixel color.",
954     "",
955     "  Alt-M     Press to edit the image matte information.",
956     "",
957     "  Alt-V     Press to composite the image with another.",
958     "",
959     "  Alt-B     Press to add a border to the image.",
960     "",
961     "  Alt-F     Press to add an ornamental border to the image.",
962     "",
963     "  Alt-Shft-!",
964     "            Press to add an image comment.",
965     "",
966     "  Ctl-A     Press to apply image processing techniques to a region",
967     "            of interest.",
968     "",
969     "  Shft-?    Press to display information about the image.",
970     "",
971     "  Shft-+    Press to map the zoom image window.",
972     "",
973     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
974     "",
975     "  F1        Press to display helpful information about display(1).",
976     "",
977     "  Find      Press to browse documentation about ImageMagick.",
978     "",
979     "  1-9       Press to change the level of magnification.",
980     "",
981     "  Use the arrow keys to move the image one pixel up, down,",
982     "  left, or right within the magnify window.  Be sure to first",
983     "  map the magnify window by pressing button 2.",
984     "",
985     "  Press ALT and one of the arrow keys to trim off one pixel",
986     "  from any side of the image.",
987     (char *) NULL,
988   },
989   *ImageMatteEditHelp[] =
990   {
991     "Matte information within an image is useful for some",
992     "operations such as image compositing (See IMAGE",
993     "COMPOSITING).  This extra channel usually defines a mask",
994     "which represents a sort of a cookie-cutter for the image.",
995     "This the case when matte is opaque (full coverage) for",
996     "pixels inside the shape, zero outside, and between 0 and",
997     "QuantumRange on the boundary.",
998     "",
999     "A small window appears showing the location of the cursor in",
1000     "the image window. You are now in matte edit mode.  To exit",
1001     "immediately, press Dismiss.  In matte edit mode, the Command",
1002     "widget has these options:",
1003     "",
1004     "    Method",
1005     "      point",
1006     "      replace",
1007     "      floodfill",
1008     "      filltoborder",
1009     "      reset",
1010     "    Border Color",
1011     "      black",
1012     "      blue",
1013     "      cyan",
1014     "      green",
1015     "      gray",
1016     "      red",
1017     "      magenta",
1018     "      yellow",
1019     "      white",
1020     "      Browser...",
1021     "    Fuzz",
1022     "      0%",
1023     "      2%",
1024     "      5%",
1025     "      10%",
1026     "      15%",
1027     "      Dialog...",
1028     "    Matte",
1029     "      Opaque",
1030     "      Transparent",
1031     "      Dialog...",
1032     "    Undo",
1033     "    Help",
1034     "    Dismiss",
1035     "",
1036     "Choose a matte editing method from the Method sub-menu of",
1037     "the Command widget.  The point method changes the matte value",
1038     "of any pixel selected with the pointer until the button is",
1039     "is released.  The replace method changes the matte value of",
1040     "any pixel that matches the color of the pixel you select with",
1041     "a button press.  Floodfill changes the matte value of any pixel",
1042     "that matches the color of the pixel you select with a button",
1043     "press and is a neighbor.  Whereas filltoborder changes the matte",
1044     "value any neighbor pixel that is not the border color.  Finally",
1045     "reset changes the entire image to the designated matte value.",
1046     "",
1047     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1048     "select the Dialog entry.  Here a dialog appears requesting a matte",
1049     "value.  The value you select is assigned as the opacity value of the",
1050     "selected pixel or pixels.",
1051     "",
1052     "Now, press any button to select a pixel within the image",
1053     "window to change its matte value.",
1054     "",
1055     "If the Magnify widget is mapped, it can be helpful in positioning",
1056     "your pointer within the image (refer to button 2).",
1057     "",
1058     "Matte information is only valid in a DirectClass image.",
1059     "Therefore, any PseudoClass image is promoted to DirectClass",
1060     "(see miff(5)).  Note that matte information for PseudoClass",
1061     "is not retained for colormapped X server visuals (e.g.",
1062     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1063     "immediately save your image to a file (refer to Write).",
1064     "Correct matte editing behavior may require a TrueColor or",
1065     "DirectColor visual or a Standard Colormap.",
1066     (char *) NULL,
1067   },
1068   *ImagePanHelp[] =
1069   {
1070     "When an image exceeds the width or height of the X server",
1071     "screen, display maps a small panning icon.  The rectangle",
1072     "within the panning icon shows the area that is currently",
1073     "displayed in the image window.  To pan about the image,",
1074     "press any button and drag the pointer within the panning",
1075     "icon.  The pan rectangle moves with the pointer and the",
1076     "image window is updated to reflect the location of the",
1077     "rectangle within the panning icon.  When you have selected",
1078     "the area of the image you wish to view, release the button.",
1079     "",
1080     "Use the arrow keys to pan the image one pixel up, down,",
1081     "left, or right within the image window.",
1082     "",
1083     "The panning icon is withdrawn if the image becomes smaller",
1084     "than the dimensions of the X server screen.",
1085     (char *) NULL,
1086   },
1087   *ImagePasteHelp[] =
1088   {
1089     "A small window appears showing the location of the cursor in",
1090     "the image window. You are now in paste mode.  To exit",
1091     "immediately, press Dismiss.  In paste mode, the Command",
1092     "widget has these options:",
1093     "",
1094     "    Operators",
1095     "      over",
1096     "      in",
1097     "      out",
1098     "      atop",
1099     "      xor",
1100     "      plus",
1101     "      minus",
1102     "      add",
1103     "      subtract",
1104     "      difference",
1105     "      replace",
1106     "    Help",
1107     "    Dismiss",
1108     "",
1109     "Choose a composite operation from the Operators sub-menu of",
1110     "the Command widget.  How each operator behaves is described",
1111     "below.  Image window is the image currently displayed on",
1112     "your X server and image is the image obtained with the File",
1113     "Browser widget.",
1114     "",
1115     "Over     The result is the union of the two image shapes,",
1116     "         with image obscuring image window in the region of",
1117     "         overlap.",
1118     "",
1119     "In       The result is simply image cut by the shape of",
1120     "         image window.  None of the image data of image",
1121     "         window is in the result.",
1122     "",
1123     "Out      The resulting image is image with the shape of",
1124     "         image window cut out.",
1125     "",
1126     "Atop     The result is the same shape as image image window,",
1127     "         with image obscuring image window where the image",
1128     "         shapes overlap.  Note this differs from over",
1129     "         because the portion of image outside image window's",
1130     "         shape does not appear in the result.",
1131     "",
1132     "Xor      The result is the image data from both image and",
1133     "         image window that is outside the overlap region.",
1134     "         The overlap region is blank.",
1135     "",
1136     "Plus     The result is just the sum of the image data.",
1137     "         Output values are cropped to QuantumRange (no overflow).",
1138     "         This operation is independent of the matte",
1139     "         channels.",
1140     "",
1141     "Minus    The result of image - image window, with underflow",
1142     "         cropped to zero.",
1143     "",
1144     "Add      The result of image + image window, with overflow",
1145     "         wrapping around (mod 256).",
1146     "",
1147     "Subtract The result of image - image window, with underflow",
1148     "         wrapping around (mod 256).  The add and subtract",
1149     "         operators can be used to perform reversible",
1150     "         transformations.",
1151     "",
1152     "Difference",
1153     "         The result of abs(image - image window).  This",
1154     "         useful for comparing two very similar images.",
1155     "",
1156     "Copy     The resulting image is image window replaced with",
1157     "         image.  Here the matte information is ignored.",
1158     "",
1159     "CopyRed  The red layer of the image window is replace with",
1160     "         the red layer of the image.  The other layers are",
1161     "         untouched.",
1162     "",
1163     "CopyGreen",
1164     "         The green layer of the image window is replace with",
1165     "         the green layer of the image.  The other layers are",
1166     "         untouched.",
1167     "",
1168     "CopyBlue The blue layer of the image window is replace with",
1169     "         the blue layer of the image.  The other layers are",
1170     "         untouched.",
1171     "",
1172     "CopyOpacity",
1173     "         The matte layer of the image window is replace with",
1174     "         the matte layer of the image.  The other layers are",
1175     "         untouched.",
1176     "",
1177     "The image compositor requires a matte, or alpha channel in",
1178     "the image for some operations.  This extra channel usually",
1179     "defines a mask which represents a sort of a cookie-cutter",
1180     "for the image.  This the case when matte is opaque (full",
1181     "coverage) for pixels inside the shape, zero outside, and",
1182     "between 0 and QuantumRange on the boundary.  If image does not",
1183     "have a matte channel, it is initialized with 0 for any pixel",
1184     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1185     "",
1186     "Note that matte information for image window is not retained",
1187     "for colormapped X server visuals (e.g. StaticColor,",
1188     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1189     "behavior may require a TrueColor or DirectColor visual or a",
1190     "Standard Colormap.",
1191     "",
1192     "Choosing a composite operator is optional.  The default",
1193     "operator is replace.  However, you must choose a location to",
1194     "paste your image and press button 1.  Press and hold the",
1195     "button before releasing and an outline of the image will",
1196     "appear to help you identify your location.",
1197     "",
1198     "The actual colors of the pasted image is saved.  However,",
1199     "the color that appears in image window may be different.",
1200     "For example, on a monochrome screen image window will appear",
1201     "black or white even though your pasted image may have",
1202     "many colors.  If the image is saved to a file it is written",
1203     "with the correct colors.  To assure the correct colors are",
1204     "saved in the final image, any PseudoClass image is promoted",
1205     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1206     "to remain PseudoClass, use -colors.",
1207     (char *) NULL,
1208   },
1209   *ImageROIHelp[] =
1210   {
1211     "In region of interest mode, the Command widget has these",
1212     "options:",
1213     "",
1214     "    Help",
1215     "    Dismiss",
1216     "",
1217     "To define a region of interest, press button 1 and drag.",
1218     "The region of interest is defined by a highlighted rectangle",
1219     "that expands or contracts as it follows the pointer.  Once",
1220     "you are satisfied with the region of interest, release the",
1221     "button.  You are now in apply mode.  In apply mode the",
1222     "Command widget has these options:",
1223     "",
1224     "      File",
1225     "        Save...",
1226     "        Print...",
1227     "      Edit",
1228     "        Undo",
1229     "        Redo",
1230     "      Transform",
1231     "        Flop",
1232     "        Flip",
1233     "        Rotate Right",
1234     "        Rotate Left",
1235     "      Enhance",
1236     "        Hue...",
1237     "        Saturation...",
1238     "        Brightness...",
1239     "        Gamma...",
1240     "        Spiff",
1241     "        Dull",
1242     "        Contrast Stretch",
1243     "        Sigmoidal Contrast...",
1244     "        Normalize",
1245     "        Equalize",
1246     "        Negate",
1247     "        Grayscale",
1248     "        Map...",
1249     "        Quantize...",
1250     "      Effects",
1251     "        Despeckle",
1252     "        Emboss",
1253     "        Reduce Noise",
1254     "        Sharpen...",
1255     "        Blur...",
1256     "        Threshold...",
1257     "        Edge Detect...",
1258     "        Spread...",
1259     "        Shade...",
1260     "        Raise...",
1261     "        Segment...",
1262     "      F/X",
1263     "        Solarize...",
1264     "        Sepia Tone...",
1265     "        Swirl...",
1266     "        Implode...",
1267     "        Vignette...",
1268     "        Wave...",
1269     "        Oil Painting...",
1270     "        Charcoal Drawing...",
1271     "      Miscellany",
1272     "        Image Info",
1273     "        Zoom Image",
1274     "        Show Preview...",
1275     "        Show Histogram",
1276     "        Show Matte",
1277     "      Help",
1278     "      Dismiss",
1279     "",
1280     "You can make adjustments to the region of interest by moving",
1281     "the pointer to one of the rectangle corners, pressing a",
1282     "button, and dragging.  Finally, choose an image processing",
1283     "technique from the Command widget.  You can choose more than",
1284     "one image processing technique to apply to an area.",
1285     "Alternatively, you can move the region of interest before",
1286     "applying another image processing technique.  To exit, press",
1287     "Dismiss.",
1288     (char *) NULL,
1289   },
1290   *ImageRotateHelp[] =
1291   {
1292     "In rotate mode, the Command widget has these options:",
1293     "",
1294     "    Pixel Color",
1295     "      black",
1296     "      blue",
1297     "      cyan",
1298     "      green",
1299     "      gray",
1300     "      red",
1301     "      magenta",
1302     "      yellow",
1303     "      white",
1304     "      Browser...",
1305     "    Direction",
1306     "      horizontal",
1307     "      vertical",
1308     "    Help",
1309     "    Dismiss",
1310     "",
1311     "Choose a background color from the Pixel Color sub-menu.",
1312     "Additional background colors can be specified with the color",
1313     "browser.  You can change the menu colors by setting the X",
1314     "resources pen1 through pen9.",
1315     "",
1316     "If you choose the color browser and press Grab, you can",
1317     "select the background color by moving the pointer to the",
1318     "desired color on the screen and press any button.",
1319     "",
1320     "Choose a point in the image window and press this button and",
1321     "hold.  Next, move the pointer to another location in the",
1322     "image.  As you move a line connects the initial location and",
1323     "the pointer.  When you release the button, the degree of",
1324     "image rotation is determined by the slope of the line you",
1325     "just drew.  The slope is relative to the direction you",
1326     "choose from the Direction sub-menu of the Command widget.",
1327     "",
1328     "To cancel the image rotation, move the pointer back to the",
1329     "starting point of the line and release the button.",
1330     (char *) NULL,
1331   };
1332 \f
1333 /*
1334   Enumeration declarations.
1335 */
1336 typedef enum
1337 {
1338   CopyMode,
1339   CropMode,
1340   CutMode
1341 } ClipboardMode;
1342
1343 typedef enum
1344 {
1345   OpenCommand,
1346   NextCommand,
1347   FormerCommand,
1348   SelectCommand,
1349   SaveCommand,
1350   PrintCommand,
1351   DeleteCommand,
1352   NewCommand,
1353   VisualDirectoryCommand,
1354   QuitCommand,
1355   UndoCommand,
1356   RedoCommand,
1357   CutCommand,
1358   CopyCommand,
1359   PasteCommand,
1360   HalfSizeCommand,
1361   OriginalSizeCommand,
1362   DoubleSizeCommand,
1363   ResizeCommand,
1364   ApplyCommand,
1365   RefreshCommand,
1366   RestoreCommand,
1367   CropCommand,
1368   ChopCommand,
1369   FlopCommand,
1370   FlipCommand,
1371   RotateRightCommand,
1372   RotateLeftCommand,
1373   RotateCommand,
1374   ShearCommand,
1375   RollCommand,
1376   TrimCommand,
1377   HueCommand,
1378   SaturationCommand,
1379   BrightnessCommand,
1380   GammaCommand,
1381   SpiffCommand,
1382   DullCommand,
1383   ContrastStretchCommand,
1384   SigmoidalContrastCommand,
1385   NormalizeCommand,
1386   EqualizeCommand,
1387   NegateCommand,
1388   GrayscaleCommand,
1389   MapCommand,
1390   QuantizeCommand,
1391   DespeckleCommand,
1392   EmbossCommand,
1393   ReduceNoiseCommand,
1394   AddNoiseCommand,
1395   SharpenCommand,
1396   BlurCommand,
1397   ThresholdCommand,
1398   EdgeDetectCommand,
1399   SpreadCommand,
1400   ShadeCommand,
1401   RaiseCommand,
1402   SegmentCommand,
1403   SolarizeCommand,
1404   SepiaToneCommand,
1405   SwirlCommand,
1406   ImplodeCommand,
1407   VignetteCommand,
1408   WaveCommand,
1409   OilPaintCommand,
1410   CharcoalDrawCommand,
1411   AnnotateCommand,
1412   DrawCommand,
1413   ColorCommand,
1414   MatteCommand,
1415   CompositeCommand,
1416   AddBorderCommand,
1417   AddFrameCommand,
1418   CommentCommand,
1419   LaunchCommand,
1420   RegionofInterestCommand,
1421   ROIHelpCommand,
1422   ROIDismissCommand,
1423   InfoCommand,
1424   ZoomCommand,
1425   ShowPreviewCommand,
1426   ShowHistogramCommand,
1427   ShowMatteCommand,
1428   BackgroundCommand,
1429   SlideShowCommand,
1430   PreferencesCommand,
1431   HelpCommand,
1432   BrowseDocumentationCommand,
1433   VersionCommand,
1434   SaveToUndoBufferCommand,
1435   FreeBuffersCommand,
1436   NullCommand
1437 } CommandType;
1438
1439 typedef enum
1440 {
1441   AnnotateNameCommand,
1442   AnnotateFontColorCommand,
1443   AnnotateBackgroundColorCommand,
1444   AnnotateRotateCommand,
1445   AnnotateHelpCommand,
1446   AnnotateDismissCommand,
1447   TextHelpCommand,
1448   TextApplyCommand,
1449   ChopDirectionCommand,
1450   ChopHelpCommand,
1451   ChopDismissCommand,
1452   HorizontalChopCommand,
1453   VerticalChopCommand,
1454   ColorEditMethodCommand,
1455   ColorEditColorCommand,
1456   ColorEditBorderCommand,
1457   ColorEditFuzzCommand,
1458   ColorEditUndoCommand,
1459   ColorEditHelpCommand,
1460   ColorEditDismissCommand,
1461   CompositeOperatorsCommand,
1462   CompositeDissolveCommand,
1463   CompositeDisplaceCommand,
1464   CompositeHelpCommand,
1465   CompositeDismissCommand,
1466   CropHelpCommand,
1467   CropDismissCommand,
1468   RectifyCopyCommand,
1469   RectifyHelpCommand,
1470   RectifyDismissCommand,
1471   DrawElementCommand,
1472   DrawColorCommand,
1473   DrawStippleCommand,
1474   DrawWidthCommand,
1475   DrawUndoCommand,
1476   DrawHelpCommand,
1477   DrawDismissCommand,
1478   MatteEditMethod,
1479   MatteEditBorderCommand,
1480   MatteEditFuzzCommand,
1481   MatteEditValueCommand,
1482   MatteEditUndoCommand,
1483   MatteEditHelpCommand,
1484   MatteEditDismissCommand,
1485   PasteOperatorsCommand,
1486   PasteHelpCommand,
1487   PasteDismissCommand,
1488   RotateColorCommand,
1489   RotateDirectionCommand,
1490   RotateCropCommand,
1491   RotateSharpenCommand,
1492   RotateHelpCommand,
1493   RotateDismissCommand,
1494   HorizontalRotateCommand,
1495   VerticalRotateCommand,
1496   TileLoadCommand,
1497   TileNextCommand,
1498   TileFormerCommand,
1499   TileDeleteCommand,
1500   TileUpdateCommand
1501 } ModeType;
1502 \f
1503 /*
1504   Stipples.
1505 */
1506 #define BricksWidth  20
1507 #define BricksHeight  20
1508 #define DiagonalWidth  16
1509 #define DiagonalHeight  16
1510 #define HighlightWidth  8
1511 #define HighlightHeight  8
1512 #define OpaqueWidth  8
1513 #define OpaqueHeight  8
1514 #define ScalesWidth  16
1515 #define ScalesHeight  16
1516 #define ShadowWidth  8
1517 #define ShadowHeight  8
1518 #define VerticalWidth  16
1519 #define VerticalHeight  16
1520 #define WavyWidth  16
1521 #define WavyHeight  16
1522 \f
1523 /*
1524   Constant declaration.
1525 */
1526 static const int
1527   RoiDelta = 8;
1528
1529 static const unsigned char
1530   BricksBitmap[] =
1531   {
1532     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1533     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1534     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1535     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1536     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1537   },
1538   DiagonalBitmap[] =
1539   {
1540     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1541     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1542     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1543   },
1544   ScalesBitmap[] =
1545   {
1546     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1547     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1548     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1549   },
1550   VerticalBitmap[] =
1551   {
1552     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1553     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1554     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1555   },
1556   WavyBitmap[] =
1557   {
1558     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1559     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1560     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1561   };
1562 \f
1563 /*
1564   Function prototypes.
1565 */
1566 static CommandType
1567   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1568     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1569
1570 static Image
1571   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1572     Image **,ExceptionInfo *),
1573   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1574   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1575     ExceptionInfo *),
1576   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1577     ExceptionInfo *);
1578
1579 static MagickBooleanType
1580   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1581     ExceptionInfo *),
1582   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1583     ExceptionInfo *),
1584   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1585     ExceptionInfo *),
1586   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1587     ExceptionInfo *),
1588   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1589     ExceptionInfo *),
1590   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1591     ExceptionInfo *),
1592   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1593   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1594     ExceptionInfo *),
1595   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1596     ExceptionInfo *),
1597   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1599   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1600     ExceptionInfo *),
1601   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1602   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1603   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1604
1605 static void
1606   XDrawPanRectangle(Display *,XWindows *),
1607   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1608     ExceptionInfo *),
1609   XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1610   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1611   XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1612   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1613     const KeySym,ExceptionInfo *),
1614   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1615   XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1616   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1617 \f
1618 /*
1619 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1620 %                                                                             %
1621 %                                                                             %
1622 %                                                                             %
1623 %   D i s p l a y I m a g e s                                                 %
1624 %                                                                             %
1625 %                                                                             %
1626 %                                                                             %
1627 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1628 %
1629 %  DisplayImages() displays an image sequence to any X window screen.  It
1630 %  returns a value other than 0 if successful.  Check the exception member
1631 %  of image to determine the reason for any failure.
1632 %
1633 %  The format of the DisplayImages method is:
1634 %
1635 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1636 %        Image *images,ExceptionInfo *exception)
1637 %
1638 %  A description of each parameter follows:
1639 %
1640 %    o image_info: the image info.
1641 %
1642 %    o image: the image.
1643 %
1644 %    o exception: return any errors or warnings in this structure.
1645 %
1646 */
1647 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1648   Image *images,ExceptionInfo *exception)
1649 {
1650   char
1651     *argv[1];
1652
1653   Display
1654     *display;
1655
1656   Image
1657     *image;
1658
1659   register ssize_t
1660     i;
1661
1662   size_t
1663     state;
1664
1665   XrmDatabase
1666     resource_database;
1667
1668   XResourceInfo
1669     resource_info;
1670
1671   assert(image_info != (const ImageInfo *) NULL);
1672   assert(image_info->signature == MagickSignature);
1673   assert(images != (Image *) NULL);
1674   assert(images->signature == MagickSignature);
1675   if (images->debug != MagickFalse)
1676     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1677   display=XOpenDisplay(image_info->server_name);
1678   if (display == (Display *) NULL)
1679     {
1680       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1681         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1682       return(MagickFalse);
1683     }
1684   if (exception->severity != UndefinedException)
1685     CatchException(exception);
1686   (void) XSetErrorHandler(XError);
1687   resource_database=XGetResourceDatabase(display,GetClientName());
1688   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1689   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1690   if (image_info->page != (char *) NULL)
1691     resource_info.image_geometry=AcquireString(image_info->page);
1692   resource_info.immutable=MagickTrue;
1693   argv[0]=AcquireString(GetClientName());
1694   state=DefaultState;
1695   for (i=0; (state & ExitState) == 0; i++)
1696   {
1697     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1698       break;
1699     image=GetImageFromList(images,i % GetImageListLength(images));
1700     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1701   }
1702   SetErrorHandler((ErrorHandler) NULL);
1703   SetWarningHandler((WarningHandler) NULL);
1704   argv[0]=DestroyString(argv[0]);
1705   (void) XCloseDisplay(display);
1706   XDestroyResourceInfo(&resource_info);
1707   if (exception->severity != UndefinedException)
1708     return(MagickFalse);
1709   return(MagickTrue);
1710 }
1711 \f
1712 /*
1713 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1714 %                                                                             %
1715 %                                                                             %
1716 %                                                                             %
1717 %   R e m o t e D i s p l a y C o m m a n d                                   %
1718 %                                                                             %
1719 %                                                                             %
1720 %                                                                             %
1721 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1722 %
1723 %  RemoteDisplayCommand() encourages a remote display program to display the
1724 %  specified image filename.
1725 %
1726 %  The format of the RemoteDisplayCommand method is:
1727 %
1728 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1729 %        const char *window,const char *filename,ExceptionInfo *exception)
1730 %
1731 %  A description of each parameter follows:
1732 %
1733 %    o image_info: the image info.
1734 %
1735 %    o window: Specifies the name or id of an X window.
1736 %
1737 %    o filename: the name of the image filename to display.
1738 %
1739 %    o exception: return any errors or warnings in this structure.
1740 %
1741 */
1742 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1743   const char *window,const char *filename,ExceptionInfo *exception)
1744 {
1745   Display
1746     *display;
1747
1748   MagickStatusType
1749     status;
1750
1751   assert(image_info != (const ImageInfo *) NULL);
1752   assert(image_info->signature == MagickSignature);
1753   assert(filename != (char *) NULL);
1754   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1755   display=XOpenDisplay(image_info->server_name);
1756   if (display == (Display *) NULL)
1757     {
1758       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1759         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1760       return(MagickFalse);
1761     }
1762   (void) XSetErrorHandler(XError);
1763   status=XRemoteCommand(display,window,filename);
1764   (void) XCloseDisplay(display);
1765   return(status != 0 ? MagickTrue : MagickFalse);
1766 }
1767 \f
1768 /*
1769 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1770 %                                                                             %
1771 %                                                                             %
1772 %                                                                             %
1773 +   X A n n o t a t e E d i t I m a g e                                       %
1774 %                                                                             %
1775 %                                                                             %
1776 %                                                                             %
1777 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1778 %
1779 %  XAnnotateEditImage() annotates the image with text.
1780 %
1781 %  The format of the XAnnotateEditImage method is:
1782 %
1783 %      MagickBooleanType XAnnotateEditImage(Display *display,
1784 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1785 %        ExceptionInfo *exception)
1786 %
1787 %  A description of each parameter follows:
1788 %
1789 %    o display: Specifies a connection to an X server;  returned from
1790 %      XOpenDisplay.
1791 %
1792 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1793 %
1794 %    o windows: Specifies a pointer to a XWindows structure.
1795 %
1796 %    o image: the image; returned from ReadImage.
1797 %
1798 */
1799
1800 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1801 {
1802   if (x > y)
1803     return(x);
1804   return(y);
1805 }
1806
1807 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1808 {
1809   if (x < y)
1810     return(x);
1811   return(y);
1812 }
1813
1814 static MagickBooleanType XAnnotateEditImage(Display *display,
1815   XResourceInfo *resource_info,XWindows *windows,Image *image,
1816   ExceptionInfo *exception)
1817 {
1818   static const char
1819     *AnnotateMenu[] =
1820     {
1821       "Font Name",
1822       "Font Color",
1823       "Box Color",
1824       "Rotate Text",
1825       "Help",
1826       "Dismiss",
1827       (char *) NULL
1828     },
1829     *TextMenu[] =
1830     {
1831       "Help",
1832       "Apply",
1833       (char *) NULL
1834     };
1835
1836   static const ModeType
1837     AnnotateCommands[] =
1838     {
1839       AnnotateNameCommand,
1840       AnnotateFontColorCommand,
1841       AnnotateBackgroundColorCommand,
1842       AnnotateRotateCommand,
1843       AnnotateHelpCommand,
1844       AnnotateDismissCommand
1845     },
1846     TextCommands[] =
1847     {
1848       TextHelpCommand,
1849       TextApplyCommand
1850     };
1851
1852   static MagickBooleanType
1853     transparent_box = MagickTrue,
1854     transparent_pen = MagickFalse;
1855
1856   static MagickRealType
1857     degrees = 0.0;
1858
1859   static unsigned int
1860     box_id = MaxNumberPens-2,
1861     font_id = 0,
1862     pen_id = 0;
1863
1864   char
1865     command[MaxTextExtent],
1866     text[MaxTextExtent];
1867
1868   const char
1869     *ColorMenu[MaxNumberPens+1];
1870
1871   Cursor
1872     cursor;
1873
1874   GC
1875     annotate_context;
1876
1877   int
1878     id,
1879     pen_number,
1880     status,
1881     x,
1882     y;
1883
1884   KeySym
1885     key_symbol;
1886
1887   register char
1888     *p;
1889
1890   register ssize_t
1891     i;
1892
1893   unsigned int
1894     height,
1895     width;
1896
1897   size_t
1898     state;
1899
1900   XAnnotateInfo
1901     *annotate_info,
1902     *previous_info;
1903
1904   XColor
1905     color;
1906
1907   XFontStruct
1908     *font_info;
1909
1910   XEvent
1911     event,
1912     text_event;
1913
1914   /*
1915     Map Command widget.
1916   */
1917   (void) CloneString(&windows->command.name,"Annotate");
1918   windows->command.data=4;
1919   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1920   (void) XMapRaised(display,windows->command.id);
1921   XClientMessage(display,windows->image.id,windows->im_protocols,
1922     windows->im_update_widget,CurrentTime);
1923   /*
1924     Track pointer until button 1 is pressed.
1925   */
1926   XQueryPosition(display,windows->image.id,&x,&y);
1927   (void) XSelectInput(display,windows->image.id,
1928     windows->image.attributes.event_mask | PointerMotionMask);
1929   cursor=XCreateFontCursor(display,XC_left_side);
1930   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1931   state=DefaultState;
1932   do
1933   {
1934     if (windows->info.mapped != MagickFalse)
1935       {
1936         /*
1937           Display pointer position.
1938         */
1939         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1940           x+windows->image.x,y+windows->image.y);
1941         XInfoWidget(display,windows,text);
1942       }
1943     /*
1944       Wait for next event.
1945     */
1946     XScreenEvent(display,windows,&event,exception);
1947     if (event.xany.window == windows->command.id)
1948       {
1949         /*
1950           Select a command from the Command widget.
1951         */
1952         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1953         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1954         if (id < 0)
1955           continue;
1956         switch (AnnotateCommands[id])
1957         {
1958           case AnnotateNameCommand:
1959           {
1960             const char
1961               *FontMenu[MaxNumberFonts];
1962
1963             int
1964               font_number;
1965
1966             /*
1967               Initialize menu selections.
1968             */
1969             for (i=0; i < MaxNumberFonts; i++)
1970               FontMenu[i]=resource_info->font_name[i];
1971             FontMenu[MaxNumberFonts-2]="Browser...";
1972             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1973             /*
1974               Select a font name from the pop-up menu.
1975             */
1976             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1977               (const char **) FontMenu,command);
1978             if (font_number < 0)
1979               break;
1980             if (font_number == (MaxNumberFonts-2))
1981               {
1982                 static char
1983                   font_name[MaxTextExtent] = "fixed";
1984
1985                 /*
1986                   Select a font name from a browser.
1987                 */
1988                 resource_info->font_name[font_number]=font_name;
1989                 XFontBrowserWidget(display,windows,"Select",font_name);
1990                 if (*font_name == '\0')
1991                   break;
1992               }
1993             /*
1994               Initialize font info.
1995             */
1996             font_info=XLoadQueryFont(display,resource_info->font_name[
1997               font_number]);
1998             if (font_info == (XFontStruct *) NULL)
1999               {
2000                 XNoticeWidget(display,windows,"Unable to load font:",
2001                   resource_info->font_name[font_number]);
2002                 break;
2003               }
2004             font_id=(unsigned int) font_number;
2005             (void) XFreeFont(display,font_info);
2006             break;
2007           }
2008           case AnnotateFontColorCommand:
2009           {
2010             /*
2011               Initialize menu selections.
2012             */
2013             for (i=0; i < (int) (MaxNumberPens-2); i++)
2014               ColorMenu[i]=resource_info->pen_colors[i];
2015             ColorMenu[MaxNumberPens-2]="transparent";
2016             ColorMenu[MaxNumberPens-1]="Browser...";
2017             ColorMenu[MaxNumberPens]=(const char *) NULL;
2018             /*
2019               Select a pen color from the pop-up menu.
2020             */
2021             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2022               (const char **) ColorMenu,command);
2023             if (pen_number < 0)
2024               break;
2025             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2026               MagickFalse;
2027             if (transparent_pen != MagickFalse)
2028               break;
2029             if (pen_number == (MaxNumberPens-1))
2030               {
2031                 static char
2032                   color_name[MaxTextExtent] = "gray";
2033
2034                 /*
2035                   Select a pen color from a dialog.
2036                 */
2037                 resource_info->pen_colors[pen_number]=color_name;
2038                 XColorBrowserWidget(display,windows,"Select",color_name);
2039                 if (*color_name == '\0')
2040                   break;
2041               }
2042             /*
2043               Set pen color.
2044             */
2045             (void) XParseColor(display,windows->map_info->colormap,
2046               resource_info->pen_colors[pen_number],&color);
2047             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2048               (unsigned int) MaxColors,&color);
2049             windows->pixel_info->pen_colors[pen_number]=color;
2050             pen_id=(unsigned int) pen_number;
2051             break;
2052           }
2053           case AnnotateBackgroundColorCommand:
2054           {
2055             /*
2056               Initialize menu selections.
2057             */
2058             for (i=0; i < (int) (MaxNumberPens-2); i++)
2059               ColorMenu[i]=resource_info->pen_colors[i];
2060             ColorMenu[MaxNumberPens-2]="transparent";
2061             ColorMenu[MaxNumberPens-1]="Browser...";
2062             ColorMenu[MaxNumberPens]=(const char *) NULL;
2063             /*
2064               Select a pen color from the pop-up menu.
2065             */
2066             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2067               (const char **) ColorMenu,command);
2068             if (pen_number < 0)
2069               break;
2070             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2071               MagickFalse;
2072             if (transparent_box != MagickFalse)
2073               break;
2074             if (pen_number == (MaxNumberPens-1))
2075               {
2076                 static char
2077                   color_name[MaxTextExtent] = "gray";
2078
2079                 /*
2080                   Select a pen color from a dialog.
2081                 */
2082                 resource_info->pen_colors[pen_number]=color_name;
2083                 XColorBrowserWidget(display,windows,"Select",color_name);
2084                 if (*color_name == '\0')
2085                   break;
2086               }
2087             /*
2088               Set pen color.
2089             */
2090             (void) XParseColor(display,windows->map_info->colormap,
2091               resource_info->pen_colors[pen_number],&color);
2092             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2093               (unsigned int) MaxColors,&color);
2094             windows->pixel_info->pen_colors[pen_number]=color;
2095             box_id=(unsigned int) pen_number;
2096             break;
2097           }
2098           case AnnotateRotateCommand:
2099           {
2100             int
2101               entry;
2102
2103             static char
2104               angle[MaxTextExtent] = "30.0";
2105
2106             static const char
2107               *RotateMenu[] =
2108               {
2109                 "-90",
2110                 "-45",
2111                 "-30",
2112                 "0",
2113                 "30",
2114                 "45",
2115                 "90",
2116                 "180",
2117                 "Dialog...",
2118                 (char *) NULL,
2119               };
2120
2121             /*
2122               Select a command from the pop-up menu.
2123             */
2124             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2125               command);
2126             if (entry < 0)
2127               break;
2128             if (entry != 8)
2129               {
2130                 degrees=InterpretLocaleValue(RotateMenu[entry],(char **) NULL);
2131                 break;
2132               }
2133             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2134               angle);
2135             if (*angle == '\0')
2136               break;
2137             degrees=InterpretLocaleValue(angle,(char **) NULL);
2138             break;
2139           }
2140           case AnnotateHelpCommand:
2141           {
2142             XTextViewWidget(display,resource_info,windows,MagickFalse,
2143               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2144             break;
2145           }
2146           case AnnotateDismissCommand:
2147           {
2148             /*
2149               Prematurely exit.
2150             */
2151             state|=EscapeState;
2152             state|=ExitState;
2153             break;
2154           }
2155           default:
2156             break;
2157         }
2158         continue;
2159       }
2160     switch (event.type)
2161     {
2162       case ButtonPress:
2163       {
2164         if (event.xbutton.button != Button1)
2165           break;
2166         if (event.xbutton.window != windows->image.id)
2167           break;
2168         /*
2169           Change to text entering mode.
2170         */
2171         x=event.xbutton.x;
2172         y=event.xbutton.y;
2173         state|=ExitState;
2174         break;
2175       }
2176       case ButtonRelease:
2177         break;
2178       case Expose:
2179         break;
2180       case KeyPress:
2181       {
2182         if (event.xkey.window != windows->image.id)
2183           break;
2184         /*
2185           Respond to a user key press.
2186         */
2187         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2188           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2189         switch ((int) key_symbol)
2190         {
2191           case XK_Escape:
2192           case XK_F20:
2193           {
2194             /*
2195               Prematurely exit.
2196             */
2197             state|=EscapeState;
2198             state|=ExitState;
2199             break;
2200           }
2201           case XK_F1:
2202           case XK_Help:
2203           {
2204             XTextViewWidget(display,resource_info,windows,MagickFalse,
2205               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2206             break;
2207           }
2208           default:
2209           {
2210             (void) XBell(display,0);
2211             break;
2212           }
2213         }
2214         break;
2215       }
2216       case MotionNotify:
2217       {
2218         /*
2219           Map and unmap Info widget as cursor crosses its boundaries.
2220         */
2221         x=event.xmotion.x;
2222         y=event.xmotion.y;
2223         if (windows->info.mapped != MagickFalse)
2224           {
2225             if ((x < (int) (windows->info.x+windows->info.width)) &&
2226                 (y < (int) (windows->info.y+windows->info.height)))
2227               (void) XWithdrawWindow(display,windows->info.id,
2228                 windows->info.screen);
2229           }
2230         else
2231           if ((x > (int) (windows->info.x+windows->info.width)) ||
2232               (y > (int) (windows->info.y+windows->info.height)))
2233             (void) XMapWindow(display,windows->info.id);
2234         break;
2235       }
2236       default:
2237         break;
2238     }
2239   } while ((state & ExitState) == 0);
2240   (void) XSelectInput(display,windows->image.id,
2241     windows->image.attributes.event_mask);
2242   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2243   if ((state & EscapeState) != 0)
2244     return(MagickTrue);
2245   /*
2246     Set font info and check boundary conditions.
2247   */
2248   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2249   if (font_info == (XFontStruct *) NULL)
2250     {
2251       XNoticeWidget(display,windows,"Unable to load font:",
2252         resource_info->font_name[font_id]);
2253       font_info=windows->font_info;
2254     }
2255   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2256     x=(int) windows->image.width-font_info->max_bounds.width;
2257   if (y < (int) (font_info->ascent+font_info->descent))
2258     y=(int) font_info->ascent+font_info->descent;
2259   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2260       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2261     return(MagickFalse);
2262   /*
2263     Initialize annotate structure.
2264   */
2265   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2266   if (annotate_info == (XAnnotateInfo *) NULL)
2267     return(MagickFalse);
2268   XGetAnnotateInfo(annotate_info);
2269   annotate_info->x=x;
2270   annotate_info->y=y;
2271   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2272     annotate_info->stencil=OpaqueStencil;
2273   else
2274     if (transparent_box == MagickFalse)
2275       annotate_info->stencil=BackgroundStencil;
2276     else
2277       annotate_info->stencil=ForegroundStencil;
2278   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2279   annotate_info->degrees=degrees;
2280   annotate_info->font_info=font_info;
2281   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2282     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2283     sizeof(*annotate_info->text));
2284   if (annotate_info->text == (char *) NULL)
2285     return(MagickFalse);
2286   /*
2287     Create cursor and set graphic context.
2288   */
2289   cursor=XCreateFontCursor(display,XC_pencil);
2290   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2291   annotate_context=windows->image.annotate_context;
2292   (void) XSetFont(display,annotate_context,font_info->fid);
2293   (void) XSetBackground(display,annotate_context,
2294     windows->pixel_info->pen_colors[box_id].pixel);
2295   (void) XSetForeground(display,annotate_context,
2296     windows->pixel_info->pen_colors[pen_id].pixel);
2297   /*
2298     Begin annotating the image with text.
2299   */
2300   (void) CloneString(&windows->command.name,"Text");
2301   windows->command.data=0;
2302   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2303   state=DefaultState;
2304   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2305   text_event.xexpose.width=(int) font_info->max_bounds.width;
2306   text_event.xexpose.height=font_info->max_bounds.ascent+
2307     font_info->max_bounds.descent;
2308   p=annotate_info->text;
2309   do
2310   {
2311     /*
2312       Display text cursor.
2313     */
2314     *p='\0';
2315     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2316     /*
2317       Wait for next event.
2318     */
2319     XScreenEvent(display,windows,&event,exception);
2320     if (event.xany.window == windows->command.id)
2321       {
2322         /*
2323           Select a command from the Command widget.
2324         */
2325         (void) XSetBackground(display,annotate_context,
2326           windows->pixel_info->background_color.pixel);
2327         (void) XSetForeground(display,annotate_context,
2328           windows->pixel_info->foreground_color.pixel);
2329         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2330         (void) XSetBackground(display,annotate_context,
2331           windows->pixel_info->pen_colors[box_id].pixel);
2332         (void) XSetForeground(display,annotate_context,
2333           windows->pixel_info->pen_colors[pen_id].pixel);
2334         if (id < 0)
2335           continue;
2336         switch (TextCommands[id])
2337         {
2338           case TextHelpCommand:
2339           {
2340             XTextViewWidget(display,resource_info,windows,MagickFalse,
2341               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2342             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2343             break;
2344           }
2345           case TextApplyCommand:
2346           {
2347             /*
2348               Finished annotating.
2349             */
2350             annotate_info->width=(unsigned int) XTextWidth(font_info,
2351               annotate_info->text,(int) strlen(annotate_info->text));
2352             XRefreshWindow(display,&windows->image,&text_event);
2353             state|=ExitState;
2354             break;
2355           }
2356           default:
2357             break;
2358         }
2359         continue;
2360       }
2361     /*
2362       Erase text cursor.
2363     */
2364     text_event.xexpose.x=x;
2365     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2366     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2367       (unsigned int) text_event.xexpose.width,(unsigned int)
2368       text_event.xexpose.height,MagickFalse);
2369     XRefreshWindow(display,&windows->image,&text_event);
2370     switch (event.type)
2371     {
2372       case ButtonPress:
2373       {
2374         if (event.xbutton.window != windows->image.id)
2375           break;
2376         if (event.xbutton.button == Button2)
2377           {
2378             /*
2379               Request primary selection.
2380             */
2381             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2382               windows->image.id,CurrentTime);
2383             break;
2384           }
2385         break;
2386       }
2387       case Expose:
2388       {
2389         if (event.xexpose.count == 0)
2390           {
2391             XAnnotateInfo
2392               *text_info;
2393
2394             /*
2395               Refresh Image window.
2396             */
2397             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2398             text_info=annotate_info;
2399             while (text_info != (XAnnotateInfo *) NULL)
2400             {
2401               if (annotate_info->stencil == ForegroundStencil)
2402                 (void) XDrawString(display,windows->image.id,annotate_context,
2403                   text_info->x,text_info->y,text_info->text,
2404                   (int) strlen(text_info->text));
2405               else
2406                 (void) XDrawImageString(display,windows->image.id,
2407                   annotate_context,text_info->x,text_info->y,text_info->text,
2408                   (int) strlen(text_info->text));
2409               text_info=text_info->previous;
2410             }
2411             (void) XDrawString(display,windows->image.id,annotate_context,
2412               x,y,"_",1);
2413           }
2414         break;
2415       }
2416       case KeyPress:
2417       {
2418         int
2419           length;
2420
2421         if (event.xkey.window != windows->image.id)
2422           break;
2423         /*
2424           Respond to a user key press.
2425         */
2426         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2427           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2428         *(command+length)='\0';
2429         if (((event.xkey.state & ControlMask) != 0) ||
2430             ((event.xkey.state & Mod1Mask) != 0))
2431           state|=ModifierState;
2432         if ((state & ModifierState) != 0)
2433           switch ((int) key_symbol)
2434           {
2435             case XK_u:
2436             case XK_U:
2437             {
2438               key_symbol=DeleteCommand;
2439               break;
2440             }
2441             default:
2442               break;
2443           }
2444         switch ((int) key_symbol)
2445         {
2446           case XK_BackSpace:
2447           {
2448             /*
2449               Erase one character.
2450             */
2451             if (p == annotate_info->text)
2452               {
2453                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2454                   break;
2455                 else
2456                   {
2457                     /*
2458                       Go to end of the previous line of text.
2459                     */
2460                     annotate_info=annotate_info->previous;
2461                     p=annotate_info->text;
2462                     x=annotate_info->x+annotate_info->width;
2463                     y=annotate_info->y;
2464                     if (annotate_info->width != 0)
2465                       p+=strlen(annotate_info->text);
2466                     break;
2467                   }
2468               }
2469             p--;
2470             x-=XTextWidth(font_info,p,1);
2471             text_event.xexpose.x=x;
2472             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2473             XRefreshWindow(display,&windows->image,&text_event);
2474             break;
2475           }
2476           case XK_bracketleft:
2477           {
2478             key_symbol=XK_Escape;
2479             break;
2480           }
2481           case DeleteCommand:
2482           {
2483             /*
2484               Erase the entire line of text.
2485             */
2486             while (p != annotate_info->text)
2487             {
2488               p--;
2489               x-=XTextWidth(font_info,p,1);
2490               text_event.xexpose.x=x;
2491               XRefreshWindow(display,&windows->image,&text_event);
2492             }
2493             break;
2494           }
2495           case XK_Escape:
2496           case XK_F20:
2497           {
2498             /*
2499               Finished annotating.
2500             */
2501             annotate_info->width=(unsigned int) XTextWidth(font_info,
2502               annotate_info->text,(int) strlen(annotate_info->text));
2503             XRefreshWindow(display,&windows->image,&text_event);
2504             state|=ExitState;
2505             break;
2506           }
2507           default:
2508           {
2509             /*
2510               Draw a single character on the Image window.
2511             */
2512             if ((state & ModifierState) != 0)
2513               break;
2514             if (*command == '\0')
2515               break;
2516             *p=(*command);
2517             if (annotate_info->stencil == ForegroundStencil)
2518               (void) XDrawString(display,windows->image.id,annotate_context,
2519                 x,y,p,1);
2520             else
2521               (void) XDrawImageString(display,windows->image.id,
2522                 annotate_context,x,y,p,1);
2523             x+=XTextWidth(font_info,p,1);
2524             p++;
2525             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2526               break;
2527           }
2528           case XK_Return:
2529           case XK_KP_Enter:
2530           {
2531             /*
2532               Advance to the next line of text.
2533             */
2534             *p='\0';
2535             annotate_info->width=(unsigned int) XTextWidth(font_info,
2536               annotate_info->text,(int) strlen(annotate_info->text));
2537             if (annotate_info->next != (XAnnotateInfo *) NULL)
2538               {
2539                 /*
2540                   Line of text already exists.
2541                 */
2542                 annotate_info=annotate_info->next;
2543                 x=annotate_info->x;
2544                 y=annotate_info->y;
2545                 p=annotate_info->text;
2546                 break;
2547               }
2548             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2549               sizeof(*annotate_info->next));
2550             if (annotate_info->next == (XAnnotateInfo *) NULL)
2551               return(MagickFalse);
2552             *annotate_info->next=(*annotate_info);
2553             annotate_info->next->previous=annotate_info;
2554             annotate_info=annotate_info->next;
2555             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2556               windows->image.width/MagickMax((ssize_t)
2557               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2558             if (annotate_info->text == (char *) NULL)
2559               return(MagickFalse);
2560             annotate_info->y+=annotate_info->height;
2561             if (annotate_info->y > (int) windows->image.height)
2562               annotate_info->y=(int) annotate_info->height;
2563             annotate_info->next=(XAnnotateInfo *) NULL;
2564             x=annotate_info->x;
2565             y=annotate_info->y;
2566             p=annotate_info->text;
2567             break;
2568           }
2569         }
2570         break;
2571       }
2572       case KeyRelease:
2573       {
2574         /*
2575           Respond to a user key release.
2576         */
2577         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2578           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2579         state&=(~ModifierState);
2580         break;
2581       }
2582       case SelectionNotify:
2583       {
2584         Atom
2585           type;
2586
2587         int
2588           format;
2589
2590         unsigned char
2591           *data;
2592
2593         unsigned long
2594           after,
2595           length;
2596
2597         /*
2598           Obtain response from primary selection.
2599         */
2600         if (event.xselection.property == (Atom) None)
2601           break;
2602         status=XGetWindowProperty(display,event.xselection.requestor,
2603           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2604           &type,&format,&length,&after,&data);
2605         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2606             (length == 0))
2607           break;
2608         /*
2609           Annotate Image window with primary selection.
2610         */
2611         for (i=0; i < (ssize_t) length; i++)
2612         {
2613           if ((char) data[i] != '\n')
2614             {
2615               /*
2616                 Draw a single character on the Image window.
2617               */
2618               *p=(char) data[i];
2619               (void) XDrawString(display,windows->image.id,annotate_context,
2620                 x,y,p,1);
2621               x+=XTextWidth(font_info,p,1);
2622               p++;
2623               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2624                 continue;
2625             }
2626           /*
2627             Advance to the next line of text.
2628           */
2629           *p='\0';
2630           annotate_info->width=(unsigned int) XTextWidth(font_info,
2631             annotate_info->text,(int) strlen(annotate_info->text));
2632           if (annotate_info->next != (XAnnotateInfo *) NULL)
2633             {
2634               /*
2635                 Line of text already exists.
2636               */
2637               annotate_info=annotate_info->next;
2638               x=annotate_info->x;
2639               y=annotate_info->y;
2640               p=annotate_info->text;
2641               continue;
2642             }
2643           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2644             sizeof(*annotate_info->next));
2645           if (annotate_info->next == (XAnnotateInfo *) NULL)
2646             return(MagickFalse);
2647           *annotate_info->next=(*annotate_info);
2648           annotate_info->next->previous=annotate_info;
2649           annotate_info=annotate_info->next;
2650           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2651             windows->image.width/MagickMax((ssize_t)
2652             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2653           if (annotate_info->text == (char *) NULL)
2654             return(MagickFalse);
2655           annotate_info->y+=annotate_info->height;
2656           if (annotate_info->y > (int) windows->image.height)
2657             annotate_info->y=(int) annotate_info->height;
2658           annotate_info->next=(XAnnotateInfo *) NULL;
2659           x=annotate_info->x;
2660           y=annotate_info->y;
2661           p=annotate_info->text;
2662         }
2663         (void) XFree((void *) data);
2664         break;
2665       }
2666       default:
2667         break;
2668     }
2669   } while ((state & ExitState) == 0);
2670   (void) XFreeCursor(display,cursor);
2671   /*
2672     Annotation is relative to image configuration.
2673   */
2674   width=(unsigned int) image->columns;
2675   height=(unsigned int) image->rows;
2676   x=0;
2677   y=0;
2678   if (windows->image.crop_geometry != (char *) NULL)
2679     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2680   /*
2681     Initialize annotated image.
2682   */
2683   XSetCursorState(display,windows,MagickTrue);
2684   XCheckRefreshWindows(display,windows);
2685   while (annotate_info != (XAnnotateInfo *) NULL)
2686   {
2687     if (annotate_info->width == 0)
2688       {
2689         /*
2690           No text on this line--  go to the next line of text.
2691         */
2692         previous_info=annotate_info->previous;
2693         annotate_info->text=(char *)
2694           RelinquishMagickMemory(annotate_info->text);
2695         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2696         annotate_info=previous_info;
2697         continue;
2698       }
2699     /*
2700       Determine pixel index for box and pen color.
2701     */
2702     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2703     if (windows->pixel_info->colors != 0)
2704       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2705         if (windows->pixel_info->pixels[i] ==
2706             windows->pixel_info->pen_colors[box_id].pixel)
2707           {
2708             windows->pixel_info->box_index=(unsigned short) i;
2709             break;
2710           }
2711     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2712     if (windows->pixel_info->colors != 0)
2713       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2714         if (windows->pixel_info->pixels[i] ==
2715             windows->pixel_info->pen_colors[pen_id].pixel)
2716           {
2717             windows->pixel_info->pen_index=(unsigned short) i;
2718             break;
2719           }
2720     /*
2721       Define the annotate geometry string.
2722     */
2723     annotate_info->x=(int)
2724       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2725     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2726       windows->image.y)/windows->image.ximage->height;
2727     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2728       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2729       height*annotate_info->height/windows->image.ximage->height,
2730       annotate_info->x+x,annotate_info->y+y);
2731     /*
2732       Annotate image with text.
2733     */
2734     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2735       exception);
2736     if (status == 0)
2737       return(MagickFalse);
2738     /*
2739       Free up memory.
2740     */
2741     previous_info=annotate_info->previous;
2742     annotate_info->text=DestroyString(annotate_info->text);
2743     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2744     annotate_info=previous_info;
2745   }
2746   (void) XSetForeground(display,annotate_context,
2747     windows->pixel_info->foreground_color.pixel);
2748   (void) XSetBackground(display,annotate_context,
2749     windows->pixel_info->background_color.pixel);
2750   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2751   XSetCursorState(display,windows,MagickFalse);
2752   (void) XFreeFont(display,font_info);
2753   /*
2754     Update image configuration.
2755   */
2756   XConfigureImageColormap(display,resource_info,windows,image,exception);
2757   (void) XConfigureImage(display,resource_info,windows,image,exception);
2758   return(MagickTrue);
2759 }
2760 \f
2761 /*
2762 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2763 %                                                                             %
2764 %                                                                             %
2765 %                                                                             %
2766 +   X B a c k g r o u n d I m a g e                                           %
2767 %                                                                             %
2768 %                                                                             %
2769 %                                                                             %
2770 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2771 %
2772 %  XBackgroundImage() displays the image in the background of a window.
2773 %
2774 %  The format of the XBackgroundImage method is:
2775 %
2776 %      MagickBooleanType XBackgroundImage(Display *display,
2777 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2778 %        ExceptionInfo *exception)
2779 %
2780 %  A description of each parameter follows:
2781 %
2782 %    o display: Specifies a connection to an X server; returned from
2783 %      XOpenDisplay.
2784 %
2785 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2786 %
2787 %    o windows: Specifies a pointer to a XWindows structure.
2788 %
2789 %    o image: the image.
2790 %
2791 %    o exception: return any errors or warnings in this structure.
2792 %
2793 */
2794 static MagickBooleanType XBackgroundImage(Display *display,
2795   XResourceInfo *resource_info,XWindows *windows,Image **image,
2796   ExceptionInfo *exception)
2797 {
2798 #define BackgroundImageTag  "Background/Image"
2799
2800   int
2801     status;
2802
2803   static char
2804     window_id[MaxTextExtent] = "root";
2805
2806   XResourceInfo
2807     background_resources;
2808
2809   /*
2810     Put image in background.
2811   */
2812   status=XDialogWidget(display,windows,"Background",
2813     "Enter window id (id 0x00 selects window with pointer):",window_id);
2814   if (*window_id == '\0')
2815     return(MagickFalse);
2816   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2817     exception);
2818   XInfoWidget(display,windows,BackgroundImageTag);
2819   XSetCursorState(display,windows,MagickTrue);
2820   XCheckRefreshWindows(display,windows);
2821   background_resources=(*resource_info);
2822   background_resources.window_id=window_id;
2823   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2824   status=XDisplayBackgroundImage(display,&background_resources,*image,
2825     exception);
2826   if (status != MagickFalse)
2827     XClientMessage(display,windows->image.id,windows->im_protocols,
2828       windows->im_retain_colors,CurrentTime);
2829   XSetCursorState(display,windows,MagickFalse);
2830   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2831     exception);
2832   return(MagickTrue);
2833 }
2834 \f
2835 /*
2836 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2837 %                                                                             %
2838 %                                                                             %
2839 %                                                                             %
2840 +   X C h o p I m a g e                                                       %
2841 %                                                                             %
2842 %                                                                             %
2843 %                                                                             %
2844 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2845 %
2846 %  XChopImage() chops the X image.
2847 %
2848 %  The format of the XChopImage method is:
2849 %
2850 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2851 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2852 %
2853 %  A description of each parameter follows:
2854 %
2855 %    o display: Specifies a connection to an X server; returned from
2856 %      XOpenDisplay.
2857 %
2858 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2859 %
2860 %    o windows: Specifies a pointer to a XWindows structure.
2861 %
2862 %    o image: the image.
2863 %
2864 %    o exception: return any errors or warnings in this structure.
2865 %
2866 */
2867 static MagickBooleanType XChopImage(Display *display,
2868   XResourceInfo *resource_info,XWindows *windows,Image **image,
2869   ExceptionInfo *exception)
2870 {
2871   static const char
2872     *ChopMenu[] =
2873     {
2874       "Direction",
2875       "Help",
2876       "Dismiss",
2877       (char *) NULL
2878     };
2879
2880   static ModeType
2881     direction = HorizontalChopCommand;
2882
2883   static const ModeType
2884     ChopCommands[] =
2885     {
2886       ChopDirectionCommand,
2887       ChopHelpCommand,
2888       ChopDismissCommand
2889     },
2890     DirectionCommands[] =
2891     {
2892       HorizontalChopCommand,
2893       VerticalChopCommand
2894     };
2895
2896   char
2897     text[MaxTextExtent];
2898
2899   Image
2900     *chop_image;
2901
2902   int
2903     id,
2904     x,
2905     y;
2906
2907   MagickRealType
2908     scale_factor;
2909
2910   RectangleInfo
2911     chop_info;
2912
2913   unsigned int
2914     distance,
2915     height,
2916     width;
2917
2918   size_t
2919     state;
2920
2921   XEvent
2922     event;
2923
2924   XSegment
2925     segment_info;
2926
2927   /*
2928     Map Command widget.
2929   */
2930   (void) CloneString(&windows->command.name,"Chop");
2931   windows->command.data=1;
2932   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2933   (void) XMapRaised(display,windows->command.id);
2934   XClientMessage(display,windows->image.id,windows->im_protocols,
2935     windows->im_update_widget,CurrentTime);
2936   /*
2937     Track pointer until button 1 is pressed.
2938   */
2939   XQueryPosition(display,windows->image.id,&x,&y);
2940   (void) XSelectInput(display,windows->image.id,
2941     windows->image.attributes.event_mask | PointerMotionMask);
2942   state=DefaultState;
2943   do
2944   {
2945     if (windows->info.mapped != MagickFalse)
2946       {
2947         /*
2948           Display pointer position.
2949         */
2950         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2951           x+windows->image.x,y+windows->image.y);
2952         XInfoWidget(display,windows,text);
2953       }
2954     /*
2955       Wait for next event.
2956     */
2957     XScreenEvent(display,windows,&event,exception);
2958     if (event.xany.window == windows->command.id)
2959       {
2960         /*
2961           Select a command from the Command widget.
2962         */
2963         id=XCommandWidget(display,windows,ChopMenu,&event);
2964         if (id < 0)
2965           continue;
2966         switch (ChopCommands[id])
2967         {
2968           case ChopDirectionCommand:
2969           {
2970             char
2971               command[MaxTextExtent];
2972
2973             static const char
2974               *Directions[] =
2975               {
2976                 "horizontal",
2977                 "vertical",
2978                 (char *) NULL,
2979               };
2980
2981             /*
2982               Select a command from the pop-up menu.
2983             */
2984             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2985             if (id >= 0)
2986               direction=DirectionCommands[id];
2987             break;
2988           }
2989           case ChopHelpCommand:
2990           {
2991             XTextViewWidget(display,resource_info,windows,MagickFalse,
2992               "Help Viewer - Image Chop",ImageChopHelp);
2993             break;
2994           }
2995           case ChopDismissCommand:
2996           {
2997             /*
2998               Prematurely exit.
2999             */
3000             state|=EscapeState;
3001             state|=ExitState;
3002             break;
3003           }
3004           default:
3005             break;
3006         }
3007         continue;
3008       }
3009     switch (event.type)
3010     {
3011       case ButtonPress:
3012       {
3013         if (event.xbutton.button != Button1)
3014           break;
3015         if (event.xbutton.window != windows->image.id)
3016           break;
3017         /*
3018           User has committed to start point of chopping line.
3019         */
3020         segment_info.x1=(short int) event.xbutton.x;
3021         segment_info.x2=(short int) event.xbutton.x;
3022         segment_info.y1=(short int) event.xbutton.y;
3023         segment_info.y2=(short int) event.xbutton.y;
3024         state|=ExitState;
3025         break;
3026       }
3027       case ButtonRelease:
3028         break;
3029       case Expose:
3030         break;
3031       case KeyPress:
3032       {
3033         char
3034           command[MaxTextExtent];
3035
3036         KeySym
3037           key_symbol;
3038
3039         if (event.xkey.window != windows->image.id)
3040           break;
3041         /*
3042           Respond to a user key press.
3043         */
3044         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3045           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3046         switch ((int) key_symbol)
3047         {
3048           case XK_Escape:
3049           case XK_F20:
3050           {
3051             /*
3052               Prematurely exit.
3053             */
3054             state|=EscapeState;
3055             state|=ExitState;
3056             break;
3057           }
3058           case XK_F1:
3059           case XK_Help:
3060           {
3061             (void) XSetFunction(display,windows->image.highlight_context,
3062               GXcopy);
3063             XTextViewWidget(display,resource_info,windows,MagickFalse,
3064               "Help Viewer - Image Chop",ImageChopHelp);
3065             (void) XSetFunction(display,windows->image.highlight_context,
3066               GXinvert);
3067             break;
3068           }
3069           default:
3070           {
3071             (void) XBell(display,0);
3072             break;
3073           }
3074         }
3075         break;
3076       }
3077       case MotionNotify:
3078       {
3079         /*
3080           Map and unmap Info widget as text cursor crosses its boundaries.
3081         */
3082         x=event.xmotion.x;
3083         y=event.xmotion.y;
3084         if (windows->info.mapped != MagickFalse)
3085           {
3086             if ((x < (int) (windows->info.x+windows->info.width)) &&
3087                 (y < (int) (windows->info.y+windows->info.height)))
3088               (void) XWithdrawWindow(display,windows->info.id,
3089                 windows->info.screen);
3090           }
3091         else
3092           if ((x > (int) (windows->info.x+windows->info.width)) ||
3093               (y > (int) (windows->info.y+windows->info.height)))
3094             (void) XMapWindow(display,windows->info.id);
3095       }
3096     }
3097   } while ((state & ExitState) == 0);
3098   (void) XSelectInput(display,windows->image.id,
3099     windows->image.attributes.event_mask);
3100   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3101   if ((state & EscapeState) != 0)
3102     return(MagickTrue);
3103   /*
3104     Draw line as pointer moves until the mouse button is released.
3105   */
3106   chop_info.width=0;
3107   chop_info.height=0;
3108   chop_info.x=0;
3109   chop_info.y=0;
3110   distance=0;
3111   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3112   state=DefaultState;
3113   do
3114   {
3115     if (distance > 9)
3116       {
3117         /*
3118           Display info and draw chopping line.
3119         */
3120         if (windows->info.mapped == MagickFalse)
3121           (void) XMapWindow(display,windows->info.id);
3122         (void) FormatLocaleString(text,MaxTextExtent,
3123           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3124           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3125         XInfoWidget(display,windows,text);
3126         XHighlightLine(display,windows->image.id,
3127           windows->image.highlight_context,&segment_info);
3128       }
3129     else
3130       if (windows->info.mapped != MagickFalse)
3131         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3132     /*
3133       Wait for next event.
3134     */
3135     XScreenEvent(display,windows,&event,exception);
3136     if (distance > 9)
3137       XHighlightLine(display,windows->image.id,
3138         windows->image.highlight_context,&segment_info);
3139     switch (event.type)
3140     {
3141       case ButtonPress:
3142       {
3143         segment_info.x2=(short int) event.xmotion.x;
3144         segment_info.y2=(short int) event.xmotion.y;
3145         break;
3146       }
3147       case ButtonRelease:
3148       {
3149         /*
3150           User has committed to chopping line.
3151         */
3152         segment_info.x2=(short int) event.xbutton.x;
3153         segment_info.y2=(short int) event.xbutton.y;
3154         state|=ExitState;
3155         break;
3156       }
3157       case Expose:
3158         break;
3159       case MotionNotify:
3160       {
3161         segment_info.x2=(short int) event.xmotion.x;
3162         segment_info.y2=(short int) event.xmotion.y;
3163       }
3164       default:
3165         break;
3166     }
3167     /*
3168       Check boundary conditions.
3169     */
3170     if (segment_info.x2 < 0)
3171       segment_info.x2=0;
3172     else
3173       if (segment_info.x2 > windows->image.ximage->width)
3174         segment_info.x2=windows->image.ximage->width;
3175     if (segment_info.y2 < 0)
3176       segment_info.y2=0;
3177     else
3178       if (segment_info.y2 > windows->image.ximage->height)
3179         segment_info.y2=windows->image.ximage->height;
3180     distance=(unsigned int)
3181       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3182        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3183     /*
3184       Compute chopping geometry.
3185     */
3186     if (direction == HorizontalChopCommand)
3187       {
3188         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3189         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3190         chop_info.height=0;
3191         chop_info.y=0;
3192         if (segment_info.x1 > (int) segment_info.x2)
3193           {
3194             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3195             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3196           }
3197       }
3198     else
3199       {
3200         chop_info.width=0;
3201         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3202         chop_info.x=0;
3203         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3204         if (segment_info.y1 > segment_info.y2)
3205           {
3206             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3207             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3208           }
3209       }
3210   } while ((state & ExitState) == 0);
3211   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3212   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3213   if (distance <= 9)
3214     return(MagickTrue);
3215   /*
3216     Image chopping is relative to image configuration.
3217   */
3218   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3219     exception);
3220   XSetCursorState(display,windows,MagickTrue);
3221   XCheckRefreshWindows(display,windows);
3222   windows->image.window_changes.width=windows->image.ximage->width-
3223     (unsigned int) chop_info.width;
3224   windows->image.window_changes.height=windows->image.ximage->height-
3225     (unsigned int) chop_info.height;
3226   width=(unsigned int) (*image)->columns;
3227   height=(unsigned int) (*image)->rows;
3228   x=0;
3229   y=0;
3230   if (windows->image.crop_geometry != (char *) NULL)
3231     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3232   scale_factor=(MagickRealType) width/windows->image.ximage->width;
3233   chop_info.x+=x;
3234   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3235   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3236   scale_factor=(MagickRealType) height/windows->image.ximage->height;
3237   chop_info.y+=y;
3238   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3239   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3240   /*
3241     Chop image.
3242   */
3243   chop_image=ChopImage(*image,&chop_info,exception);
3244   XSetCursorState(display,windows,MagickFalse);
3245   if (chop_image == (Image *) NULL)
3246     return(MagickFalse);
3247   *image=DestroyImage(*image);
3248   *image=chop_image;
3249   /*
3250     Update image configuration.
3251   */
3252   XConfigureImageColormap(display,resource_info,windows,*image,exception);
3253   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3254   return(MagickTrue);
3255 }
3256 \f
3257 /*
3258 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3259 %                                                                             %
3260 %                                                                             %
3261 %                                                                             %
3262 +   X C o l o r E d i t I m a g e                                             %
3263 %                                                                             %
3264 %                                                                             %
3265 %                                                                             %
3266 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3267 %
3268 %  XColorEditImage() allows the user to interactively change the color of one
3269 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3270 %
3271 %  The format of the XColorEditImage method is:
3272 %
3273 %      MagickBooleanType XColorEditImage(Display *display,
3274 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3275 %          ExceptionInfo *exception)
3276 %
3277 %  A description of each parameter follows:
3278 %
3279 %    o display: Specifies a connection to an X server;  returned from
3280 %      XOpenDisplay.
3281 %
3282 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3283 %
3284 %    o windows: Specifies a pointer to a XWindows structure.
3285 %
3286 %    o image: the image; returned from ReadImage.
3287 %
3288 %    o exception: return any errors or warnings in this structure.
3289 %
3290 */
3291 static MagickBooleanType XColorEditImage(Display *display,
3292   XResourceInfo *resource_info,XWindows *windows,Image **image,
3293   ExceptionInfo *exception)
3294 {
3295   static const char
3296     *ColorEditMenu[] =
3297     {
3298       "Method",
3299       "Pixel Color",
3300       "Border Color",
3301       "Fuzz",
3302       "Undo",
3303       "Help",
3304       "Dismiss",
3305       (char *) NULL
3306     };
3307
3308   static const ModeType
3309     ColorEditCommands[] =
3310     {
3311       ColorEditMethodCommand,
3312       ColorEditColorCommand,
3313       ColorEditBorderCommand,
3314       ColorEditFuzzCommand,
3315       ColorEditUndoCommand,
3316       ColorEditHelpCommand,
3317       ColorEditDismissCommand
3318     };
3319
3320   static PaintMethod
3321     method = PointMethod;
3322
3323   static unsigned int
3324     pen_id = 0;
3325
3326   static XColor
3327     border_color = { 0, 0, 0, 0, 0, 0 };
3328
3329   char
3330     command[MaxTextExtent],
3331     text[MaxTextExtent];
3332
3333   Cursor
3334     cursor;
3335
3336   int
3337     entry,
3338     id,
3339     x,
3340     x_offset,
3341     y,
3342     y_offset;
3343
3344   register Quantum
3345     *q;
3346
3347   register ssize_t
3348     i;
3349
3350   unsigned int
3351     height,
3352     width;
3353
3354   size_t
3355     state;
3356
3357   XColor
3358     color;
3359
3360   XEvent
3361     event;
3362
3363   /*
3364     Map Command widget.
3365   */
3366   (void) CloneString(&windows->command.name,"Color Edit");
3367   windows->command.data=4;
3368   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3369   (void) XMapRaised(display,windows->command.id);
3370   XClientMessage(display,windows->image.id,windows->im_protocols,
3371     windows->im_update_widget,CurrentTime);
3372   /*
3373     Make cursor.
3374   */
3375   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3376     resource_info->background_color,resource_info->foreground_color);
3377   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3378   /*
3379     Track pointer until button 1 is pressed.
3380   */
3381   XQueryPosition(display,windows->image.id,&x,&y);
3382   (void) XSelectInput(display,windows->image.id,
3383     windows->image.attributes.event_mask | PointerMotionMask);
3384   state=DefaultState;
3385   do
3386   {
3387     if (windows->info.mapped != MagickFalse)
3388       {
3389         /*
3390           Display pointer position.
3391         */
3392         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3393           x+windows->image.x,y+windows->image.y);
3394         XInfoWidget(display,windows,text);
3395       }
3396     /*
3397       Wait for next event.
3398     */
3399     XScreenEvent(display,windows,&event,exception);
3400     if (event.xany.window == windows->command.id)
3401       {
3402         /*
3403           Select a command from the Command widget.
3404         */
3405         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3406         if (id < 0)
3407           {
3408             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3409             continue;
3410           }
3411         switch (ColorEditCommands[id])
3412         {
3413           case ColorEditMethodCommand:
3414           {
3415             char
3416               **methods;
3417
3418             /*
3419               Select a method from the pop-up menu.
3420             */
3421             methods=(char **) GetCommandOptions(MagickMethodOptions);
3422             if (methods == (char **) NULL)
3423               break;
3424             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3425               (const char **) methods,command);
3426             if (entry >= 0)
3427               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3428                 MagickFalse,methods[entry]);
3429             methods=DestroyStringList(methods);
3430             break;
3431           }
3432           case ColorEditColorCommand:
3433           {
3434             const char
3435               *ColorMenu[MaxNumberPens];
3436
3437             int
3438               pen_number;
3439
3440             /*
3441               Initialize menu selections.
3442             */
3443             for (i=0; i < (int) (MaxNumberPens-2); i++)
3444               ColorMenu[i]=resource_info->pen_colors[i];
3445             ColorMenu[MaxNumberPens-2]="Browser...";
3446             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3447             /*
3448               Select a pen color from the pop-up menu.
3449             */
3450             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3451               (const char **) ColorMenu,command);
3452             if (pen_number < 0)
3453               break;
3454             if (pen_number == (MaxNumberPens-2))
3455               {
3456                 static char
3457                   color_name[MaxTextExtent] = "gray";
3458
3459                 /*
3460                   Select a pen color from a dialog.
3461                 */
3462                 resource_info->pen_colors[pen_number]=color_name;
3463                 XColorBrowserWidget(display,windows,"Select",color_name);
3464                 if (*color_name == '\0')
3465                   break;
3466               }
3467             /*
3468               Set pen color.
3469             */
3470             (void) XParseColor(display,windows->map_info->colormap,
3471               resource_info->pen_colors[pen_number],&color);
3472             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3473               (unsigned int) MaxColors,&color);
3474             windows->pixel_info->pen_colors[pen_number]=color;
3475             pen_id=(unsigned int) pen_number;
3476             break;
3477           }
3478           case ColorEditBorderCommand:
3479           {
3480             const char
3481               *ColorMenu[MaxNumberPens];
3482
3483             int
3484               pen_number;
3485
3486             /*
3487               Initialize menu selections.
3488             */
3489             for (i=0; i < (int) (MaxNumberPens-2); i++)
3490               ColorMenu[i]=resource_info->pen_colors[i];
3491             ColorMenu[MaxNumberPens-2]="Browser...";
3492             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3493             /*
3494               Select a pen color from the pop-up menu.
3495             */
3496             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3497               (const char **) ColorMenu,command);
3498             if (pen_number < 0)
3499               break;
3500             if (pen_number == (MaxNumberPens-2))
3501               {
3502                 static char
3503                   color_name[MaxTextExtent] = "gray";
3504
3505                 /*
3506                   Select a pen color from a dialog.
3507                 */
3508                 resource_info->pen_colors[pen_number]=color_name;
3509                 XColorBrowserWidget(display,windows,"Select",color_name);
3510                 if (*color_name == '\0')
3511                   break;
3512               }
3513             /*
3514               Set border color.
3515             */
3516             (void) XParseColor(display,windows->map_info->colormap,
3517               resource_info->pen_colors[pen_number],&border_color);
3518             break;
3519           }
3520           case ColorEditFuzzCommand:
3521           {
3522             static char
3523               fuzz[MaxTextExtent];
3524
3525             static const char
3526               *FuzzMenu[] =
3527               {
3528                 "0%",
3529                 "2%",
3530                 "5%",
3531                 "10%",
3532                 "15%",
3533                 "Dialog...",
3534                 (char *) NULL,
3535               };
3536
3537             /*
3538               Select a command from the pop-up menu.
3539             */
3540             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3541               command);
3542             if (entry < 0)
3543               break;
3544             if (entry != 5)
3545               {
3546                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],(double)
3547                   QuantumRange+1.0);
3548                 break;
3549               }
3550             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3551             (void) XDialogWidget(display,windows,"Ok",
3552               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3553             if (*fuzz == '\0')
3554               break;
3555             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3556             (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
3557             break;
3558           }
3559           case ColorEditUndoCommand:
3560           {
3561             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3562               image,exception);
3563             break;
3564           }
3565           case ColorEditHelpCommand:
3566           default:
3567           {
3568             XTextViewWidget(display,resource_info,windows,MagickFalse,
3569               "Help Viewer - Image Annotation",ImageColorEditHelp);
3570             break;
3571           }
3572           case ColorEditDismissCommand:
3573           {
3574             /*
3575               Prematurely exit.
3576             */
3577             state|=EscapeState;
3578             state|=ExitState;
3579             break;
3580           }
3581         }
3582         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3583         continue;
3584       }
3585     switch (event.type)
3586     {
3587       case ButtonPress:
3588       {
3589         if (event.xbutton.button != Button1)
3590           break;
3591         if ((event.xbutton.window != windows->image.id) &&
3592             (event.xbutton.window != windows->magnify.id))
3593           break;
3594         /*
3595           exit loop.
3596         */
3597         x=event.xbutton.x;
3598         y=event.xbutton.y;
3599         (void) XMagickCommand(display,resource_info,windows,
3600           SaveToUndoBufferCommand,image,exception);
3601         state|=UpdateConfigurationState;
3602         break;
3603       }
3604       case ButtonRelease:
3605       {
3606         if (event.xbutton.button != Button1)
3607           break;
3608         if ((event.xbutton.window != windows->image.id) &&
3609             (event.xbutton.window != windows->magnify.id))
3610           break;
3611         /*
3612           Update colormap information.
3613         */
3614         x=event.xbutton.x;
3615         y=event.xbutton.y;
3616         XConfigureImageColormap(display,resource_info,windows,*image,exception);
3617         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3618         XInfoWidget(display,windows,text);
3619         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3620         state&=(~UpdateConfigurationState);
3621         break;
3622       }
3623       case Expose:
3624         break;
3625       case KeyPress:
3626       {
3627         KeySym
3628           key_symbol;
3629
3630         if (event.xkey.window == windows->magnify.id)
3631           {
3632             Window
3633               window;
3634
3635             window=windows->magnify.id;
3636             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3637           }
3638         if (event.xkey.window != windows->image.id)
3639           break;
3640         /*
3641           Respond to a user key press.
3642         */
3643         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3644           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3645         switch ((int) key_symbol)
3646         {
3647           case XK_Escape:
3648           case XK_F20:
3649           {
3650             /*
3651               Prematurely exit.
3652             */
3653             state|=ExitState;
3654             break;
3655           }
3656           case XK_F1:
3657           case XK_Help:
3658           {
3659             XTextViewWidget(display,resource_info,windows,MagickFalse,
3660               "Help Viewer - Image Annotation",ImageColorEditHelp);
3661             break;
3662           }
3663           default:
3664           {
3665             (void) XBell(display,0);
3666             break;
3667           }
3668         }
3669         break;
3670       }
3671       case MotionNotify:
3672       {
3673         /*
3674           Map and unmap Info widget as cursor crosses its boundaries.
3675         */
3676         x=event.xmotion.x;
3677         y=event.xmotion.y;
3678         if (windows->info.mapped != MagickFalse)
3679           {
3680             if ((x < (int) (windows->info.x+windows->info.width)) &&
3681                 (y < (int) (windows->info.y+windows->info.height)))
3682               (void) XWithdrawWindow(display,windows->info.id,
3683                 windows->info.screen);
3684           }
3685         else
3686           if ((x > (int) (windows->info.x+windows->info.width)) ||
3687               (y > (int) (windows->info.y+windows->info.height)))
3688             (void) XMapWindow(display,windows->info.id);
3689         break;
3690       }
3691       default:
3692         break;
3693     }
3694     if (event.xany.window == windows->magnify.id)
3695       {
3696         x=windows->magnify.x-windows->image.x;
3697         y=windows->magnify.y-windows->image.y;
3698       }
3699     x_offset=x;
3700     y_offset=y;
3701     if ((state & UpdateConfigurationState) != 0)
3702       {
3703         CacheView
3704           *image_view;
3705
3706         int
3707           x,
3708           y;
3709
3710         /*
3711           Pixel edit is relative to image configuration.
3712         */
3713         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3714           MagickTrue);
3715         color=windows->pixel_info->pen_colors[pen_id];
3716         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3717         width=(unsigned int) (*image)->columns;
3718         height=(unsigned int) (*image)->rows;
3719         x=0;
3720         y=0;
3721         if (windows->image.crop_geometry != (char *) NULL)
3722           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3723             &width,&height);
3724         x_offset=(int)
3725           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3726         y_offset=(int)
3727           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3728         if ((x_offset < 0) || (y_offset < 0))
3729           continue;
3730         if ((x_offset >= (int) (*image)->columns) ||
3731             (y_offset >= (int) (*image)->rows))
3732           continue;
3733         image_view=AcquireCacheView(*image);
3734         switch (method)
3735         {
3736           case PointMethod:
3737           default:
3738           {
3739             /*
3740               Update color information using point algorithm.
3741             */
3742             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3743               return(MagickFalse);
3744             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3745               (ssize_t) y_offset,1,1,exception);
3746             if (q == (Quantum *) NULL)
3747               break;
3748             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3749             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3750             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3751             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3752             break;
3753           }
3754           case ReplaceMethod:
3755           {
3756             PixelInfo
3757               pixel,
3758               target;
3759
3760             Quantum
3761               virtual_pixel[CompositePixelChannel];
3762
3763             /*
3764               Update color information using replace algorithm.
3765             */
3766             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3767               (ssize_t) y_offset,virtual_pixel,exception);
3768             target.red=virtual_pixel[RedPixelChannel];
3769             target.green=virtual_pixel[GreenPixelChannel];
3770             target.blue=virtual_pixel[BluePixelChannel];
3771             target.alpha=virtual_pixel[AlphaPixelChannel];
3772             if ((*image)->storage_class == DirectClass)
3773               {
3774                 for (y=0; y < (int) (*image)->rows; y++)
3775                 {
3776                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3777                     (*image)->columns,1,exception);
3778                   if (q == (Quantum *) NULL)
3779                     break;
3780                   for (x=0; x < (int) (*image)->columns; x++)
3781                   {
3782                     GetPixelInfoPixel(*image,q,&pixel);
3783                     if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3784                       {
3785                         SetPixelRed(*image,ScaleShortToQuantum(
3786                           color.red),q);
3787                         SetPixelGreen(*image,ScaleShortToQuantum(
3788                           color.green),q);
3789                         SetPixelBlue(*image,ScaleShortToQuantum(
3790                           color.blue),q);
3791                       }
3792                     q+=GetPixelChannels(*image);
3793                   }
3794                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3795                     break;
3796                 }
3797               }
3798             else
3799               {
3800                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3801                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3802                     {
3803                       (*image)->colormap[i].red=ScaleShortToQuantum(
3804                         color.red);
3805                       (*image)->colormap[i].green=ScaleShortToQuantum(
3806                         color.green);
3807                       (*image)->colormap[i].blue=ScaleShortToQuantum(
3808                         color.blue);
3809                     }
3810                 (void) SyncImage(*image,exception);
3811               }
3812             break;
3813           }
3814           case FloodfillMethod:
3815           case FillToBorderMethod:
3816           {
3817             DrawInfo
3818               *draw_info;
3819
3820             PixelInfo
3821               target;
3822
3823             /*
3824               Update color information using floodfill algorithm.
3825             */
3826             (void) GetOneVirtualMagickPixel(*image,
3827               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3828               y_offset,&target,exception);
3829             if (method == FillToBorderMethod)
3830               {
3831                 target.red=(MagickRealType)
3832                   ScaleShortToQuantum(border_color.red);
3833                 target.green=(MagickRealType)
3834                   ScaleShortToQuantum(border_color.green);
3835                 target.blue=(MagickRealType)
3836                   ScaleShortToQuantum(border_color.blue);
3837               }
3838             draw_info=CloneDrawInfo(resource_info->image_info,
3839               (DrawInfo *) NULL);
3840             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3841               AllCompliance,&draw_info->fill,exception);
3842             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3843               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3844               MagickFalse : MagickTrue,exception);
3845             draw_info=DestroyDrawInfo(draw_info);
3846             break;
3847           }
3848           case ResetMethod:
3849           {
3850             /*
3851               Update color information using reset algorithm.
3852             */
3853             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3854               return(MagickFalse);
3855             for (y=0; y < (int) (*image)->rows; y++)
3856             {
3857               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3858                 (*image)->columns,1,exception);
3859               if (q == (Quantum *) NULL)
3860                 break;
3861               for (x=0; x < (int) (*image)->columns; x++)
3862               {
3863                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3864                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3865                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3866                 q+=GetPixelChannels(*image);
3867               }
3868               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3869                 break;
3870             }
3871             break;
3872           }
3873         }
3874         image_view=DestroyCacheView(image_view);
3875         state&=(~UpdateConfigurationState);
3876       }
3877   } while ((state & ExitState) == 0);
3878   (void) XSelectInput(display,windows->image.id,
3879     windows->image.attributes.event_mask);
3880   XSetCursorState(display,windows,MagickFalse);
3881   (void) XFreeCursor(display,cursor);
3882   return(MagickTrue);
3883 }
3884 \f
3885 /*
3886 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3887 %                                                                             %
3888 %                                                                             %
3889 %                                                                             %
3890 +   X C o m p o s i t e I m a g e                                             %
3891 %                                                                             %
3892 %                                                                             %
3893 %                                                                             %
3894 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3895 %
3896 %  XCompositeImage() requests an image name from the user, reads the image and
3897 %  composites it with the X window image at a location the user chooses with
3898 %  the pointer.
3899 %
3900 %  The format of the XCompositeImage method is:
3901 %
3902 %      MagickBooleanType XCompositeImage(Display *display,
3903 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3904 %        ExceptionInfo *exception)
3905 %
3906 %  A description of each parameter follows:
3907 %
3908 %    o display: Specifies a connection to an X server;  returned from
3909 %      XOpenDisplay.
3910 %
3911 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3912 %
3913 %    o windows: Specifies a pointer to a XWindows structure.
3914 %
3915 %    o image: the image; returned from ReadImage.
3916 %
3917 %    o exception: return any errors or warnings in this structure.
3918 %
3919 */
3920 static MagickBooleanType XCompositeImage(Display *display,
3921   XResourceInfo *resource_info,XWindows *windows,Image *image,
3922   ExceptionInfo *exception)
3923 {
3924   static char
3925     displacement_geometry[MaxTextExtent] = "30x30",
3926     filename[MaxTextExtent] = "\0";
3927
3928   static const char
3929     *CompositeMenu[] =
3930     {
3931       "Operators",
3932       "Dissolve",
3933       "Displace",
3934       "Help",
3935       "Dismiss",
3936       (char *) NULL
3937     };
3938
3939   static CompositeOperator
3940     compose = CopyCompositeOp;
3941
3942   static const ModeType
3943     CompositeCommands[] =
3944     {
3945       CompositeOperatorsCommand,
3946       CompositeDissolveCommand,
3947       CompositeDisplaceCommand,
3948       CompositeHelpCommand,
3949       CompositeDismissCommand
3950     };
3951
3952   char
3953     text[MaxTextExtent];
3954
3955   Cursor
3956     cursor;
3957
3958   Image
3959     *composite_image;
3960
3961   int
3962     entry,
3963     id,
3964     x,
3965     y;
3966
3967   MagickRealType
3968     blend,
3969     scale_factor;
3970
3971   RectangleInfo
3972     highlight_info,
3973     composite_info;
3974
3975   unsigned int
3976     height,
3977     width;
3978
3979   size_t
3980     state;
3981
3982   XEvent
3983     event;
3984
3985   /*
3986     Request image file name from user.
3987   */
3988   XFileBrowserWidget(display,windows,"Composite",filename);
3989   if (*filename == '\0')
3990     return(MagickTrue);
3991   /*
3992     Read image.
3993   */
3994   XSetCursorState(display,windows,MagickTrue);
3995   XCheckRefreshWindows(display,windows);
3996   (void) CopyMagickString(resource_info->image_info->filename,filename,
3997     MaxTextExtent);
3998   composite_image=ReadImage(resource_info->image_info,exception);
3999   CatchException(exception);
4000   XSetCursorState(display,windows,MagickFalse);
4001   if (composite_image == (Image *) NULL)
4002     return(MagickFalse);
4003   /*
4004     Map Command widget.
4005   */
4006   (void) CloneString(&windows->command.name,"Composite");
4007   windows->command.data=1;
4008   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4009   (void) XMapRaised(display,windows->command.id);
4010   XClientMessage(display,windows->image.id,windows->im_protocols,
4011     windows->im_update_widget,CurrentTime);
4012   /*
4013     Track pointer until button 1 is pressed.
4014   */
4015   XQueryPosition(display,windows->image.id,&x,&y);
4016   (void) XSelectInput(display,windows->image.id,
4017     windows->image.attributes.event_mask | PointerMotionMask);
4018   composite_info.x=(ssize_t) windows->image.x+x;
4019   composite_info.y=(ssize_t) windows->image.y+y;
4020   composite_info.width=0;
4021   composite_info.height=0;
4022   cursor=XCreateFontCursor(display,XC_ul_angle);
4023   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4024   blend=0.0;
4025   state=DefaultState;
4026   do
4027   {
4028     if (windows->info.mapped != MagickFalse)
4029       {
4030         /*
4031           Display pointer position.
4032         */
4033         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4034           (long) composite_info.x,(long) composite_info.y);
4035         XInfoWidget(display,windows,text);
4036       }
4037     highlight_info=composite_info;
4038     highlight_info.x=composite_info.x-windows->image.x;
4039     highlight_info.y=composite_info.y-windows->image.y;
4040     XHighlightRectangle(display,windows->image.id,
4041       windows->image.highlight_context,&highlight_info);
4042     /*
4043       Wait for next event.
4044     */
4045     XScreenEvent(display,windows,&event,exception);
4046     XHighlightRectangle(display,windows->image.id,
4047       windows->image.highlight_context,&highlight_info);
4048     if (event.xany.window == windows->command.id)
4049       {
4050         /*
4051           Select a command from the Command widget.
4052         */
4053         id=XCommandWidget(display,windows,CompositeMenu,&event);
4054         if (id < 0)
4055           continue;
4056         switch (CompositeCommands[id])
4057         {
4058           case CompositeOperatorsCommand:
4059           {
4060             char
4061               command[MaxTextExtent],
4062               **operators;
4063
4064             /*
4065               Select a command from the pop-up menu.
4066             */
4067             operators=GetCommandOptions(MagickComposeOptions);
4068             if (operators == (char **) NULL)
4069               break;
4070             entry=XMenuWidget(display,windows,CompositeMenu[id],
4071               (const char **) operators,command);
4072             if (entry >= 0)
4073               compose=(CompositeOperator) ParseCommandOption(
4074                 MagickComposeOptions,MagickFalse,operators[entry]);
4075             operators=DestroyStringList(operators);
4076             break;
4077           }
4078           case CompositeDissolveCommand:
4079           {
4080             static char
4081               factor[MaxTextExtent] = "20.0";
4082
4083             /*
4084               Dissolve the two images a given percent.
4085             */
4086             (void) XSetFunction(display,windows->image.highlight_context,
4087               GXcopy);
4088             (void) XDialogWidget(display,windows,"Dissolve",
4089               "Enter the blend factor (0.0 - 99.9%):",factor);
4090             (void) XSetFunction(display,windows->image.highlight_context,
4091               GXinvert);
4092             if (*factor == '\0')
4093               break;
4094             blend=InterpretLocaleValue(factor,(char **) NULL);
4095             compose=DissolveCompositeOp;
4096             break;
4097           }
4098           case CompositeDisplaceCommand:
4099           {
4100             /*
4101               Get horizontal and vertical scale displacement geometry.
4102             */
4103             (void) XSetFunction(display,windows->image.highlight_context,
4104               GXcopy);
4105             (void) XDialogWidget(display,windows,"Displace",
4106               "Enter the horizontal and vertical scale:",displacement_geometry);
4107             (void) XSetFunction(display,windows->image.highlight_context,
4108               GXinvert);
4109             if (*displacement_geometry == '\0')
4110               break;
4111             compose=DisplaceCompositeOp;
4112             break;
4113           }
4114           case CompositeHelpCommand:
4115           {
4116             (void) XSetFunction(display,windows->image.highlight_context,
4117               GXcopy);
4118             XTextViewWidget(display,resource_info,windows,MagickFalse,
4119               "Help Viewer - Image Composite",ImageCompositeHelp);
4120             (void) XSetFunction(display,windows->image.highlight_context,
4121               GXinvert);
4122             break;
4123           }
4124           case CompositeDismissCommand:
4125           {
4126             /*
4127               Prematurely exit.
4128             */
4129             state|=EscapeState;
4130             state|=ExitState;
4131             break;
4132           }
4133           default:
4134             break;
4135         }
4136         continue;
4137       }
4138     switch (event.type)
4139     {
4140       case ButtonPress:
4141       {
4142         if (image->debug != MagickFalse)
4143           (void) LogMagickEvent(X11Event,GetMagickModule(),
4144             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4145             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4146         if (event.xbutton.button != Button1)
4147           break;
4148         if (event.xbutton.window != windows->image.id)
4149           break;
4150         /*
4151           Change cursor.
4152         */
4153         composite_info.width=composite_image->columns;
4154         composite_info.height=composite_image->rows;
4155         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4156         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4157         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4158         break;
4159       }
4160       case ButtonRelease:
4161       {
4162         if (image->debug != MagickFalse)
4163           (void) LogMagickEvent(X11Event,GetMagickModule(),
4164             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4165             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4166         if (event.xbutton.button != Button1)
4167           break;
4168         if (event.xbutton.window != windows->image.id)
4169           break;
4170         if ((composite_info.width != 0) && (composite_info.height != 0))
4171           {
4172             /*
4173               User has selected the location of the composite image.
4174             */
4175             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4176             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4177             state|=ExitState;
4178           }
4179         break;
4180       }
4181       case Expose:
4182         break;
4183       case KeyPress:
4184       {
4185         char
4186           command[MaxTextExtent];
4187
4188         KeySym
4189           key_symbol;
4190
4191         int
4192           length;
4193
4194         if (event.xkey.window != windows->image.id)
4195           break;
4196         /*
4197           Respond to a user key press.
4198         */
4199         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4200           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4201         *(command+length)='\0';
4202         if (image->debug != MagickFalse)
4203           (void) LogMagickEvent(X11Event,GetMagickModule(),
4204             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4205         switch ((int) key_symbol)
4206         {
4207           case XK_Escape:
4208           case XK_F20:
4209           {
4210             /*
4211               Prematurely exit.
4212             */
4213             composite_image=DestroyImage(composite_image);
4214             state|=EscapeState;
4215             state|=ExitState;
4216             break;
4217           }
4218           case XK_F1:
4219           case XK_Help:
4220           {
4221             (void) XSetFunction(display,windows->image.highlight_context,
4222               GXcopy);
4223             XTextViewWidget(display,resource_info,windows,MagickFalse,
4224               "Help Viewer - Image Composite",ImageCompositeHelp);
4225             (void) XSetFunction(display,windows->image.highlight_context,
4226               GXinvert);
4227             break;
4228           }
4229           default:
4230           {
4231             (void) XBell(display,0);
4232             break;
4233           }
4234         }
4235         break;
4236       }
4237       case MotionNotify:
4238       {
4239         /*
4240           Map and unmap Info widget as text cursor crosses its boundaries.
4241         */
4242         x=event.xmotion.x;
4243         y=event.xmotion.y;
4244         if (windows->info.mapped != MagickFalse)
4245           {
4246             if ((x < (int) (windows->info.x+windows->info.width)) &&
4247                 (y < (int) (windows->info.y+windows->info.height)))
4248               (void) XWithdrawWindow(display,windows->info.id,
4249                 windows->info.screen);
4250           }
4251         else
4252           if ((x > (int) (windows->info.x+windows->info.width)) ||
4253               (y > (int) (windows->info.y+windows->info.height)))
4254             (void) XMapWindow(display,windows->info.id);
4255         composite_info.x=(ssize_t) windows->image.x+x;
4256         composite_info.y=(ssize_t) windows->image.y+y;
4257         break;
4258       }
4259       default:
4260       {
4261         if (image->debug != MagickFalse)
4262           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4263             event.type);
4264         break;
4265       }
4266     }
4267   } while ((state & ExitState) == 0);
4268   (void) XSelectInput(display,windows->image.id,
4269     windows->image.attributes.event_mask);
4270   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4271   XSetCursorState(display,windows,MagickFalse);
4272   (void) XFreeCursor(display,cursor);
4273   if ((state & EscapeState) != 0)
4274     return(MagickTrue);
4275   /*
4276     Image compositing is relative to image configuration.
4277   */
4278   XSetCursorState(display,windows,MagickTrue);
4279   XCheckRefreshWindows(display,windows);
4280   width=(unsigned int) image->columns;
4281   height=(unsigned int) image->rows;
4282   x=0;
4283   y=0;
4284   if (windows->image.crop_geometry != (char *) NULL)
4285     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4286   scale_factor=(MagickRealType) width/windows->image.ximage->width;
4287   composite_info.x+=x;
4288   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4289   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4290   scale_factor=(MagickRealType) height/windows->image.ximage->height;
4291   composite_info.y+=y;
4292   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4293   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4294   if ((composite_info.width != composite_image->columns) ||
4295       (composite_info.height != composite_image->rows))
4296     {
4297       Image
4298         *resize_image;
4299
4300       /*
4301         Scale composite image.
4302       */
4303       resize_image=ResizeImage(composite_image,composite_info.width,
4304         composite_info.height,composite_image->filter,composite_image->blur,
4305         exception);
4306       composite_image=DestroyImage(composite_image);
4307       if (resize_image == (Image *) NULL)
4308         {
4309           XSetCursorState(display,windows,MagickFalse);
4310           return(MagickFalse);
4311         }
4312       composite_image=resize_image;
4313     }
4314   if (compose == DisplaceCompositeOp)
4315     (void) SetImageArtifact(composite_image,"compose:args",
4316       displacement_geometry);
4317   if (blend != 0.0)
4318     {
4319       CacheView
4320         *image_view;
4321
4322       int
4323         y;
4324
4325       Quantum
4326         opacity;
4327
4328       register int
4329         x;
4330
4331       register Quantum
4332         *q;
4333
4334       /*
4335         Create mattes for blending.
4336       */
4337       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4338       opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4339         ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4340       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4341         return(MagickFalse);
4342       image->matte=MagickTrue;
4343       image_view=AcquireCacheView(image);
4344       for (y=0; y < (int) image->rows; y++)
4345       {
4346         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4347           exception);
4348         if (q == (Quantum *) NULL)
4349           break;
4350         for (x=0; x < (int) image->columns; x++)
4351         {
4352           SetPixelAlpha(image,opacity,q);
4353           q+=GetPixelChannels(image);
4354         }
4355         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4356           break;
4357       }
4358       image_view=DestroyCacheView(image_view);
4359     }
4360   /*
4361     Composite image with X Image window.
4362   */
4363   (void) CompositeImage(image,compose,composite_image,composite_info.x,
4364     composite_info.y,exception);
4365   composite_image=DestroyImage(composite_image);
4366   XSetCursorState(display,windows,MagickFalse);
4367   /*
4368     Update image configuration.
4369   */
4370   XConfigureImageColormap(display,resource_info,windows,image,exception);
4371   (void) XConfigureImage(display,resource_info,windows,image,exception);
4372   return(MagickTrue);
4373 }
4374 \f
4375 /*
4376 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4377 %                                                                             %
4378 %                                                                             %
4379 %                                                                             %
4380 +   X C o n f i g u r e I m a g e                                             %
4381 %                                                                             %
4382 %                                                                             %
4383 %                                                                             %
4384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4385 %
4386 %  XConfigureImage() creates a new X image.  It also notifies the window
4387 %  manager of the new image size and configures the transient widows.
4388 %
4389 %  The format of the XConfigureImage method is:
4390 %
4391 %      MagickBooleanType XConfigureImage(Display *display,
4392 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4393 %        ExceptionInfo *exception)
4394 %
4395 %  A description of each parameter follows:
4396 %
4397 %    o display: Specifies a connection to an X server; returned from
4398 %      XOpenDisplay.
4399 %
4400 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4401 %
4402 %    o windows: Specifies a pointer to a XWindows structure.
4403 %
4404 %    o image: the image.
4405 %
4406 %    o exception: return any errors or warnings in this structure.
4407 %
4408 %    o exception: return any errors or warnings in this structure.
4409 %
4410 */
4411 static MagickBooleanType XConfigureImage(Display *display,
4412   XResourceInfo *resource_info,XWindows *windows,Image *image,
4413   ExceptionInfo *exception)
4414 {
4415   char
4416     geometry[MaxTextExtent];
4417
4418   MagickStatusType
4419     status;
4420
4421   size_t
4422     mask,
4423     height,
4424     width;
4425
4426   ssize_t
4427     x,
4428     y;
4429
4430   XSizeHints
4431     *size_hints;
4432
4433   XWindowChanges
4434     window_changes;
4435
4436   /*
4437     Dismiss if window dimensions are zero.
4438   */
4439   width=(unsigned int) windows->image.window_changes.width;
4440   height=(unsigned int) windows->image.window_changes.height;
4441   if (image->debug != MagickFalse)
4442     (void) LogMagickEvent(X11Event,GetMagickModule(),
4443       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4444       windows->image.ximage->height,(double) width,(double) height);
4445   if ((width*height) == 0)
4446     return(MagickTrue);
4447   x=0;
4448   y=0;
4449   /*
4450     Resize image to fit Image window dimensions.
4451   */
4452   XSetCursorState(display,windows,MagickTrue);
4453   (void) XFlush(display);
4454   if (((int) width != windows->image.ximage->width) ||
4455       ((int) height != windows->image.ximage->height))
4456     image->taint=MagickTrue;
4457   windows->magnify.x=(int)
4458     width*windows->magnify.x/windows->image.ximage->width;
4459   windows->magnify.y=(int)
4460     height*windows->magnify.y/windows->image.ximage->height;
4461   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4462   windows->image.y=(int)
4463     (height*windows->image.y/windows->image.ximage->height);
4464   status=XMakeImage(display,resource_info,&windows->image,image,
4465     (unsigned int) width,(unsigned int) height,exception);
4466   if (status == MagickFalse)
4467     XNoticeWidget(display,windows,"Unable to configure X image:",
4468       windows->image.name);
4469   /*
4470     Notify window manager of the new configuration.
4471   */
4472   if (resource_info->image_geometry != (char *) NULL)
4473     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4474       resource_info->image_geometry);
4475   else
4476     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4477       XDisplayWidth(display,windows->image.screen),
4478       XDisplayHeight(display,windows->image.screen));
4479   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4480   window_changes.width=(int) width;
4481   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4482     window_changes.width=XDisplayWidth(display,windows->image.screen);
4483   window_changes.height=(int) height;
4484   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4485     window_changes.height=XDisplayHeight(display,windows->image.screen);
4486   mask=(size_t) (CWWidth | CWHeight);
4487   if (resource_info->backdrop)
4488     {
4489       mask|=CWX | CWY;
4490       window_changes.x=(int)
4491         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4492       window_changes.y=(int)
4493         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4494     }
4495   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4496     (unsigned int) mask,&window_changes);
4497   (void) XClearWindow(display,windows->image.id);
4498   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4499   /*
4500     Update Magnify window configuration.
4501   */
4502   if (windows->magnify.mapped != MagickFalse)
4503     XMakeMagnifyImage(display,windows,exception);
4504   windows->pan.crop_geometry=windows->image.crop_geometry;
4505   XBestIconSize(display,&windows->pan,image);
4506   while (((windows->pan.width << 1) < MaxIconSize) &&
4507          ((windows->pan.height << 1) < MaxIconSize))
4508   {
4509     windows->pan.width<<=1;
4510     windows->pan.height<<=1;
4511   }
4512   if (windows->pan.geometry != (char *) NULL)
4513     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4514       &windows->pan.width,&windows->pan.height);
4515   window_changes.width=(int) windows->pan.width;
4516   window_changes.height=(int) windows->pan.height;
4517   size_hints=XAllocSizeHints();
4518   if (size_hints != (XSizeHints *) NULL)
4519     {
4520       /*
4521         Set new size hints.
4522       */
4523       size_hints->flags=PSize | PMinSize | PMaxSize;
4524       size_hints->width=window_changes.width;
4525       size_hints->height=window_changes.height;
4526       size_hints->min_width=size_hints->width;
4527       size_hints->min_height=size_hints->height;
4528       size_hints->max_width=size_hints->width;
4529       size_hints->max_height=size_hints->height;
4530       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4531       (void) XFree((void *) size_hints);
4532     }
4533   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4534     (unsigned int) (CWWidth | CWHeight),&window_changes);
4535   /*
4536     Update icon window configuration.
4537   */
4538   windows->icon.crop_geometry=windows->image.crop_geometry;
4539   XBestIconSize(display,&windows->icon,image);
4540   window_changes.width=(int) windows->icon.width;
4541   window_changes.height=(int) windows->icon.height;
4542   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4543     (unsigned int) (CWWidth | CWHeight),&window_changes);
4544   XSetCursorState(display,windows,MagickFalse);
4545   return(status != 0 ? MagickTrue : MagickFalse);
4546 }
4547 \f
4548 /*
4549 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4550 %                                                                             %
4551 %                                                                             %
4552 %                                                                             %
4553 +   X C r o p I m a g e                                                       %
4554 %                                                                             %
4555 %                                                                             %
4556 %                                                                             %
4557 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4558 %
4559 %  XCropImage() allows the user to select a region of the image and crop, copy,
4560 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4561 %  the image with XPasteImage.
4562 %
4563 %  The format of the XCropImage method is:
4564 %
4565 %      MagickBooleanType XCropImage(Display *display,
4566 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4567 %        const ClipboardMode mode,ExceptionInfo *exception)
4568 %
4569 %  A description of each parameter follows:
4570 %
4571 %    o display: Specifies a connection to an X server; returned from
4572 %      XOpenDisplay.
4573 %
4574 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4575 %
4576 %    o windows: Specifies a pointer to a XWindows structure.
4577 %
4578 %    o image: the image; returned from ReadImage.
4579 %
4580 %    o mode: This unsigned value specified whether the image should be
4581 %      cropped, copied, or cut.
4582 %
4583 %    o exception: return any errors or warnings in this structure.
4584 %
4585 */
4586 static MagickBooleanType XCropImage(Display *display,
4587   XResourceInfo *resource_info,XWindows *windows,Image *image,
4588   const ClipboardMode mode,ExceptionInfo *exception)
4589 {
4590   static const char
4591     *CropModeMenu[] =
4592     {
4593       "Help",
4594       "Dismiss",
4595       (char *) NULL
4596     },
4597     *RectifyModeMenu[] =
4598     {
4599       "Crop",
4600       "Help",
4601       "Dismiss",
4602       (char *) NULL
4603     };
4604
4605   static const ModeType
4606     CropCommands[] =
4607     {
4608       CropHelpCommand,
4609       CropDismissCommand
4610     },
4611     RectifyCommands[] =
4612     {
4613       RectifyCopyCommand,
4614       RectifyHelpCommand,
4615       RectifyDismissCommand
4616     };
4617
4618   CacheView
4619     *image_view;
4620
4621   char
4622     command[MaxTextExtent],
4623     text[MaxTextExtent];
4624
4625   Cursor
4626     cursor;
4627
4628   int
4629     id,
4630     x,
4631     y;
4632
4633   KeySym
4634     key_symbol;
4635
4636   Image
4637     *crop_image;
4638
4639   MagickRealType
4640     scale_factor;
4641
4642   RectangleInfo
4643     crop_info,
4644     highlight_info;
4645
4646   register Quantum
4647     *q;
4648
4649   unsigned int
4650     height,
4651     width;
4652
4653   size_t
4654     state;
4655
4656   XEvent
4657     event;
4658
4659   /*
4660     Map Command widget.
4661   */
4662   switch (mode)
4663   {
4664     case CopyMode:
4665     {
4666       (void) CloneString(&windows->command.name,"Copy");
4667       break;
4668     }
4669     case CropMode:
4670     {
4671       (void) CloneString(&windows->command.name,"Crop");
4672       break;
4673     }
4674     case CutMode:
4675     {
4676       (void) CloneString(&windows->command.name,"Cut");
4677       break;
4678     }
4679   }
4680   RectifyModeMenu[0]=windows->command.name;
4681   windows->command.data=0;
4682   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4683   (void) XMapRaised(display,windows->command.id);
4684   XClientMessage(display,windows->image.id,windows->im_protocols,
4685     windows->im_update_widget,CurrentTime);
4686   /*
4687     Track pointer until button 1 is pressed.
4688   */
4689   XQueryPosition(display,windows->image.id,&x,&y);
4690   (void) XSelectInput(display,windows->image.id,
4691     windows->image.attributes.event_mask | PointerMotionMask);
4692   crop_info.x=(ssize_t) windows->image.x+x;
4693   crop_info.y=(ssize_t) windows->image.y+y;
4694   crop_info.width=0;
4695   crop_info.height=0;
4696   cursor=XCreateFontCursor(display,XC_fleur);
4697   state=DefaultState;
4698   do
4699   {
4700     if (windows->info.mapped != MagickFalse)
4701       {
4702         /*
4703           Display pointer position.
4704         */
4705         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4706           (long) crop_info.x,(long) crop_info.y);
4707         XInfoWidget(display,windows,text);
4708       }
4709     /*
4710       Wait for next event.
4711     */
4712     XScreenEvent(display,windows,&event,exception);
4713     if (event.xany.window == windows->command.id)
4714       {
4715         /*
4716           Select a command from the Command widget.
4717         */
4718         id=XCommandWidget(display,windows,CropModeMenu,&event);
4719         if (id < 0)
4720           continue;
4721         switch (CropCommands[id])
4722         {
4723           case CropHelpCommand:
4724           {
4725             switch (mode)
4726             {
4727               case CopyMode:
4728               {
4729                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4730                   "Help Viewer - Image Copy",ImageCopyHelp);
4731                 break;
4732               }
4733               case CropMode:
4734               {
4735                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4736                   "Help Viewer - Image Crop",ImageCropHelp);
4737                 break;
4738               }
4739               case CutMode:
4740               {
4741                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4742                   "Help Viewer - Image Cut",ImageCutHelp);
4743                 break;
4744               }
4745             }
4746             break;
4747           }
4748           case CropDismissCommand:
4749           {
4750             /*
4751               Prematurely exit.
4752             */
4753             state|=EscapeState;
4754             state|=ExitState;
4755             break;
4756           }
4757           default:
4758             break;
4759         }
4760         continue;
4761       }
4762     switch (event.type)
4763     {
4764       case ButtonPress:
4765       {
4766         if (event.xbutton.button != Button1)
4767           break;
4768         if (event.xbutton.window != windows->image.id)
4769           break;
4770         /*
4771           Note first corner of cropping rectangle-- exit loop.
4772         */
4773         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4774         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4775         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4776         state|=ExitState;
4777         break;
4778       }
4779       case ButtonRelease:
4780         break;
4781       case Expose:
4782         break;
4783       case KeyPress:
4784       {
4785         if (event.xkey.window != windows->image.id)
4786           break;
4787         /*
4788           Respond to a user key press.
4789         */
4790         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4791           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4792         switch ((int) key_symbol)
4793         {
4794           case XK_Escape:
4795           case XK_F20:
4796           {
4797             /*
4798               Prematurely exit.
4799             */
4800             state|=EscapeState;
4801             state|=ExitState;
4802             break;
4803           }
4804           case XK_F1:
4805           case XK_Help:
4806           {
4807             switch (mode)
4808             {
4809               case CopyMode:
4810               {
4811                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4812                   "Help Viewer - Image Copy",ImageCopyHelp);
4813                 break;
4814               }
4815               case CropMode:
4816               {
4817                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4818                   "Help Viewer - Image Crop",ImageCropHelp);
4819                 break;
4820               }
4821               case CutMode:
4822               {
4823                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4824                   "Help Viewer - Image Cut",ImageCutHelp);
4825                 break;
4826               }
4827             }
4828             break;
4829           }
4830           default:
4831           {
4832             (void) XBell(display,0);
4833             break;
4834           }
4835         }
4836         break;
4837       }
4838       case MotionNotify:
4839       {
4840         if (event.xmotion.window != windows->image.id)
4841           break;
4842         /*
4843           Map and unmap Info widget as text cursor crosses its boundaries.
4844         */
4845         x=event.xmotion.x;
4846         y=event.xmotion.y;
4847         if (windows->info.mapped != MagickFalse)
4848           {
4849             if ((x < (int) (windows->info.x+windows->info.width)) &&
4850                 (y < (int) (windows->info.y+windows->info.height)))
4851               (void) XWithdrawWindow(display,windows->info.id,
4852                 windows->info.screen);
4853           }
4854         else
4855           if ((x > (int) (windows->info.x+windows->info.width)) ||
4856               (y > (int) (windows->info.y+windows->info.height)))
4857             (void) XMapWindow(display,windows->info.id);
4858         crop_info.x=(ssize_t) windows->image.x+x;
4859         crop_info.y=(ssize_t) windows->image.y+y;
4860         break;
4861       }
4862       default:
4863         break;
4864     }
4865   } while ((state & ExitState) == 0);
4866   (void) XSelectInput(display,windows->image.id,
4867     windows->image.attributes.event_mask);
4868   if ((state & EscapeState) != 0)
4869     {
4870       /*
4871         User want to exit without cropping.
4872       */
4873       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4874       (void) XFreeCursor(display,cursor);
4875       return(MagickTrue);
4876     }
4877   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4878   do
4879   {
4880     /*
4881       Size rectangle as pointer moves until the mouse button is released.
4882     */
4883     x=(int) crop_info.x;
4884     y=(int) crop_info.y;
4885     crop_info.width=0;
4886     crop_info.height=0;
4887     state=DefaultState;
4888     do
4889     {
4890       highlight_info=crop_info;
4891       highlight_info.x=crop_info.x-windows->image.x;
4892       highlight_info.y=crop_info.y-windows->image.y;
4893       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4894         {
4895           /*
4896             Display info and draw cropping rectangle.
4897           */
4898           if (windows->info.mapped == MagickFalse)
4899             (void) XMapWindow(display,windows->info.id);
4900           (void) FormatLocaleString(text,MaxTextExtent,
4901             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4902             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4903           XInfoWidget(display,windows,text);
4904           XHighlightRectangle(display,windows->image.id,
4905             windows->image.highlight_context,&highlight_info);
4906         }
4907       else
4908         if (windows->info.mapped != MagickFalse)
4909           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4910       /*
4911         Wait for next event.
4912       */
4913       XScreenEvent(display,windows,&event,exception);
4914       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4915         XHighlightRectangle(display,windows->image.id,
4916           windows->image.highlight_context,&highlight_info);
4917       switch (event.type)
4918       {
4919         case ButtonPress:
4920         {
4921           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4922           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4923           break;
4924         }
4925         case ButtonRelease:
4926         {
4927           /*
4928             User has committed to cropping rectangle.
4929           */
4930           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4931           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4932           XSetCursorState(display,windows,MagickFalse);
4933           state|=ExitState;
4934           windows->command.data=0;
4935           (void) XCommandWidget(display,windows,RectifyModeMenu,
4936             (XEvent *) NULL);
4937           break;
4938         }
4939         case Expose:
4940           break;
4941         case MotionNotify:
4942         {
4943           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4944           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4945         }
4946         default:
4947           break;
4948       }
4949       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4950           ((state & ExitState) != 0))
4951         {
4952           /*
4953             Check boundary conditions.
4954           */
4955           if (crop_info.x < 0)
4956             crop_info.x=0;
4957           else
4958             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4959               crop_info.x=(ssize_t) windows->image.ximage->width;
4960           if ((int) crop_info.x < x)
4961             crop_info.width=(unsigned int) (x-crop_info.x);
4962           else
4963             {
4964               crop_info.width=(unsigned int) (crop_info.x-x);
4965               crop_info.x=(ssize_t) x;
4966             }
4967           if (crop_info.y < 0)
4968             crop_info.y=0;
4969           else
4970             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4971               crop_info.y=(ssize_t) windows->image.ximage->height;
4972           if ((int) crop_info.y < y)
4973             crop_info.height=(unsigned int) (y-crop_info.y);
4974           else
4975             {
4976               crop_info.height=(unsigned int) (crop_info.y-y);
4977               crop_info.y=(ssize_t) y;
4978             }
4979         }
4980     } while ((state & ExitState) == 0);
4981     /*
4982       Wait for user to grab a corner of the rectangle or press return.
4983     */
4984     state=DefaultState;
4985     (void) XMapWindow(display,windows->info.id);
4986     do
4987     {
4988       if (windows->info.mapped != MagickFalse)
4989         {
4990           /*
4991             Display pointer position.
4992           */
4993           (void) FormatLocaleString(text,MaxTextExtent,
4994             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4995             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4996           XInfoWidget(display,windows,text);
4997         }
4998       highlight_info=crop_info;
4999       highlight_info.x=crop_info.x-windows->image.x;
5000       highlight_info.y=crop_info.y-windows->image.y;
5001       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
5002         {
5003           state|=EscapeState;
5004           state|=ExitState;
5005           break;
5006         }
5007       XHighlightRectangle(display,windows->image.id,
5008         windows->image.highlight_context,&highlight_info);
5009       XScreenEvent(display,windows,&event,exception);
5010       if (event.xany.window == windows->command.id)
5011         {
5012           /*
5013             Select a command from the Command widget.
5014           */
5015           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5016           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5017           (void) XSetFunction(display,windows->image.highlight_context,
5018             GXinvert);
5019           XHighlightRectangle(display,windows->image.id,
5020             windows->image.highlight_context,&highlight_info);
5021           if (id >= 0)
5022             switch (RectifyCommands[id])
5023             {
5024               case RectifyCopyCommand:
5025               {
5026                 state|=ExitState;
5027                 break;
5028               }
5029               case RectifyHelpCommand:
5030               {
5031                 (void) XSetFunction(display,windows->image.highlight_context,
5032                   GXcopy);
5033                 switch (mode)
5034                 {
5035                   case CopyMode:
5036                   {
5037                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5038                       "Help Viewer - Image Copy",ImageCopyHelp);
5039                     break;
5040                   }
5041                   case CropMode:
5042                   {
5043                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5044                       "Help Viewer - Image Crop",ImageCropHelp);
5045                     break;
5046                   }
5047                   case CutMode:
5048                   {
5049                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5050                       "Help Viewer - Image Cut",ImageCutHelp);
5051                     break;
5052                   }
5053                 }
5054                 (void) XSetFunction(display,windows->image.highlight_context,
5055                   GXinvert);
5056                 break;
5057               }
5058               case RectifyDismissCommand:
5059               {
5060                 /*
5061                   Prematurely exit.
5062                 */
5063                 state|=EscapeState;
5064                 state|=ExitState;
5065                 break;
5066               }
5067               default:
5068                 break;
5069             }
5070           continue;
5071         }
5072       XHighlightRectangle(display,windows->image.id,
5073         windows->image.highlight_context,&highlight_info);
5074       switch (event.type)
5075       {
5076         case ButtonPress:
5077         {
5078           if (event.xbutton.button != Button1)
5079             break;
5080           if (event.xbutton.window != windows->image.id)
5081             break;
5082           x=windows->image.x+event.xbutton.x;
5083           y=windows->image.y+event.xbutton.y;
5084           if ((x < (int) (crop_info.x+RoiDelta)) &&
5085               (x > (int) (crop_info.x-RoiDelta)) &&
5086               (y < (int) (crop_info.y+RoiDelta)) &&
5087               (y > (int) (crop_info.y-RoiDelta)))
5088             {
5089               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5090               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5091               state|=UpdateConfigurationState;
5092               break;
5093             }
5094           if ((x < (int) (crop_info.x+RoiDelta)) &&
5095               (x > (int) (crop_info.x-RoiDelta)) &&
5096               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5097               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5098             {
5099               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5100               state|=UpdateConfigurationState;
5101               break;
5102             }
5103           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5104               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5105               (y < (int) (crop_info.y+RoiDelta)) &&
5106               (y > (int) (crop_info.y-RoiDelta)))
5107             {
5108               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5109               state|=UpdateConfigurationState;
5110               break;
5111             }
5112           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5113               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5114               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5115               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5116             {
5117               state|=UpdateConfigurationState;
5118               break;
5119             }
5120         }
5121         case ButtonRelease:
5122         {
5123           if (event.xbutton.window == windows->pan.id)
5124             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5125                 (highlight_info.y != crop_info.y-windows->image.y))
5126               XHighlightRectangle(display,windows->image.id,
5127                 windows->image.highlight_context,&highlight_info);
5128           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5129             event.xbutton.time);
5130           break;
5131         }
5132         case Expose:
5133         {
5134           if (event.xexpose.window == windows->image.id)
5135             if (event.xexpose.count == 0)
5136               {
5137                 event.xexpose.x=(int) highlight_info.x;
5138                 event.xexpose.y=(int) highlight_info.y;
5139                 event.xexpose.width=(int) highlight_info.width;
5140                 event.xexpose.height=(int) highlight_info.height;
5141                 XRefreshWindow(display,&windows->image,&event);
5142               }
5143           if (event.xexpose.window == windows->info.id)
5144             if (event.xexpose.count == 0)
5145               XInfoWidget(display,windows,text);
5146           break;
5147         }
5148         case KeyPress:
5149         {
5150           if (event.xkey.window != windows->image.id)
5151             break;
5152           /*
5153             Respond to a user key press.
5154           */
5155           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5156             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5157           switch ((int) key_symbol)
5158           {
5159             case XK_Escape:
5160             case XK_F20:
5161               state|=EscapeState;
5162             case XK_Return:
5163             {
5164               state|=ExitState;
5165               break;
5166             }
5167             case XK_Home:
5168             case XK_KP_Home:
5169             {
5170               crop_info.x=(ssize_t) (windows->image.width/2L-
5171                 crop_info.width/2L);
5172               crop_info.y=(ssize_t) (windows->image.height/2L-
5173                 crop_info.height/2L);
5174               break;
5175             }
5176             case XK_Left:
5177             case XK_KP_Left:
5178             {
5179               crop_info.x--;
5180               break;
5181             }
5182             case XK_Up:
5183             case XK_KP_Up:
5184             case XK_Next:
5185             {
5186               crop_info.y--;
5187               break;
5188             }
5189             case XK_Right:
5190             case XK_KP_Right:
5191             {
5192               crop_info.x++;
5193               break;
5194             }
5195             case XK_Prior:
5196             case XK_Down:
5197             case XK_KP_Down:
5198             {
5199               crop_info.y++;
5200               break;
5201             }
5202             case XK_F1:
5203             case XK_Help:
5204             {
5205               (void) XSetFunction(display,windows->image.highlight_context,
5206                 GXcopy);
5207               switch (mode)
5208               {
5209                 case CopyMode:
5210                 {
5211                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5212                     "Help Viewer - Image Copy",ImageCopyHelp);
5213                   break;
5214                 }
5215                 case CropMode:
5216                 {
5217                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5218                     "Help Viewer - Image Cropg",ImageCropHelp);
5219                   break;
5220                 }
5221                 case CutMode:
5222                 {
5223                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5224                     "Help Viewer - Image Cutg",ImageCutHelp);
5225                   break;
5226                 }
5227               }
5228               (void) XSetFunction(display,windows->image.highlight_context,
5229                 GXinvert);
5230               break;
5231             }
5232             default:
5233             {
5234               (void) XBell(display,0);
5235               break;
5236             }
5237           }
5238           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5239             event.xkey.time);
5240           break;
5241         }
5242         case KeyRelease:
5243           break;
5244         case MotionNotify:
5245         {
5246           if (event.xmotion.window != windows->image.id)
5247             break;
5248           /*
5249             Map and unmap Info widget as text cursor crosses its boundaries.
5250           */
5251           x=event.xmotion.x;
5252           y=event.xmotion.y;
5253           if (windows->info.mapped != MagickFalse)
5254             {
5255               if ((x < (int) (windows->info.x+windows->info.width)) &&
5256                   (y < (int) (windows->info.y+windows->info.height)))
5257                 (void) XWithdrawWindow(display,windows->info.id,
5258                   windows->info.screen);
5259             }
5260           else
5261             if ((x > (int) (windows->info.x+windows->info.width)) ||
5262                 (y > (int) (windows->info.y+windows->info.height)))
5263               (void) XMapWindow(display,windows->info.id);
5264           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5265           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5266           break;
5267         }
5268         case SelectionRequest:
5269         {
5270           XSelectionEvent
5271             notify;
5272
5273           XSelectionRequestEvent
5274             *request;
5275
5276           /*
5277             Set primary selection.
5278           */
5279           (void) FormatLocaleString(text,MaxTextExtent,
5280             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5281             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5282           request=(&(event.xselectionrequest));
5283           (void) XChangeProperty(request->display,request->requestor,
5284             request->property,request->target,8,PropModeReplace,
5285             (unsigned char *) text,(int) strlen(text));
5286           notify.type=SelectionNotify;
5287           notify.display=request->display;
5288           notify.requestor=request->requestor;
5289           notify.selection=request->selection;
5290           notify.target=request->target;
5291           notify.time=request->time;
5292           if (request->property == None)
5293             notify.property=request->target;
5294           else
5295             notify.property=request->property;
5296           (void) XSendEvent(request->display,request->requestor,False,0,
5297             (XEvent *) &notify);
5298         }
5299         default:
5300           break;
5301       }
5302       if ((state & UpdateConfigurationState) != 0)
5303         {
5304           (void) XPutBackEvent(display,&event);
5305           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5306           break;
5307         }
5308     } while ((state & ExitState) == 0);
5309   } while ((state & ExitState) == 0);
5310   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5311   XSetCursorState(display,windows,MagickFalse);
5312   if ((state & EscapeState) != 0)
5313     return(MagickTrue);
5314   if (mode == CropMode)
5315     if (((int) crop_info.width != windows->image.ximage->width) ||
5316         ((int) crop_info.height != windows->image.ximage->height))
5317       {
5318         /*
5319           Reconfigure Image window as defined by cropping rectangle.
5320         */
5321         XSetCropGeometry(display,windows,&crop_info,image);
5322         windows->image.window_changes.width=(int) crop_info.width;
5323         windows->image.window_changes.height=(int) crop_info.height;
5324         (void) XConfigureImage(display,resource_info,windows,image,exception);
5325         return(MagickTrue);
5326       }
5327   /*
5328     Copy image before applying image transforms.
5329   */
5330   XSetCursorState(display,windows,MagickTrue);
5331   XCheckRefreshWindows(display,windows);
5332   width=(unsigned int) image->columns;
5333   height=(unsigned int) image->rows;
5334   x=0;
5335   y=0;
5336   if (windows->image.crop_geometry != (char *) NULL)
5337     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5338   scale_factor=(MagickRealType) width/windows->image.ximage->width;
5339   crop_info.x+=x;
5340   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5341   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5342   scale_factor=(MagickRealType) height/windows->image.ximage->height;
5343   crop_info.y+=y;
5344   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5345   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5346   crop_image=CropImage(image,&crop_info,exception);
5347   XSetCursorState(display,windows,MagickFalse);
5348   if (crop_image == (Image *) NULL)
5349     return(MagickFalse);
5350   if (resource_info->copy_image != (Image *) NULL)
5351     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5352   resource_info->copy_image=crop_image;
5353   if (mode == CopyMode)
5354     {
5355       (void) XConfigureImage(display,resource_info,windows,image,exception);
5356       return(MagickTrue);
5357     }
5358   /*
5359     Cut image.
5360   */
5361   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5362     return(MagickFalse);
5363   image->matte=MagickTrue;
5364   image_view=AcquireCacheView(image);
5365   for (y=0; y < (int) crop_info.height; y++)
5366   {
5367     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5368       crop_info.width,1,exception);
5369     if (q == (Quantum *) NULL)
5370       break;
5371     for (x=0; x < (int) crop_info.width; x++)
5372     {
5373       SetPixelAlpha(image,TransparentAlpha,q);
5374       q+=GetPixelChannels(image);
5375     }
5376     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5377       break;
5378   }
5379   image_view=DestroyCacheView(image_view);
5380   /*
5381     Update image configuration.
5382   */
5383   XConfigureImageColormap(display,resource_info,windows,image,exception);
5384   (void) XConfigureImage(display,resource_info,windows,image,exception);
5385   return(MagickTrue);
5386 }
5387 \f
5388 /*
5389 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5390 %                                                                             %
5391 %                                                                             %
5392 %                                                                             %
5393 +   X D r a w I m a g e                                                       %
5394 %                                                                             %
5395 %                                                                             %
5396 %                                                                             %
5397 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5398 %
5399 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5400 %  the image.
5401 %
5402 %  The format of the XDrawEditImage method is:
5403 %
5404 %      MagickBooleanType XDrawEditImage(Display *display,
5405 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5406 %        ExceptionInfo *exception)
5407 %
5408 %  A description of each parameter follows:
5409 %
5410 %    o display: Specifies a connection to an X server; returned from
5411 %      XOpenDisplay.
5412 %
5413 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5414 %
5415 %    o windows: Specifies a pointer to a XWindows structure.
5416 %
5417 %    o image: the image.
5418 %
5419 %    o exception: return any errors or warnings in this structure.
5420 %
5421 */
5422 static MagickBooleanType XDrawEditImage(Display *display,
5423   XResourceInfo *resource_info,XWindows *windows,Image **image,
5424   ExceptionInfo *exception)
5425 {
5426   static const char
5427     *DrawMenu[] =
5428     {
5429       "Element",
5430       "Color",
5431       "Stipple",
5432       "Width",
5433       "Undo",
5434       "Help",
5435       "Dismiss",
5436       (char *) NULL
5437     };
5438
5439   static ElementType
5440     element = PointElement;
5441
5442   static const ModeType
5443     DrawCommands[] =
5444     {
5445       DrawElementCommand,
5446       DrawColorCommand,
5447       DrawStippleCommand,
5448       DrawWidthCommand,
5449       DrawUndoCommand,
5450       DrawHelpCommand,
5451       DrawDismissCommand
5452     };
5453
5454   static Pixmap
5455     stipple = (Pixmap) NULL;
5456
5457   static unsigned int
5458     pen_id = 0,
5459     line_width = 1;
5460
5461   char
5462     command[MaxTextExtent],
5463     text[MaxTextExtent];
5464
5465   Cursor
5466     cursor;
5467
5468   int
5469     entry,
5470     id,
5471     number_coordinates,
5472     x,
5473     y;
5474
5475   MagickRealType
5476     degrees;
5477
5478   MagickStatusType
5479     status;
5480
5481   RectangleInfo
5482     rectangle_info;
5483
5484   register int
5485     i;
5486
5487   unsigned int
5488     distance,
5489     height,
5490     max_coordinates,
5491     width;
5492
5493   size_t
5494     state;
5495
5496   Window
5497     root_window;
5498
5499   XDrawInfo
5500     draw_info;
5501
5502   XEvent
5503     event;
5504
5505   XPoint
5506     *coordinate_info;
5507
5508   XSegment
5509     line_info;
5510
5511   /*
5512     Allocate polygon info.
5513   */
5514   max_coordinates=2048;
5515   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5516     sizeof(*coordinate_info));
5517   if (coordinate_info == (XPoint *) NULL)
5518     {
5519       (void) ThrowMagickException(exception,GetMagickModule(),
5520         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5521       return(MagickFalse);
5522     }
5523   /*
5524     Map Command widget.
5525   */
5526   (void) CloneString(&windows->command.name,"Draw");
5527   windows->command.data=4;
5528   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5529   (void) XMapRaised(display,windows->command.id);
5530   XClientMessage(display,windows->image.id,windows->im_protocols,
5531     windows->im_update_widget,CurrentTime);
5532   /*
5533     Wait for first button press.
5534   */
5535   root_window=XRootWindow(display,XDefaultScreen(display));
5536   draw_info.stencil=OpaqueStencil;
5537   status=MagickTrue;
5538   cursor=XCreateFontCursor(display,XC_tcross);
5539   for ( ; ; )
5540   {
5541     XQueryPosition(display,windows->image.id,&x,&y);
5542     (void) XSelectInput(display,windows->image.id,
5543       windows->image.attributes.event_mask | PointerMotionMask);
5544     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5545     state=DefaultState;
5546     do
5547     {
5548       if (windows->info.mapped != MagickFalse)
5549         {
5550           /*
5551             Display pointer position.
5552           */
5553           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5554             x+windows->image.x,y+windows->image.y);
5555           XInfoWidget(display,windows,text);
5556         }
5557       /*
5558         Wait for next event.
5559       */
5560       XScreenEvent(display,windows,&event,exception);
5561       if (event.xany.window == windows->command.id)
5562         {
5563           /*
5564             Select a command from the Command widget.
5565           */
5566           id=XCommandWidget(display,windows,DrawMenu,&event);
5567           if (id < 0)
5568             continue;
5569           switch (DrawCommands[id])
5570           {
5571             case DrawElementCommand:
5572             {
5573               static const char
5574                 *Elements[] =
5575                 {
5576                   "point",
5577                   "line",
5578                   "rectangle",
5579                   "fill rectangle",
5580                   "circle",
5581                   "fill circle",
5582                   "ellipse",
5583                   "fill ellipse",
5584                   "polygon",
5585                   "fill polygon",
5586                   (char *) NULL,
5587                 };
5588
5589               /*
5590                 Select a command from the pop-up menu.
5591               */
5592               element=(ElementType) (XMenuWidget(display,windows,
5593                 DrawMenu[id],Elements,command)+1);
5594               break;
5595             }
5596             case DrawColorCommand:
5597             {
5598               const char
5599                 *ColorMenu[MaxNumberPens+1];
5600
5601               int
5602                 pen_number;
5603
5604               MagickBooleanType
5605                 transparent;
5606
5607               XColor
5608                 color;
5609
5610               /*
5611                 Initialize menu selections.
5612               */
5613               for (i=0; i < (int) (MaxNumberPens-2); i++)
5614                 ColorMenu[i]=resource_info->pen_colors[i];
5615               ColorMenu[MaxNumberPens-2]="transparent";
5616               ColorMenu[MaxNumberPens-1]="Browser...";
5617               ColorMenu[MaxNumberPens]=(char *) NULL;
5618               /*
5619                 Select a pen color from the pop-up menu.
5620               */
5621               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5622                 (const char **) ColorMenu,command);
5623               if (pen_number < 0)
5624                 break;
5625               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5626                 MagickFalse;
5627               if (transparent != MagickFalse)
5628                 {
5629                   draw_info.stencil=TransparentStencil;
5630                   break;
5631                 }
5632               if (pen_number == (MaxNumberPens-1))
5633                 {
5634                   static char
5635                     color_name[MaxTextExtent] = "gray";
5636
5637                   /*
5638                     Select a pen color from a dialog.
5639                   */
5640                   resource_info->pen_colors[pen_number]=color_name;
5641                   XColorBrowserWidget(display,windows,"Select",color_name);
5642                   if (*color_name == '\0')
5643                     break;
5644                 }
5645               /*
5646                 Set pen color.
5647               */
5648               (void) XParseColor(display,windows->map_info->colormap,
5649                 resource_info->pen_colors[pen_number],&color);
5650               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5651                 (unsigned int) MaxColors,&color);
5652               windows->pixel_info->pen_colors[pen_number]=color;
5653               pen_id=(unsigned int) pen_number;
5654               draw_info.stencil=OpaqueStencil;
5655               break;
5656             }
5657             case DrawStippleCommand:
5658             {
5659               Image
5660                 *stipple_image;
5661
5662               ImageInfo
5663                 *image_info;
5664
5665               int
5666                 status;
5667
5668               static char
5669                 filename[MaxTextExtent] = "\0";
5670
5671               static const char
5672                 *StipplesMenu[] =
5673                 {
5674                   "Brick",
5675                   "Diagonal",
5676                   "Scales",
5677                   "Vertical",
5678                   "Wavy",
5679                   "Translucent",
5680                   "Opaque",
5681                   (char *) NULL,
5682                   (char *) NULL,
5683                 };
5684
5685               /*
5686                 Select a command from the pop-up menu.
5687               */
5688               StipplesMenu[7]="Open...";
5689               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5690                 command);
5691               if (entry < 0)
5692                 break;
5693               if (stipple != (Pixmap) NULL)
5694                 (void) XFreePixmap(display,stipple);
5695               stipple=(Pixmap) NULL;
5696               if (entry != 7)
5697                 {
5698                   switch (entry)
5699                   {
5700                     case 0:
5701                     {
5702                       stipple=XCreateBitmapFromData(display,root_window,
5703                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5704                       break;
5705                     }
5706                     case 1:
5707                     {
5708                       stipple=XCreateBitmapFromData(display,root_window,
5709                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5710                       break;
5711                     }
5712                     case 2:
5713                     {
5714                       stipple=XCreateBitmapFromData(display,root_window,
5715                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5716                       break;
5717                     }
5718                     case 3:
5719                     {
5720                       stipple=XCreateBitmapFromData(display,root_window,
5721                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5722                       break;
5723                     }
5724                     case 4:
5725                     {
5726                       stipple=XCreateBitmapFromData(display,root_window,
5727                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5728                       break;
5729                     }
5730                     case 5:
5731                     {
5732                       stipple=XCreateBitmapFromData(display,root_window,
5733                         (char *) HighlightBitmap,HighlightWidth,
5734                         HighlightHeight);
5735                       break;
5736                     }
5737                     case 6:
5738                     default:
5739                     {
5740                       stipple=XCreateBitmapFromData(display,root_window,
5741                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5742                       break;
5743                     }
5744                   }
5745                   break;
5746                 }
5747               XFileBrowserWidget(display,windows,"Stipple",filename);
5748               if (*filename == '\0')
5749                 break;
5750               /*
5751                 Read image.
5752               */
5753               XSetCursorState(display,windows,MagickTrue);
5754               XCheckRefreshWindows(display,windows);
5755               image_info=AcquireImageInfo();
5756               (void) CopyMagickString(image_info->filename,filename,
5757                 MaxTextExtent);
5758               stipple_image=ReadImage(image_info,exception);
5759               CatchException(exception);
5760               XSetCursorState(display,windows,MagickFalse);
5761               if (stipple_image == (Image *) NULL)
5762                 break;
5763               (void) AcquireUniqueFileResource(filename);
5764               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5765                 "xbm:%s",filename);
5766               (void) WriteImage(image_info,stipple_image,exception);
5767               stipple_image=DestroyImage(stipple_image);
5768               image_info=DestroyImageInfo(image_info);
5769               status=XReadBitmapFile(display,root_window,filename,&width,
5770                 &height,&stipple,&x,&y);
5771               (void) RelinquishUniqueFileResource(filename);
5772               if ((status != BitmapSuccess) != 0)
5773                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5774                   filename);
5775               break;
5776             }
5777             case DrawWidthCommand:
5778             {
5779               static char
5780                 width[MaxTextExtent] = "0";
5781
5782               static const char
5783                 *WidthsMenu[] =
5784                 {
5785                   "1",
5786                   "2",
5787                   "4",
5788                   "8",
5789                   "16",
5790                   "Dialog...",
5791                   (char *) NULL,
5792                 };
5793
5794               /*
5795                 Select a command from the pop-up menu.
5796               */
5797               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5798                 command);
5799               if (entry < 0)
5800                 break;
5801               if (entry != 5)
5802                 {
5803                   line_width=(unsigned int) StringToUnsignedLong(
5804                     WidthsMenu[entry]);
5805                   break;
5806                 }
5807               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5808                 width);
5809               if (*width == '\0')
5810                 break;
5811               line_width=(unsigned int) StringToUnsignedLong(width);
5812               break;
5813             }
5814             case DrawUndoCommand:
5815             {
5816               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5817                 image,exception);
5818               break;
5819             }
5820             case DrawHelpCommand:
5821             {
5822               XTextViewWidget(display,resource_info,windows,MagickFalse,
5823                 "Help Viewer - Image Rotation",ImageDrawHelp);
5824               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5825               break;
5826             }
5827             case DrawDismissCommand:
5828             {
5829               /*
5830                 Prematurely exit.
5831               */
5832               state|=EscapeState;
5833               state|=ExitState;
5834               break;
5835             }
5836             default:
5837               break;
5838           }
5839           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5840           continue;
5841         }
5842       switch (event.type)
5843       {
5844         case ButtonPress:
5845         {
5846           if (event.xbutton.button != Button1)
5847             break;
5848           if (event.xbutton.window != windows->image.id)
5849             break;
5850           /*
5851             exit loop.
5852           */
5853           x=event.xbutton.x;
5854           y=event.xbutton.y;
5855           state|=ExitState;
5856           break;
5857         }
5858         case ButtonRelease:
5859           break;
5860         case Expose:
5861           break;
5862         case KeyPress:
5863         {
5864           KeySym
5865             key_symbol;
5866
5867           if (event.xkey.window != windows->image.id)
5868             break;
5869           /*
5870             Respond to a user key press.
5871           */
5872           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5873             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5874           switch ((int) key_symbol)
5875           {
5876             case XK_Escape:
5877             case XK_F20:
5878             {
5879               /*
5880                 Prematurely exit.
5881               */
5882               state|=EscapeState;
5883               state|=ExitState;
5884               break;
5885             }
5886             case XK_F1:
5887             case XK_Help:
5888             {
5889               XTextViewWidget(display,resource_info,windows,MagickFalse,
5890                 "Help Viewer - Image Rotation",ImageDrawHelp);
5891               break;
5892             }
5893             default:
5894             {
5895               (void) XBell(display,0);
5896               break;
5897             }
5898           }
5899           break;
5900         }
5901         case MotionNotify:
5902         {
5903           /*
5904             Map and unmap Info widget as text cursor crosses its boundaries.
5905           */
5906           x=event.xmotion.x;
5907           y=event.xmotion.y;
5908           if (windows->info.mapped != MagickFalse)
5909             {
5910               if ((x < (int) (windows->info.x+windows->info.width)) &&
5911                   (y < (int) (windows->info.y+windows->info.height)))
5912                 (void) XWithdrawWindow(display,windows->info.id,
5913                   windows->info.screen);
5914             }
5915           else
5916             if ((x > (int) (windows->info.x+windows->info.width)) ||
5917                 (y > (int) (windows->info.y+windows->info.height)))
5918               (void) XMapWindow(display,windows->info.id);
5919           break;
5920         }
5921       }
5922     } while ((state & ExitState) == 0);
5923     (void) XSelectInput(display,windows->image.id,
5924       windows->image.attributes.event_mask);
5925     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5926     if ((state & EscapeState) != 0)
5927       break;
5928     /*
5929       Draw element as pointer moves until the button is released.
5930     */
5931     distance=0;
5932     degrees=0.0;
5933     line_info.x1=x;
5934     line_info.y1=y;
5935     line_info.x2=x;
5936     line_info.y2=y;
5937     rectangle_info.x=(ssize_t) x;
5938     rectangle_info.y=(ssize_t) y;
5939     rectangle_info.width=0;
5940     rectangle_info.height=0;
5941     number_coordinates=1;
5942     coordinate_info->x=x;
5943     coordinate_info->y=y;
5944     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5945     state=DefaultState;
5946     do
5947     {
5948       switch (element)
5949       {
5950         case PointElement:
5951         default:
5952         {
5953           if (number_coordinates > 1)
5954             {
5955               (void) XDrawLines(display,windows->image.id,
5956                 windows->image.highlight_context,coordinate_info,
5957                 number_coordinates,CoordModeOrigin);
5958               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5959                 coordinate_info[number_coordinates-1].x,
5960                 coordinate_info[number_coordinates-1].y);
5961               XInfoWidget(display,windows,text);
5962             }
5963           break;
5964         }
5965         case LineElement:
5966         {
5967           if (distance > 9)
5968             {
5969               /*
5970                 Display angle of the line.
5971               */
5972               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5973                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5974               (void) FormatLocaleString(text,MaxTextExtent," %g",
5975                 (double) degrees);
5976               XInfoWidget(display,windows,text);
5977               XHighlightLine(display,windows->image.id,
5978                 windows->image.highlight_context,&line_info);
5979             }
5980           else
5981             if (windows->info.mapped != MagickFalse)
5982               (void) XWithdrawWindow(display,windows->info.id,
5983                 windows->info.screen);
5984           break;
5985         }
5986         case RectangleElement:
5987         case FillRectangleElement:
5988         {
5989           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5990             {
5991               /*
5992                 Display info and draw drawing rectangle.
5993               */
5994               (void) FormatLocaleString(text,MaxTextExtent,
5995                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5996                 (double) rectangle_info.height,(double) rectangle_info.x,
5997                 (double) rectangle_info.y);
5998               XInfoWidget(display,windows,text);
5999               XHighlightRectangle(display,windows->image.id,
6000                 windows->image.highlight_context,&rectangle_info);
6001             }
6002           else
6003             if (windows->info.mapped != MagickFalse)
6004               (void) XWithdrawWindow(display,windows->info.id,
6005                 windows->info.screen);
6006           break;
6007         }
6008         case CircleElement:
6009         case FillCircleElement:
6010         case EllipseElement:
6011         case FillEllipseElement:
6012         {
6013           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6014             {
6015               /*
6016                 Display info and draw drawing rectangle.
6017               */
6018               (void) FormatLocaleString(text,MaxTextExtent,
6019                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6020                 (double) rectangle_info.height,(double) rectangle_info.x,
6021                 (double) rectangle_info.y);
6022               XInfoWidget(display,windows,text);
6023               XHighlightEllipse(display,windows->image.id,
6024                 windows->image.highlight_context,&rectangle_info);
6025             }
6026           else
6027             if (windows->info.mapped != MagickFalse)
6028               (void) XWithdrawWindow(display,windows->info.id,
6029                 windows->info.screen);
6030           break;
6031         }
6032         case PolygonElement:
6033         case FillPolygonElement:
6034         {
6035           if (number_coordinates > 1)
6036             (void) XDrawLines(display,windows->image.id,
6037               windows->image.highlight_context,coordinate_info,
6038               number_coordinates,CoordModeOrigin);
6039           if (distance > 9)
6040             {
6041               /*
6042                 Display angle of the line.
6043               */
6044               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6045                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6046               (void) FormatLocaleString(text,MaxTextExtent," %g",
6047                 (double) degrees);
6048               XInfoWidget(display,windows,text);
6049               XHighlightLine(display,windows->image.id,
6050                 windows->image.highlight_context,&line_info);
6051             }
6052           else
6053             if (windows->info.mapped != MagickFalse)
6054               (void) XWithdrawWindow(display,windows->info.id,
6055                 windows->info.screen);
6056           break;
6057         }
6058       }
6059       /*
6060         Wait for next event.
6061       */
6062       XScreenEvent(display,windows,&event,exception);
6063       switch (element)
6064       {
6065         case PointElement:
6066         default:
6067         {
6068           if (number_coordinates > 1)
6069             (void) XDrawLines(display,windows->image.id,
6070               windows->image.highlight_context,coordinate_info,
6071               number_coordinates,CoordModeOrigin);
6072           break;
6073         }
6074         case LineElement:
6075         {
6076           if (distance > 9)
6077             XHighlightLine(display,windows->image.id,
6078               windows->image.highlight_context,&line_info);
6079           break;
6080         }
6081         case RectangleElement:
6082         case FillRectangleElement:
6083         {
6084           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6085             XHighlightRectangle(display,windows->image.id,
6086               windows->image.highlight_context,&rectangle_info);
6087           break;
6088         }
6089         case CircleElement:
6090         case FillCircleElement:
6091         case EllipseElement:
6092         case FillEllipseElement:
6093         {
6094           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6095             XHighlightEllipse(display,windows->image.id,
6096               windows->image.highlight_context,&rectangle_info);
6097           break;
6098         }
6099         case PolygonElement:
6100         case FillPolygonElement:
6101         {
6102           if (number_coordinates > 1)
6103             (void) XDrawLines(display,windows->image.id,
6104               windows->image.highlight_context,coordinate_info,
6105               number_coordinates,CoordModeOrigin);
6106           if (distance > 9)
6107             XHighlightLine(display,windows->image.id,
6108               windows->image.highlight_context,&line_info);
6109           break;
6110         }
6111       }
6112       switch (event.type)
6113       {
6114         case ButtonPress:
6115           break;
6116         case ButtonRelease:
6117         {
6118           /*
6119             User has committed to element.
6120           */
6121           line_info.x2=event.xbutton.x;
6122           line_info.y2=event.xbutton.y;
6123           rectangle_info.x=(ssize_t) event.xbutton.x;
6124           rectangle_info.y=(ssize_t) event.xbutton.y;
6125           coordinate_info[number_coordinates].x=event.xbutton.x;
6126           coordinate_info[number_coordinates].y=event.xbutton.y;
6127           if (((element != PolygonElement) &&
6128                (element != FillPolygonElement)) || (distance <= 9))
6129             {
6130               state|=ExitState;
6131               break;
6132             }
6133           number_coordinates++;
6134           if (number_coordinates < (int) max_coordinates)
6135             {
6136               line_info.x1=event.xbutton.x;
6137               line_info.y1=event.xbutton.y;
6138               break;
6139             }
6140           max_coordinates<<=1;
6141           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6142             max_coordinates,sizeof(*coordinate_info));
6143           if (coordinate_info == (XPoint *) NULL)
6144             (void) ThrowMagickException(exception,GetMagickModule(),
6145               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6146           break;
6147         }
6148         case Expose:
6149           break;
6150         case MotionNotify:
6151         {
6152           if (event.xmotion.window != windows->image.id)
6153             break;
6154           if (element != PointElement)
6155             {
6156               line_info.x2=event.xmotion.x;
6157               line_info.y2=event.xmotion.y;
6158               rectangle_info.x=(ssize_t) event.xmotion.x;
6159               rectangle_info.y=(ssize_t) event.xmotion.y;
6160               break;
6161             }
6162           coordinate_info[number_coordinates].x=event.xbutton.x;
6163           coordinate_info[number_coordinates].y=event.xbutton.y;
6164           number_coordinates++;
6165           if (number_coordinates < (int) max_coordinates)
6166             break;
6167           max_coordinates<<=1;
6168           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6169             max_coordinates,sizeof(*coordinate_info));
6170           if (coordinate_info == (XPoint *) NULL)
6171             (void) ThrowMagickException(exception,GetMagickModule(),
6172               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6173           break;
6174         }
6175         default:
6176           break;
6177       }
6178       /*
6179         Check boundary conditions.
6180       */
6181       if (line_info.x2 < 0)
6182         line_info.x2=0;
6183       else
6184         if (line_info.x2 > (int) windows->image.width)
6185           line_info.x2=(short) windows->image.width;
6186       if (line_info.y2 < 0)
6187         line_info.y2=0;
6188       else
6189         if (line_info.y2 > (int) windows->image.height)
6190           line_info.y2=(short) windows->image.height;
6191       distance=(unsigned int)
6192         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6193          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6194       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6195           ((state & ExitState) != 0))
6196         {
6197           if (rectangle_info.x < 0)
6198             rectangle_info.x=0;
6199           else
6200             if (rectangle_info.x > (ssize_t) windows->image.width)
6201               rectangle_info.x=(ssize_t) windows->image.width;
6202           if ((int) rectangle_info.x < x)
6203             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6204           else
6205             {
6206               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6207               rectangle_info.x=(ssize_t) x;
6208             }
6209           if (rectangle_info.y < 0)
6210             rectangle_info.y=0;
6211           else
6212             if (rectangle_info.y > (ssize_t) windows->image.height)
6213               rectangle_info.y=(ssize_t) windows->image.height;
6214           if ((int) rectangle_info.y < y)
6215             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6216           else
6217             {
6218               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6219               rectangle_info.y=(ssize_t) y;
6220             }
6221         }
6222     } while ((state & ExitState) == 0);
6223     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6224     if ((element == PointElement) || (element == PolygonElement) ||
6225         (element == FillPolygonElement))
6226       {
6227         /*
6228           Determine polygon bounding box.
6229         */
6230         rectangle_info.x=(ssize_t) coordinate_info->x;
6231         rectangle_info.y=(ssize_t) coordinate_info->y;
6232         x=coordinate_info->x;
6233         y=coordinate_info->y;
6234         for (i=1; i < number_coordinates; i++)
6235         {
6236           if (coordinate_info[i].x > x)
6237             x=coordinate_info[i].x;
6238           if (coordinate_info[i].y > y)
6239             y=coordinate_info[i].y;
6240           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6241             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6242           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6243             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6244         }
6245         rectangle_info.width=(size_t) (x-rectangle_info.x);
6246         rectangle_info.height=(size_t) (y-rectangle_info.y);
6247         for (i=0; i < number_coordinates; i++)
6248         {
6249           coordinate_info[i].x-=rectangle_info.x;
6250           coordinate_info[i].y-=rectangle_info.y;
6251         }
6252       }
6253     else
6254       if (distance <= 9)
6255         continue;
6256       else
6257         if ((element == RectangleElement) ||
6258             (element == CircleElement) || (element == EllipseElement))
6259           {
6260             rectangle_info.width--;
6261             rectangle_info.height--;
6262           }
6263     /*
6264       Drawing is relative to image configuration.
6265     */
6266     draw_info.x=(int) rectangle_info.x;
6267     draw_info.y=(int) rectangle_info.y;
6268     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6269       image,exception);
6270     width=(unsigned int) (*image)->columns;
6271     height=(unsigned int) (*image)->rows;
6272     x=0;
6273     y=0;
6274     if (windows->image.crop_geometry != (char *) NULL)
6275       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6276     draw_info.x+=windows->image.x-(line_width/2);
6277     if (draw_info.x < 0)
6278       draw_info.x=0;
6279     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6280     draw_info.y+=windows->image.y-(line_width/2);
6281     if (draw_info.y < 0)
6282       draw_info.y=0;
6283     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6284     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6285     if (draw_info.width > (unsigned int) (*image)->columns)
6286       draw_info.width=(unsigned int) (*image)->columns;
6287     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6288     if (draw_info.height > (unsigned int) (*image)->rows)
6289       draw_info.height=(unsigned int) (*image)->rows;
6290     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6291       width*draw_info.width/windows->image.ximage->width,
6292       height*draw_info.height/windows->image.ximage->height,
6293       draw_info.x+x,draw_info.y+y);
6294     /*
6295       Initialize drawing attributes.
6296     */
6297     draw_info.degrees=0.0;
6298     draw_info.element=element;
6299     draw_info.stipple=stipple;
6300     draw_info.line_width=line_width;
6301     draw_info.line_info=line_info;
6302     if (line_info.x1 > (int) (line_width/2))
6303       draw_info.line_info.x1=(short) line_width/2;
6304     if (line_info.y1 > (int) (line_width/2))
6305       draw_info.line_info.y1=(short) line_width/2;
6306     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6307     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6308     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6309       {
6310         draw_info.line_info.x2=(-draw_info.line_info.x2);
6311         draw_info.line_info.y2=(-draw_info.line_info.y2);
6312       }
6313     if (draw_info.line_info.x2 < 0)
6314       {
6315         draw_info.line_info.x2=(-draw_info.line_info.x2);
6316         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6317       }
6318     if (draw_info.line_info.y2 < 0)
6319       {
6320         draw_info.line_info.y2=(-draw_info.line_info.y2);
6321         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6322       }
6323     draw_info.rectangle_info=rectangle_info;
6324     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6325       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6326     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6327       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6328     draw_info.number_coordinates=(unsigned int) number_coordinates;
6329     draw_info.coordinate_info=coordinate_info;
6330     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6331     /*
6332       Draw element on image.
6333     */
6334     XSetCursorState(display,windows,MagickTrue);
6335     XCheckRefreshWindows(display,windows);
6336     status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6337     XSetCursorState(display,windows,MagickFalse);
6338     /*
6339       Update image colormap and return to image drawing.
6340     */
6341     XConfigureImageColormap(display,resource_info,windows,*image,exception);
6342     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6343   }
6344   XSetCursorState(display,windows,MagickFalse);
6345   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6346   return(status != 0 ? MagickTrue : MagickFalse);
6347 }
6348 \f
6349 /*
6350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6351 %                                                                             %
6352 %                                                                             %
6353 %                                                                             %
6354 +   X D r a w P a n R e c t a n g l e                                         %
6355 %                                                                             %
6356 %                                                                             %
6357 %                                                                             %
6358 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6359 %
6360 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6361 %  displays a zoom image and the rectangle shows which portion of the image is
6362 %  displayed in the Image window.
6363 %
6364 %  The format of the XDrawPanRectangle method is:
6365 %
6366 %      XDrawPanRectangle(Display *display,XWindows *windows)
6367 %
6368 %  A description of each parameter follows:
6369 %
6370 %    o display: Specifies a connection to an X server;  returned from
6371 %      XOpenDisplay.
6372 %
6373 %    o windows: Specifies a pointer to a XWindows structure.
6374 %
6375 */
6376 static void XDrawPanRectangle(Display *display,XWindows *windows)
6377 {
6378   MagickRealType
6379     scale_factor;
6380
6381   RectangleInfo
6382     highlight_info;
6383
6384   /*
6385     Determine dimensions of the panning rectangle.
6386   */
6387   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6388   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6389   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6390   scale_factor=(MagickRealType)
6391     windows->pan.height/windows->image.ximage->height;
6392   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6393   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6394   /*
6395     Display the panning rectangle.
6396   */
6397   (void) XClearWindow(display,windows->pan.id);
6398   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6399     &highlight_info);
6400 }
6401 \f
6402 /*
6403 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6404 %                                                                             %
6405 %                                                                             %
6406 %                                                                             %
6407 +   X I m a g e C a c h e                                                     %
6408 %                                                                             %
6409 %                                                                             %
6410 %                                                                             %
6411 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6412 %
6413 %  XImageCache() handles the creation, manipulation, and destruction of the
6414 %  image cache (undo and redo buffers).
6415 %
6416 %  The format of the XImageCache method is:
6417 %
6418 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6419 %        XWindows *windows,const CommandType command,Image **image,
6420 %        ExceptionInfo *exception)
6421 %
6422 %  A description of each parameter follows:
6423 %
6424 %    o display: Specifies a connection to an X server; returned from
6425 %      XOpenDisplay.
6426 %
6427 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6428 %
6429 %    o windows: Specifies a pointer to a XWindows structure.
6430 %
6431 %    o command: Specifies a command to perform.
6432 %
6433 %    o image: the image;  XImageCache may transform the image and return a new
6434 %      image pointer.
6435 %
6436 %    o exception: return any errors or warnings in this structure.
6437 %
6438 */
6439 static void XImageCache(Display *display,XResourceInfo *resource_info,
6440   XWindows *windows,const CommandType command,Image **image,
6441   ExceptionInfo *exception)
6442 {
6443   Image
6444     *cache_image;
6445
6446   static Image
6447     *redo_image = (Image *) NULL,
6448     *undo_image = (Image *) NULL;
6449
6450   switch (command)
6451   {
6452     case FreeBuffersCommand:
6453     {
6454       /*
6455         Free memory from the undo and redo cache.
6456       */
6457       while (undo_image != (Image *) NULL)
6458       {
6459         cache_image=undo_image;
6460         undo_image=GetPreviousImageInList(undo_image);
6461         cache_image->list=DestroyImage(cache_image->list);
6462         cache_image=DestroyImage(cache_image);
6463       }
6464       undo_image=NewImageList();
6465       if (redo_image != (Image *) NULL)
6466         redo_image=DestroyImage(redo_image);
6467       redo_image=NewImageList();
6468       return;
6469     }
6470     case UndoCommand:
6471     {
6472       char
6473         image_geometry[MaxTextExtent];
6474
6475       /*
6476         Undo the last image transformation.
6477       */
6478       if (undo_image == (Image *) NULL)
6479         {
6480           (void) XBell(display,0);
6481           return;
6482         }
6483       cache_image=undo_image;
6484       undo_image=GetPreviousImageInList(undo_image);
6485       windows->image.window_changes.width=(int) cache_image->columns;
6486       windows->image.window_changes.height=(int) cache_image->rows;
6487       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6488         windows->image.ximage->width,windows->image.ximage->height);
6489       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6490         exception);
6491       if (windows->image.crop_geometry != (char *) NULL)
6492         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6493           windows->image.crop_geometry);
6494       windows->image.crop_geometry=cache_image->geometry;
6495       if (redo_image != (Image *) NULL)
6496         redo_image=DestroyImage(redo_image);
6497       redo_image=(*image);
6498       *image=cache_image->list;
6499       cache_image=DestroyImage(cache_image);
6500       if (windows->image.orphan != MagickFalse)
6501         return;
6502       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6503       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6504       return;
6505     }
6506     case CutCommand:
6507     case PasteCommand:
6508     case ApplyCommand:
6509     case HalfSizeCommand:
6510     case OriginalSizeCommand:
6511     case DoubleSizeCommand:
6512     case ResizeCommand:
6513     case TrimCommand:
6514     case CropCommand:
6515     case ChopCommand:
6516     case FlipCommand:
6517     case FlopCommand:
6518     case RotateRightCommand:
6519     case RotateLeftCommand:
6520     case RotateCommand:
6521     case ShearCommand:
6522     case RollCommand:
6523     case NegateCommand:
6524     case ContrastStretchCommand:
6525     case SigmoidalContrastCommand:
6526     case NormalizeCommand:
6527     case EqualizeCommand:
6528     case HueCommand:
6529     case SaturationCommand:
6530     case BrightnessCommand:
6531     case GammaCommand:
6532     case SpiffCommand:
6533     case DullCommand:
6534     case GrayscaleCommand:
6535     case MapCommand:
6536     case QuantizeCommand:
6537     case DespeckleCommand:
6538     case EmbossCommand:
6539     case ReduceNoiseCommand:
6540     case AddNoiseCommand:
6541     case SharpenCommand:
6542     case BlurCommand:
6543     case ThresholdCommand:
6544     case EdgeDetectCommand:
6545     case SpreadCommand:
6546     case ShadeCommand:
6547     case RaiseCommand:
6548     case SegmentCommand:
6549     case SolarizeCommand:
6550     case SepiaToneCommand:
6551     case SwirlCommand:
6552     case ImplodeCommand:
6553     case VignetteCommand:
6554     case WaveCommand:
6555     case OilPaintCommand:
6556     case CharcoalDrawCommand:
6557     case AnnotateCommand:
6558     case AddBorderCommand:
6559     case AddFrameCommand:
6560     case CompositeCommand:
6561     case CommentCommand:
6562     case LaunchCommand:
6563     case RegionofInterestCommand:
6564     case SaveToUndoBufferCommand:
6565     case RedoCommand:
6566     {
6567       Image
6568         *previous_image;
6569
6570       ssize_t
6571         bytes;
6572
6573       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6574       if (undo_image != (Image *) NULL)
6575         {
6576           /*
6577             Ensure the undo cache has enough memory available.
6578           */
6579           previous_image=undo_image;
6580           while (previous_image != (Image *) NULL)
6581           {
6582             bytes+=previous_image->list->columns*previous_image->list->rows*
6583               sizeof(PixelInfo);
6584             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6585               {
6586                 previous_image=GetPreviousImageInList(previous_image);
6587                 continue;
6588               }
6589             bytes-=previous_image->list->columns*previous_image->list->rows*
6590               sizeof(PixelInfo);
6591             if (previous_image == undo_image)
6592               undo_image=NewImageList();
6593             else
6594               previous_image->next->previous=NewImageList();
6595             break;
6596           }
6597           while (previous_image != (Image *) NULL)
6598           {
6599             /*
6600               Delete any excess memory from undo cache.
6601             */
6602             cache_image=previous_image;
6603             previous_image=GetPreviousImageInList(previous_image);
6604             cache_image->list=DestroyImage(cache_image->list);
6605             cache_image=DestroyImage(cache_image);
6606           }
6607         }
6608       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6609         break;
6610       /*
6611         Save image before transformations are applied.
6612       */
6613       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6614       if (cache_image == (Image *) NULL)
6615         break;
6616       XSetCursorState(display,windows,MagickTrue);
6617       XCheckRefreshWindows(display,windows);
6618       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6619       XSetCursorState(display,windows,MagickFalse);
6620       if (cache_image->list == (Image *) NULL)
6621         {
6622           cache_image=DestroyImage(cache_image);
6623           break;
6624         }
6625       cache_image->columns=(size_t) windows->image.ximage->width;
6626       cache_image->rows=(size_t) windows->image.ximage->height;
6627       cache_image->geometry=windows->image.crop_geometry;
6628       if (windows->image.crop_geometry != (char *) NULL)
6629         {
6630           cache_image->geometry=AcquireString((char *) NULL);
6631           (void) CopyMagickString(cache_image->geometry,
6632             windows->image.crop_geometry,MaxTextExtent);
6633         }
6634       if (undo_image == (Image *) NULL)
6635         {
6636           undo_image=cache_image;
6637           break;
6638         }
6639       undo_image->next=cache_image;
6640       undo_image->next->previous=undo_image;
6641       undo_image=undo_image->next;
6642       break;
6643     }
6644     default:
6645       break;
6646   }
6647   if (command == RedoCommand)
6648     {
6649       /*
6650         Redo the last image transformation.
6651       */
6652       if (redo_image == (Image *) NULL)
6653         {
6654           (void) XBell(display,0);
6655           return;
6656         }
6657       windows->image.window_changes.width=(int) redo_image->columns;
6658       windows->image.window_changes.height=(int) redo_image->rows;
6659       if (windows->image.crop_geometry != (char *) NULL)
6660         windows->image.crop_geometry=(char *)
6661           RelinquishMagickMemory(windows->image.crop_geometry);
6662       windows->image.crop_geometry=redo_image->geometry;
6663       *image=DestroyImage(*image);
6664       *image=redo_image;
6665       redo_image=NewImageList();
6666       if (windows->image.orphan != MagickFalse)
6667         return;
6668       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6669       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6670       return;
6671     }
6672   if (command != InfoCommand)
6673     return;
6674   /*
6675     Display image info.
6676   */
6677   XSetCursorState(display,windows,MagickTrue);
6678   XCheckRefreshWindows(display,windows);
6679   XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6680   XSetCursorState(display,windows,MagickFalse);
6681 }
6682 \f
6683 /*
6684 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6685 %                                                                             %
6686 %                                                                             %
6687 %                                                                             %
6688 +   X I m a g e W i n d o w C o m m a n d                                     %
6689 %                                                                             %
6690 %                                                                             %
6691 %                                                                             %
6692 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6693 %
6694 %  XImageWindowCommand() makes a transform to the image or Image window as
6695 %  specified by a user menu button or keyboard command.
6696 %
6697 %  The format of the XImageWindowCommand method is:
6698 %
6699 %      CommandType XImageWindowCommand(Display *display,
6700 %        XResourceInfo *resource_info,XWindows *windows,
6701 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6702 %        ExceptionInfo *exception)
6703 %
6704 %  A description of each parameter follows:
6705 %
6706 %    o nexus:  Method XImageWindowCommand returns an image when the
6707 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6708 %      image is returned.
6709 %
6710 %    o display: Specifies a connection to an X server; returned from
6711 %      XOpenDisplay.
6712 %
6713 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6714 %
6715 %    o windows: Specifies a pointer to a XWindows structure.
6716 %
6717 %    o state: key mask.
6718 %
6719 %    o key_symbol: Specifies a command to perform.
6720 %
6721 %    o image: the image;  XImageWIndowCommand may transform the image and
6722 %      return a new image pointer.
6723 %
6724 %    o exception: return any errors or warnings in this structure.
6725 %
6726 */
6727 static CommandType XImageWindowCommand(Display *display,
6728   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6729   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6730 {
6731   static char
6732     delta[MaxTextExtent] = "";
6733
6734   static const char
6735     Digits[] = "01234567890";
6736
6737   static KeySym
6738     last_symbol = XK_0;
6739
6740   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6741     {
6742       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6743         {
6744           *delta='\0';
6745           resource_info->quantum=1;
6746         }
6747       last_symbol=key_symbol;
6748       delta[strlen(delta)+1]='\0';
6749       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6750       resource_info->quantum=StringToLong(delta);
6751       return(NullCommand);
6752     }
6753   last_symbol=key_symbol;
6754   if (resource_info->immutable)
6755     {
6756       /*
6757         Virtual image window has a restricted command set.
6758       */
6759       switch (key_symbol)
6760       {
6761         case XK_question:
6762           return(InfoCommand);
6763         case XK_p:
6764         case XK_Print:
6765           return(PrintCommand);
6766         case XK_space:
6767           return(NextCommand);
6768         case XK_q:
6769         case XK_Escape:
6770           return(QuitCommand);
6771         default:
6772           break;
6773       }
6774       return(NullCommand);
6775     }
6776   switch ((int) key_symbol)
6777   {
6778     case XK_o:
6779     {
6780       if ((state & ControlMask) == 0)
6781         break;
6782       return(OpenCommand);
6783     }
6784     case XK_space:
6785       return(NextCommand);
6786     case XK_BackSpace:
6787       return(FormerCommand);
6788     case XK_s:
6789     {
6790       if ((state & Mod1Mask) != 0)
6791         return(SwirlCommand);
6792       if ((state & ControlMask) == 0)
6793         return(ShearCommand);
6794       return(SaveCommand);
6795     }
6796     case XK_p:
6797     case XK_Print:
6798     {
6799       if ((state & Mod1Mask) != 0)
6800         return(OilPaintCommand);
6801       if ((state & Mod4Mask) != 0)
6802         return(ColorCommand);
6803       if ((state & ControlMask) == 0)
6804         return(NullCommand);
6805       return(PrintCommand);
6806     }
6807     case XK_d:
6808     {
6809       if ((state & Mod4Mask) != 0)
6810         return(DrawCommand);
6811       if ((state & ControlMask) == 0)
6812         return(NullCommand);
6813       return(DeleteCommand);
6814     }
6815     case XK_Select:
6816     {
6817       if ((state & ControlMask) == 0)
6818         return(NullCommand);
6819       return(SelectCommand);
6820     }
6821     case XK_n:
6822     {
6823       if ((state & ControlMask) == 0)
6824         return(NullCommand);
6825       return(NewCommand);
6826     }
6827     case XK_q:
6828     case XK_Escape:
6829       return(QuitCommand);
6830     case XK_z:
6831     case XK_Undo:
6832     {
6833       if ((state & ControlMask) == 0)
6834         return(NullCommand);
6835       return(UndoCommand);
6836     }
6837     case XK_r:
6838     case XK_Redo:
6839     {
6840       if ((state & ControlMask) == 0)
6841         return(RollCommand);
6842       return(RedoCommand);
6843     }
6844     case XK_x:
6845     {
6846       if ((state & ControlMask) == 0)
6847         return(NullCommand);
6848       return(CutCommand);
6849     }
6850     case XK_c:
6851     {
6852       if ((state & Mod1Mask) != 0)
6853         return(CharcoalDrawCommand);
6854       if ((state & ControlMask) == 0)
6855         return(CropCommand);
6856       return(CopyCommand);
6857     }
6858     case XK_v:
6859     case XK_Insert:
6860     {
6861       if ((state & Mod4Mask) != 0)
6862         return(CompositeCommand);
6863       if ((state & ControlMask) == 0)
6864         return(FlipCommand);
6865       return(PasteCommand);
6866     }
6867     case XK_less:
6868       return(HalfSizeCommand);
6869     case XK_minus:
6870       return(OriginalSizeCommand);
6871     case XK_greater:
6872       return(DoubleSizeCommand);
6873     case XK_percent:
6874       return(ResizeCommand);
6875     case XK_at:
6876       return(RefreshCommand);
6877     case XK_bracketleft:
6878       return(ChopCommand);
6879     case XK_h:
6880       return(FlopCommand);
6881     case XK_slash:
6882       return(RotateRightCommand);
6883     case XK_backslash:
6884       return(RotateLeftCommand);
6885     case XK_asterisk:
6886       return(RotateCommand);
6887     case XK_t:
6888       return(TrimCommand);
6889     case XK_H:
6890       return(HueCommand);
6891     case XK_S:
6892       return(SaturationCommand);
6893     case XK_L:
6894       return(BrightnessCommand);
6895     case XK_G:
6896       return(GammaCommand);
6897     case XK_C:
6898       return(SpiffCommand);
6899     case XK_Z:
6900       return(DullCommand);
6901     case XK_N:
6902       return(NormalizeCommand);
6903     case XK_equal:
6904       return(EqualizeCommand);
6905     case XK_asciitilde:
6906       return(NegateCommand);
6907     case XK_period:
6908       return(GrayscaleCommand);
6909     case XK_numbersign:
6910       return(QuantizeCommand);
6911     case XK_F2:
6912       return(DespeckleCommand);
6913     case XK_F3:
6914       return(EmbossCommand);
6915     case XK_F4:
6916       return(ReduceNoiseCommand);
6917     case XK_F5:
6918       return(AddNoiseCommand);
6919     case XK_F6:
6920       return(SharpenCommand);
6921     case XK_F7:
6922       return(BlurCommand);
6923     case XK_F8:
6924       return(ThresholdCommand);
6925     case XK_F9:
6926       return(EdgeDetectCommand);
6927     case XK_F10:
6928       return(SpreadCommand);
6929     case XK_F11:
6930       return(ShadeCommand);
6931     case XK_F12:
6932       return(RaiseCommand);
6933     case XK_F13:
6934       return(SegmentCommand);
6935     case XK_i:
6936     {
6937       if ((state & Mod1Mask) == 0)
6938         return(NullCommand);
6939       return(ImplodeCommand);
6940     }
6941     case XK_w:
6942     {
6943       if ((state & Mod1Mask) == 0)
6944         return(NullCommand);
6945       return(WaveCommand);
6946     }
6947     case XK_m:
6948     {
6949       if ((state & Mod4Mask) == 0)
6950         return(NullCommand);
6951       return(MatteCommand);
6952     }
6953     case XK_b:
6954     {
6955       if ((state & Mod4Mask) == 0)
6956         return(NullCommand);
6957       return(AddBorderCommand);
6958     }
6959     case XK_f:
6960     {
6961       if ((state & Mod4Mask) == 0)
6962         return(NullCommand);
6963       return(AddFrameCommand);
6964     }
6965     case XK_exclam:
6966     {
6967       if ((state & Mod4Mask) == 0)
6968         return(NullCommand);
6969       return(CommentCommand);
6970     }
6971     case XK_a:
6972     {
6973       if ((state & Mod1Mask) != 0)
6974         return(ApplyCommand);
6975       if ((state & Mod4Mask) != 0)
6976         return(AnnotateCommand);
6977       if ((state & ControlMask) == 0)
6978         return(NullCommand);
6979       return(RegionofInterestCommand);
6980     }
6981     case XK_question:
6982       return(InfoCommand);
6983     case XK_plus:
6984       return(ZoomCommand);
6985     case XK_P:
6986     {
6987       if ((state & ShiftMask) == 0)
6988         return(NullCommand);
6989       return(ShowPreviewCommand);
6990     }
6991     case XK_Execute:
6992       return(LaunchCommand);
6993     case XK_F1:
6994       return(HelpCommand);
6995     case XK_Find:
6996       return(BrowseDocumentationCommand);
6997     case XK_Menu:
6998     {
6999       (void) XMapRaised(display,windows->command.id);
7000       return(NullCommand);
7001     }
7002     case XK_Next:
7003     case XK_Prior:
7004     case XK_Home:
7005     case XK_KP_Home:
7006     {
7007       XTranslateImage(display,windows,*image,key_symbol);
7008       return(NullCommand);
7009     }
7010     case XK_Up:
7011     case XK_KP_Up:
7012     case XK_Down:
7013     case XK_KP_Down:
7014     case XK_Left:
7015     case XK_KP_Left:
7016     case XK_Right:
7017     case XK_KP_Right:
7018     {
7019       if ((state & Mod1Mask) != 0)
7020         {
7021           RectangleInfo
7022             crop_info;
7023
7024           /*
7025             Trim one pixel from edge of image.
7026           */
7027           crop_info.x=0;
7028           crop_info.y=0;
7029           crop_info.width=(size_t) windows->image.ximage->width;
7030           crop_info.height=(size_t) windows->image.ximage->height;
7031           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7032             {
7033               if (resource_info->quantum >= (int) crop_info.height)
7034                 resource_info->quantum=(int) crop_info.height-1;
7035               crop_info.height-=resource_info->quantum;
7036             }
7037           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7038             {
7039               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7040                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7041               crop_info.y+=resource_info->quantum;
7042               crop_info.height-=resource_info->quantum;
7043             }
7044           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7045             {
7046               if (resource_info->quantum >= (int) crop_info.width)
7047                 resource_info->quantum=(int) crop_info.width-1;
7048               crop_info.width-=resource_info->quantum;
7049             }
7050           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7051             {
7052               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7053                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7054               crop_info.x+=resource_info->quantum;
7055               crop_info.width-=resource_info->quantum;
7056             }
7057           if ((int) (windows->image.x+windows->image.width) >
7058               (int) crop_info.width)
7059             windows->image.x=(int) (crop_info.width-windows->image.width);
7060           if ((int) (windows->image.y+windows->image.height) >
7061               (int) crop_info.height)
7062             windows->image.y=(int) (crop_info.height-windows->image.height);
7063           XSetCropGeometry(display,windows,&crop_info,*image);
7064           windows->image.window_changes.width=(int) crop_info.width;
7065           windows->image.window_changes.height=(int) crop_info.height;
7066           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7067           (void) XConfigureImage(display,resource_info,windows,*image,
7068             exception);
7069           return(NullCommand);
7070         }
7071       XTranslateImage(display,windows,*image,key_symbol);
7072       return(NullCommand);
7073     }
7074     default:
7075       return(NullCommand);
7076   }
7077   return(NullCommand);
7078 }
7079 \f
7080 /*
7081 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7082 %                                                                             %
7083 %                                                                             %
7084 %                                                                             %
7085 +   X M a g i c k C o m m a n d                                               %
7086 %                                                                             %
7087 %                                                                             %
7088 %                                                                             %
7089 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7090 %
7091 %  XMagickCommand() makes a transform to the image or Image window as
7092 %  specified by a user menu button or keyboard command.
7093 %
7094 %  The format of the XMagickCommand method is:
7095 %
7096 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7097 %        XWindows *windows,const CommandType command,Image **image,
7098 %        ExceptionInfo *exception)
7099 %
7100 %  A description of each parameter follows:
7101 %
7102 %    o display: Specifies a connection to an X server; returned from
7103 %      XOpenDisplay.
7104 %
7105 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7106 %
7107 %    o windows: Specifies a pointer to a XWindows structure.
7108 %
7109 %    o command: Specifies a command to perform.
7110 %
7111 %    o image: the image;  XMagickCommand may transform the image and return a
7112 %      new image pointer.
7113 %
7114 %    o exception: return any errors or warnings in this structure.
7115 %
7116 */
7117 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7118   XWindows *windows,const CommandType command,Image **image,
7119   ExceptionInfo *exception)
7120 {
7121   char
7122     filename[MaxTextExtent],
7123     geometry[MaxTextExtent],
7124     modulate_factors[MaxTextExtent];
7125
7126   GeometryInfo
7127     geometry_info;
7128
7129   Image
7130     *nexus;
7131
7132   ImageInfo
7133     *image_info;
7134
7135   int
7136     x,
7137     y;
7138
7139   MagickStatusType
7140     flags,
7141     status;
7142
7143   QuantizeInfo
7144     quantize_info;
7145
7146   RectangleInfo
7147     page_geometry;
7148
7149   register int
7150     i;
7151
7152   static char
7153     color[MaxTextExtent] = "gray";
7154
7155   unsigned int
7156     height,
7157     width;
7158
7159   /*
7160     Process user command.
7161   */
7162   XCheckRefreshWindows(display,windows);
7163   XImageCache(display,resource_info,windows,command,image,exception);
7164   nexus=NewImageList();
7165   windows->image.window_changes.width=windows->image.ximage->width;
7166   windows->image.window_changes.height=windows->image.ximage->height;
7167   image_info=CloneImageInfo(resource_info->image_info);
7168   SetGeometryInfo(&geometry_info);
7169   GetQuantizeInfo(&quantize_info);
7170   switch (command)
7171   {
7172     case OpenCommand:
7173     {
7174       /*
7175         Load image.
7176       */
7177       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7178       break;
7179     }
7180     case NextCommand:
7181     {
7182       /*
7183         Display next image.
7184       */
7185       for (i=0; i < resource_info->quantum; i++)
7186         XClientMessage(display,windows->image.id,windows->im_protocols,
7187           windows->im_next_image,CurrentTime);
7188       break;
7189     }
7190     case FormerCommand:
7191     {
7192       /*
7193         Display former image.
7194       */
7195       for (i=0; i < resource_info->quantum; i++)
7196         XClientMessage(display,windows->image.id,windows->im_protocols,
7197           windows->im_former_image,CurrentTime);
7198       break;
7199     }
7200     case SelectCommand:
7201     {
7202       int
7203         status;
7204
7205       /*
7206         Select image.
7207       */
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 (status == MagickFalse)
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 (status == MagickFalse)
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=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
7267       if (status != MagickFalse)
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 (resource_info->confirm_exit == MagickFalse)
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 (status == MagickFalse)
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 (status == MagickFalse)
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 (windows->image.orphan != MagickFalse)
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 (windows->image.orphan != MagickFalse)
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 (status == MagickFalse)
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 (status == MagickFalse)
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 (status == MagickFalse)
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 (windows->image.orphan != MagickFalse)
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 (windows->image.orphan != MagickFalse)
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=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7732       /*
7733         Trim image.
7734       */
7735       status=XTrimImage(display,resource_info,windows,*image,exception);
7736       if (status == MagickFalse)
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 (windows->image.orphan != MagickFalse)
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 (windows->image.orphan != MagickFalse)
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 (windows->image.orphan != MagickFalse)
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 (windows->image.orphan != MagickFalse)
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 (windows->image.orphan != MagickFalse)
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 (windows->image.orphan != MagickFalse)
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=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7912       (void) ContrastStretchImage(*image,black_point,white_point,
7913         exception);
7914       XSetCursorState(display,windows,MagickFalse);
7915       if (windows->image.orphan != MagickFalse)
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 (windows->image.orphan != MagickFalse)
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 (windows->image.orphan != MagickFalse)
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 (windows->image.orphan != MagickFalse)
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 (windows->image.orphan != MagickFalse)
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)->matte == MagickFalse ?
8011         GrayscaleType : GrayscaleMatteType,exception);
8012       XSetCursorState(display,windows,MagickFalse);
8013       if (windows->image.orphan != MagickFalse)
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 (windows->image.orphan != MagickFalse)
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=status != 0 ? MagickTrue : MagickFalse;
8075       (void) QuantizeImage(&quantize_info,*image,exception);
8076       XSetCursorState(display,windows,MagickFalse);
8077       if (windows->image.orphan != MagickFalse)
8078         break;
8079       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8080       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8081       break;
8082     }
8083     case DespeckleCommand:
8084     {
8085       Image
8086         *despeckle_image;
8087
8088       /*
8089         Despeckle image.
8090       */
8091       XSetCursorState(display,windows,MagickTrue);
8092       XCheckRefreshWindows(display,windows);
8093       despeckle_image=DespeckleImage(*image,exception);
8094       if (despeckle_image != (Image *) NULL)
8095         {
8096           *image=DestroyImage(*image);
8097           *image=despeckle_image;
8098         }
8099       CatchException(exception);
8100       XSetCursorState(display,windows,MagickFalse);
8101       if (windows->image.orphan != MagickFalse)
8102         break;
8103       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8104       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8105       break;
8106     }
8107     case EmbossCommand:
8108     {
8109       Image
8110         *emboss_image;
8111
8112       static char
8113         radius[MaxTextExtent] = "0.0x1.0";
8114
8115       /*
8116         Query user for emboss radius.
8117       */
8118       (void) XDialogWidget(display,windows,"Emboss",
8119         "Enter the emboss radius and standard deviation:",radius);
8120       if (*radius == '\0')
8121         break;
8122       /*
8123         Reduce noise in the image.
8124       */
8125       XSetCursorState(display,windows,MagickTrue);
8126       XCheckRefreshWindows(display,windows);
8127       flags=ParseGeometry(radius,&geometry_info);
8128       if ((flags & SigmaValue) == 0)
8129         geometry_info.sigma=1.0;
8130       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8131         exception);
8132       if (emboss_image != (Image *) NULL)
8133         {
8134           *image=DestroyImage(*image);
8135           *image=emboss_image;
8136         }
8137       CatchException(exception);
8138       XSetCursorState(display,windows,MagickFalse);
8139       if (windows->image.orphan != MagickFalse)
8140         break;
8141       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8142       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8143       break;
8144     }
8145     case ReduceNoiseCommand:
8146     {
8147       Image
8148         *noise_image;
8149
8150       static char
8151         radius[MaxTextExtent] = "0";
8152
8153       /*
8154         Query user for noise radius.
8155       */
8156       (void) XDialogWidget(display,windows,"Reduce Noise",
8157         "Enter the noise radius:",radius);
8158       if (*radius == '\0')
8159         break;
8160       /*
8161         Reduce noise in the image.
8162       */
8163       XSetCursorState(display,windows,MagickTrue);
8164       XCheckRefreshWindows(display,windows);
8165       flags=ParseGeometry(radius,&geometry_info);
8166       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8167         geometry_info.rho,(size_t) geometry_info.rho,exception);
8168       if (noise_image != (Image *) NULL)
8169         {
8170           *image=DestroyImage(*image);
8171           *image=noise_image;
8172         }
8173       CatchException(exception);
8174       XSetCursorState(display,windows,MagickFalse);
8175       if (windows->image.orphan != MagickFalse)
8176         break;
8177       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8178       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8179       break;
8180     }
8181     case AddNoiseCommand:
8182     {
8183       char
8184         **noises;
8185
8186       Image
8187         *noise_image;
8188
8189       static char
8190         noise_type[MaxTextExtent] = "Gaussian";
8191
8192       /*
8193         Add noise to the image.
8194       */
8195       noises=GetCommandOptions(MagickNoiseOptions);
8196       if (noises == (char **) NULL)
8197         break;
8198       XListBrowserWidget(display,windows,&windows->widget,
8199         (const char **) noises,"Add Noise",
8200         "Select a type of noise to add to your image:",noise_type);
8201       noises=DestroyStringList(noises);
8202       if (*noise_type == '\0')
8203         break;
8204       XSetCursorState(display,windows,MagickTrue);
8205       XCheckRefreshWindows(display,windows);
8206       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8207         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8208       if (noise_image != (Image *) NULL)
8209         {
8210           *image=DestroyImage(*image);
8211           *image=noise_image;
8212         }
8213       CatchException(exception);
8214       XSetCursorState(display,windows,MagickFalse);
8215       if (windows->image.orphan != MagickFalse)
8216         break;
8217       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8218       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8219       break;
8220     }
8221     case SharpenCommand:
8222     {
8223       Image
8224         *sharp_image;
8225
8226       static char
8227         radius[MaxTextExtent] = "0.0x1.0";
8228
8229       /*
8230         Query user for sharpen radius.
8231       */
8232       (void) XDialogWidget(display,windows,"Sharpen",
8233         "Enter the sharpen radius and standard deviation:",radius);
8234       if (*radius == '\0')
8235         break;
8236       /*
8237         Sharpen image scanlines.
8238       */
8239       XSetCursorState(display,windows,MagickTrue);
8240       XCheckRefreshWindows(display,windows);
8241       flags=ParseGeometry(radius,&geometry_info);
8242       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8243         geometry_info.xi,exception);
8244       if (sharp_image != (Image *) NULL)
8245         {
8246           *image=DestroyImage(*image);
8247           *image=sharp_image;
8248         }
8249       CatchException(exception);
8250       XSetCursorState(display,windows,MagickFalse);
8251       if (windows->image.orphan != MagickFalse)
8252         break;
8253       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8254       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8255       break;
8256     }
8257     case BlurCommand:
8258     {
8259       Image
8260         *blur_image;
8261
8262       static char
8263         radius[MaxTextExtent] = "0.0x1.0";
8264
8265       /*
8266         Query user for blur radius.
8267       */
8268       (void) XDialogWidget(display,windows,"Blur",
8269         "Enter the blur radius and standard deviation:",radius);
8270       if (*radius == '\0')
8271         break;
8272       /*
8273         Blur an image.
8274       */
8275       XSetCursorState(display,windows,MagickTrue);
8276       XCheckRefreshWindows(display,windows);
8277       flags=ParseGeometry(radius,&geometry_info);
8278       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8279         geometry_info.xi,exception);
8280       if (blur_image != (Image *) NULL)
8281         {
8282           *image=DestroyImage(*image);
8283           *image=blur_image;
8284         }
8285       CatchException(exception);
8286       XSetCursorState(display,windows,MagickFalse);
8287       if (windows->image.orphan != MagickFalse)
8288         break;
8289       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8290       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8291       break;
8292     }
8293     case ThresholdCommand:
8294     {
8295       double
8296         threshold;
8297
8298       static char
8299         factor[MaxTextExtent] = "128";
8300
8301       /*
8302         Query user for threshold value.
8303       */
8304       (void) XDialogWidget(display,windows,"Threshold",
8305         "Enter threshold value:",factor);
8306       if (*factor == '\0')
8307         break;
8308       /*
8309         Gamma correct image.
8310       */
8311       XSetCursorState(display,windows,MagickTrue);
8312       XCheckRefreshWindows(display,windows);
8313       threshold=SiPrefixToDouble(factor,QuantumRange);
8314       (void) BilevelImage(*image,threshold,exception);
8315       XSetCursorState(display,windows,MagickFalse);
8316       if (windows->image.orphan != MagickFalse)
8317         break;
8318       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8319       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8320       break;
8321     }
8322     case EdgeDetectCommand:
8323     {
8324       Image
8325         *edge_image;
8326
8327       static char
8328         radius[MaxTextExtent] = "0";
8329
8330       /*
8331         Query user for edge factor.
8332       */
8333       (void) XDialogWidget(display,windows,"Detect Edges",
8334         "Enter the edge detect radius:",radius);
8335       if (*radius == '\0')
8336         break;
8337       /*
8338         Detect edge in image.
8339       */
8340       XSetCursorState(display,windows,MagickTrue);
8341       XCheckRefreshWindows(display,windows);
8342       flags=ParseGeometry(radius,&geometry_info);
8343       edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8344         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 (windows->image.orphan != MagickFalse)
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,geometry_info.sigma,
8380         exception);
8381       if (spread_image != (Image *) NULL)
8382         {
8383           *image=DestroyImage(*image);
8384           *image=spread_image;
8385         }
8386       CatchException(exception);
8387       XSetCursorState(display,windows,MagickFalse);
8388       if (windows->image.orphan != MagickFalse)
8389         break;
8390       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8391       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8392       break;
8393     }
8394     case ShadeCommand:
8395     {
8396       Image
8397         *shade_image;
8398
8399       int
8400         status;
8401
8402       static char
8403         geometry[MaxTextExtent] = "30x30";
8404
8405       /*
8406         Query user for the shade geometry.
8407       */
8408       status=XDialogWidget(display,windows,"Shade",
8409         "Enter the azimuth and elevation of the light source:",geometry);
8410       if (*geometry == '\0')
8411         break;
8412       /*
8413         Shade image pixels.
8414       */
8415       XSetCursorState(display,windows,MagickTrue);
8416       XCheckRefreshWindows(display,windows);
8417       flags=ParseGeometry(geometry,&geometry_info);
8418       if ((flags & SigmaValue) == 0)
8419         geometry_info.sigma=1.0;
8420       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8421         geometry_info.rho,geometry_info.sigma,exception);
8422       if (shade_image != (Image *) NULL)
8423         {
8424           *image=DestroyImage(*image);
8425           *image=shade_image;
8426         }
8427       CatchException(exception);
8428       XSetCursorState(display,windows,MagickFalse);
8429       if (windows->image.orphan != MagickFalse)
8430         break;
8431       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8432       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8433       break;
8434     }
8435     case RaiseCommand:
8436     {
8437       static char
8438         bevel_width[MaxTextExtent] = "10";
8439
8440       /*
8441         Query user for bevel width.
8442       */
8443       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8444       if (*bevel_width == '\0')
8445         break;
8446       /*
8447         Raise an image.
8448       */
8449       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8450         exception);
8451       XSetCursorState(display,windows,MagickTrue);
8452       XCheckRefreshWindows(display,windows);
8453       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8454         exception);
8455       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8456       XSetCursorState(display,windows,MagickFalse);
8457       if (windows->image.orphan != MagickFalse)
8458         break;
8459       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8460       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8461       break;
8462     }
8463     case SegmentCommand:
8464     {
8465       static char
8466         threshold[MaxTextExtent] = "1.0x1.5";
8467
8468       /*
8469         Query user for smoothing threshold.
8470       */
8471       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8472         threshold);
8473       if (*threshold == '\0')
8474         break;
8475       /*
8476         Segment an image.
8477       */
8478       XSetCursorState(display,windows,MagickTrue);
8479       XCheckRefreshWindows(display,windows);
8480       flags=ParseGeometry(threshold,&geometry_info);
8481       if ((flags & SigmaValue) == 0)
8482         geometry_info.sigma=1.0;
8483       (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8484         geometry_info.sigma,exception);
8485       XSetCursorState(display,windows,MagickFalse);
8486       if (windows->image.orphan != MagickFalse)
8487         break;
8488       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8489       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8490       break;
8491     }
8492     case SepiaToneCommand:
8493     {
8494       double
8495         threshold;
8496
8497       Image
8498         *sepia_image;
8499
8500       static char
8501         factor[MaxTextExtent] = "80%";
8502
8503       /*
8504         Query user for sepia-tone factor.
8505       */
8506       (void) XDialogWidget(display,windows,"Sepia Tone",
8507         "Enter the sepia tone factor (0 - 99.9%):",factor);
8508       if (*factor == '\0')
8509         break;
8510       /*
8511         Sepia tone image pixels.
8512       */
8513       XSetCursorState(display,windows,MagickTrue);
8514       XCheckRefreshWindows(display,windows);
8515       threshold=SiPrefixToDouble(factor,QuantumRange);
8516       sepia_image=SepiaToneImage(*image,threshold,exception);
8517       if (sepia_image != (Image *) NULL)
8518         {
8519           *image=DestroyImage(*image);
8520           *image=sepia_image;
8521         }
8522       CatchException(exception);
8523       XSetCursorState(display,windows,MagickFalse);
8524       if (windows->image.orphan != MagickFalse)
8525         break;
8526       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8527       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8528       break;
8529     }
8530     case SolarizeCommand:
8531     {
8532       double
8533         threshold;
8534
8535       static char
8536         factor[MaxTextExtent] = "60%";
8537
8538       /*
8539         Query user for solarize factor.
8540       */
8541       (void) XDialogWidget(display,windows,"Solarize",
8542         "Enter the solarize factor (0 - 99.9%):",factor);
8543       if (*factor == '\0')
8544         break;
8545       /*
8546         Solarize image pixels.
8547       */
8548       XSetCursorState(display,windows,MagickTrue);
8549       XCheckRefreshWindows(display,windows);
8550       threshold=SiPrefixToDouble(factor,QuantumRange);
8551       (void) SolarizeImage(*image,threshold,exception);
8552       XSetCursorState(display,windows,MagickFalse);
8553       if (windows->image.orphan != MagickFalse)
8554         break;
8555       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8556       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8557       break;
8558     }
8559     case SwirlCommand:
8560     {
8561       Image
8562         *swirl_image;
8563
8564       static char
8565         degrees[MaxTextExtent] = "60";
8566
8567       /*
8568         Query user for swirl angle.
8569       */
8570       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8571         degrees);
8572       if (*degrees == '\0')
8573         break;
8574       /*
8575         Swirl image pixels about the center.
8576       */
8577       XSetCursorState(display,windows,MagickTrue);
8578       XCheckRefreshWindows(display,windows);
8579       flags=ParseGeometry(degrees,&geometry_info);
8580       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8581         exception);
8582       if (swirl_image != (Image *) NULL)
8583         {
8584           *image=DestroyImage(*image);
8585           *image=swirl_image;
8586         }
8587       CatchException(exception);
8588       XSetCursorState(display,windows,MagickFalse);
8589       if (windows->image.orphan != MagickFalse)
8590         break;
8591       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8592       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8593       break;
8594     }
8595     case ImplodeCommand:
8596     {
8597       Image
8598         *implode_image;
8599
8600       static char
8601         factor[MaxTextExtent] = "0.3";
8602
8603       /*
8604         Query user for implode factor.
8605       */
8606       (void) XDialogWidget(display,windows,"Implode",
8607         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8608       if (*factor == '\0')
8609         break;
8610       /*
8611         Implode image pixels about the center.
8612       */
8613       XSetCursorState(display,windows,MagickTrue);
8614       XCheckRefreshWindows(display,windows);
8615       flags=ParseGeometry(factor,&geometry_info);
8616       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8617         exception);
8618       if (implode_image != (Image *) NULL)
8619         {
8620           *image=DestroyImage(*image);
8621           *image=implode_image;
8622         }
8623       CatchException(exception);
8624       XSetCursorState(display,windows,MagickFalse);
8625       if (windows->image.orphan != MagickFalse)
8626         break;
8627       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8628       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8629       break;
8630     }
8631     case VignetteCommand:
8632     {
8633       Image
8634         *vignette_image;
8635
8636       static char
8637         geometry[MaxTextExtent] = "0x20";
8638
8639       /*
8640         Query user for the vignette geometry.
8641       */
8642       (void) XDialogWidget(display,windows,"Vignette",
8643         "Enter the radius, sigma, and x and y offsets:",geometry);
8644       if (*geometry == '\0')
8645         break;
8646       /*
8647         Soften the edges of the image in vignette style
8648       */
8649       XSetCursorState(display,windows,MagickTrue);
8650       XCheckRefreshWindows(display,windows);
8651       flags=ParseGeometry(geometry,&geometry_info);
8652       if ((flags & SigmaValue) == 0)
8653         geometry_info.sigma=1.0;
8654       if ((flags & XiValue) == 0)
8655         geometry_info.xi=0.1*(*image)->columns;
8656       if ((flags & PsiValue) == 0)
8657         geometry_info.psi=0.1*(*image)->rows;
8658       vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8659         (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8660         0.5),exception);
8661       if (vignette_image != (Image *) NULL)
8662         {
8663           *image=DestroyImage(*image);
8664           *image=vignette_image;
8665         }
8666       CatchException(exception);
8667       XSetCursorState(display,windows,MagickFalse);
8668       if (windows->image.orphan != MagickFalse)
8669         break;
8670       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8671       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8672       break;
8673     }
8674     case WaveCommand:
8675     {
8676       Image
8677         *wave_image;
8678
8679       static char
8680         geometry[MaxTextExtent] = "25x150";
8681
8682       /*
8683         Query user for the wave geometry.
8684       */
8685       (void) XDialogWidget(display,windows,"Wave",
8686         "Enter the amplitude and length of the wave:",geometry);
8687       if (*geometry == '\0')
8688         break;
8689       /*
8690         Alter an image along a sine wave.
8691       */
8692       XSetCursorState(display,windows,MagickTrue);
8693       XCheckRefreshWindows(display,windows);
8694       flags=ParseGeometry(geometry,&geometry_info);
8695       if ((flags & SigmaValue) == 0)
8696         geometry_info.sigma=1.0;
8697       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8698         (*image)->interpolate,exception);
8699       if (wave_image != (Image *) NULL)
8700         {
8701           *image=DestroyImage(*image);
8702           *image=wave_image;
8703         }
8704       CatchException(exception);
8705       XSetCursorState(display,windows,MagickFalse);
8706       if (windows->image.orphan != MagickFalse)
8707         break;
8708       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8709       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8710       break;
8711     }
8712     case OilPaintCommand:
8713     {
8714       Image
8715         *paint_image;
8716
8717       static char
8718         radius[MaxTextExtent] = "0";
8719
8720       /*
8721         Query user for circular neighborhood radius.
8722       */
8723       (void) XDialogWidget(display,windows,"Oil Paint",
8724         "Enter the mask radius:",radius);
8725       if (*radius == '\0')
8726         break;
8727       /*
8728         OilPaint image scanlines.
8729       */
8730       XSetCursorState(display,windows,MagickTrue);
8731       XCheckRefreshWindows(display,windows);
8732       flags=ParseGeometry(radius,&geometry_info);
8733       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8734         exception);
8735       if (paint_image != (Image *) NULL)
8736         {
8737           *image=DestroyImage(*image);
8738           *image=paint_image;
8739         }
8740       CatchException(exception);
8741       XSetCursorState(display,windows,MagickFalse);
8742       if (windows->image.orphan != MagickFalse)
8743         break;
8744       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8745       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8746       break;
8747     }
8748     case CharcoalDrawCommand:
8749     {
8750       Image
8751         *charcoal_image;
8752
8753       static char
8754         radius[MaxTextExtent] = "0x1";
8755
8756       /*
8757         Query user for charcoal radius.
8758       */
8759       (void) XDialogWidget(display,windows,"Charcoal Draw",
8760         "Enter the charcoal radius and sigma:",radius);
8761       if (*radius == '\0')
8762         break;
8763       /*
8764         Charcoal the image.
8765       */
8766       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8767         exception);
8768       XSetCursorState(display,windows,MagickTrue);
8769       XCheckRefreshWindows(display,windows);
8770       flags=ParseGeometry(radius,&geometry_info);
8771       if ((flags & SigmaValue) == 0)
8772         geometry_info.sigma=geometry_info.rho;
8773       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8774         geometry_info.xi,exception);
8775       if (charcoal_image != (Image *) NULL)
8776         {
8777           *image=DestroyImage(*image);
8778           *image=charcoal_image;
8779         }
8780       CatchException(exception);
8781       XSetCursorState(display,windows,MagickFalse);
8782       if (windows->image.orphan != MagickFalse)
8783         break;
8784       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8785       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8786       break;
8787     }
8788     case AnnotateCommand:
8789     {
8790       /*
8791         Annotate the image with text.
8792       */
8793       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8794       if (status == MagickFalse)
8795         {
8796           XNoticeWidget(display,windows,"Unable to annotate X image",
8797             (*image)->filename);
8798           break;
8799         }
8800       break;
8801     }
8802     case DrawCommand:
8803     {
8804       /*
8805         Draw image.
8806       */
8807       status=XDrawEditImage(display,resource_info,windows,image,exception);
8808       if (status == MagickFalse)
8809         {
8810           XNoticeWidget(display,windows,"Unable to draw on the X image",
8811             (*image)->filename);
8812           break;
8813         }
8814       break;
8815     }
8816     case ColorCommand:
8817     {
8818       /*
8819         Color edit.
8820       */
8821       status=XColorEditImage(display,resource_info,windows,image,exception);
8822       if (status == MagickFalse)
8823         {
8824           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8825             (*image)->filename);
8826           break;
8827         }
8828       break;
8829     }
8830     case MatteCommand:
8831     {
8832       /*
8833         Matte edit.
8834       */
8835       status=XMatteEditImage(display,resource_info,windows,image,exception);
8836       if (status == MagickFalse)
8837         {
8838           XNoticeWidget(display,windows,"Unable to matte edit X image",
8839             (*image)->filename);
8840           break;
8841         }
8842       break;
8843     }
8844     case CompositeCommand:
8845     {
8846       /*
8847         Composite image.
8848       */
8849       status=XCompositeImage(display,resource_info,windows,*image,
8850         exception);
8851       if (status == MagickFalse)
8852         {
8853           XNoticeWidget(display,windows,"Unable to composite X image",
8854             (*image)->filename);
8855           break;
8856         }
8857       break;
8858     }
8859     case AddBorderCommand:
8860     {
8861       Image
8862         *border_image;
8863
8864       static char
8865         geometry[MaxTextExtent] = "6x6";
8866
8867       /*
8868         Query user for border color and geometry.
8869       */
8870       XColorBrowserWidget(display,windows,"Select",color);
8871       if (*color == '\0')
8872         break;
8873       (void) XDialogWidget(display,windows,"Add Border",
8874         "Enter border geometry:",geometry);
8875       if (*geometry == '\0')
8876         break;
8877       /*
8878         Add a border to the image.
8879       */
8880       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8881         exception);
8882       XSetCursorState(display,windows,MagickTrue);
8883       XCheckRefreshWindows(display,windows);
8884       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8885         exception);
8886       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8887         exception);
8888       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8889         exception);
8890       if (border_image != (Image *) NULL)
8891         {
8892           *image=DestroyImage(*image);
8893           *image=border_image;
8894         }
8895       CatchException(exception);
8896       XSetCursorState(display,windows,MagickFalse);
8897       if (windows->image.orphan != MagickFalse)
8898         break;
8899       windows->image.window_changes.width=(int) (*image)->columns;
8900       windows->image.window_changes.height=(int) (*image)->rows;
8901       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8902       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8903       break;
8904     }
8905     case AddFrameCommand:
8906     {
8907       FrameInfo
8908         frame_info;
8909
8910       Image
8911         *frame_image;
8912
8913       static char
8914         geometry[MaxTextExtent] = "6x6";
8915
8916       /*
8917         Query user for frame color and geometry.
8918       */
8919       XColorBrowserWidget(display,windows,"Select",color);
8920       if (*color == '\0')
8921         break;
8922       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8923         geometry);
8924       if (*geometry == '\0')
8925         break;
8926       /*
8927         Surround image with an ornamental border.
8928       */
8929       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8930         exception);
8931       XSetCursorState(display,windows,MagickTrue);
8932       XCheckRefreshWindows(display,windows);
8933       (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8934         exception);
8935       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8936         exception);
8937       frame_info.width=page_geometry.width;
8938       frame_info.height=page_geometry.height;
8939       frame_info.outer_bevel=page_geometry.x;
8940       frame_info.inner_bevel=page_geometry.y;
8941       frame_info.x=(ssize_t) frame_info.width;
8942       frame_info.y=(ssize_t) frame_info.height;
8943       frame_info.width=(*image)->columns+2*frame_info.width;
8944       frame_info.height=(*image)->rows+2*frame_info.height;
8945       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8946       if (frame_image != (Image *) NULL)
8947         {
8948           *image=DestroyImage(*image);
8949           *image=frame_image;
8950         }
8951       CatchException(exception);
8952       XSetCursorState(display,windows,MagickFalse);
8953       if (windows->image.orphan != MagickFalse)
8954         break;
8955       windows->image.window_changes.width=(int) (*image)->columns;
8956       windows->image.window_changes.height=(int) (*image)->rows;
8957       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8958       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8959       break;
8960     }
8961     case CommentCommand:
8962     {
8963       const char
8964         *value;
8965
8966       FILE
8967         *file;
8968
8969       int
8970         unique_file;
8971
8972       /*
8973         Edit image comment.
8974       */
8975       unique_file=AcquireUniqueFileResource(image_info->filename);
8976       if (unique_file == -1)
8977         XNoticeWidget(display,windows,"Unable to edit image comment",
8978           image_info->filename);
8979       value=GetImageProperty(*image,"comment",exception);
8980       if (value == (char *) NULL)
8981         unique_file=close(unique_file)-1;
8982       else
8983         {
8984           register const char
8985             *p;
8986
8987           file=fdopen(unique_file,"w");
8988           if (file == (FILE *) NULL)
8989             {
8990               XNoticeWidget(display,windows,"Unable to edit image comment",
8991                 image_info->filename);
8992               break;
8993             }
8994           for (p=value; *p != '\0'; p++)
8995             (void) fputc((int) *p,file);
8996           (void) fputc('\n',file);
8997           (void) fclose(file);
8998         }
8999       XSetCursorState(display,windows,MagickTrue);
9000       XCheckRefreshWindows(display,windows);
9001       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
9002         exception);
9003       if (status == MagickFalse)
9004         XNoticeWidget(display,windows,"Unable to edit image comment",
9005           (char *) NULL);
9006       else
9007         {
9008           char
9009             *comment;
9010
9011           comment=FileToString(image_info->filename,~0UL,exception);
9012           if (comment != (char *) NULL)
9013             {
9014               (void) SetImageProperty(*image,"comment",comment,exception);
9015               (*image)->taint=MagickTrue;
9016             }
9017         }
9018       (void) RelinquishUniqueFileResource(image_info->filename);
9019       XSetCursorState(display,windows,MagickFalse);
9020       break;
9021     }
9022     case LaunchCommand:
9023     {
9024       /*
9025         Launch program.
9026       */
9027       XSetCursorState(display,windows,MagickTrue);
9028       XCheckRefreshWindows(display,windows);
9029       (void) AcquireUniqueFilename(filename);
9030       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9031         filename);
9032       status=WriteImage(image_info,*image,exception);
9033       if (status == MagickFalse)
9034         XNoticeWidget(display,windows,"Unable to launch image editor",
9035           (char *) NULL);
9036       else
9037         {
9038           nexus=ReadImage(resource_info->image_info,exception);
9039           CatchException(exception);
9040           XClientMessage(display,windows->image.id,windows->im_protocols,
9041             windows->im_next_image,CurrentTime);
9042         }
9043       (void) RelinquishUniqueFileResource(filename);
9044       XSetCursorState(display,windows,MagickFalse);
9045       break;
9046     }
9047     case RegionofInterestCommand:
9048     {
9049       /*
9050         Apply an image processing technique to a region of interest.
9051       */
9052       (void) XROIImage(display,resource_info,windows,image,exception);
9053       break;
9054     }
9055     case InfoCommand:
9056       break;
9057     case ZoomCommand:
9058     {
9059       /*
9060         Zoom image.
9061       */
9062       if (windows->magnify.mapped != MagickFalse)
9063         (void) XRaiseWindow(display,windows->magnify.id);
9064       else
9065         {
9066           /*
9067             Make magnify image.
9068           */
9069           XSetCursorState(display,windows,MagickTrue);
9070           (void) XMapRaised(display,windows->magnify.id);
9071           XSetCursorState(display,windows,MagickFalse);
9072         }
9073       break;
9074     }
9075     case ShowPreviewCommand:
9076     {
9077       char
9078         **previews;
9079
9080       Image
9081         *preview_image;
9082
9083       static char
9084         preview_type[MaxTextExtent] = "Gamma";
9085
9086       /*
9087         Select preview type from menu.
9088       */
9089       previews=GetCommandOptions(MagickPreviewOptions);
9090       if (previews == (char **) NULL)
9091         break;
9092       XListBrowserWidget(display,windows,&windows->widget,
9093         (const char **) previews,"Preview",
9094         "Select an enhancement, effect, or F/X:",preview_type);
9095       previews=DestroyStringList(previews);
9096       if (*preview_type == '\0')
9097         break;
9098       /*
9099         Show image preview.
9100       */
9101       XSetCursorState(display,windows,MagickTrue);
9102       XCheckRefreshWindows(display,windows);
9103       image_info->preview_type=(PreviewType)
9104         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9105       image_info->group=(ssize_t) windows->image.id;
9106       (void) DeleteImageProperty(*image,"label");
9107       (void) SetImageProperty(*image,"label","Preview",exception);
9108       (void) AcquireUniqueFilename(filename);
9109       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9110         filename);
9111       status=WriteImage(image_info,*image,exception);
9112       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9113       preview_image=ReadImage(image_info,exception);
9114       (void) RelinquishUniqueFileResource(filename);
9115       if (preview_image == (Image *) NULL)
9116         break;
9117       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9118         filename);
9119       status=WriteImage(image_info,preview_image,exception);
9120       preview_image=DestroyImage(preview_image);
9121       if (status == MagickFalse)
9122         XNoticeWidget(display,windows,"Unable to show image preview",
9123           (*image)->filename);
9124       XDelay(display,1500);
9125       XSetCursorState(display,windows,MagickFalse);
9126       break;
9127     }
9128     case ShowHistogramCommand:
9129     {
9130       Image
9131         *histogram_image;
9132
9133       /*
9134         Show image histogram.
9135       */
9136       XSetCursorState(display,windows,MagickTrue);
9137       XCheckRefreshWindows(display,windows);
9138       image_info->group=(ssize_t) windows->image.id;
9139       (void) DeleteImageProperty(*image,"label");
9140       (void) SetImageProperty(*image,"label","Histogram",exception);
9141       (void) AcquireUniqueFilename(filename);
9142       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9143         filename);
9144       status=WriteImage(image_info,*image,exception);
9145       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9146       histogram_image=ReadImage(image_info,exception);
9147       (void) RelinquishUniqueFileResource(filename);
9148       if (histogram_image == (Image *) NULL)
9149         break;
9150       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9151         "show:%s",filename);
9152       status=WriteImage(image_info,histogram_image,exception);
9153       histogram_image=DestroyImage(histogram_image);
9154       if (status == MagickFalse)
9155         XNoticeWidget(display,windows,"Unable to show histogram",
9156           (*image)->filename);
9157       XDelay(display,1500);
9158       XSetCursorState(display,windows,MagickFalse);
9159       break;
9160     }
9161     case ShowMatteCommand:
9162     {
9163       Image
9164         *matte_image;
9165
9166       if ((*image)->matte == MagickFalse)
9167         {
9168           XNoticeWidget(display,windows,
9169             "Image does not have any matte information",(*image)->filename);
9170           break;
9171         }
9172       /*
9173         Show image matte.
9174       */
9175       XSetCursorState(display,windows,MagickTrue);
9176       XCheckRefreshWindows(display,windows);
9177       image_info->group=(ssize_t) windows->image.id;
9178       (void) DeleteImageProperty(*image,"label");
9179       (void) SetImageProperty(*image,"label","Matte",exception);
9180       (void) AcquireUniqueFilename(filename);
9181       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9182         filename);
9183       status=WriteImage(image_info,*image,exception);
9184       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9185       matte_image=ReadImage(image_info,exception);
9186       (void) RelinquishUniqueFileResource(filename);
9187       if (matte_image == (Image *) NULL)
9188         break;
9189       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9190         filename);
9191       status=WriteImage(image_info,matte_image,exception);
9192       matte_image=DestroyImage(matte_image);
9193       if (status == MagickFalse)
9194         XNoticeWidget(display,windows,"Unable to show matte",
9195           (*image)->filename);
9196       XDelay(display,1500);
9197       XSetCursorState(display,windows,MagickFalse);
9198       break;
9199     }
9200     case BackgroundCommand:
9201     {
9202       /*
9203         Background image.
9204       */
9205       status=XBackgroundImage(display,resource_info,windows,image,exception);
9206       if (status == MagickFalse)
9207         break;
9208       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9209       if (nexus != (Image *) NULL)
9210         XClientMessage(display,windows->image.id,windows->im_protocols,
9211           windows->im_next_image,CurrentTime);
9212       break;
9213     }
9214     case SlideShowCommand:
9215     {
9216       static char
9217         delay[MaxTextExtent] = "5";
9218
9219       /*
9220         Display next image after pausing.
9221       */
9222       (void) XDialogWidget(display,windows,"Slide Show",
9223         "Pause how many 1/100ths of a second between images:",delay);
9224       if (*delay == '\0')
9225         break;
9226       resource_info->delay=StringToUnsignedLong(delay);
9227       XClientMessage(display,windows->image.id,windows->im_protocols,
9228         windows->im_next_image,CurrentTime);
9229       break;
9230     }
9231     case PreferencesCommand:
9232     {
9233       /*
9234         Set user preferences.
9235       */
9236       status=XPreferencesWidget(display,resource_info,windows);
9237       if (status == MagickFalse)
9238         break;
9239       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9240       if (nexus != (Image *) NULL)
9241         XClientMessage(display,windows->image.id,windows->im_protocols,
9242           windows->im_next_image,CurrentTime);
9243       break;
9244     }
9245     case HelpCommand:
9246     {
9247       /*
9248         User requested help.
9249       */
9250       XTextViewWidget(display,resource_info,windows,MagickFalse,
9251         "Help Viewer - Display",DisplayHelp);
9252       break;
9253     }
9254     case BrowseDocumentationCommand:
9255     {
9256       Atom
9257         mozilla_atom;
9258
9259       Window
9260         mozilla_window,
9261         root_window;
9262
9263       /*
9264         Browse the ImageMagick documentation.
9265       */
9266       root_window=XRootWindow(display,XDefaultScreen(display));
9267       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9268       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9269       if (mozilla_window != (Window) NULL)
9270         {
9271           char
9272             command[MaxTextExtent],
9273             *url;
9274
9275           /*
9276             Display documentation using Netscape remote control.
9277           */
9278           url=GetMagickHomeURL();
9279           (void) FormatLocaleString(command,MaxTextExtent,
9280             "openurl(%s,new-tab)",url);
9281           url=DestroyString(url);
9282           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9283           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9284             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9285           XSetCursorState(display,windows,MagickFalse);
9286           break;
9287         }
9288       XSetCursorState(display,windows,MagickTrue);
9289       XCheckRefreshWindows(display,windows);
9290       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9291         exception);
9292       if (status == MagickFalse)
9293         XNoticeWidget(display,windows,"Unable to browse documentation",
9294           (char *) NULL);
9295       XDelay(display,1500);
9296       XSetCursorState(display,windows,MagickFalse);
9297       break;
9298     }
9299     case VersionCommand:
9300     {
9301       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9302         GetMagickCopyright());
9303       break;
9304     }
9305     case SaveToUndoBufferCommand:
9306       break;
9307     default:
9308     {
9309       (void) XBell(display,0);
9310       break;
9311     }
9312   }
9313   image_info=DestroyImageInfo(image_info);
9314   return(nexus);
9315 }
9316 \f
9317 /*
9318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9319 %                                                                             %
9320 %                                                                             %
9321 %                                                                             %
9322 +   X M a g n i f y I m a g e                                                 %
9323 %                                                                             %
9324 %                                                                             %
9325 %                                                                             %
9326 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9327 %
9328 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9329 %  The magnified portion is displayed in a separate window.
9330 %
9331 %  The format of the XMagnifyImage method is:
9332 %
9333 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9334 %        ExceptionInfo *exception)
9335 %
9336 %  A description of each parameter follows:
9337 %
9338 %    o display: Specifies a connection to an X server;  returned from
9339 %      XOpenDisplay.
9340 %
9341 %    o windows: Specifies a pointer to a XWindows structure.
9342 %
9343 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9344 %      the entire image is refreshed.
9345 %
9346 %    o exception: return any errors or warnings in this structure.
9347 %
9348 */
9349 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9350   ExceptionInfo *exception)
9351 {
9352   char
9353     text[MaxTextExtent];
9354
9355   register int
9356     x,
9357     y;
9358
9359   size_t
9360     state;
9361
9362   /*
9363     Update magnified image until the mouse button is released.
9364   */
9365   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9366   state=DefaultState;
9367   x=event->xbutton.x;
9368   y=event->xbutton.y;
9369   windows->magnify.x=(int) windows->image.x+x;
9370   windows->magnify.y=(int) windows->image.y+y;
9371   do
9372   {
9373     /*
9374       Map and unmap Info widget as text cursor crosses its boundaries.
9375     */
9376     if (windows->info.mapped != MagickFalse)
9377       {
9378         if ((x < (int) (windows->info.x+windows->info.width)) &&
9379             (y < (int) (windows->info.y+windows->info.height)))
9380           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9381       }
9382     else
9383       if ((x > (int) (windows->info.x+windows->info.width)) ||
9384           (y > (int) (windows->info.y+windows->info.height)))
9385         (void) XMapWindow(display,windows->info.id);
9386     if (windows->info.mapped != MagickFalse)
9387       {
9388         /*
9389           Display pointer position.
9390         */
9391         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9392           windows->magnify.x,windows->magnify.y);
9393         XInfoWidget(display,windows,text);
9394       }
9395     /*
9396       Wait for next event.
9397     */
9398     XScreenEvent(display,windows,event,exception);
9399     switch (event->type)
9400     {
9401       case ButtonPress:
9402         break;
9403       case ButtonRelease:
9404       {
9405         /*
9406           User has finished magnifying image.
9407         */
9408         x=event->xbutton.x;
9409         y=event->xbutton.y;
9410         state|=ExitState;
9411         break;
9412       }
9413       case Expose:
9414         break;
9415       case MotionNotify:
9416       {
9417         x=event->xmotion.x;
9418         y=event->xmotion.y;
9419         break;
9420       }
9421       default:
9422         break;
9423     }
9424     /*
9425       Check boundary conditions.
9426     */
9427     if (x < 0)
9428       x=0;
9429     else
9430       if (x >= (int) windows->image.width)
9431         x=(int) windows->image.width-1;
9432     if (y < 0)
9433       y=0;
9434     else
9435      if (y >= (int) windows->image.height)
9436        y=(int) windows->image.height-1;
9437   } while ((state & ExitState) == 0);
9438   /*
9439     Display magnified image.
9440   */
9441   XSetCursorState(display,windows,MagickFalse);
9442 }
9443 \f
9444 /*
9445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9446 %                                                                             %
9447 %                                                                             %
9448 %                                                                             %
9449 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9450 %                                                                             %
9451 %                                                                             %
9452 %                                                                             %
9453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9454 %
9455 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9456 %  pixel as specified by the key symbol.
9457 %
9458 %  The format of the XMagnifyWindowCommand method is:
9459 %
9460 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9461 %        const MagickStatusType state,const KeySym key_symbol,
9462 %        ExceptionInfo *exception)
9463 %
9464 %  A description of each parameter follows:
9465 %
9466 %    o display: Specifies a connection to an X server; returned from
9467 %      XOpenDisplay.
9468 %
9469 %    o windows: Specifies a pointer to a XWindows structure.
9470 %
9471 %    o state: key mask.
9472 %
9473 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9474 %      to trim.
9475 %
9476 %    o exception: return any errors or warnings in this structure.
9477 %
9478 */
9479 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9480   const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9481 {
9482   unsigned int
9483     quantum;
9484
9485   /*
9486     User specified a magnify factor or position.
9487   */
9488   quantum=1;
9489   if ((state & Mod1Mask) != 0)
9490     quantum=10;
9491   switch ((int) key_symbol)
9492   {
9493     case QuitCommand:
9494     {
9495       (void) XWithdrawWindow(display,windows->magnify.id,
9496         windows->magnify.screen);
9497       break;
9498     }
9499     case XK_Home:
9500     case XK_KP_Home:
9501     {
9502       windows->magnify.x=(int) windows->image.width/2;
9503       windows->magnify.y=(int) windows->image.height/2;
9504       break;
9505     }
9506     case XK_Left:
9507     case XK_KP_Left:
9508     {
9509       if (windows->magnify.x > 0)
9510         windows->magnify.x-=quantum;
9511       break;
9512     }
9513     case XK_Up:
9514     case XK_KP_Up:
9515     {
9516       if (windows->magnify.y > 0)
9517         windows->magnify.y-=quantum;
9518       break;
9519     }
9520     case XK_Right:
9521     case XK_KP_Right:
9522     {
9523       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9524         windows->magnify.x+=quantum;
9525       break;
9526     }
9527     case XK_Down:
9528     case XK_KP_Down:
9529     {
9530       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9531         windows->magnify.y+=quantum;
9532       break;
9533     }
9534     case XK_0:
9535     case XK_1:
9536     case XK_2:
9537     case XK_3:
9538     case XK_4:
9539     case XK_5:
9540     case XK_6:
9541     case XK_7:
9542     case XK_8:
9543     case XK_9:
9544     {
9545       windows->magnify.data=(key_symbol-XK_0);
9546       break;
9547     }
9548     case XK_KP_0:
9549     case XK_KP_1:
9550     case XK_KP_2:
9551     case XK_KP_3:
9552     case XK_KP_4:
9553     case XK_KP_5:
9554     case XK_KP_6:
9555     case XK_KP_7:
9556     case XK_KP_8:
9557     case XK_KP_9:
9558     {
9559       windows->magnify.data=(key_symbol-XK_KP_0);
9560       break;
9561     }
9562     default:
9563       break;
9564   }
9565   XMakeMagnifyImage(display,windows,exception);
9566 }
9567 \f
9568 /*
9569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9570 %                                                                             %
9571 %                                                                             %
9572 %                                                                             %
9573 +   X M a k e P a n I m a g e                                                 %
9574 %                                                                             %
9575 %                                                                             %
9576 %                                                                             %
9577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9578 %
9579 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9580 %  icon window.
9581 %
9582 %  The format of the XMakePanImage method is:
9583 %
9584 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9585 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9586 %
9587 %  A description of each parameter follows:
9588 %
9589 %    o display: Specifies a connection to an X server;  returned from
9590 %      XOpenDisplay.
9591 %
9592 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9593 %
9594 %    o windows: Specifies a pointer to a XWindows structure.
9595 %
9596 %    o image: the image.
9597 %
9598 %    o exception: return any errors or warnings in this structure.
9599 %
9600 */
9601 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9602   XWindows *windows,Image *image,ExceptionInfo *exception)
9603 {
9604   MagickStatusType
9605     status;
9606
9607   /*
9608     Create and display image for panning icon.
9609   */
9610   XSetCursorState(display,windows,MagickTrue);
9611   XCheckRefreshWindows(display,windows);
9612   windows->pan.x=(int) windows->image.x;
9613   windows->pan.y=(int) windows->image.y;
9614   status=XMakeImage(display,resource_info,&windows->pan,image,
9615     windows->pan.width,windows->pan.height,exception);
9616   if (status == MagickFalse)
9617     ThrowXWindowFatalException(ResourceLimitError,
9618      "MemoryAllocationFailed",image->filename);
9619   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9620     windows->pan.pixmap);
9621   (void) XClearWindow(display,windows->pan.id);
9622   XDrawPanRectangle(display,windows);
9623   XSetCursorState(display,windows,MagickFalse);
9624 }
9625 \f
9626 /*
9627 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9628 %                                                                             %
9629 %                                                                             %
9630 %                                                                             %
9631 +   X M a t t a E d i t I m a g e                                             %
9632 %                                                                             %
9633 %                                                                             %
9634 %                                                                             %
9635 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9636 %
9637 %  XMatteEditImage() allows the user to interactively change the Matte channel
9638 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9639 %  before the matte information is stored.
9640 %
9641 %  The format of the XMatteEditImage method is:
9642 %
9643 %      MagickBooleanType XMatteEditImage(Display *display,
9644 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9645 %        ExceptionInfo *exception)
9646 %
9647 %  A description of each parameter follows:
9648 %
9649 %    o display: Specifies a connection to an X server;  returned from
9650 %      XOpenDisplay.
9651 %
9652 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9653 %
9654 %    o windows: Specifies a pointer to a XWindows structure.
9655 %
9656 %    o image: the image; returned from ReadImage.
9657 %
9658 %    o exception: return any errors or warnings in this structure.
9659 %
9660 */
9661 static MagickBooleanType XMatteEditImage(Display *display,
9662   XResourceInfo *resource_info,XWindows *windows,Image **image,
9663   ExceptionInfo *exception)
9664 {
9665   static char
9666     matte[MaxTextExtent] = "0";
9667
9668   static const char
9669     *MatteEditMenu[] =
9670     {
9671       "Method",
9672       "Border Color",
9673       "Fuzz",
9674       "Matte Value",
9675       "Undo",
9676       "Help",
9677       "Dismiss",
9678       (char *) NULL
9679     };
9680
9681   static const ModeType
9682     MatteEditCommands[] =
9683     {
9684       MatteEditMethod,
9685       MatteEditBorderCommand,
9686       MatteEditFuzzCommand,
9687       MatteEditValueCommand,
9688       MatteEditUndoCommand,
9689       MatteEditHelpCommand,
9690       MatteEditDismissCommand
9691     };
9692
9693   static PaintMethod
9694     method = PointMethod;
9695
9696   static XColor
9697     border_color = { 0, 0, 0, 0, 0, 0 };
9698
9699   char
9700     command[MaxTextExtent],
9701     text[MaxTextExtent];
9702
9703   Cursor
9704     cursor;
9705
9706   int
9707     entry,
9708     id,
9709     x,
9710     x_offset,
9711     y,
9712     y_offset;
9713
9714   register int
9715     i;
9716
9717   register Quantum
9718     *q;
9719
9720   unsigned int
9721     height,
9722     width;
9723
9724   size_t
9725     state;
9726
9727   XEvent
9728     event;
9729
9730   /*
9731     Map Command widget.
9732   */
9733   (void) CloneString(&windows->command.name,"Matte Edit");
9734   windows->command.data=4;
9735   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9736   (void) XMapRaised(display,windows->command.id);
9737   XClientMessage(display,windows->image.id,windows->im_protocols,
9738     windows->im_update_widget,CurrentTime);
9739   /*
9740     Make cursor.
9741   */
9742   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9743     resource_info->background_color,resource_info->foreground_color);
9744   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9745   /*
9746     Track pointer until button 1 is pressed.
9747   */
9748   XQueryPosition(display,windows->image.id,&x,&y);
9749   (void) XSelectInput(display,windows->image.id,
9750     windows->image.attributes.event_mask | PointerMotionMask);
9751   state=DefaultState;
9752   do
9753   {
9754     if (windows->info.mapped != MagickFalse)
9755       {
9756         /*
9757           Display pointer position.
9758         */
9759         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9760           x+windows->image.x,y+windows->image.y);
9761         XInfoWidget(display,windows,text);
9762       }
9763     /*
9764       Wait for next event.
9765     */
9766     XScreenEvent(display,windows,&event,exception);
9767     if (event.xany.window == windows->command.id)
9768       {
9769         /*
9770           Select a command from the Command widget.
9771         */
9772         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9773         if (id < 0)
9774           {
9775             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9776             continue;
9777           }
9778         switch (MatteEditCommands[id])
9779         {
9780           case MatteEditMethod:
9781           {
9782             char
9783               **methods;
9784
9785             /*
9786               Select a method from the pop-up menu.
9787             */
9788             methods=GetCommandOptions(MagickMethodOptions);
9789             if (methods == (char **) NULL)
9790               break;
9791             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9792               (const char **) methods,command);
9793             if (entry >= 0)
9794               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9795                 MagickFalse,methods[entry]);
9796             methods=DestroyStringList(methods);
9797             break;
9798           }
9799           case MatteEditBorderCommand:
9800           {
9801             const char
9802               *ColorMenu[MaxNumberPens];
9803
9804             int
9805               pen_number;
9806
9807             /*
9808               Initialize menu selections.
9809             */
9810             for (i=0; i < (int) (MaxNumberPens-2); i++)
9811               ColorMenu[i]=resource_info->pen_colors[i];
9812             ColorMenu[MaxNumberPens-2]="Browser...";
9813             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9814             /*
9815               Select a pen color from the pop-up menu.
9816             */
9817             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9818               (const char **) ColorMenu,command);
9819             if (pen_number < 0)
9820               break;
9821             if (pen_number == (MaxNumberPens-2))
9822               {
9823                 static char
9824                   color_name[MaxTextExtent] = "gray";
9825
9826                 /*
9827                   Select a pen color from a dialog.
9828                 */
9829                 resource_info->pen_colors[pen_number]=color_name;
9830                 XColorBrowserWidget(display,windows,"Select",color_name);
9831                 if (*color_name == '\0')
9832                   break;
9833               }
9834             /*
9835               Set border color.
9836             */
9837             (void) XParseColor(display,windows->map_info->colormap,
9838               resource_info->pen_colors[pen_number],&border_color);
9839             break;
9840           }
9841           case MatteEditFuzzCommand:
9842           {
9843             static char
9844               fuzz[MaxTextExtent];
9845
9846             static const char
9847               *FuzzMenu[] =
9848               {
9849                 "0%",
9850                 "2%",
9851                 "5%",
9852                 "10%",
9853                 "15%",
9854                 "Dialog...",
9855                 (char *) NULL,
9856               };
9857
9858             /*
9859               Select a command from the pop-up menu.
9860             */
9861             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9862               command);
9863             if (entry < 0)
9864               break;
9865             if (entry != 5)
9866               {
9867                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],(double)
9868                   QuantumRange+1.0);
9869                 break;
9870               }
9871             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9872             (void) XDialogWidget(display,windows,"Ok",
9873               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9874             if (*fuzz == '\0')
9875               break;
9876             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9877             (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+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 (windows->info.mapped != MagickFalse)
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 (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10097           return(MagickFalse);
10098         (*image)->matte=MagickTrue;
10099         image_view=AcquireCacheView(*image);
10100         switch (method)
10101         {
10102           case PointMethod:
10103           default:
10104           {
10105             /*
10106               Update matte information using point algorithm.
10107             */
10108             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10109               (ssize_t) y_offset,1,1,exception);
10110             if (q == (Quantum *) NULL)
10111               break;
10112             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10113             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10114             break;
10115           }
10116           case ReplaceMethod:
10117           {
10118             PixelInfo
10119               pixel,
10120               target;
10121
10122             Quantum
10123               virtual_pixel[CompositePixelChannel];
10124
10125             /*
10126               Update matte information using replace algorithm.
10127             */
10128             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10129               (ssize_t) y_offset,virtual_pixel,exception);
10130             target.red=virtual_pixel[RedPixelChannel];
10131             target.green=virtual_pixel[GreenPixelChannel];
10132             target.blue=virtual_pixel[BluePixelChannel];
10133             target.alpha=virtual_pixel[AlphaPixelChannel];
10134             for (y=0; y < (int) (*image)->rows; y++)
10135             {
10136               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10137                 (*image)->columns,1,exception);
10138               if (q == (Quantum *) NULL)
10139                 break;
10140               for (x=0; x < (int) (*image)->columns; x++)
10141               {
10142                 GetPixelInfoPixel(*image,q,&pixel);
10143                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10144                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10145                 q+=GetPixelChannels(*image);
10146               }
10147               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10148                 break;
10149             }
10150             break;
10151           }
10152           case FloodfillMethod:
10153           case FillToBorderMethod:
10154           {
10155             ChannelType
10156               channel_mask;
10157
10158             DrawInfo
10159               *draw_info;
10160
10161             PixelInfo
10162               target;
10163
10164             /*
10165               Update matte information using floodfill algorithm.
10166             */
10167             (void) GetOneVirtualMagickPixel(*image,
10168               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10169               y_offset,&target,exception);
10170             if (method == FillToBorderMethod)
10171               {
10172                 target.red=(MagickRealType) ScaleShortToQuantum(
10173                   border_color.red);
10174                 target.green=(MagickRealType) ScaleShortToQuantum(
10175                   border_color.green);
10176                 target.blue=(MagickRealType) ScaleShortToQuantum(
10177                   border_color.blue);
10178               }
10179             draw_info=CloneDrawInfo(resource_info->image_info,
10180               (DrawInfo *) NULL);
10181             draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10182               (char **) NULL));
10183             channel_mask=SetPixelChannelMask(*image,AlphaChannel); 
10184             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10185               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10186               MagickFalse : MagickTrue,exception);
10187             (void) SetPixelChannelMap(*image,channel_mask);
10188             draw_info=DestroyDrawInfo(draw_info);
10189             break;
10190           }
10191           case ResetMethod:
10192           {
10193             /*
10194               Update matte information using reset algorithm.
10195             */
10196             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10197               return(MagickFalse);
10198             for (y=0; y < (int) (*image)->rows; y++)
10199             {
10200               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10201                 (*image)->columns,1,exception);
10202               if (q == (Quantum *) NULL)
10203                 break;
10204               for (x=0; x < (int) (*image)->columns; x++)
10205               {
10206                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10207                 q+=GetPixelChannels(*image);
10208               }
10209               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10210                 break;
10211             }
10212             if (StringToLong(matte) == (long) OpaqueAlpha)
10213               (*image)->matte=MagickFalse;
10214             break;
10215           }
10216         }
10217         image_view=DestroyCacheView(image_view);
10218         state&=(~UpdateConfigurationState);
10219       }
10220   } while ((state & ExitState) == 0);
10221   (void) XSelectInput(display,windows->image.id,
10222     windows->image.attributes.event_mask);
10223   XSetCursorState(display,windows,MagickFalse);
10224   (void) XFreeCursor(display,cursor);
10225   return(MagickTrue);
10226 }
10227 \f
10228 /*
10229 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10230 %                                                                             %
10231 %                                                                             %
10232 %                                                                             %
10233 +   X O p e n I m a g e                                                       %
10234 %                                                                             %
10235 %                                                                             %
10236 %                                                                             %
10237 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10238 %
10239 %  XOpenImage() loads an image from a file.
10240 %
10241 %  The format of the XOpenImage method is:
10242 %
10243 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10244 %       XWindows *windows,const unsigned int command)
10245 %
10246 %  A description of each parameter follows:
10247 %
10248 %    o display: Specifies a connection to an X server; returned from
10249 %      XOpenDisplay.
10250 %
10251 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10252 %
10253 %    o windows: Specifies a pointer to a XWindows structure.
10254 %
10255 %    o command: A value other than zero indicates that the file is selected
10256 %      from the command line argument list.
10257 %
10258 */
10259 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10260   XWindows *windows,const MagickBooleanType command)
10261 {
10262   const MagickInfo
10263     *magick_info;
10264
10265   ExceptionInfo
10266     *exception;
10267
10268   Image
10269     *nexus;
10270
10271   ImageInfo
10272     *image_info;
10273
10274   static char
10275     filename[MaxTextExtent] = "\0";
10276
10277   /*
10278     Request file name from user.
10279   */
10280   if (command == MagickFalse)
10281     XFileBrowserWidget(display,windows,"Open",filename);
10282   else
10283     {
10284       char
10285         **filelist,
10286         **files;
10287
10288       int
10289         count,
10290         status;
10291
10292       register int
10293         i,
10294         j;
10295
10296       /*
10297         Select next image from the command line.
10298       */
10299       status=XGetCommand(display,windows->image.id,&files,&count);
10300       if (status == 0)
10301         {
10302           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10303           return((Image *) NULL);
10304         }
10305       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10306       if (filelist == (char **) NULL)
10307         {
10308           ThrowXWindowFatalException(ResourceLimitError,
10309             "MemoryAllocationFailed","...");
10310           (void) XFreeStringList(files);
10311           return((Image *) NULL);
10312         }
10313       j=0;
10314       for (i=1; i < count; i++)
10315         if (*files[i] != '-')
10316           filelist[j++]=files[i];
10317       filelist[j]=(char *) NULL;
10318       XListBrowserWidget(display,windows,&windows->widget,
10319         (const char **) filelist,"Load","Select Image to Load:",filename);
10320       filelist=(char **) RelinquishMagickMemory(filelist);
10321       (void) XFreeStringList(files);
10322     }
10323   if (*filename == '\0')
10324     return((Image *) NULL);
10325   image_info=CloneImageInfo(resource_info->image_info);
10326   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10327     (void *) NULL);
10328   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10329   exception=AcquireExceptionInfo();
10330   (void) SetImageInfo(image_info,0,exception);
10331   if (LocaleCompare(image_info->magick,"X") == 0)
10332     {
10333       char
10334         seconds[MaxTextExtent];
10335
10336       /*
10337         User may want to delay the X server screen grab.
10338       */
10339       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10340       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10341         seconds);
10342       if (*seconds == '\0')
10343         return((Image *) NULL);
10344       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10345     }
10346   magick_info=GetMagickInfo(image_info->magick,exception);
10347   if ((magick_info != (const MagickInfo *) NULL) &&
10348       (magick_info->raw != MagickFalse))
10349     {
10350       char
10351         geometry[MaxTextExtent];
10352
10353       /*
10354         Request image size from the user.
10355       */
10356       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10357       if (image_info->size != (char *) NULL)
10358         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10359       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10360         geometry);
10361       (void) CloneString(&image_info->size,geometry);
10362     }
10363   /*
10364     Load the image.
10365   */
10366   XSetCursorState(display,windows,MagickTrue);
10367   XCheckRefreshWindows(display,windows);
10368   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10369   nexus=ReadImage(image_info,exception);
10370   CatchException(exception);
10371   XSetCursorState(display,windows,MagickFalse);
10372   if (nexus != (Image *) NULL)
10373     XClientMessage(display,windows->image.id,windows->im_protocols,
10374       windows->im_next_image,CurrentTime);
10375   else
10376     {
10377       char
10378         *text,
10379         **textlist;
10380
10381       /*
10382         Unknown image format.
10383       */
10384       text=FileToString(filename,~0,exception);
10385       if (text == (char *) NULL)
10386         return((Image *) NULL);
10387       textlist=StringToList(text);
10388       if (textlist != (char **) NULL)
10389         {
10390           char
10391             title[MaxTextExtent];
10392
10393           register int
10394             i;
10395
10396           (void) FormatLocaleString(title,MaxTextExtent,
10397             "Unknown format: %s",filename);
10398           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10399             (const char **) textlist);
10400           for (i=0; textlist[i] != (char *) NULL; i++)
10401             textlist[i]=DestroyString(textlist[i]);
10402           textlist=(char **) RelinquishMagickMemory(textlist);
10403         }
10404       text=DestroyString(text);
10405     }
10406   exception=DestroyExceptionInfo(exception);
10407   image_info=DestroyImageInfo(image_info);
10408   return(nexus);
10409 }
10410 \f
10411 /*
10412 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10413 %                                                                             %
10414 %                                                                             %
10415 %                                                                             %
10416 +   X P a n I m a g e                                                         %
10417 %                                                                             %
10418 %                                                                             %
10419 %                                                                             %
10420 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10421 %
10422 %  XPanImage() pans the image until the mouse button is released.
10423 %
10424 %  The format of the XPanImage method is:
10425 %
10426 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10427 %        ExceptionInfo *exception)
10428 %
10429 %  A description of each parameter follows:
10430 %
10431 %    o display: Specifies a connection to an X server;  returned from
10432 %      XOpenDisplay.
10433 %
10434 %    o windows: Specifies a pointer to a XWindows structure.
10435 %
10436 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10437 %      the entire image is refreshed.
10438 %
10439 %    o exception: return any errors or warnings in this structure.
10440 %
10441 */
10442 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10443   ExceptionInfo *exception)
10444 {
10445   char
10446     text[MaxTextExtent];
10447
10448   Cursor
10449     cursor;
10450
10451   MagickRealType
10452     x_factor,
10453     y_factor;
10454
10455   RectangleInfo
10456     pan_info;
10457
10458   size_t
10459     state;
10460
10461   /*
10462     Define cursor.
10463   */
10464   if ((windows->image.ximage->width > (int) windows->image.width) &&
10465       (windows->image.ximage->height > (int) windows->image.height))
10466     cursor=XCreateFontCursor(display,XC_fleur);
10467   else
10468     if (windows->image.ximage->width > (int) windows->image.width)
10469       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10470     else
10471       if (windows->image.ximage->height > (int) windows->image.height)
10472         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10473       else
10474         cursor=XCreateFontCursor(display,XC_arrow);
10475   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10476   /*
10477     Pan image as pointer moves until the mouse button is released.
10478   */
10479   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10480   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10481   pan_info.width=windows->pan.width*windows->image.width/
10482     windows->image.ximage->width;
10483   pan_info.height=windows->pan.height*windows->image.height/
10484     windows->image.ximage->height;
10485   pan_info.x=0;
10486   pan_info.y=0;
10487   state=UpdateConfigurationState;
10488   do
10489   {
10490     switch (event->type)
10491     {
10492       case ButtonPress:
10493       {
10494         /*
10495           User choose an initial pan location.
10496         */
10497         pan_info.x=(ssize_t) event->xbutton.x;
10498         pan_info.y=(ssize_t) event->xbutton.y;
10499         state|=UpdateConfigurationState;
10500         break;
10501       }
10502       case ButtonRelease:
10503       {
10504         /*
10505           User has finished panning the image.
10506         */
10507         pan_info.x=(ssize_t) event->xbutton.x;
10508         pan_info.y=(ssize_t) event->xbutton.y;
10509         state|=UpdateConfigurationState | ExitState;
10510         break;
10511       }
10512       case MotionNotify:
10513       {
10514         pan_info.x=(ssize_t) event->xmotion.x;
10515         pan_info.y=(ssize_t) event->xmotion.y;
10516         state|=UpdateConfigurationState;
10517       }
10518       default:
10519         break;
10520     }
10521     if ((state & UpdateConfigurationState) != 0)
10522       {
10523         /*
10524           Check boundary conditions.
10525         */
10526         if (pan_info.x < (ssize_t) (pan_info.width/2))
10527           pan_info.x=0;
10528         else
10529           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10530         if (pan_info.x < 0)
10531           pan_info.x=0;
10532         else
10533           if ((int) (pan_info.x+windows->image.width) >
10534               windows->image.ximage->width)
10535             pan_info.x=(ssize_t)
10536               (windows->image.ximage->width-windows->image.width);
10537         if (pan_info.y < (ssize_t) (pan_info.height/2))
10538           pan_info.y=0;
10539         else
10540           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10541         if (pan_info.y < 0)
10542           pan_info.y=0;
10543         else
10544           if ((int) (pan_info.y+windows->image.height) >
10545               windows->image.ximage->height)
10546             pan_info.y=(ssize_t)
10547               (windows->image.ximage->height-windows->image.height);
10548         if ((windows->image.x != (int) pan_info.x) ||
10549             (windows->image.y != (int) pan_info.y))
10550           {
10551             /*
10552               Display image pan offset.
10553             */
10554             windows->image.x=(int) pan_info.x;
10555             windows->image.y=(int) pan_info.y;
10556             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10557               windows->image.width,windows->image.height,windows->image.x,
10558               windows->image.y);
10559             XInfoWidget(display,windows,text);
10560             /*
10561               Refresh Image window.
10562             */
10563             XDrawPanRectangle(display,windows);
10564             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10565           }
10566         state&=(~UpdateConfigurationState);
10567       }
10568     /*
10569       Wait for next event.
10570     */
10571     if ((state & ExitState) == 0)
10572       XScreenEvent(display,windows,event,exception);
10573   } while ((state & ExitState) == 0);
10574   /*
10575     Restore cursor.
10576   */
10577   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10578   (void) XFreeCursor(display,cursor);
10579   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10580 }
10581 \f
10582 /*
10583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10584 %                                                                             %
10585 %                                                                             %
10586 %                                                                             %
10587 +   X P a s t e I m a g e                                                     %
10588 %                                                                             %
10589 %                                                                             %
10590 %                                                                             %
10591 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10592 %
10593 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10594 %  window image at a location the user chooses with the pointer.
10595 %
10596 %  The format of the XPasteImage method is:
10597 %
10598 %      MagickBooleanType XPasteImage(Display *display,
10599 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10600 %        ExceptionInfo *exception)
10601 %
10602 %  A description of each parameter follows:
10603 %
10604 %    o display: Specifies a connection to an X server;  returned from
10605 %      XOpenDisplay.
10606 %
10607 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10608 %
10609 %    o windows: Specifies a pointer to a XWindows structure.
10610 %
10611 %    o image: the image; returned from ReadImage.
10612 %
10613 %    o exception: return any errors or warnings in this structure.
10614 %
10615 */
10616 static MagickBooleanType XPasteImage(Display *display,
10617   XResourceInfo *resource_info,XWindows *windows,Image *image,
10618   ExceptionInfo *exception)
10619 {
10620   static const char
10621     *PasteMenu[] =
10622     {
10623       "Operator",
10624       "Help",
10625       "Dismiss",
10626       (char *) NULL
10627     };
10628
10629   static const ModeType
10630     PasteCommands[] =
10631     {
10632       PasteOperatorsCommand,
10633       PasteHelpCommand,
10634       PasteDismissCommand
10635     };
10636
10637   static CompositeOperator
10638     compose = CopyCompositeOp;
10639
10640   char
10641     text[MaxTextExtent];
10642
10643   Cursor
10644     cursor;
10645
10646   Image
10647     *paste_image;
10648
10649   int
10650     entry,
10651     id,
10652     x,
10653     y;
10654
10655   MagickRealType
10656     scale_factor;
10657
10658   RectangleInfo
10659     highlight_info,
10660     paste_info;
10661
10662   unsigned int
10663     height,
10664     width;
10665
10666   size_t
10667     state;
10668
10669   XEvent
10670     event;
10671
10672   /*
10673     Copy image.
10674   */
10675   if (resource_info->copy_image == (Image *) NULL)
10676     return(MagickFalse);
10677   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10678   /*
10679     Map Command widget.
10680   */
10681   (void) CloneString(&windows->command.name,"Paste");
10682   windows->command.data=1;
10683   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10684   (void) XMapRaised(display,windows->command.id);
10685   XClientMessage(display,windows->image.id,windows->im_protocols,
10686     windows->im_update_widget,CurrentTime);
10687   /*
10688     Track pointer until button 1 is pressed.
10689   */
10690   XSetCursorState(display,windows,MagickFalse);
10691   XQueryPosition(display,windows->image.id,&x,&y);
10692   (void) XSelectInput(display,windows->image.id,
10693     windows->image.attributes.event_mask | PointerMotionMask);
10694   paste_info.x=(ssize_t) windows->image.x+x;
10695   paste_info.y=(ssize_t) windows->image.y+y;
10696   paste_info.width=0;
10697   paste_info.height=0;
10698   cursor=XCreateFontCursor(display,XC_ul_angle);
10699   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10700   state=DefaultState;
10701   do
10702   {
10703     if (windows->info.mapped != MagickFalse)
10704       {
10705         /*
10706           Display pointer position.
10707         */
10708         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10709           (long) paste_info.x,(long) paste_info.y);
10710         XInfoWidget(display,windows,text);
10711       }
10712     highlight_info=paste_info;
10713     highlight_info.x=paste_info.x-windows->image.x;
10714     highlight_info.y=paste_info.y-windows->image.y;
10715     XHighlightRectangle(display,windows->image.id,
10716       windows->image.highlight_context,&highlight_info);
10717     /*
10718       Wait for next event.
10719     */
10720     XScreenEvent(display,windows,&event,exception);
10721     XHighlightRectangle(display,windows->image.id,
10722       windows->image.highlight_context,&highlight_info);
10723     if (event.xany.window == windows->command.id)
10724       {
10725         /*
10726           Select a command from the Command widget.
10727         */
10728         id=XCommandWidget(display,windows,PasteMenu,&event);
10729         if (id < 0)
10730           continue;
10731         switch (PasteCommands[id])
10732         {
10733           case PasteOperatorsCommand:
10734           {
10735             char
10736               command[MaxTextExtent],
10737               **operators;
10738
10739             /*
10740               Select a command from the pop-up menu.
10741             */
10742             operators=GetCommandOptions(MagickComposeOptions);
10743             if (operators == (char **) NULL)
10744               break;
10745             entry=XMenuWidget(display,windows,PasteMenu[id],
10746               (const char **) operators,command);
10747             if (entry >= 0)
10748               compose=(CompositeOperator) ParseCommandOption(
10749                 MagickComposeOptions,MagickFalse,operators[entry]);
10750             operators=DestroyStringList(operators);
10751             break;
10752           }
10753           case PasteHelpCommand:
10754           {
10755             XTextViewWidget(display,resource_info,windows,MagickFalse,
10756               "Help Viewer - Image Composite",ImagePasteHelp);
10757             break;
10758           }
10759           case PasteDismissCommand:
10760           {
10761             /*
10762               Prematurely exit.
10763             */
10764             state|=EscapeState;
10765             state|=ExitState;
10766             break;
10767           }
10768           default:
10769             break;
10770         }
10771         continue;
10772       }
10773     switch (event.type)
10774     {
10775       case ButtonPress:
10776       {
10777         if (image->debug != MagickFalse)
10778           (void) LogMagickEvent(X11Event,GetMagickModule(),
10779             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10780             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10781         if (event.xbutton.button != Button1)
10782           break;
10783         if (event.xbutton.window != windows->image.id)
10784           break;
10785         /*
10786           Paste rectangle is relative to image configuration.
10787         */
10788         width=(unsigned int) image->columns;
10789         height=(unsigned int) image->rows;
10790         x=0;
10791         y=0;
10792         if (windows->image.crop_geometry != (char *) NULL)
10793           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10794             &width,&height);
10795         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10796         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10797         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10798         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10799         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10800         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10801         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10802         break;
10803       }
10804       case ButtonRelease:
10805       {
10806         if (image->debug != MagickFalse)
10807           (void) LogMagickEvent(X11Event,GetMagickModule(),
10808             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10809             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10810         if (event.xbutton.button != Button1)
10811           break;
10812         if (event.xbutton.window != windows->image.id)
10813           break;
10814         if ((paste_info.width != 0) && (paste_info.height != 0))
10815           {
10816             /*
10817               User has selected the location of the paste image.
10818             */
10819             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10820             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10821             state|=ExitState;
10822           }
10823         break;
10824       }
10825       case Expose:
10826         break;
10827       case KeyPress:
10828       {
10829         char
10830           command[MaxTextExtent];
10831
10832         KeySym
10833           key_symbol;
10834
10835         int
10836           length;
10837
10838         if (event.xkey.window != windows->image.id)
10839           break;
10840         /*
10841           Respond to a user key press.
10842         */
10843         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10844           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10845         *(command+length)='\0';
10846         if (image->debug != MagickFalse)
10847           (void) LogMagickEvent(X11Event,GetMagickModule(),
10848             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10849         switch ((int) key_symbol)
10850         {
10851           case XK_Escape:
10852           case XK_F20:
10853           {
10854             /*
10855               Prematurely exit.
10856             */
10857             paste_image=DestroyImage(paste_image);
10858             state|=EscapeState;
10859             state|=ExitState;
10860             break;
10861           }
10862           case XK_F1:
10863           case XK_Help:
10864           {
10865             (void) XSetFunction(display,windows->image.highlight_context,
10866               GXcopy);
10867             XTextViewWidget(display,resource_info,windows,MagickFalse,
10868               "Help Viewer - Image Composite",ImagePasteHelp);
10869             (void) XSetFunction(display,windows->image.highlight_context,
10870               GXinvert);
10871             break;
10872           }
10873           default:
10874           {
10875             (void) XBell(display,0);
10876             break;
10877           }
10878         }
10879         break;
10880       }
10881       case MotionNotify:
10882       {
10883         /*
10884           Map and unmap Info widget as text cursor crosses its boundaries.
10885         */
10886         x=event.xmotion.x;
10887         y=event.xmotion.y;
10888         if (windows->info.mapped != MagickFalse)
10889           {
10890             if ((x < (int) (windows->info.x+windows->info.width)) &&
10891                 (y < (int) (windows->info.y+windows->info.height)))
10892               (void) XWithdrawWindow(display,windows->info.id,
10893                 windows->info.screen);
10894           }
10895         else
10896           if ((x > (int) (windows->info.x+windows->info.width)) ||
10897               (y > (int) (windows->info.y+windows->info.height)))
10898             (void) XMapWindow(display,windows->info.id);
10899         paste_info.x=(ssize_t) windows->image.x+x;
10900         paste_info.y=(ssize_t) windows->image.y+y;
10901         break;
10902       }
10903       default:
10904       {
10905         if (image->debug != MagickFalse)
10906           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10907             event.type);
10908         break;
10909       }
10910     }
10911   } while ((state & ExitState) == 0);
10912   (void) XSelectInput(display,windows->image.id,
10913     windows->image.attributes.event_mask);
10914   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10915   XSetCursorState(display,windows,MagickFalse);
10916   (void) XFreeCursor(display,cursor);
10917   if ((state & EscapeState) != 0)
10918     return(MagickTrue);
10919   /*
10920     Image pasting is relative to image configuration.
10921   */
10922   XSetCursorState(display,windows,MagickTrue);
10923   XCheckRefreshWindows(display,windows);
10924   width=(unsigned int) image->columns;
10925   height=(unsigned int) image->rows;
10926   x=0;
10927   y=0;
10928   if (windows->image.crop_geometry != (char *) NULL)
10929     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10930   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10931   paste_info.x+=x;
10932   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10933   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10934   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10935   paste_info.y+=y;
10936   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10937   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10938   /*
10939     Paste image with X Image window.
10940   */
10941   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y,
10942     exception);
10943   paste_image=DestroyImage(paste_image);
10944   XSetCursorState(display,windows,MagickFalse);
10945   /*
10946     Update image colormap.
10947   */
10948   XConfigureImageColormap(display,resource_info,windows,image,exception);
10949   (void) XConfigureImage(display,resource_info,windows,image,exception);
10950   return(MagickTrue);
10951 }
10952 \f
10953 /*
10954 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10955 %                                                                             %
10956 %                                                                             %
10957 %                                                                             %
10958 +   X P r i n t I m a g e                                                     %
10959 %                                                                             %
10960 %                                                                             %
10961 %                                                                             %
10962 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10963 %
10964 %  XPrintImage() prints an image to a Postscript printer.
10965 %
10966 %  The format of the XPrintImage method is:
10967 %
10968 %      MagickBooleanType XPrintImage(Display *display,
10969 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10970 %        ExceptionInfo *exception)
10971 %
10972 %  A description of each parameter follows:
10973 %
10974 %    o display: Specifies a connection to an X server; returned from
10975 %      XOpenDisplay.
10976 %
10977 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10978 %
10979 %    o windows: Specifies a pointer to a XWindows structure.
10980 %
10981 %    o image: the image.
10982 %
10983 %    o exception: return any errors or warnings in this structure.
10984 %
10985 */
10986 static MagickBooleanType XPrintImage(Display *display,
10987   XResourceInfo *resource_info,XWindows *windows,Image *image,
10988   ExceptionInfo *exception)
10989 {
10990   char
10991     filename[MaxTextExtent],
10992     geometry[MaxTextExtent];
10993
10994   Image
10995     *print_image;
10996
10997   ImageInfo
10998     *image_info;
10999
11000   MagickStatusType
11001     status;
11002
11003   /*
11004     Request Postscript page geometry from user.
11005   */
11006   image_info=CloneImageInfo(resource_info->image_info);
11007   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
11008   if (image_info->page != (char *) NULL)
11009     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11010   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11011     "Select Postscript Page Geometry:",geometry);
11012   if (*geometry == '\0')
11013     return(MagickTrue);
11014   image_info->page=GetPageGeometry(geometry);
11015   /*
11016     Apply image transforms.
11017   */
11018   XSetCursorState(display,windows,MagickTrue);
11019   XCheckRefreshWindows(display,windows);
11020   print_image=CloneImage(image,0,0,MagickTrue,exception);
11021   if (print_image == (Image *) NULL)
11022     return(MagickFalse);
11023   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11024     windows->image.ximage->width,windows->image.ximage->height);
11025   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11026     exception);
11027   /*
11028     Print image.
11029   */
11030   (void) AcquireUniqueFilename(filename);
11031   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11032     filename);
11033   status=WriteImage(image_info,print_image,exception);
11034   (void) RelinquishUniqueFileResource(filename);
11035   print_image=DestroyImage(print_image);
11036   image_info=DestroyImageInfo(image_info);
11037   XSetCursorState(display,windows,MagickFalse);
11038   return(status != 0 ? MagickTrue : MagickFalse);
11039 }
11040 \f
11041 /*
11042 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11043 %                                                                             %
11044 %                                                                             %
11045 %                                                                             %
11046 +   X R O I I m a g e                                                         %
11047 %                                                                             %
11048 %                                                                             %
11049 %                                                                             %
11050 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11051 %
11052 %  XROIImage() applies an image processing technique to a region of interest.
11053 %
11054 %  The format of the XROIImage method is:
11055 %
11056 %      MagickBooleanType XROIImage(Display *display,
11057 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11058 %        ExceptionInfo *exception)
11059 %
11060 %  A description of each parameter follows:
11061 %
11062 %    o display: Specifies a connection to an X server; returned from
11063 %      XOpenDisplay.
11064 %
11065 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11066 %
11067 %    o windows: Specifies a pointer to a XWindows structure.
11068 %
11069 %    o image: the image; returned from ReadImage.
11070 %
11071 %    o exception: return any errors or warnings in this structure.
11072 %
11073 */
11074 static MagickBooleanType XROIImage(Display *display,
11075   XResourceInfo *resource_info,XWindows *windows,Image **image,
11076   ExceptionInfo *exception)
11077 {
11078 #define ApplyMenus  7
11079
11080   static const char
11081     *ROIMenu[] =
11082     {
11083       "Help",
11084       "Dismiss",
11085       (char *) NULL
11086     },
11087     *ApplyMenu[] =
11088     {
11089       "File",
11090       "Edit",
11091       "Transform",
11092       "Enhance",
11093       "Effects",
11094       "F/X",
11095       "Miscellany",
11096       "Help",
11097       "Dismiss",
11098       (char *) NULL
11099     },
11100     *FileMenu[] =
11101     {
11102       "Save...",
11103       "Print...",
11104       (char *) NULL
11105     },
11106     *EditMenu[] =
11107     {
11108       "Undo",
11109       "Redo",
11110       (char *) NULL
11111     },
11112     *TransformMenu[] =
11113     {
11114       "Flop",
11115       "Flip",
11116       "Rotate Right",
11117       "Rotate Left",
11118       (char *) NULL
11119     },
11120     *EnhanceMenu[] =
11121     {
11122       "Hue...",
11123       "Saturation...",
11124       "Brightness...",
11125       "Gamma...",
11126       "Spiff",
11127       "Dull",
11128       "Contrast Stretch...",
11129       "Sigmoidal Contrast...",
11130       "Normalize",
11131       "Equalize",
11132       "Negate",
11133       "Grayscale",
11134       "Map...",
11135       "Quantize...",
11136       (char *) NULL
11137     },
11138     *EffectsMenu[] =
11139     {
11140       "Despeckle",
11141       "Emboss",
11142       "Reduce Noise",
11143       "Add Noise",
11144       "Sharpen...",
11145       "Blur...",
11146       "Threshold...",
11147       "Edge Detect...",
11148       "Spread...",
11149       "Shade...",
11150       "Raise...",
11151       "Segment...",
11152       (char *) NULL
11153     },
11154     *FXMenu[] =
11155     {
11156       "Solarize...",
11157       "Sepia Tone...",
11158       "Swirl...",
11159       "Implode...",
11160       "Vignette...",
11161       "Wave...",
11162       "Oil Paint...",
11163       "Charcoal Draw...",
11164       (char *) NULL
11165     },
11166     *MiscellanyMenu[] =
11167     {
11168       "Image Info",
11169       "Zoom Image",
11170       "Show Preview...",
11171       "Show Histogram",
11172       "Show Matte",
11173       (char *) NULL
11174     };
11175
11176   static const char
11177     **Menus[ApplyMenus] =
11178     {
11179       FileMenu,
11180       EditMenu,
11181       TransformMenu,
11182       EnhanceMenu,
11183       EffectsMenu,
11184       FXMenu,
11185       MiscellanyMenu
11186     };
11187
11188   static const CommandType
11189     ApplyCommands[] =
11190     {
11191       NullCommand,
11192       NullCommand,
11193       NullCommand,
11194       NullCommand,
11195       NullCommand,
11196       NullCommand,
11197       NullCommand,
11198       HelpCommand,
11199       QuitCommand
11200     },
11201     FileCommands[] =
11202     {
11203       SaveCommand,
11204       PrintCommand
11205     },
11206     EditCommands[] =
11207     {
11208       UndoCommand,
11209       RedoCommand
11210     },
11211     TransformCommands[] =
11212     {
11213       FlopCommand,
11214       FlipCommand,
11215       RotateRightCommand,
11216       RotateLeftCommand
11217     },
11218     EnhanceCommands[] =
11219     {
11220       HueCommand,
11221       SaturationCommand,
11222       BrightnessCommand,
11223       GammaCommand,
11224       SpiffCommand,
11225       DullCommand,
11226       ContrastStretchCommand,
11227       SigmoidalContrastCommand,
11228       NormalizeCommand,
11229       EqualizeCommand,
11230       NegateCommand,
11231       GrayscaleCommand,
11232       MapCommand,
11233       QuantizeCommand
11234     },
11235     EffectsCommands[] =
11236     {
11237       DespeckleCommand,
11238       EmbossCommand,
11239       ReduceNoiseCommand,
11240       AddNoiseCommand,
11241       SharpenCommand,
11242       BlurCommand,
11243       EdgeDetectCommand,
11244       SpreadCommand,
11245       ShadeCommand,
11246       RaiseCommand,
11247       SegmentCommand
11248     },
11249     FXCommands[] =
11250     {
11251       SolarizeCommand,
11252       SepiaToneCommand,
11253       SwirlCommand,
11254       ImplodeCommand,
11255       VignetteCommand,
11256       WaveCommand,
11257       OilPaintCommand,
11258       CharcoalDrawCommand
11259     },
11260     MiscellanyCommands[] =
11261     {
11262       InfoCommand,
11263       ZoomCommand,
11264       ShowPreviewCommand,
11265       ShowHistogramCommand,
11266       ShowMatteCommand
11267     },
11268     ROICommands[] =
11269     {
11270       ROIHelpCommand,
11271       ROIDismissCommand
11272     };
11273
11274   static const CommandType
11275     *Commands[ApplyMenus] =
11276     {
11277       FileCommands,
11278       EditCommands,
11279       TransformCommands,
11280       EnhanceCommands,
11281       EffectsCommands,
11282       FXCommands,
11283       MiscellanyCommands
11284     };
11285
11286   char
11287     command[MaxTextExtent],
11288     text[MaxTextExtent];
11289
11290   CommandType
11291     command_type;
11292
11293   Cursor
11294     cursor;
11295
11296   Image
11297     *roi_image;
11298
11299   int
11300     entry,
11301     id,
11302     x,
11303     y;
11304
11305   MagickRealType
11306     scale_factor;
11307
11308   MagickProgressMonitor
11309     progress_monitor;
11310
11311   RectangleInfo
11312     crop_info,
11313     highlight_info,
11314     roi_info;
11315
11316   unsigned int
11317     height,
11318     width;
11319
11320   size_t
11321     state;
11322
11323   XEvent
11324     event;
11325
11326   /*
11327     Map Command widget.
11328   */
11329   (void) CloneString(&windows->command.name,"ROI");
11330   windows->command.data=0;
11331   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11332   (void) XMapRaised(display,windows->command.id);
11333   XClientMessage(display,windows->image.id,windows->im_protocols,
11334     windows->im_update_widget,CurrentTime);
11335   /*
11336     Track pointer until button 1 is pressed.
11337   */
11338   XQueryPosition(display,windows->image.id,&x,&y);
11339   (void) XSelectInput(display,windows->image.id,
11340     windows->image.attributes.event_mask | PointerMotionMask);
11341   roi_info.x=(ssize_t) windows->image.x+x;
11342   roi_info.y=(ssize_t) windows->image.y+y;
11343   roi_info.width=0;
11344   roi_info.height=0;
11345   cursor=XCreateFontCursor(display,XC_fleur);
11346   state=DefaultState;
11347   do
11348   {
11349     if (windows->info.mapped != MagickFalse)
11350       {
11351         /*
11352           Display pointer position.
11353         */
11354         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11355           (long) roi_info.x,(long) roi_info.y);
11356         XInfoWidget(display,windows,text);
11357       }
11358     /*
11359       Wait for next event.
11360     */
11361     XScreenEvent(display,windows,&event,exception);
11362     if (event.xany.window == windows->command.id)
11363       {
11364         /*
11365           Select a command from the Command widget.
11366         */
11367         id=XCommandWidget(display,windows,ROIMenu,&event);
11368         if (id < 0)
11369           continue;
11370         switch (ROICommands[id])
11371         {
11372           case ROIHelpCommand:
11373           {
11374             XTextViewWidget(display,resource_info,windows,MagickFalse,
11375               "Help Viewer - Region of Interest",ImageROIHelp);
11376             break;
11377           }
11378           case ROIDismissCommand:
11379           {
11380             /*
11381               Prematurely exit.
11382             */
11383             state|=EscapeState;
11384             state|=ExitState;
11385             break;
11386           }
11387           default:
11388             break;
11389         }
11390         continue;
11391       }
11392     switch (event.type)
11393     {
11394       case ButtonPress:
11395       {
11396         if (event.xbutton.button != Button1)
11397           break;
11398         if (event.xbutton.window != windows->image.id)
11399           break;
11400         /*
11401           Note first corner of region of interest rectangle-- exit loop.
11402         */
11403         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11404         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11405         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11406         state|=ExitState;
11407         break;
11408       }
11409       case ButtonRelease:
11410         break;
11411       case Expose:
11412         break;
11413       case KeyPress:
11414       {
11415         KeySym
11416           key_symbol;
11417
11418         if (event.xkey.window != windows->image.id)
11419           break;
11420         /*
11421           Respond to a user key press.
11422         */
11423         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11424           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11425         switch ((int) key_symbol)
11426         {
11427           case XK_Escape:
11428           case XK_F20:
11429           {
11430             /*
11431               Prematurely exit.
11432             */
11433             state|=EscapeState;
11434             state|=ExitState;
11435             break;
11436           }
11437           case XK_F1:
11438           case XK_Help:
11439           {
11440             XTextViewWidget(display,resource_info,windows,MagickFalse,
11441               "Help Viewer - Region of Interest",ImageROIHelp);
11442             break;
11443           }
11444           default:
11445           {
11446             (void) XBell(display,0);
11447             break;
11448           }
11449         }
11450         break;
11451       }
11452       case MotionNotify:
11453       {
11454         /*
11455           Map and unmap Info widget as text cursor crosses its boundaries.
11456         */
11457         x=event.xmotion.x;
11458         y=event.xmotion.y;
11459         if (windows->info.mapped != MagickFalse)
11460           {
11461             if ((x < (int) (windows->info.x+windows->info.width)) &&
11462                 (y < (int) (windows->info.y+windows->info.height)))
11463               (void) XWithdrawWindow(display,windows->info.id,
11464                 windows->info.screen);
11465           }
11466         else
11467           if ((x > (int) (windows->info.x+windows->info.width)) ||
11468               (y > (int) (windows->info.y+windows->info.height)))
11469             (void) XMapWindow(display,windows->info.id);
11470         roi_info.x=(ssize_t) windows->image.x+x;
11471         roi_info.y=(ssize_t) windows->image.y+y;
11472         break;
11473       }
11474       default:
11475         break;
11476     }
11477   } while ((state & ExitState) == 0);
11478   (void) XSelectInput(display,windows->image.id,
11479     windows->image.attributes.event_mask);
11480   if ((state & EscapeState) != 0)
11481     {
11482       /*
11483         User want to exit without region of interest.
11484       */
11485       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11486       (void) XFreeCursor(display,cursor);
11487       return(MagickTrue);
11488     }
11489   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11490   do
11491   {
11492     /*
11493       Size rectangle as pointer moves until the mouse button is released.
11494     */
11495     x=(int) roi_info.x;
11496     y=(int) roi_info.y;
11497     roi_info.width=0;
11498     roi_info.height=0;
11499     state=DefaultState;
11500     do
11501     {
11502       highlight_info=roi_info;
11503       highlight_info.x=roi_info.x-windows->image.x;
11504       highlight_info.y=roi_info.y-windows->image.y;
11505       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11506         {
11507           /*
11508             Display info and draw region of interest rectangle.
11509           */
11510           if (windows->info.mapped == MagickFalse)
11511             (void) XMapWindow(display,windows->info.id);
11512           (void) FormatLocaleString(text,MaxTextExtent,
11513             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11514             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11515           XInfoWidget(display,windows,text);
11516           XHighlightRectangle(display,windows->image.id,
11517             windows->image.highlight_context,&highlight_info);
11518         }
11519       else
11520         if (windows->info.mapped != MagickFalse)
11521           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11522       /*
11523         Wait for next event.
11524       */
11525       XScreenEvent(display,windows,&event,exception);
11526       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11527         XHighlightRectangle(display,windows->image.id,
11528           windows->image.highlight_context,&highlight_info);
11529       switch (event.type)
11530       {
11531         case ButtonPress:
11532         {
11533           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11534           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11535           break;
11536         }
11537         case ButtonRelease:
11538         {
11539           /*
11540             User has committed to region of interest rectangle.
11541           */
11542           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11543           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11544           XSetCursorState(display,windows,MagickFalse);
11545           state|=ExitState;
11546           if (LocaleCompare(windows->command.name,"Apply") == 0)
11547             break;
11548           (void) CloneString(&windows->command.name,"Apply");
11549           windows->command.data=ApplyMenus;
11550           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11551           break;
11552         }
11553         case Expose:
11554           break;
11555         case MotionNotify:
11556         {
11557           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11558           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11559         }
11560         default:
11561           break;
11562       }
11563       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11564           ((state & ExitState) != 0))
11565         {
11566           /*
11567             Check boundary conditions.
11568           */
11569           if (roi_info.x < 0)
11570             roi_info.x=0;
11571           else
11572             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11573               roi_info.x=(ssize_t) windows->image.ximage->width;
11574           if ((int) roi_info.x < x)
11575             roi_info.width=(unsigned int) (x-roi_info.x);
11576           else
11577             {
11578               roi_info.width=(unsigned int) (roi_info.x-x);
11579               roi_info.x=(ssize_t) x;
11580             }
11581           if (roi_info.y < 0)
11582             roi_info.y=0;
11583           else
11584             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11585               roi_info.y=(ssize_t) windows->image.ximage->height;
11586           if ((int) roi_info.y < y)
11587             roi_info.height=(unsigned int) (y-roi_info.y);
11588           else
11589             {
11590               roi_info.height=(unsigned int) (roi_info.y-y);
11591               roi_info.y=(ssize_t) y;
11592             }
11593         }
11594     } while ((state & ExitState) == 0);
11595     /*
11596       Wait for user to grab a corner of the rectangle or press return.
11597     */
11598     state=DefaultState;
11599     command_type=NullCommand;
11600     (void) XMapWindow(display,windows->info.id);
11601     do
11602     {
11603       if (windows->info.mapped != MagickFalse)
11604         {
11605           /*
11606             Display pointer position.
11607           */
11608           (void) FormatLocaleString(text,MaxTextExtent,
11609             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11610             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11611           XInfoWidget(display,windows,text);
11612         }
11613       highlight_info=roi_info;
11614       highlight_info.x=roi_info.x-windows->image.x;
11615       highlight_info.y=roi_info.y-windows->image.y;
11616       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11617         {
11618           state|=EscapeState;
11619           state|=ExitState;
11620           break;
11621         }
11622       if ((state & UpdateRegionState) != 0)
11623         {
11624           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11625           switch (command_type)
11626           {
11627             case UndoCommand:
11628             case RedoCommand:
11629             {
11630               (void) XMagickCommand(display,resource_info,windows,command_type,
11631                 image,exception);
11632               break;
11633             }
11634             default:
11635             {
11636               /*
11637                 Region of interest is relative to image configuration.
11638               */
11639               progress_monitor=SetImageProgressMonitor(*image,
11640                 (MagickProgressMonitor) NULL,(*image)->client_data);
11641               crop_info=roi_info;
11642               width=(unsigned int) (*image)->columns;
11643               height=(unsigned int) (*image)->rows;
11644               x=0;
11645               y=0;
11646               if (windows->image.crop_geometry != (char *) NULL)
11647                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11648                   &width,&height);
11649               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11650               crop_info.x+=x;
11651               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11652               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11653               scale_factor=(MagickRealType)
11654                 height/windows->image.ximage->height;
11655               crop_info.y+=y;
11656               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11657               crop_info.height=(unsigned int)
11658                 (scale_factor*crop_info.height+0.5);
11659               roi_image=CropImage(*image,&crop_info,exception);
11660               (void) SetImageProgressMonitor(*image,progress_monitor,
11661                 (*image)->client_data);
11662               if (roi_image == (Image *) NULL)
11663                 continue;
11664               /*
11665                 Apply image processing technique to the region of interest.
11666               */
11667               windows->image.orphan=MagickTrue;
11668               (void) XMagickCommand(display,resource_info,windows,command_type,
11669                 &roi_image,exception);
11670               progress_monitor=SetImageProgressMonitor(*image,
11671                 (MagickProgressMonitor) NULL,(*image)->client_data);
11672               (void) XMagickCommand(display,resource_info,windows,
11673                 SaveToUndoBufferCommand,image,exception);
11674               windows->image.orphan=MagickFalse;
11675               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11676                 crop_info.x,crop_info.y,exception);
11677               roi_image=DestroyImage(roi_image);
11678               (void) SetImageProgressMonitor(*image,progress_monitor,
11679                 (*image)->client_data);
11680               break;
11681             }
11682           }
11683           if (command_type != InfoCommand)
11684             {
11685               XConfigureImageColormap(display,resource_info,windows,*image,
11686                 exception);
11687               (void) XConfigureImage(display,resource_info,windows,*image,
11688                 exception);
11689             }
11690           XCheckRefreshWindows(display,windows);
11691           XInfoWidget(display,windows,text);
11692           (void) XSetFunction(display,windows->image.highlight_context,
11693             GXinvert);
11694           state&=(~UpdateRegionState);
11695         }
11696       XHighlightRectangle(display,windows->image.id,
11697         windows->image.highlight_context,&highlight_info);
11698       XScreenEvent(display,windows,&event,exception);
11699       if (event.xany.window == windows->command.id)
11700         {
11701           /*
11702             Select a command from the Command widget.
11703           */
11704           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11705           command_type=NullCommand;
11706           id=XCommandWidget(display,windows,ApplyMenu,&event);
11707           if (id >= 0)
11708             {
11709               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11710               command_type=ApplyCommands[id];
11711               if (id < ApplyMenus)
11712                 {
11713                   /*
11714                     Select a command from a pop-up menu.
11715                   */
11716                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11717                     (const char **) Menus[id],command);
11718                   if (entry >= 0)
11719                     {
11720                       (void) CopyMagickString(command,Menus[id][entry],
11721                         MaxTextExtent);
11722                       command_type=Commands[id][entry];
11723                     }
11724                 }
11725             }
11726           (void) XSetFunction(display,windows->image.highlight_context,
11727             GXinvert);
11728           XHighlightRectangle(display,windows->image.id,
11729             windows->image.highlight_context,&highlight_info);
11730           if (command_type == HelpCommand)
11731             {
11732               (void) XSetFunction(display,windows->image.highlight_context,
11733                 GXcopy);
11734               XTextViewWidget(display,resource_info,windows,MagickFalse,
11735                 "Help Viewer - Region of Interest",ImageROIHelp);
11736               (void) XSetFunction(display,windows->image.highlight_context,
11737                 GXinvert);
11738               continue;
11739             }
11740           if (command_type == QuitCommand)
11741             {
11742               /*
11743                 exit.
11744               */
11745               state|=EscapeState;
11746               state|=ExitState;
11747               continue;
11748             }
11749           if (command_type != NullCommand)
11750             state|=UpdateRegionState;
11751           continue;
11752         }
11753       XHighlightRectangle(display,windows->image.id,
11754         windows->image.highlight_context,&highlight_info);
11755       switch (event.type)
11756       {
11757         case ButtonPress:
11758         {
11759           x=windows->image.x;
11760           y=windows->image.y;
11761           if (event.xbutton.button != Button1)
11762             break;
11763           if (event.xbutton.window != windows->image.id)
11764             break;
11765           x=windows->image.x+event.xbutton.x;
11766           y=windows->image.y+event.xbutton.y;
11767           if ((x < (int) (roi_info.x+RoiDelta)) &&
11768               (x > (int) (roi_info.x-RoiDelta)) &&
11769               (y < (int) (roi_info.y+RoiDelta)) &&
11770               (y > (int) (roi_info.y-RoiDelta)))
11771             {
11772               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11773               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11774               state|=UpdateConfigurationState;
11775               break;
11776             }
11777           if ((x < (int) (roi_info.x+RoiDelta)) &&
11778               (x > (int) (roi_info.x-RoiDelta)) &&
11779               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11780               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11781             {
11782               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11783               state|=UpdateConfigurationState;
11784               break;
11785             }
11786           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11787               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11788               (y < (int) (roi_info.y+RoiDelta)) &&
11789               (y > (int) (roi_info.y-RoiDelta)))
11790             {
11791               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11792               state|=UpdateConfigurationState;
11793               break;
11794             }
11795           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11796               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11797               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11798               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11799             {
11800               state|=UpdateConfigurationState;
11801               break;
11802             }
11803         }
11804         case ButtonRelease:
11805         {
11806           if (event.xbutton.window == windows->pan.id)
11807             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11808                 (highlight_info.y != crop_info.y-windows->image.y))
11809               XHighlightRectangle(display,windows->image.id,
11810                 windows->image.highlight_context,&highlight_info);
11811           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11812             event.xbutton.time);
11813           break;
11814         }
11815         case Expose:
11816         {
11817           if (event.xexpose.window == windows->image.id)
11818             if (event.xexpose.count == 0)
11819               {
11820                 event.xexpose.x=(int) highlight_info.x;
11821                 event.xexpose.y=(int) highlight_info.y;
11822                 event.xexpose.width=(int) highlight_info.width;
11823                 event.xexpose.height=(int) highlight_info.height;
11824                 XRefreshWindow(display,&windows->image,&event);
11825               }
11826           if (event.xexpose.window == windows->info.id)
11827             if (event.xexpose.count == 0)
11828               XInfoWidget(display,windows,text);
11829           break;
11830         }
11831         case KeyPress:
11832         {
11833           KeySym
11834             key_symbol;
11835
11836           if (event.xkey.window != windows->image.id)
11837             break;
11838           /*
11839             Respond to a user key press.
11840           */
11841           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11842             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11843           switch ((int) key_symbol)
11844           {
11845             case XK_Shift_L:
11846             case XK_Shift_R:
11847               break;
11848             case XK_Escape:
11849             case XK_F20:
11850               state|=EscapeState;
11851             case XK_Return:
11852             {
11853               state|=ExitState;
11854               break;
11855             }
11856             case XK_Home:
11857             case XK_KP_Home:
11858             {
11859               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11860               roi_info.y=(ssize_t) (windows->image.height/2L-
11861                 roi_info.height/2L);
11862               break;
11863             }
11864             case XK_Left:
11865             case XK_KP_Left:
11866             {
11867               roi_info.x--;
11868               break;
11869             }
11870             case XK_Up:
11871             case XK_KP_Up:
11872             case XK_Next:
11873             {
11874               roi_info.y--;
11875               break;
11876             }
11877             case XK_Right:
11878             case XK_KP_Right:
11879             {
11880               roi_info.x++;
11881               break;
11882             }
11883             case XK_Prior:
11884             case XK_Down:
11885             case XK_KP_Down:
11886             {
11887               roi_info.y++;
11888               break;
11889             }
11890             case XK_F1:
11891             case XK_Help:
11892             {
11893               (void) XSetFunction(display,windows->image.highlight_context,
11894                 GXcopy);
11895               XTextViewWidget(display,resource_info,windows,MagickFalse,
11896                 "Help Viewer - Region of Interest",ImageROIHelp);
11897               (void) XSetFunction(display,windows->image.highlight_context,
11898                 GXinvert);
11899               break;
11900             }
11901             default:
11902             {
11903               command_type=XImageWindowCommand(display,resource_info,windows,
11904                 event.xkey.state,key_symbol,image,exception);
11905               if (command_type != NullCommand)
11906                 state|=UpdateRegionState;
11907               break;
11908             }
11909           }
11910           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11911             event.xkey.time);
11912           break;
11913         }
11914         case KeyRelease:
11915           break;
11916         case MotionNotify:
11917         {
11918           if (event.xbutton.window != windows->image.id)
11919             break;
11920           /*
11921             Map and unmap Info widget as text cursor crosses its boundaries.
11922           */
11923           x=event.xmotion.x;
11924           y=event.xmotion.y;
11925           if (windows->info.mapped != MagickFalse)
11926             {
11927               if ((x < (int) (windows->info.x+windows->info.width)) &&
11928                   (y < (int) (windows->info.y+windows->info.height)))
11929                 (void) XWithdrawWindow(display,windows->info.id,
11930                   windows->info.screen);
11931             }
11932           else
11933             if ((x > (int) (windows->info.x+windows->info.width)) ||
11934                 (y > (int) (windows->info.y+windows->info.height)))
11935               (void) XMapWindow(display,windows->info.id);
11936           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11937           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11938           break;
11939         }
11940         case SelectionRequest:
11941         {
11942           XSelectionEvent
11943             notify;
11944
11945           XSelectionRequestEvent
11946             *request;
11947
11948           /*
11949             Set primary selection.
11950           */
11951           (void) FormatLocaleString(text,MaxTextExtent,
11952             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11953             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11954           request=(&(event.xselectionrequest));
11955           (void) XChangeProperty(request->display,request->requestor,
11956             request->property,request->target,8,PropModeReplace,
11957             (unsigned char *) text,(int) strlen(text));
11958           notify.type=SelectionNotify;
11959           notify.display=request->display;
11960           notify.requestor=request->requestor;
11961           notify.selection=request->selection;
11962           notify.target=request->target;
11963           notify.time=request->time;
11964           if (request->property == None)
11965             notify.property=request->target;
11966           else
11967             notify.property=request->property;
11968           (void) XSendEvent(request->display,request->requestor,False,0,
11969             (XEvent *) &notify);
11970         }
11971         default:
11972           break;
11973       }
11974       if ((state & UpdateConfigurationState) != 0)
11975         {
11976           (void) XPutBackEvent(display,&event);
11977           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11978           break;
11979         }
11980     } while ((state & ExitState) == 0);
11981   } while ((state & ExitState) == 0);
11982   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11983   XSetCursorState(display,windows,MagickFalse);
11984   if ((state & EscapeState) != 0)
11985     return(MagickTrue);
11986   return(MagickTrue);
11987 }
11988 \f
11989 /*
11990 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11991 %                                                                             %
11992 %                                                                             %
11993 %                                                                             %
11994 +   X R o t a t e I m a g e                                                   %
11995 %                                                                             %
11996 %                                                                             %
11997 %                                                                             %
11998 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11999 %
12000 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
12001 %  rotation angle is computed from the slope of a line drawn by the user.
12002 %
12003 %  The format of the XRotateImage method is:
12004 %
12005 %      MagickBooleanType XRotateImage(Display *display,
12006 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
12007 %        Image **image,ExceptionInfo *exception)
12008 %
12009 %  A description of each parameter follows:
12010 %
12011 %    o display: Specifies a connection to an X server; returned from
12012 %      XOpenDisplay.
12013 %
12014 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12015 %
12016 %    o windows: Specifies a pointer to a XWindows structure.
12017 %
12018 %    o degrees: Specifies the number of degrees to rotate the image.
12019 %
12020 %    o image: the image.
12021 %
12022 %    o exception: return any errors or warnings in this structure.
12023 %
12024 */
12025 static MagickBooleanType XRotateImage(Display *display,
12026   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12027   ExceptionInfo *exception)
12028 {
12029   static const char
12030     *RotateMenu[] =
12031     {
12032       "Pixel Color",
12033       "Direction",
12034       "Help",
12035       "Dismiss",
12036       (char *) NULL
12037     };
12038
12039   static ModeType
12040     direction = HorizontalRotateCommand;
12041
12042   static const ModeType
12043     DirectionCommands[] =
12044     {
12045       HorizontalRotateCommand,
12046       VerticalRotateCommand
12047     },
12048     RotateCommands[] =
12049     {
12050       RotateColorCommand,
12051       RotateDirectionCommand,
12052       RotateHelpCommand,
12053       RotateDismissCommand
12054     };
12055
12056   static unsigned int
12057     pen_id = 0;
12058
12059   char
12060     command[MaxTextExtent],
12061     text[MaxTextExtent];
12062
12063   Image
12064     *rotate_image;
12065
12066   int
12067     id,
12068     x,
12069     y;
12070
12071   MagickRealType
12072     normalized_degrees;
12073
12074   register int
12075     i;
12076
12077   unsigned int
12078     height,
12079     rotations,
12080     width;
12081
12082   if (degrees == 0.0)
12083     {
12084       unsigned int
12085         distance;
12086
12087       size_t
12088         state;
12089
12090       XEvent
12091         event;
12092
12093       XSegment
12094         rotate_info;
12095
12096       /*
12097         Map Command widget.
12098       */
12099       (void) CloneString(&windows->command.name,"Rotate");
12100       windows->command.data=2;
12101       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12102       (void) XMapRaised(display,windows->command.id);
12103       XClientMessage(display,windows->image.id,windows->im_protocols,
12104         windows->im_update_widget,CurrentTime);
12105       /*
12106         Wait for first button press.
12107       */
12108       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12109       XQueryPosition(display,windows->image.id,&x,&y);
12110       rotate_info.x1=x;
12111       rotate_info.y1=y;
12112       rotate_info.x2=x;
12113       rotate_info.y2=y;
12114       state=DefaultState;
12115       do
12116       {
12117         XHighlightLine(display,windows->image.id,
12118           windows->image.highlight_context,&rotate_info);
12119         /*
12120           Wait for next event.
12121         */
12122         XScreenEvent(display,windows,&event,exception);
12123         XHighlightLine(display,windows->image.id,
12124           windows->image.highlight_context,&rotate_info);
12125         if (event.xany.window == windows->command.id)
12126           {
12127             /*
12128               Select a command from the Command widget.
12129             */
12130             id=XCommandWidget(display,windows,RotateMenu,&event);
12131             if (id < 0)
12132               continue;
12133             (void) XSetFunction(display,windows->image.highlight_context,
12134               GXcopy);
12135             switch (RotateCommands[id])
12136             {
12137               case RotateColorCommand:
12138               {
12139                 const char
12140                   *ColorMenu[MaxNumberPens];
12141
12142                 int
12143                   pen_number;
12144
12145                 XColor
12146                   color;
12147
12148                 /*
12149                   Initialize menu selections.
12150                 */
12151                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12152                   ColorMenu[i]=resource_info->pen_colors[i];
12153                 ColorMenu[MaxNumberPens-2]="Browser...";
12154                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12155                 /*
12156                   Select a pen color from the pop-up menu.
12157                 */
12158                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12159                   (const char **) ColorMenu,command);
12160                 if (pen_number < 0)
12161                   break;
12162                 if (pen_number == (MaxNumberPens-2))
12163                   {
12164                     static char
12165                       color_name[MaxTextExtent] = "gray";
12166
12167                     /*
12168                       Select a pen color from a dialog.
12169                     */
12170                     resource_info->pen_colors[pen_number]=color_name;
12171                     XColorBrowserWidget(display,windows,"Select",color_name);
12172                     if (*color_name == '\0')
12173                       break;
12174                   }
12175                 /*
12176                   Set pen color.
12177                 */
12178                 (void) XParseColor(display,windows->map_info->colormap,
12179                   resource_info->pen_colors[pen_number],&color);
12180                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12181                   (unsigned int) MaxColors,&color);
12182                 windows->pixel_info->pen_colors[pen_number]=color;
12183                 pen_id=(unsigned int) pen_number;
12184                 break;
12185               }
12186               case RotateDirectionCommand:
12187               {
12188                 static const char
12189                   *Directions[] =
12190                   {
12191                     "horizontal",
12192                     "vertical",
12193                     (char *) NULL,
12194                   };
12195
12196                 /*
12197                   Select a command from the pop-up menu.
12198                 */
12199                 id=XMenuWidget(display,windows,RotateMenu[id],
12200                   Directions,command);
12201                 if (id >= 0)
12202                   direction=DirectionCommands[id];
12203                 break;
12204               }
12205               case RotateHelpCommand:
12206               {
12207                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12208                   "Help Viewer - Image Rotation",ImageRotateHelp);
12209                 break;
12210               }
12211               case RotateDismissCommand:
12212               {
12213                 /*
12214                   Prematurely exit.
12215                 */
12216                 state|=EscapeState;
12217                 state|=ExitState;
12218                 break;
12219               }
12220               default:
12221                 break;
12222             }
12223             (void) XSetFunction(display,windows->image.highlight_context,
12224               GXinvert);
12225             continue;
12226           }
12227         switch (event.type)
12228         {
12229           case ButtonPress:
12230           {
12231             if (event.xbutton.button != Button1)
12232               break;
12233             if (event.xbutton.window != windows->image.id)
12234               break;
12235             /*
12236               exit loop.
12237             */
12238             (void) XSetFunction(display,windows->image.highlight_context,
12239               GXcopy);
12240             rotate_info.x1=event.xbutton.x;
12241             rotate_info.y1=event.xbutton.y;
12242             state|=ExitState;
12243             break;
12244           }
12245           case ButtonRelease:
12246             break;
12247           case Expose:
12248             break;
12249           case KeyPress:
12250           {
12251             char
12252               command[MaxTextExtent];
12253
12254             KeySym
12255               key_symbol;
12256
12257             if (event.xkey.window != windows->image.id)
12258               break;
12259             /*
12260               Respond to a user key press.
12261             */
12262             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12263               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12264             switch ((int) key_symbol)
12265             {
12266               case XK_Escape:
12267               case XK_F20:
12268               {
12269                 /*
12270                   Prematurely exit.
12271                 */
12272                 state|=EscapeState;
12273                 state|=ExitState;
12274                 break;
12275               }
12276               case XK_F1:
12277               case XK_Help:
12278               {
12279                 (void) XSetFunction(display,windows->image.highlight_context,
12280                   GXcopy);
12281                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12282                   "Help Viewer - Image Rotation",ImageRotateHelp);
12283                 (void) XSetFunction(display,windows->image.highlight_context,
12284                   GXinvert);
12285                 break;
12286               }
12287               default:
12288               {
12289                 (void) XBell(display,0);
12290                 break;
12291               }
12292             }
12293             break;
12294           }
12295           case MotionNotify:
12296           {
12297             rotate_info.x1=event.xmotion.x;
12298             rotate_info.y1=event.xmotion.y;
12299           }
12300         }
12301         rotate_info.x2=rotate_info.x1;
12302         rotate_info.y2=rotate_info.y1;
12303         if (direction == HorizontalRotateCommand)
12304           rotate_info.x2+=32;
12305         else
12306           rotate_info.y2-=32;
12307       } while ((state & ExitState) == 0);
12308       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12309       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12310       if ((state & EscapeState) != 0)
12311         return(MagickTrue);
12312       /*
12313         Draw line as pointer moves until the mouse button is released.
12314       */
12315       distance=0;
12316       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12317       state=DefaultState;
12318       do
12319       {
12320         if (distance > 9)
12321           {
12322             /*
12323               Display info and draw rotation line.
12324             */
12325             if (windows->info.mapped == MagickFalse)
12326               (void) XMapWindow(display,windows->info.id);
12327             (void) FormatLocaleString(text,MaxTextExtent," %g",
12328               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12329             XInfoWidget(display,windows,text);
12330             XHighlightLine(display,windows->image.id,
12331               windows->image.highlight_context,&rotate_info);
12332           }
12333         else
12334           if (windows->info.mapped != MagickFalse)
12335             (void) XWithdrawWindow(display,windows->info.id,
12336               windows->info.screen);
12337         /*
12338           Wait for next event.
12339         */
12340         XScreenEvent(display,windows,&event,exception);
12341         if (distance > 9)
12342           XHighlightLine(display,windows->image.id,
12343             windows->image.highlight_context,&rotate_info);
12344         switch (event.type)
12345         {
12346           case ButtonPress:
12347             break;
12348           case ButtonRelease:
12349           {
12350             /*
12351               User has committed to rotation line.
12352             */
12353             rotate_info.x2=event.xbutton.x;
12354             rotate_info.y2=event.xbutton.y;
12355             state|=ExitState;
12356             break;
12357           }
12358           case Expose:
12359             break;
12360           case MotionNotify:
12361           {
12362             rotate_info.x2=event.xmotion.x;
12363             rotate_info.y2=event.xmotion.y;
12364           }
12365           default:
12366             break;
12367         }
12368         /*
12369           Check boundary conditions.
12370         */
12371         if (rotate_info.x2 < 0)
12372           rotate_info.x2=0;
12373         else
12374           if (rotate_info.x2 > (int) windows->image.width)
12375             rotate_info.x2=(short) windows->image.width;
12376         if (rotate_info.y2 < 0)
12377           rotate_info.y2=0;
12378         else
12379           if (rotate_info.y2 > (int) windows->image.height)
12380             rotate_info.y2=(short) windows->image.height;
12381         /*
12382           Compute rotation angle from the slope of the line.
12383         */
12384         degrees=0.0;
12385         distance=(unsigned int)
12386           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12387           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12388         if (distance > 9)
12389           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12390             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12391       } while ((state & ExitState) == 0);
12392       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12393       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12394       if (distance <= 9)
12395         return(MagickTrue);
12396     }
12397   if (direction == VerticalRotateCommand)
12398     degrees-=90.0;
12399   if (degrees == 0.0)
12400     return(MagickTrue);
12401   /*
12402     Rotate image.
12403   */
12404   normalized_degrees=degrees;
12405   while (normalized_degrees < -45.0)
12406     normalized_degrees+=360.0;
12407   for (rotations=0; normalized_degrees > 45.0; rotations++)
12408     normalized_degrees-=90.0;
12409   if (normalized_degrees != 0.0)
12410     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12411       exception);
12412   XSetCursorState(display,windows,MagickTrue);
12413   XCheckRefreshWindows(display,windows);
12414   (*image)->background_color.red=ScaleShortToQuantum(
12415     windows->pixel_info->pen_colors[pen_id].red);
12416   (*image)->background_color.green=ScaleShortToQuantum(
12417     windows->pixel_info->pen_colors[pen_id].green);
12418   (*image)->background_color.blue=ScaleShortToQuantum(
12419     windows->pixel_info->pen_colors[pen_id].blue);
12420   rotate_image=RotateImage(*image,degrees,exception);
12421   XSetCursorState(display,windows,MagickFalse);
12422   if (rotate_image == (Image *) NULL)
12423     return(MagickFalse);
12424   *image=DestroyImage(*image);
12425   *image=rotate_image;
12426   if (windows->image.crop_geometry != (char *) NULL)
12427     {
12428       /*
12429         Rotate crop geometry.
12430       */
12431       width=(unsigned int) (*image)->columns;
12432       height=(unsigned int) (*image)->rows;
12433       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12434       switch (rotations % 4)
12435       {
12436         default:
12437         case 0:
12438           break;
12439         case 1:
12440         {
12441           /*
12442             Rotate 90 degrees.
12443           */
12444           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12445             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12446             (int) height-y,x);
12447           break;
12448         }
12449         case 2:
12450         {
12451           /*
12452             Rotate 180 degrees.
12453           */
12454           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12455             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12456           break;
12457         }
12458         case 3:
12459         {
12460           /*
12461             Rotate 270 degrees.
12462           */
12463           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12464             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12465           break;
12466         }
12467       }
12468     }
12469   if (windows->image.orphan != MagickFalse)
12470     return(MagickTrue);
12471   if (normalized_degrees != 0.0)
12472     {
12473       /*
12474         Update image colormap.
12475       */
12476       windows->image.window_changes.width=(int) (*image)->columns;
12477       windows->image.window_changes.height=(int) (*image)->rows;
12478       if (windows->image.crop_geometry != (char *) NULL)
12479         {
12480           /*
12481             Obtain dimensions of image from crop geometry.
12482           */
12483           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12484             &width,&height);
12485           windows->image.window_changes.width=(int) width;
12486           windows->image.window_changes.height=(int) height;
12487         }
12488       XConfigureImageColormap(display,resource_info,windows,*image,exception);
12489     }
12490   else
12491     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12492       {
12493         windows->image.window_changes.width=windows->image.ximage->height;
12494         windows->image.window_changes.height=windows->image.ximage->width;
12495       }
12496   /*
12497     Update image configuration.
12498   */
12499   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12500   return(MagickTrue);
12501 }
12502 \f
12503 /*
12504 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12505 %                                                                             %
12506 %                                                                             %
12507 %                                                                             %
12508 +   X S a v e I m a g e                                                       %
12509 %                                                                             %
12510 %                                                                             %
12511 %                                                                             %
12512 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12513 %
12514 %  XSaveImage() saves an image to a file.
12515 %
12516 %  The format of the XSaveImage method is:
12517 %
12518 %      MagickBooleanType XSaveImage(Display *display,
12519 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12520 %        ExceptionInfo *exception)
12521 %
12522 %  A description of each parameter follows:
12523 %
12524 %    o display: Specifies a connection to an X server; returned from
12525 %      XOpenDisplay.
12526 %
12527 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12528 %
12529 %    o windows: Specifies a pointer to a XWindows structure.
12530 %
12531 %    o image: the image.
12532 %
12533 %    o exception: return any errors or warnings in this structure.
12534 %
12535 */
12536 static MagickBooleanType XSaveImage(Display *display,
12537   XResourceInfo *resource_info,XWindows *windows,Image *image,
12538   ExceptionInfo *exception)
12539 {
12540   char
12541     filename[MaxTextExtent],
12542     geometry[MaxTextExtent];
12543
12544   Image
12545     *save_image;
12546
12547   ImageInfo
12548     *image_info;
12549
12550   MagickStatusType
12551     status;
12552
12553   /*
12554     Request file name from user.
12555   */
12556   if (resource_info->write_filename != (char *) NULL)
12557     (void) CopyMagickString(filename,resource_info->write_filename,
12558       MaxTextExtent);
12559   else
12560     {
12561       char
12562         path[MaxTextExtent];
12563
12564       int
12565         status;
12566
12567       GetPathComponent(image->filename,HeadPath,path);
12568       GetPathComponent(image->filename,TailPath,filename);
12569       if (*path != '\0')
12570         {
12571           status=chdir(path);
12572           if (status == -1)
12573             (void) ThrowMagickException(exception,GetMagickModule(),
12574               FileOpenError,"UnableToOpenFile","%s",path);
12575         }
12576     }
12577   XFileBrowserWidget(display,windows,"Save",filename);
12578   if (*filename == '\0')
12579     return(MagickTrue);
12580   if (IsPathAccessible(filename) != MagickFalse)
12581     {
12582       int
12583         status;
12584
12585       /*
12586         File exists-- seek user's permission before overwriting.
12587       */
12588       status=XConfirmWidget(display,windows,"Overwrite",filename);
12589       if (status <= 0)
12590         return(MagickTrue);
12591     }
12592   image_info=CloneImageInfo(resource_info->image_info);
12593   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12594   (void) SetImageInfo(image_info,1,exception);
12595   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12596       (LocaleCompare(image_info->magick,"JPG") == 0))
12597     {
12598       char
12599         quality[MaxTextExtent];
12600
12601       int
12602         status;
12603
12604       /*
12605         Request JPEG quality from user.
12606       */
12607       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12608         image->quality);
12609       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12610         quality);
12611       if (*quality == '\0')
12612         return(MagickTrue);
12613       image->quality=StringToUnsignedLong(quality);
12614       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12615     }
12616   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12617       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12618       (LocaleCompare(image_info->magick,"PS") == 0) ||
12619       (LocaleCompare(image_info->magick,"PS2") == 0))
12620     {
12621       char
12622         geometry[MaxTextExtent];
12623
12624       /*
12625         Request page geometry from user.
12626       */
12627       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12628       if (LocaleCompare(image_info->magick,"PDF") == 0)
12629         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12630       if (image_info->page != (char *) NULL)
12631         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12632       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12633         "Select page geometry:",geometry);
12634       if (*geometry != '\0')
12635         image_info->page=GetPageGeometry(geometry);
12636     }
12637   /*
12638     Apply image transforms.
12639   */
12640   XSetCursorState(display,windows,MagickTrue);
12641   XCheckRefreshWindows(display,windows);
12642   save_image=CloneImage(image,0,0,MagickTrue,exception);
12643   if (save_image == (Image *) NULL)
12644     return(MagickFalse);
12645   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12646     windows->image.ximage->width,windows->image.ximage->height);
12647   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12648     exception);
12649   /*
12650     Write image.
12651   */
12652   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12653   status=WriteImage(image_info,save_image,exception);
12654   if (status != MagickFalse)
12655     image->taint=MagickFalse;
12656   save_image=DestroyImage(save_image);
12657   image_info=DestroyImageInfo(image_info);
12658   XSetCursorState(display,windows,MagickFalse);
12659   return(status != 0 ? MagickTrue : MagickFalse);
12660 }
12661 \f
12662 /*
12663 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12664 %                                                                             %
12665 %                                                                             %
12666 %                                                                             %
12667 +   X S c r e e n E v e n t                                                   %
12668 %                                                                             %
12669 %                                                                             %
12670 %                                                                             %
12671 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12672 %
12673 %  XScreenEvent() handles global events associated with the Pan and Magnify
12674 %  windows.
12675 %
12676 %  The format of the XScreenEvent function is:
12677 %
12678 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12679 %        ExceptionInfo *exception)
12680 %
12681 %  A description of each parameter follows:
12682 %
12683 %    o display: Specifies a pointer to the Display structure;  returned from
12684 %      XOpenDisplay.
12685 %
12686 %    o windows: Specifies a pointer to a XWindows structure.
12687 %
12688 %    o event: Specifies a pointer to a X11 XEvent structure.
12689 %
12690 %    o exception: return any errors or warnings in this structure.
12691 %
12692 */
12693
12694 #if defined(__cplusplus) || defined(c_plusplus)
12695 extern "C" {
12696 #endif
12697
12698 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12699 {
12700   register XWindows
12701     *windows;
12702
12703   windows=(XWindows *) data;
12704   if ((event->type == ClientMessage) &&
12705       (event->xclient.window == windows->image.id))
12706     return(MagickFalse);
12707   return(MagickTrue);
12708 }
12709
12710 #if defined(__cplusplus) || defined(c_plusplus)
12711 }
12712 #endif
12713
12714 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12715   ExceptionInfo *exception)
12716 {
12717   register int
12718     x,
12719     y;
12720
12721   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12722   if (event->xany.window == windows->command.id)
12723     return;
12724   switch (event->type)
12725   {
12726     case ButtonPress:
12727     case ButtonRelease:
12728     {
12729       if ((event->xbutton.button == Button3) &&
12730           (event->xbutton.state & Mod1Mask))
12731         {
12732           /*
12733             Convert Alt-Button3 to Button2.
12734           */
12735           event->xbutton.button=Button2;
12736           event->xbutton.state&=(~Mod1Mask);
12737         }
12738       if (event->xbutton.window == windows->backdrop.id)
12739         {
12740           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12741             event->xbutton.time);
12742           break;
12743         }
12744       if (event->xbutton.window == windows->pan.id)
12745         {
12746           XPanImage(display,windows,event,exception);
12747           break;
12748         }
12749       if (event->xbutton.window == windows->image.id)
12750         if (event->xbutton.button == Button2)
12751           {
12752             /*
12753               Update magnified image.
12754             */
12755             x=event->xbutton.x;
12756             y=event->xbutton.y;
12757             if (x < 0)
12758               x=0;
12759             else
12760               if (x >= (int) windows->image.width)
12761                 x=(int) (windows->image.width-1);
12762             windows->magnify.x=(int) windows->image.x+x;
12763             if (y < 0)
12764               y=0;
12765             else
12766              if (y >= (int) windows->image.height)
12767                y=(int) (windows->image.height-1);
12768             windows->magnify.y=windows->image.y+y;
12769             if (windows->magnify.mapped == MagickFalse)
12770               (void) XMapRaised(display,windows->magnify.id);
12771             XMakeMagnifyImage(display,windows,exception);
12772             if (event->type == ButtonRelease)
12773               (void) XWithdrawWindow(display,windows->info.id,
12774                 windows->info.screen);
12775             break;
12776           }
12777       break;
12778     }
12779     case ClientMessage:
12780     {
12781       /*
12782         If client window delete message, exit.
12783       */
12784       if (event->xclient.message_type != windows->wm_protocols)
12785         break;
12786       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12787         break;
12788       if (event->xclient.window == windows->magnify.id)
12789         {
12790           (void) XWithdrawWindow(display,windows->magnify.id,
12791             windows->magnify.screen);
12792           break;
12793         }
12794       break;
12795     }
12796     case ConfigureNotify:
12797     {
12798       if (event->xconfigure.window == windows->magnify.id)
12799         {
12800           unsigned int
12801             magnify;
12802
12803           /*
12804             Magnify window has a new configuration.
12805           */
12806           windows->magnify.width=(unsigned int) event->xconfigure.width;
12807           windows->magnify.height=(unsigned int) event->xconfigure.height;
12808           if (windows->magnify.mapped == MagickFalse)
12809             break;
12810           magnify=1;
12811           while ((int) magnify <= event->xconfigure.width)
12812             magnify<<=1;
12813           while ((int) magnify <= event->xconfigure.height)
12814             magnify<<=1;
12815           magnify>>=1;
12816           if (((int) magnify != event->xconfigure.width) ||
12817               ((int) magnify != event->xconfigure.height))
12818             {
12819               XWindowChanges
12820                 window_changes;
12821
12822               window_changes.width=(int) magnify;
12823               window_changes.height=(int) magnify;
12824               (void) XReconfigureWMWindow(display,windows->magnify.id,
12825                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12826                 &window_changes);
12827               break;
12828             }
12829           XMakeMagnifyImage(display,windows,exception);
12830           break;
12831         }
12832       break;
12833     }
12834     case Expose:
12835     {
12836       if (event->xexpose.window == windows->image.id)
12837         {
12838           XRefreshWindow(display,&windows->image,event);
12839           break;
12840         }
12841       if (event->xexpose.window == windows->pan.id)
12842         if (event->xexpose.count == 0)
12843           {
12844             XDrawPanRectangle(display,windows);
12845             break;
12846           }
12847       if (event->xexpose.window == windows->magnify.id)
12848         if (event->xexpose.count == 0)
12849           {
12850             XMakeMagnifyImage(display,windows,exception);
12851             break;
12852           }
12853       break;
12854     }
12855     case KeyPress:
12856     {
12857       char
12858         command[MaxTextExtent];
12859
12860       KeySym
12861         key_symbol;
12862
12863       if (event->xkey.window != windows->magnify.id)
12864         break;
12865       /*
12866         Respond to a user key press.
12867       */
12868       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12869         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12870       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12871         exception);
12872       break;
12873     }
12874     case MapNotify:
12875     {
12876       if (event->xmap.window == windows->magnify.id)
12877         {
12878           windows->magnify.mapped=MagickTrue;
12879           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12880           break;
12881         }
12882       if (event->xmap.window == windows->info.id)
12883         {
12884           windows->info.mapped=MagickTrue;
12885           break;
12886         }
12887       break;
12888     }
12889     case MotionNotify:
12890     {
12891       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12892       if (event->xmotion.window == windows->image.id)
12893         if (windows->magnify.mapped != MagickFalse)
12894           {
12895             /*
12896               Update magnified image.
12897             */
12898             x=event->xmotion.x;
12899             y=event->xmotion.y;
12900             if (x < 0)
12901               x=0;
12902             else
12903               if (x >= (int) windows->image.width)
12904                 x=(int) (windows->image.width-1);
12905             windows->magnify.x=(int) windows->image.x+x;
12906             if (y < 0)
12907               y=0;
12908             else
12909              if (y >= (int) windows->image.height)
12910                y=(int) (windows->image.height-1);
12911             windows->magnify.y=windows->image.y+y;
12912             XMakeMagnifyImage(display,windows,exception);
12913           }
12914       break;
12915     }
12916     case UnmapNotify:
12917     {
12918       if (event->xunmap.window == windows->magnify.id)
12919         {
12920           windows->magnify.mapped=MagickFalse;
12921           break;
12922         }
12923       if (event->xunmap.window == windows->info.id)
12924         {
12925           windows->info.mapped=MagickFalse;
12926           break;
12927         }
12928       break;
12929     }
12930     default:
12931       break;
12932   }
12933 }
12934 \f
12935 /*
12936 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12937 %                                                                             %
12938 %                                                                             %
12939 %                                                                             %
12940 +   X S e t C r o p G e o m e t r y                                           %
12941 %                                                                             %
12942 %                                                                             %
12943 %                                                                             %
12944 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12945 %
12946 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12947 %  and translates it to a cropping geometry relative to the image.
12948 %
12949 %  The format of the XSetCropGeometry method is:
12950 %
12951 %      void XSetCropGeometry(Display *display,XWindows *windows,
12952 %        RectangleInfo *crop_info,Image *image)
12953 %
12954 %  A description of each parameter follows:
12955 %
12956 %    o display: Specifies a connection to an X server; returned from
12957 %      XOpenDisplay.
12958 %
12959 %    o windows: Specifies a pointer to a XWindows structure.
12960 %
12961 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12962 %      Image window to crop.
12963 %
12964 %    o image: the image.
12965 %
12966 */
12967 static void XSetCropGeometry(Display *display,XWindows *windows,
12968   RectangleInfo *crop_info,Image *image)
12969 {
12970   char
12971     text[MaxTextExtent];
12972
12973   int
12974     x,
12975     y;
12976
12977   MagickRealType
12978     scale_factor;
12979
12980   unsigned int
12981     height,
12982     width;
12983
12984   if (windows->info.mapped != MagickFalse)
12985     {
12986       /*
12987         Display info on cropping rectangle.
12988       */
12989       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12990         (double) crop_info->width,(double) crop_info->height,(double)
12991         crop_info->x,(double) crop_info->y);
12992       XInfoWidget(display,windows,text);
12993     }
12994   /*
12995     Cropping geometry is relative to any previous crop geometry.
12996   */
12997   x=0;
12998   y=0;
12999   width=(unsigned int) image->columns;
13000   height=(unsigned int) image->rows;
13001   if (windows->image.crop_geometry != (char *) NULL)
13002     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13003   else
13004     windows->image.crop_geometry=AcquireString((char *) NULL);
13005   /*
13006     Define the crop geometry string from the cropping rectangle.
13007   */
13008   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13009   if (crop_info->x > 0)
13010     x+=(int) (scale_factor*crop_info->x+0.5);
13011   width=(unsigned int) (scale_factor*crop_info->width+0.5);
13012   if (width == 0)
13013     width=1;
13014   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13015   if (crop_info->y > 0)
13016     y+=(int) (scale_factor*crop_info->y+0.5);
13017   height=(unsigned int) (scale_factor*crop_info->height+0.5);
13018   if (height == 0)
13019     height=1;
13020   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13021     "%ux%u%+d%+d",width,height,x,y);
13022 }
13023 \f
13024 /*
13025 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13026 %                                                                             %
13027 %                                                                             %
13028 %                                                                             %
13029 +   X T i l e I m a g e                                                       %
13030 %                                                                             %
13031 %                                                                             %
13032 %                                                                             %
13033 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13034 %
13035 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13036 %  The load or delete command is chosen from a menu.
13037 %
13038 %  The format of the XTileImage method is:
13039 %
13040 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13041 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13042 %
13043 %  A description of each parameter follows:
13044 %
13045 %    o tile_image:  XTileImage reads or deletes the tile image
13046 %      and returns it.  A null image is returned if an error occurs.
13047 %
13048 %    o display: Specifies a connection to an X server;  returned from
13049 %      XOpenDisplay.
13050 %
13051 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13052 %
13053 %    o windows: Specifies a pointer to a XWindows structure.
13054 %
13055 %    o image: the image; returned from ReadImage.
13056 %
13057 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13058 %      the entire image is refreshed.
13059 %
13060 %    o exception: return any errors or warnings in this structure.
13061 %
13062 */
13063 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13064   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13065 {
13066   static const char
13067     *VerbMenu[] =
13068     {
13069       "Load",
13070       "Next",
13071       "Former",
13072       "Delete",
13073       "Update",
13074       (char *) NULL,
13075     };
13076
13077   static const ModeType
13078     TileCommands[] =
13079     {
13080       TileLoadCommand,
13081       TileNextCommand,
13082       TileFormerCommand,
13083       TileDeleteCommand,
13084       TileUpdateCommand
13085     };
13086
13087   char
13088     command[MaxTextExtent],
13089     filename[MaxTextExtent];
13090
13091   Image
13092     *tile_image;
13093
13094   int
13095     id,
13096     status,
13097     tile,
13098     x,
13099     y;
13100
13101   MagickRealType
13102     scale_factor;
13103
13104   register char
13105     *p,
13106     *q;
13107
13108   register int
13109     i;
13110
13111   unsigned int
13112     height,
13113     width;
13114
13115   /*
13116     Tile image is relative to montage image configuration.
13117   */
13118   x=0;
13119   y=0;
13120   width=(unsigned int) image->columns;
13121   height=(unsigned int) image->rows;
13122   if (windows->image.crop_geometry != (char *) NULL)
13123     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13124   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13125   event->xbutton.x+=windows->image.x;
13126   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13127   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13128   event->xbutton.y+=windows->image.y;
13129   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13130   /*
13131     Determine size and location of each tile in the visual image directory.
13132   */
13133   width=(unsigned int) image->columns;
13134   height=(unsigned int) image->rows;
13135   x=0;
13136   y=0;
13137   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13138   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13139     (event->xbutton.x-x)/width;
13140   if (tile < 0)
13141     {
13142       /*
13143         Button press is outside any tile.
13144       */
13145       (void) XBell(display,0);
13146       return((Image *) NULL);
13147     }
13148   /*
13149     Determine file name from the tile directory.
13150   */
13151   p=image->directory;
13152   for (i=tile; (i != 0) && (*p != '\0'); )
13153   {
13154     if (*p == '\n')
13155       i--;
13156     p++;
13157   }
13158   if (*p == '\0')
13159     {
13160       /*
13161         Button press is outside any tile.
13162       */
13163       (void) XBell(display,0);
13164       return((Image *) NULL);
13165     }
13166   /*
13167     Select a command from the pop-up menu.
13168   */
13169   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13170   if (id < 0)
13171     return((Image *) NULL);
13172   q=p;
13173   while ((*q != '\n') && (*q != '\0'))
13174     q++;
13175   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13176   /*
13177     Perform command for the selected tile.
13178   */
13179   XSetCursorState(display,windows,MagickTrue);
13180   XCheckRefreshWindows(display,windows);
13181   tile_image=NewImageList();
13182   switch (TileCommands[id])
13183   {
13184     case TileLoadCommand:
13185     {
13186       /*
13187         Load tile image.
13188       */
13189       XCheckRefreshWindows(display,windows);
13190       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13191         MaxTextExtent);
13192       (void) CopyMagickString(resource_info->image_info->filename,filename,
13193         MaxTextExtent);
13194       tile_image=ReadImage(resource_info->image_info,exception);
13195       CatchException(exception);
13196       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13197       break;
13198     }
13199     case TileNextCommand:
13200     {
13201       /*
13202         Display next image.
13203       */
13204       XClientMessage(display,windows->image.id,windows->im_protocols,
13205         windows->im_next_image,CurrentTime);
13206       break;
13207     }
13208     case TileFormerCommand:
13209     {
13210       /*
13211         Display former image.
13212       */
13213       XClientMessage(display,windows->image.id,windows->im_protocols,
13214         windows->im_former_image,CurrentTime);
13215       break;
13216     }
13217     case TileDeleteCommand:
13218     {
13219       /*
13220         Delete tile image.
13221       */
13222       if (IsPathAccessible(filename) == MagickFalse)
13223         {
13224           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13225           break;
13226         }
13227       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13228       if (status <= 0)
13229         break;
13230       status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
13231       if (status != MagickFalse)
13232         {
13233           XNoticeWidget(display,windows,"Unable to delete image file:",
13234             filename);
13235           break;
13236         }
13237     }
13238     case TileUpdateCommand:
13239     {
13240       int
13241         x_offset,
13242         y_offset;
13243
13244       PixelInfo
13245         pixel;
13246
13247       Quantum
13248         virtual_pixel[CompositePixelChannel];
13249
13250       register int
13251         j;
13252
13253       register Quantum
13254         *s;
13255
13256       /*
13257         Ensure all the images exist.
13258       */
13259       tile=0;
13260       GetPixelInfo(image,&pixel);
13261       for (p=image->directory; *p != '\0'; p++)
13262       {
13263         CacheView
13264           *image_view;
13265
13266         q=p;
13267         while ((*q != '\n') && (*q != '\0'))
13268           q++;
13269         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13270         p=q;
13271         if (IsPathAccessible(filename) != MagickFalse)
13272           {
13273             tile++;
13274             continue;
13275           }
13276         /*
13277           Overwrite tile with background color.
13278         */
13279         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13280         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13281         image_view=AcquireCacheView(image);
13282         (void) GetOneCacheViewVirtualPixel(image_view,0,0,virtual_pixel,
13283           exception);
13284         pixel.red=virtual_pixel[RedPixelChannel];
13285         pixel.green=virtual_pixel[GreenPixelChannel];
13286         pixel.blue=virtual_pixel[BluePixelChannel];
13287         pixel.alpha=virtual_pixel[AlphaPixelChannel];
13288         for (i=0; i < (int) height; i++)
13289         {
13290           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13291             y_offset+i,width,1,exception);
13292           if (s == (Quantum *) NULL)
13293             break;
13294           for (j=0; j < (int) width; j++)
13295           {
13296             SetPixelPixelInfo(image,&pixel,s);
13297             s+=GetPixelChannels(image);
13298           }
13299           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13300             break;
13301         }
13302         image_view=DestroyCacheView(image_view);
13303         tile++;
13304       }
13305       windows->image.window_changes.width=(int) image->columns;
13306       windows->image.window_changes.height=(int) image->rows;
13307       XConfigureImageColormap(display,resource_info,windows,image,exception);
13308       (void) XConfigureImage(display,resource_info,windows,image,exception);
13309       break;
13310     }
13311     default:
13312       break;
13313   }
13314   XSetCursorState(display,windows,MagickFalse);
13315   return(tile_image);
13316 }
13317 \f
13318 /*
13319 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13320 %                                                                             %
13321 %                                                                             %
13322 %                                                                             %
13323 +   X T r a n s l a t e I m a g e                                             %
13324 %                                                                             %
13325 %                                                                             %
13326 %                                                                             %
13327 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13328 %
13329 %  XTranslateImage() translates the image within an Image window by one pixel
13330 %  as specified by the key symbol.  If the image has a `montage string the
13331 %  translation is respect to the width and height contained within the string.
13332 %
13333 %  The format of the XTranslateImage method is:
13334 %
13335 %      void XTranslateImage(Display *display,XWindows *windows,
13336 %        Image *image,const KeySym key_symbol)
13337 %
13338 %  A description of each parameter follows:
13339 %
13340 %    o display: Specifies a connection to an X server; returned from
13341 %      XOpenDisplay.
13342 %
13343 %    o windows: Specifies a pointer to a XWindows structure.
13344 %
13345 %    o image: the image.
13346 %
13347 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13348 %      to trim.
13349 %
13350 */
13351 static void XTranslateImage(Display *display,XWindows *windows,
13352   Image *image,const KeySym key_symbol)
13353 {
13354   char
13355     text[MaxTextExtent];
13356
13357   int
13358     x,
13359     y;
13360
13361   unsigned int
13362     x_offset,
13363     y_offset;
13364
13365   /*
13366     User specified a pan position offset.
13367   */
13368   x_offset=windows->image.width;
13369   y_offset=windows->image.height;
13370   if (image->montage != (char *) NULL)
13371     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13372   switch ((int) key_symbol)
13373   {
13374     case XK_Home:
13375     case XK_KP_Home:
13376     {
13377       windows->image.x=(int) windows->image.width/2;
13378       windows->image.y=(int) windows->image.height/2;
13379       break;
13380     }
13381     case XK_Left:
13382     case XK_KP_Left:
13383     {
13384       windows->image.x-=x_offset;
13385       break;
13386     }
13387     case XK_Next:
13388     case XK_Up:
13389     case XK_KP_Up:
13390     {
13391       windows->image.y-=y_offset;
13392       break;
13393     }
13394     case XK_Right:
13395     case XK_KP_Right:
13396     {
13397       windows->image.x+=x_offset;
13398       break;
13399     }
13400     case XK_Prior:
13401     case XK_Down:
13402     case XK_KP_Down:
13403     {
13404       windows->image.y+=y_offset;
13405       break;
13406     }
13407     default:
13408       return;
13409   }
13410   /*
13411     Check boundary conditions.
13412   */
13413   if (windows->image.x < 0)
13414     windows->image.x=0;
13415   else
13416     if ((int) (windows->image.x+windows->image.width) >
13417         windows->image.ximage->width)
13418       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13419   if (windows->image.y < 0)
13420     windows->image.y=0;
13421   else
13422     if ((int) (windows->image.y+windows->image.height) >
13423         windows->image.ximage->height)
13424       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13425   /*
13426     Refresh Image window.
13427   */
13428   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13429     windows->image.width,windows->image.height,windows->image.x,
13430     windows->image.y);
13431   XInfoWidget(display,windows,text);
13432   XCheckRefreshWindows(display,windows);
13433   XDrawPanRectangle(display,windows);
13434   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13435   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13436 }
13437 \f
13438 /*
13439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13440 %                                                                             %
13441 %                                                                             %
13442 %                                                                             %
13443 +   X T r i m I m a g e                                                       %
13444 %                                                                             %
13445 %                                                                             %
13446 %                                                                             %
13447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13448 %
13449 %  XTrimImage() trims the edges from the Image window.
13450 %
13451 %  The format of the XTrimImage method is:
13452 %
13453 %      MagickBooleanType XTrimImage(Display *display,
13454 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13455 %        ExceptionInfo *exception)
13456 %
13457 %  A description of each parameter follows:
13458 %
13459 %    o display: Specifies a connection to an X server; returned from
13460 %      XOpenDisplay.
13461 %
13462 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13463 %
13464 %    o windows: Specifies a pointer to a XWindows structure.
13465 %
13466 %    o image: the image.
13467 %
13468 %    o exception: return any errors or warnings in this structure.
13469 %
13470 */
13471 static MagickBooleanType XTrimImage(Display *display,
13472   XResourceInfo *resource_info,XWindows *windows,Image *image,
13473   ExceptionInfo *exception)
13474 {
13475   RectangleInfo
13476     trim_info;
13477
13478   register int
13479     x,
13480     y;
13481
13482   size_t
13483     background,
13484     pixel;
13485
13486   /*
13487     Trim edges from image.
13488   */
13489   XSetCursorState(display,windows,MagickTrue);
13490   XCheckRefreshWindows(display,windows);
13491   /*
13492     Crop the left edge.
13493   */
13494   background=XGetPixel(windows->image.ximage,0,0);
13495   trim_info.width=(size_t) windows->image.ximage->width;
13496   for (x=0; x < windows->image.ximage->width; x++)
13497   {
13498     for (y=0; y < windows->image.ximage->height; y++)
13499     {
13500       pixel=XGetPixel(windows->image.ximage,x,y);
13501       if (pixel != background)
13502         break;
13503     }
13504     if (y < windows->image.ximage->height)
13505       break;
13506   }
13507   trim_info.x=(ssize_t) x;
13508   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13509     {
13510       XSetCursorState(display,windows,MagickFalse);
13511       return(MagickFalse);
13512     }
13513   /*
13514     Crop the right edge.
13515   */
13516   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13517   for (x=windows->image.ximage->width-1; x != 0; x--)
13518   {
13519     for (y=0; y < windows->image.ximage->height; y++)
13520     {
13521       pixel=XGetPixel(windows->image.ximage,x,y);
13522       if (pixel != background)
13523         break;
13524     }
13525     if (y < windows->image.ximage->height)
13526       break;
13527   }
13528   trim_info.width=(size_t) (x-trim_info.x+1);
13529   /*
13530     Crop the top edge.
13531   */
13532   background=XGetPixel(windows->image.ximage,0,0);
13533   trim_info.height=(size_t) windows->image.ximage->height;
13534   for (y=0; y < windows->image.ximage->height; y++)
13535   {
13536     for (x=0; x < windows->image.ximage->width; x++)
13537     {
13538       pixel=XGetPixel(windows->image.ximage,x,y);
13539       if (pixel != background)
13540         break;
13541     }
13542     if (x < windows->image.ximage->width)
13543       break;
13544   }
13545   trim_info.y=(ssize_t) y;
13546   /*
13547     Crop the bottom edge.
13548   */
13549   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13550   for (y=windows->image.ximage->height-1; y != 0; y--)
13551   {
13552     for (x=0; x < windows->image.ximage->width; x++)
13553     {
13554       pixel=XGetPixel(windows->image.ximage,x,y);
13555       if (pixel != background)
13556         break;
13557     }
13558     if (x < windows->image.ximage->width)
13559       break;
13560   }
13561   trim_info.height=(size_t) y-trim_info.y+1;
13562   if (((unsigned int) trim_info.width != windows->image.width) ||
13563       ((unsigned int) trim_info.height != windows->image.height))
13564     {
13565       /*
13566         Reconfigure Image window as defined by the trimming rectangle.
13567       */
13568       XSetCropGeometry(display,windows,&trim_info,image);
13569       windows->image.window_changes.width=(int) trim_info.width;
13570       windows->image.window_changes.height=(int) trim_info.height;
13571       (void) XConfigureImage(display,resource_info,windows,image,exception);
13572     }
13573   XSetCursorState(display,windows,MagickFalse);
13574   return(MagickTrue);
13575 }
13576 \f
13577 /*
13578 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13579 %                                                                             %
13580 %                                                                             %
13581 %                                                                             %
13582 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13583 %                                                                             %
13584 %                                                                             %
13585 %                                                                             %
13586 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13587 %
13588 %  XVisualDirectoryImage() creates a Visual Image Directory.
13589 %
13590 %  The format of the XVisualDirectoryImage method is:
13591 %
13592 %      Image *XVisualDirectoryImage(Display *display,
13593 %        XResourceInfo *resource_info,XWindows *windows,
13594 %        ExceptionInfo *exception)
13595 %
13596 %  A description of each parameter follows:
13597 %
13598 %    o display: Specifies a connection to an X server; returned from
13599 %      XOpenDisplay.
13600 %
13601 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13602 %
13603 %    o windows: Specifies a pointer to a XWindows structure.
13604 %
13605 %    o exception: return any errors or warnings in this structure.
13606 %
13607 */
13608 static Image *XVisualDirectoryImage(Display *display,
13609   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13610 {
13611 #define TileImageTag  "Scale/Image"
13612 #define XClientName  "montage"
13613
13614   char
13615     **filelist;
13616
13617   Image
13618     *images,
13619     *montage_image,
13620     *next_image,
13621     *thumbnail_image;
13622
13623   ImageInfo
13624     *read_info;
13625
13626   int
13627     number_files;
13628
13629   MagickBooleanType
13630     backdrop;
13631
13632   MagickStatusType
13633     status;
13634
13635   MontageInfo
13636     *montage_info;
13637
13638   RectangleInfo
13639     geometry;
13640
13641   register int
13642     i;
13643
13644   static char
13645     filename[MaxTextExtent] = "\0",
13646     filenames[MaxTextExtent] = "*";
13647
13648   XResourceInfo
13649     background_resources;
13650
13651   /*
13652     Request file name from user.
13653   */
13654   XFileBrowserWidget(display,windows,"Directory",filenames);
13655   if (*filenames == '\0')
13656     return((Image *) NULL);
13657   /*
13658     Expand the filenames.
13659   */
13660   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13661   if (filelist == (char **) NULL)
13662     {
13663       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13664         filenames);
13665       return((Image *) NULL);
13666     }
13667   number_files=1;
13668   filelist[0]=filenames;
13669   status=ExpandFilenames(&number_files,&filelist);
13670   if ((status == MagickFalse) || (number_files == 0))
13671     {
13672       if (number_files == 0)
13673         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13674       else
13675         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13676           filenames);
13677       return((Image *) NULL);
13678     }
13679   /*
13680     Set image background resources.
13681   */
13682   background_resources=(*resource_info);
13683   background_resources.window_id=AcquireString("");
13684   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13685     "0x%lx",windows->image.id);
13686   background_resources.backdrop=MagickTrue;
13687   /*
13688     Read each image and convert them to a tile.
13689   */
13690   backdrop=(windows->visual_info->klass == TrueColor) ||
13691     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13692   read_info=CloneImageInfo(resource_info->image_info);
13693   (void) SetImageOption(read_info,"jpeg:size","120x120");
13694   (void) CloneString(&read_info->size,DefaultTileGeometry);
13695   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13696     (void *) NULL);
13697   images=NewImageList();
13698   XSetCursorState(display,windows,MagickTrue);
13699   XCheckRefreshWindows(display,windows);
13700   for (i=0; i < (int) number_files; i++)
13701   {
13702     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13703     filelist[i]=DestroyString(filelist[i]);
13704     *read_info->magick='\0';
13705     next_image=ReadImage(read_info,exception);
13706     CatchException(exception);
13707     if (next_image != (Image *) NULL)
13708       {
13709         (void) DeleteImageProperty(next_image,"label");
13710         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13711           read_info,next_image,DefaultTileLabel,exception),exception);
13712         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13713           exception);
13714         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13715           geometry.height,exception);
13716         if (thumbnail_image != (Image *) NULL)
13717           {
13718             next_image=DestroyImage(next_image);
13719             next_image=thumbnail_image;
13720           }
13721         if (backdrop)
13722           {
13723             (void) XDisplayBackgroundImage(display,&background_resources,
13724               next_image,exception);
13725             XSetCursorState(display,windows,MagickTrue);
13726           }
13727         AppendImageToList(&images,next_image);
13728         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13729           {
13730             MagickBooleanType
13731               proceed;
13732
13733             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13734               (MagickSizeType) number_files);
13735             if (proceed == MagickFalse)
13736               break;
13737           }
13738       }
13739   }
13740   filelist=(char **) RelinquishMagickMemory(filelist);
13741   if (images == (Image *) NULL)
13742     {
13743       read_info=DestroyImageInfo(read_info);
13744       XSetCursorState(display,windows,MagickFalse);
13745       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13746       return((Image *) NULL);
13747     }
13748   /*
13749     Create the Visual Image Directory.
13750   */
13751   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13752   montage_info->pointsize=10;
13753   if (resource_info->font != (char *) NULL)
13754     (void) CloneString(&montage_info->font,resource_info->font);
13755   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13756   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13757     images),exception);
13758   images=DestroyImageList(images);
13759   montage_info=DestroyMontageInfo(montage_info);
13760   read_info=DestroyImageInfo(read_info);
13761   XSetCursorState(display,windows,MagickFalse);
13762   if (montage_image == (Image *) NULL)
13763     return(montage_image);
13764   XClientMessage(display,windows->image.id,windows->im_protocols,
13765     windows->im_next_image,CurrentTime);
13766   return(montage_image);
13767 }
13768 \f
13769 /*
13770 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13771 %                                                                             %
13772 %                                                                             %
13773 %                                                                             %
13774 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13775 %                                                                             %
13776 %                                                                             %
13777 %                                                                             %
13778 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13779 %
13780 %  XDisplayBackgroundImage() displays an image in the background of a window.
13781 %
13782 %  The format of the XDisplayBackgroundImage method is:
13783 %
13784 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13785 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13786 %
13787 %  A description of each parameter follows:
13788 %
13789 %    o display: Specifies a connection to an X server;  returned from
13790 %      XOpenDisplay.
13791 %
13792 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13793 %
13794 %    o image: the image.
13795 %
13796 %    o exception: return any errors or warnings in this structure.
13797 %
13798 */
13799 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13800   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13801 {
13802   char
13803     geometry[MaxTextExtent],
13804     visual_type[MaxTextExtent];
13805
13806   int
13807     height,
13808     status,
13809     width;
13810
13811   RectangleInfo
13812     geometry_info;
13813
13814   static XPixelInfo
13815     pixel;
13816
13817   static XStandardColormap
13818     *map_info;
13819
13820   static XVisualInfo
13821     *visual_info = (XVisualInfo *) NULL;
13822
13823   static XWindowInfo
13824     window_info;
13825
13826   size_t
13827     delay;
13828
13829   Window
13830     root_window;
13831
13832   XGCValues
13833     context_values;
13834
13835   XResourceInfo
13836     resources;
13837
13838   XWindowAttributes
13839     window_attributes;
13840
13841   /*
13842     Determine target window.
13843   */
13844   assert(image != (Image *) NULL);
13845   assert(image->signature == MagickSignature);
13846   if (image->debug != MagickFalse)
13847     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13848   resources=(*resource_info);
13849   window_info.id=(Window) NULL;
13850   root_window=XRootWindow(display,XDefaultScreen(display));
13851   if (LocaleCompare(resources.window_id,"root") == 0)
13852     window_info.id=root_window;
13853   else
13854     {
13855       if (isdigit((unsigned char) *resources.window_id) != 0)
13856         window_info.id=XWindowByID(display,root_window,
13857           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13858       if (window_info.id == (Window) NULL)
13859         window_info.id=XWindowByName(display,root_window,resources.window_id);
13860     }
13861   if (window_info.id == (Window) NULL)
13862     {
13863       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13864         resources.window_id);
13865       return(MagickFalse);
13866     }
13867   /*
13868     Determine window visual id.
13869   */
13870   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13871   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13872   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13873   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13874   if (status != 0)
13875     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13876       XVisualIDFromVisual(window_attributes.visual));
13877   if (visual_info == (XVisualInfo *) NULL)
13878     {
13879       /*
13880         Allocate standard colormap.
13881       */
13882       map_info=XAllocStandardColormap();
13883       if (map_info == (XStandardColormap *) NULL)
13884         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13885           image->filename);
13886       map_info->colormap=(Colormap) NULL;
13887       pixel.pixels=(unsigned long *) NULL;
13888       /*
13889         Initialize visual info.
13890       */
13891       resources.map_type=(char *) NULL;
13892       resources.visual_type=visual_type;
13893       visual_info=XBestVisualInfo(display,map_info,&resources);
13894       if (visual_info == (XVisualInfo *) NULL)
13895         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13896           resources.visual_type);
13897       /*
13898         Initialize window info.
13899       */
13900       window_info.ximage=(XImage *) NULL;
13901       window_info.matte_image=(XImage *) NULL;
13902       window_info.pixmap=(Pixmap) NULL;
13903       window_info.matte_pixmap=(Pixmap) NULL;
13904     }
13905   /*
13906     Free previous root colors.
13907   */
13908   if (window_info.id == root_window)
13909     (void) XDestroyWindowColors(display,root_window);
13910   /*
13911     Initialize Standard Colormap.
13912   */
13913   resources.colormap=SharedColormap;
13914   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13915     exception);
13916   /*
13917     Graphic context superclass.
13918   */
13919   context_values.background=pixel.background_color.pixel;
13920   context_values.foreground=pixel.foreground_color.pixel;
13921   pixel.annotate_context=XCreateGC(display,window_info.id,
13922     (size_t) (GCBackground | GCForeground),&context_values);
13923   if (pixel.annotate_context == (GC) NULL)
13924     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13925       image->filename);
13926   /*
13927     Initialize Image window attributes.
13928   */
13929   window_info.name=AcquireString("\0");
13930   window_info.icon_name=AcquireString("\0");
13931   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13932     &resources,&window_info);
13933   /*
13934     Create the X image.
13935   */
13936   window_info.width=(unsigned int) image->columns;
13937   window_info.height=(unsigned int) image->rows;
13938   if ((image->columns != window_info.width) ||
13939       (image->rows != window_info.height))
13940     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13941       image->filename);
13942   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13943     window_attributes.width,window_attributes.height);
13944   geometry_info.width=window_info.width;
13945   geometry_info.height=window_info.height;
13946   geometry_info.x=(ssize_t) window_info.x;
13947   geometry_info.y=(ssize_t) window_info.y;
13948   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13949     &geometry_info.width,&geometry_info.height);
13950   window_info.width=(unsigned int) geometry_info.width;
13951   window_info.height=(unsigned int) geometry_info.height;
13952   window_info.x=(int) geometry_info.x;
13953   window_info.y=(int) geometry_info.y;
13954   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13955     window_info.height,exception);
13956   if (status == MagickFalse)
13957     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13958       image->filename);
13959   window_info.x=0;
13960   window_info.y=0;
13961   if (image->debug != MagickFalse)
13962     {
13963       (void) LogMagickEvent(X11Event,GetMagickModule(),
13964         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13965         (double) image->columns,(double) image->rows);
13966       if (image->colors != 0)
13967         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13968           image->colors);
13969       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13970     }
13971   /*
13972     Adjust image dimensions as specified by backdrop or geometry options.
13973   */
13974   width=(int) window_info.width;
13975   height=(int) window_info.height;
13976   if (resources.backdrop != MagickFalse)
13977     {
13978       /*
13979         Center image on window.
13980       */
13981       window_info.x=(window_attributes.width/2)-
13982         (window_info.ximage->width/2);
13983       window_info.y=(window_attributes.height/2)-
13984         (window_info.ximage->height/2);
13985       width=window_attributes.width;
13986       height=window_attributes.height;
13987     }
13988   if ((resources.image_geometry != (char *) NULL) &&
13989       (*resources.image_geometry != '\0'))
13990     {
13991       char
13992         default_geometry[MaxTextExtent];
13993
13994       int
13995         flags,
13996         gravity;
13997
13998       XSizeHints
13999         *size_hints;
14000
14001       /*
14002         User specified geometry.
14003       */
14004       size_hints=XAllocSizeHints();
14005       if (size_hints == (XSizeHints *) NULL)
14006         ThrowXWindowFatalException(ResourceLimitFatalError,
14007           "MemoryAllocationFailed",image->filename);
14008       size_hints->flags=0L;
14009       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
14010         width,height);
14011       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14012         default_geometry,window_info.border_width,size_hints,&window_info.x,
14013         &window_info.y,&width,&height,&gravity);
14014       if (flags & (XValue | YValue))
14015         {
14016           width=window_attributes.width;
14017           height=window_attributes.height;
14018         }
14019       (void) XFree((void *) size_hints);
14020     }
14021   /*
14022     Create the X pixmap.
14023   */
14024   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14025     (unsigned int) height,window_info.depth);
14026   if (window_info.pixmap == (Pixmap) NULL)
14027     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14028       image->filename);
14029   /*
14030     Display pixmap on the window.
14031   */
14032   if (((unsigned int) width > window_info.width) ||
14033       ((unsigned int) height > window_info.height))
14034     (void) XFillRectangle(display,window_info.pixmap,
14035       window_info.annotate_context,0,0,(unsigned int) width,
14036       (unsigned int) height);
14037   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14038     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14039     window_info.width,(unsigned int) window_info.height);
14040   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14041   (void) XClearWindow(display,window_info.id);
14042   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14043   XDelay(display,delay == 0UL ? 10UL : delay);
14044   (void) XSync(display,MagickFalse);
14045   return(window_info.id == root_window ? MagickTrue : MagickFalse);
14046 }
14047 \f
14048 /*
14049 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14050 %                                                                             %
14051 %                                                                             %
14052 %                                                                             %
14053 +   X D i s p l a y I m a g e                                                 %
14054 %                                                                             %
14055 %                                                                             %
14056 %                                                                             %
14057 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14058 %
14059 %  XDisplayImage() displays an image via X11.  A new image is created and
14060 %  returned if the user interactively transforms the displayed image.
14061 %
14062 %  The format of the XDisplayImage method is:
14063 %
14064 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14065 %        char **argv,int argc,Image **image,size_t *state,
14066 %        ExceptionInfo *exception)
14067 %
14068 %  A description of each parameter follows:
14069 %
14070 %    o nexus:  Method XDisplayImage returns an image when the
14071 %      user chooses 'Open Image' from the command menu or picks a tile
14072 %      from the image directory.  Otherwise a null image is returned.
14073 %
14074 %    o display: Specifies a connection to an X server;  returned from
14075 %      XOpenDisplay.
14076 %
14077 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14078 %
14079 %    o argv: Specifies the application's argument list.
14080 %
14081 %    o argc: Specifies the number of arguments.
14082 %
14083 %    o image: Specifies an address to an address of an Image structure;
14084 %
14085 %    o exception: return any errors or warnings in this structure.
14086 %
14087 */
14088 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14089   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14090 {
14091 #define MagnifySize  256  /* must be a power of 2 */
14092 #define MagickMenus  10
14093 #define MagickTitle  "Commands"
14094
14095   static const char
14096     *CommandMenu[] =
14097     {
14098       "File",
14099       "Edit",
14100       "View",
14101       "Transform",
14102       "Enhance",
14103       "Effects",
14104       "F/X",
14105       "Image Edit",
14106       "Miscellany",
14107       "Help",
14108       (char *) NULL
14109     },
14110     *FileMenu[] =
14111     {
14112       "Open...",
14113       "Next",
14114       "Former",
14115       "Select...",
14116       "Save...",
14117       "Print...",
14118       "Delete...",
14119       "New...",
14120       "Visual Directory...",
14121       "Quit",
14122       (char *) NULL
14123     },
14124     *EditMenu[] =
14125     {
14126       "Undo",
14127       "Redo",
14128       "Cut",
14129       "Copy",
14130       "Paste",
14131       (char *) NULL
14132     },
14133     *ViewMenu[] =
14134     {
14135       "Half Size",
14136       "Original Size",
14137       "Double Size",
14138       "Resize...",
14139       "Apply",
14140       "Refresh",
14141       "Restore",
14142       (char *) NULL
14143     },
14144     *TransformMenu[] =
14145     {
14146       "Crop",
14147       "Chop",
14148       "Flop",
14149       "Flip",
14150       "Rotate Right",
14151       "Rotate Left",
14152       "Rotate...",
14153       "Shear...",
14154       "Roll...",
14155       "Trim Edges",
14156       (char *) NULL
14157     },
14158     *EnhanceMenu[] =
14159     {
14160       "Hue...",
14161       "Saturation...",
14162       "Brightness...",
14163       "Gamma...",
14164       "Spiff",
14165       "Dull",
14166       "Contrast Stretch...",
14167       "Sigmoidal Contrast...",
14168       "Normalize",
14169       "Equalize",
14170       "Negate",
14171       "Grayscale",
14172       "Map...",
14173       "Quantize...",
14174       (char *) NULL
14175     },
14176     *EffectsMenu[] =
14177     {
14178       "Despeckle",
14179       "Emboss",
14180       "Reduce Noise",
14181       "Add Noise...",
14182       "Sharpen...",
14183       "Blur...",
14184       "Threshold...",
14185       "Edge Detect...",
14186       "Spread...",
14187       "Shade...",
14188       "Raise...",
14189       "Segment...",
14190       (char *) NULL
14191     },
14192     *FXMenu[] =
14193     {
14194       "Solarize...",
14195       "Sepia Tone...",
14196       "Swirl...",
14197       "Implode...",
14198       "Vignette...",
14199       "Wave...",
14200       "Oil Paint...",
14201       "Charcoal Draw...",
14202       (char *) NULL
14203     },
14204     *ImageEditMenu[] =
14205     {
14206       "Annotate...",
14207       "Draw...",
14208       "Color...",
14209       "Matte...",
14210       "Composite...",
14211       "Add Border...",
14212       "Add Frame...",
14213       "Comment...",
14214       "Launch...",
14215       "Region of Interest...",
14216       (char *) NULL
14217     },
14218     *MiscellanyMenu[] =
14219     {
14220       "Image Info",
14221       "Zoom Image",
14222       "Show Preview...",
14223       "Show Histogram",
14224       "Show Matte",
14225       "Background...",
14226       "Slide Show...",
14227       "Preferences...",
14228       (char *) NULL
14229     },
14230     *HelpMenu[] =
14231     {
14232       "Overview",
14233       "Browse Documentation",
14234       "About Display",
14235       (char *) NULL
14236     },
14237     *ShortCutsMenu[] =
14238     {
14239       "Next",
14240       "Former",
14241       "Open...",
14242       "Save...",
14243       "Print...",
14244       "Undo",
14245       "Restore",
14246       "Image Info",
14247       "Quit",
14248       (char *) NULL
14249     },
14250     *VirtualMenu[] =
14251     {
14252       "Image Info",
14253       "Print",
14254       "Next",
14255       "Quit",
14256       (char *) NULL
14257     };
14258
14259   static const char
14260     **Menus[MagickMenus] =
14261     {
14262       FileMenu,
14263       EditMenu,
14264       ViewMenu,
14265       TransformMenu,
14266       EnhanceMenu,
14267       EffectsMenu,
14268       FXMenu,
14269       ImageEditMenu,
14270       MiscellanyMenu,
14271       HelpMenu
14272     };
14273
14274   static CommandType
14275     CommandMenus[] =
14276     {
14277       NullCommand,
14278       NullCommand,
14279       NullCommand,
14280       NullCommand,
14281       NullCommand,
14282       NullCommand,
14283       NullCommand,
14284       NullCommand,
14285       NullCommand,
14286       NullCommand,
14287     },
14288     FileCommands[] =
14289     {
14290       OpenCommand,
14291       NextCommand,
14292       FormerCommand,
14293       SelectCommand,
14294       SaveCommand,
14295       PrintCommand,
14296       DeleteCommand,
14297       NewCommand,
14298       VisualDirectoryCommand,
14299       QuitCommand
14300     },
14301     EditCommands[] =
14302     {
14303       UndoCommand,
14304       RedoCommand,
14305       CutCommand,
14306       CopyCommand,
14307       PasteCommand
14308     },
14309     ViewCommands[] =
14310     {
14311       HalfSizeCommand,
14312       OriginalSizeCommand,
14313       DoubleSizeCommand,
14314       ResizeCommand,
14315       ApplyCommand,
14316       RefreshCommand,
14317       RestoreCommand
14318     },
14319     TransformCommands[] =
14320     {
14321       CropCommand,
14322       ChopCommand,
14323       FlopCommand,
14324       FlipCommand,
14325       RotateRightCommand,
14326       RotateLeftCommand,
14327       RotateCommand,
14328       ShearCommand,
14329       RollCommand,
14330       TrimCommand
14331     },
14332     EnhanceCommands[] =
14333     {
14334       HueCommand,
14335       SaturationCommand,
14336       BrightnessCommand,
14337       GammaCommand,
14338       SpiffCommand,
14339       DullCommand,
14340       ContrastStretchCommand,
14341       SigmoidalContrastCommand,
14342       NormalizeCommand,
14343       EqualizeCommand,
14344       NegateCommand,
14345       GrayscaleCommand,
14346       MapCommand,
14347       QuantizeCommand
14348     },
14349     EffectsCommands[] =
14350     {
14351       DespeckleCommand,
14352       EmbossCommand,
14353       ReduceNoiseCommand,
14354       AddNoiseCommand,
14355       SharpenCommand,
14356       BlurCommand,
14357       ThresholdCommand,
14358       EdgeDetectCommand,
14359       SpreadCommand,
14360       ShadeCommand,
14361       RaiseCommand,
14362       SegmentCommand
14363     },
14364     FXCommands[] =
14365     {
14366       SolarizeCommand,
14367       SepiaToneCommand,
14368       SwirlCommand,
14369       ImplodeCommand,
14370       VignetteCommand,
14371       WaveCommand,
14372       OilPaintCommand,
14373       CharcoalDrawCommand
14374     },
14375     ImageEditCommands[] =
14376     {
14377       AnnotateCommand,
14378       DrawCommand,
14379       ColorCommand,
14380       MatteCommand,
14381       CompositeCommand,
14382       AddBorderCommand,
14383       AddFrameCommand,
14384       CommentCommand,
14385       LaunchCommand,
14386       RegionofInterestCommand
14387     },
14388     MiscellanyCommands[] =
14389     {
14390       InfoCommand,
14391       ZoomCommand,
14392       ShowPreviewCommand,
14393       ShowHistogramCommand,
14394       ShowMatteCommand,
14395       BackgroundCommand,
14396       SlideShowCommand,
14397       PreferencesCommand
14398     },
14399     HelpCommands[] =
14400     {
14401       HelpCommand,
14402       BrowseDocumentationCommand,
14403       VersionCommand
14404     },
14405     ShortCutsCommands[] =
14406     {
14407       NextCommand,
14408       FormerCommand,
14409       OpenCommand,
14410       SaveCommand,
14411       PrintCommand,
14412       UndoCommand,
14413       RestoreCommand,
14414       InfoCommand,
14415       QuitCommand
14416     },
14417     VirtualCommands[] =
14418     {
14419       InfoCommand,
14420       PrintCommand,
14421       NextCommand,
14422       QuitCommand
14423     };
14424
14425   static CommandType
14426     *Commands[MagickMenus] =
14427     {
14428       FileCommands,
14429       EditCommands,
14430       ViewCommands,
14431       TransformCommands,
14432       EnhanceCommands,
14433       EffectsCommands,
14434       FXCommands,
14435       ImageEditCommands,
14436       MiscellanyCommands,
14437       HelpCommands
14438     };
14439
14440   char
14441     command[MaxTextExtent],
14442     *directory,
14443     geometry[MaxTextExtent],
14444     resource_name[MaxTextExtent];
14445
14446   CommandType
14447     command_type;
14448
14449   Image
14450     *display_image,
14451     *nexus;
14452
14453   int
14454     entry,
14455     id;
14456
14457   KeySym
14458     key_symbol;
14459
14460   MagickStatusType
14461     context_mask,
14462     status;
14463
14464   RectangleInfo
14465     geometry_info;
14466
14467   register int
14468     i;
14469
14470   static char
14471     working_directory[MaxTextExtent];
14472
14473   static XPoint
14474     vid_info;
14475
14476   static XWindowInfo
14477     *magick_windows[MaxXWindows];
14478
14479   static unsigned int
14480     number_windows;
14481
14482   struct stat
14483     attributes;
14484
14485   time_t
14486     timer,
14487     timestamp,
14488     update_time;
14489
14490   unsigned int
14491     height,
14492     width;
14493
14494   size_t
14495     delay;
14496
14497   WarningHandler
14498     warning_handler;
14499
14500   Window
14501     root_window;
14502
14503   XClassHint
14504     *class_hints;
14505
14506   XEvent
14507     event;
14508
14509   XFontStruct
14510     *font_info;
14511
14512   XGCValues
14513     context_values;
14514
14515   XPixelInfo
14516     *icon_pixel,
14517     *pixel;
14518
14519   XResourceInfo
14520     *icon_resources;
14521
14522   XStandardColormap
14523     *icon_map,
14524     *map_info;
14525
14526   XVisualInfo
14527     *icon_visual,
14528     *visual_info;
14529
14530   XWindowChanges
14531     window_changes;
14532
14533   XWindows
14534     *windows;
14535
14536   XWMHints
14537     *manager_hints;
14538
14539   assert(image != (Image **) NULL);
14540   assert((*image)->signature == MagickSignature);
14541   if ((*image)->debug != MagickFalse)
14542     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14543   display_image=(*image);
14544   warning_handler=(WarningHandler) NULL;
14545   windows=XSetWindows((XWindows *) ~0);
14546   if (windows != (XWindows *) NULL)
14547     {
14548       int
14549         status;
14550
14551       status=chdir(working_directory);
14552       if (status == -1)
14553         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14554           "UnableToOpenFile","%s",working_directory);
14555       warning_handler=resource_info->display_warnings ?
14556         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14557       warning_handler=resource_info->display_warnings ?
14558         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14559     }
14560   else
14561     {
14562       /*
14563         Allocate windows structure.
14564       */
14565       resource_info->colors=display_image->colors;
14566       windows=XSetWindows(XInitializeWindows(display,resource_info));
14567       if (windows == (XWindows *) NULL)
14568         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14569           (*image)->filename);
14570       /*
14571         Initialize window id's.
14572       */
14573       number_windows=0;
14574       magick_windows[number_windows++]=(&windows->icon);
14575       magick_windows[number_windows++]=(&windows->backdrop);
14576       magick_windows[number_windows++]=(&windows->image);
14577       magick_windows[number_windows++]=(&windows->info);
14578       magick_windows[number_windows++]=(&windows->command);
14579       magick_windows[number_windows++]=(&windows->widget);
14580       magick_windows[number_windows++]=(&windows->popup);
14581       magick_windows[number_windows++]=(&windows->magnify);
14582       magick_windows[number_windows++]=(&windows->pan);
14583       for (i=0; i < (int) number_windows; i++)
14584         magick_windows[i]->id=(Window) NULL;
14585       vid_info.x=0;
14586       vid_info.y=0;
14587     }
14588   /*
14589     Initialize font info.
14590   */
14591   if (windows->font_info != (XFontStruct *) NULL)
14592     (void) XFreeFont(display,windows->font_info);
14593   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14594   if (windows->font_info == (XFontStruct *) NULL)
14595     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14596       resource_info->font);
14597   /*
14598     Initialize Standard Colormap.
14599   */
14600   map_info=windows->map_info;
14601   icon_map=windows->icon_map;
14602   visual_info=windows->visual_info;
14603   icon_visual=windows->icon_visual;
14604   pixel=windows->pixel_info;
14605   icon_pixel=windows->icon_pixel;
14606   font_info=windows->font_info;
14607   icon_resources=windows->icon_resources;
14608   class_hints=windows->class_hints;
14609   manager_hints=windows->manager_hints;
14610   root_window=XRootWindow(display,visual_info->screen);
14611   nexus=NewImageList();
14612   if (display_image->debug != MagickFalse)
14613     {
14614       (void) LogMagickEvent(X11Event,GetMagickModule(),
14615         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14616         (double) display_image->scene,(double) display_image->columns,
14617         (double) display_image->rows);
14618       if (display_image->colors != 0)
14619         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14620           display_image->colors);
14621       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14622         display_image->magick);
14623     }
14624   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14625     map_info,pixel,exception);
14626   display_image->taint=MagickFalse;
14627   /*
14628     Initialize graphic context.
14629   */
14630   windows->context.id=(Window) NULL;
14631   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14632     resource_info,&windows->context);
14633   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14634   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14635   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14636   manager_hints->flags=InputHint | StateHint;
14637   manager_hints->input=MagickFalse;
14638   manager_hints->initial_state=WithdrawnState;
14639   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14640     &windows->context);
14641   if (display_image->debug != MagickFalse)
14642     (void) LogMagickEvent(X11Event,GetMagickModule(),
14643       "Window id: 0x%lx (context)",windows->context.id);
14644   context_values.background=pixel->background_color.pixel;
14645   context_values.font=font_info->fid;
14646   context_values.foreground=pixel->foreground_color.pixel;
14647   context_values.graphics_exposures=MagickFalse;
14648   context_mask=(MagickStatusType)
14649     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14650   if (pixel->annotate_context != (GC) NULL)
14651     (void) XFreeGC(display,pixel->annotate_context);
14652   pixel->annotate_context=XCreateGC(display,windows->context.id,
14653     context_mask,&context_values);
14654   if (pixel->annotate_context == (GC) NULL)
14655     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14656       display_image->filename);
14657   context_values.background=pixel->depth_color.pixel;
14658   if (pixel->widget_context != (GC) NULL)
14659     (void) XFreeGC(display,pixel->widget_context);
14660   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14661     &context_values);
14662   if (pixel->widget_context == (GC) NULL)
14663     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14664       display_image->filename);
14665   context_values.background=pixel->foreground_color.pixel;
14666   context_values.foreground=pixel->background_color.pixel;
14667   context_values.plane_mask=context_values.background ^
14668     context_values.foreground;
14669   if (pixel->highlight_context != (GC) NULL)
14670     (void) XFreeGC(display,pixel->highlight_context);
14671   pixel->highlight_context=XCreateGC(display,windows->context.id,
14672     (size_t) (context_mask | GCPlaneMask),&context_values);
14673   if (pixel->highlight_context == (GC) NULL)
14674     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14675       display_image->filename);
14676   (void) XDestroyWindow(display,windows->context.id);
14677   /*
14678     Initialize icon window.
14679   */
14680   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14681     icon_resources,&windows->icon);
14682   windows->icon.geometry=resource_info->icon_geometry;
14683   XBestIconSize(display,&windows->icon,display_image);
14684   windows->icon.attributes.colormap=XDefaultColormap(display,
14685     icon_visual->screen);
14686   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14687   manager_hints->flags=InputHint | StateHint;
14688   manager_hints->input=MagickFalse;
14689   manager_hints->initial_state=IconicState;
14690   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14691     &windows->icon);
14692   if (display_image->debug != MagickFalse)
14693     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14694       windows->icon.id);
14695   /*
14696     Initialize graphic context for icon window.
14697   */
14698   if (icon_pixel->annotate_context != (GC) NULL)
14699     (void) XFreeGC(display,icon_pixel->annotate_context);
14700   context_values.background=icon_pixel->background_color.pixel;
14701   context_values.foreground=icon_pixel->foreground_color.pixel;
14702   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14703     (size_t) (GCBackground | GCForeground),&context_values);
14704   if (icon_pixel->annotate_context == (GC) NULL)
14705     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14706       display_image->filename);
14707   windows->icon.annotate_context=icon_pixel->annotate_context;
14708   /*
14709     Initialize Image window.
14710   */
14711   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14712     &windows->image);
14713   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14714   if (resource_info->use_shared_memory == MagickFalse)
14715     windows->image.shared_memory=MagickFalse;
14716   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14717     {
14718       char
14719         *title;
14720
14721       title=InterpretImageProperties(resource_info->image_info,display_image,
14722         resource_info->title,exception);
14723       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14724       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14725       title=DestroyString(title);
14726     }
14727   else
14728     {
14729       char
14730         filename[MaxTextExtent];
14731
14732       /*
14733         Window name is the base of the filename.
14734       */
14735       GetPathComponent(display_image->magick_filename,TailPath,filename);
14736       if (display_image->scene == 0)
14737         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14738           "%s: %s",MagickPackageName,filename);
14739       else
14740         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14741           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14742           (double) display_image->scene,(double) GetImageListLength(
14743           display_image));
14744       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14745     }
14746   if (resource_info->immutable)
14747     windows->image.immutable=MagickTrue;
14748   windows->image.use_pixmap=resource_info->use_pixmap;
14749   windows->image.geometry=resource_info->image_geometry;
14750   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14751     XDisplayWidth(display,visual_info->screen),
14752     XDisplayHeight(display,visual_info->screen));
14753   geometry_info.width=display_image->columns;
14754   geometry_info.height=display_image->rows;
14755   geometry_info.x=0;
14756   geometry_info.y=0;
14757   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14758     &geometry_info.width,&geometry_info.height);
14759   windows->image.width=(unsigned int) geometry_info.width;
14760   windows->image.height=(unsigned int) geometry_info.height;
14761   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14762     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14763     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14764     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14765   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14766     resource_info,&windows->backdrop);
14767   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14768     {
14769       /*
14770         Initialize backdrop window.
14771       */
14772       windows->backdrop.x=0;
14773       windows->backdrop.y=0;
14774       (void) CloneString(&windows->backdrop.name,"Backdrop");
14775       windows->backdrop.flags=(size_t) (USSize | USPosition);
14776       windows->backdrop.width=(unsigned int)
14777         XDisplayWidth(display,visual_info->screen);
14778       windows->backdrop.height=(unsigned int)
14779         XDisplayHeight(display,visual_info->screen);
14780       windows->backdrop.border_width=0;
14781       windows->backdrop.immutable=MagickTrue;
14782       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14783         ButtonReleaseMask;
14784       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14785         StructureNotifyMask;
14786       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14787       manager_hints->icon_window=windows->icon.id;
14788       manager_hints->input=MagickTrue;
14789       manager_hints->initial_state=resource_info->iconic ? IconicState :
14790         NormalState;
14791       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14792         &windows->backdrop);
14793       if (display_image->debug != MagickFalse)
14794         (void) LogMagickEvent(X11Event,GetMagickModule(),
14795           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14796       (void) XMapWindow(display,windows->backdrop.id);
14797       (void) XClearWindow(display,windows->backdrop.id);
14798       if (windows->image.id != (Window) NULL)
14799         {
14800           (void) XDestroyWindow(display,windows->image.id);
14801           windows->image.id=(Window) NULL;
14802         }
14803       /*
14804         Position image in the center the backdrop.
14805       */
14806       windows->image.flags|=USPosition;
14807       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14808         (windows->image.width/2);
14809       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14810         (windows->image.height/2);
14811     }
14812   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14813   manager_hints->icon_window=windows->icon.id;
14814   manager_hints->input=MagickTrue;
14815   manager_hints->initial_state=resource_info->iconic ? IconicState :
14816     NormalState;
14817   if (windows->group_leader.id != (Window) NULL)
14818     {
14819       /*
14820         Follow the leader.
14821       */
14822       manager_hints->flags|=WindowGroupHint;
14823       manager_hints->window_group=windows->group_leader.id;
14824       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14825       if (display_image->debug != MagickFalse)
14826         (void) LogMagickEvent(X11Event,GetMagickModule(),
14827           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14828     }
14829   XMakeWindow(display,
14830     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14831     argv,argc,class_hints,manager_hints,&windows->image);
14832   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14833     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14834   if (windows->group_leader.id != (Window) NULL)
14835     (void) XSetTransientForHint(display,windows->image.id,
14836       windows->group_leader.id);
14837   if (display_image->debug != MagickFalse)
14838     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14839       windows->image.id);
14840   /*
14841     Initialize Info widget.
14842   */
14843   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14844     &windows->info);
14845   (void) CloneString(&windows->info.name,"Info");
14846   (void) CloneString(&windows->info.icon_name,"Info");
14847   windows->info.border_width=1;
14848   windows->info.x=2;
14849   windows->info.y=2;
14850   windows->info.flags|=PPosition;
14851   windows->info.attributes.win_gravity=UnmapGravity;
14852   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14853     StructureNotifyMask;
14854   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14855   manager_hints->input=MagickFalse;
14856   manager_hints->initial_state=NormalState;
14857   manager_hints->window_group=windows->image.id;
14858   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14859     &windows->info);
14860   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14861     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14862   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14863     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14864   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14865   if (windows->image.mapped != MagickFalse)
14866     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14867   if (display_image->debug != MagickFalse)
14868     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14869       windows->info.id);
14870   /*
14871     Initialize Command widget.
14872   */
14873   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14874     resource_info,&windows->command);
14875   windows->command.data=MagickMenus;
14876   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14877   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14878     resource_info->client_name);
14879   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14880     resource_name,"geometry",(char *) NULL);
14881   (void) CloneString(&windows->command.name,MagickTitle);
14882   windows->command.border_width=0;
14883   windows->command.flags|=PPosition;
14884   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14885     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14886     OwnerGrabButtonMask | StructureNotifyMask;
14887   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14888   manager_hints->input=MagickTrue;
14889   manager_hints->initial_state=NormalState;
14890   manager_hints->window_group=windows->image.id;
14891   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14892     &windows->command);
14893   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14894     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14895     HighlightHeight);
14896   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14897     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14898   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14899   if (windows->command.mapped != MagickFalse)
14900     (void) XMapRaised(display,windows->command.id);
14901   if (display_image->debug != MagickFalse)
14902     (void) LogMagickEvent(X11Event,GetMagickModule(),
14903       "Window id: 0x%lx (command)",windows->command.id);
14904   /*
14905     Initialize Widget window.
14906   */
14907   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14908     resource_info,&windows->widget);
14909   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14910     resource_info->client_name);
14911   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14912     resource_name,"geometry",(char *) NULL);
14913   windows->widget.border_width=0;
14914   windows->widget.flags|=PPosition;
14915   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14916     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14917     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14918     StructureNotifyMask;
14919   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14920   manager_hints->input=MagickTrue;
14921   manager_hints->initial_state=NormalState;
14922   manager_hints->window_group=windows->image.id;
14923   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14924     &windows->widget);
14925   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14926     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14927   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14928     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14929   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14930   if (display_image->debug != MagickFalse)
14931     (void) LogMagickEvent(X11Event,GetMagickModule(),
14932       "Window id: 0x%lx (widget)",windows->widget.id);
14933   /*
14934     Initialize popup window.
14935   */
14936   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14937     resource_info,&windows->popup);
14938   windows->popup.border_width=0;
14939   windows->popup.flags|=PPosition;
14940   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14941     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14942     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14943   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14944   manager_hints->input=MagickTrue;
14945   manager_hints->initial_state=NormalState;
14946   manager_hints->window_group=windows->image.id;
14947   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14948     &windows->popup);
14949   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14950     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14951   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14952     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14953   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14954   if (display_image->debug != MagickFalse)
14955     (void) LogMagickEvent(X11Event,GetMagickModule(),
14956       "Window id: 0x%lx (pop up)",windows->popup.id);
14957   /*
14958     Initialize Magnify window and cursor.
14959   */
14960   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14961     resource_info,&windows->magnify);
14962   if (resource_info->use_shared_memory == MagickFalse)
14963     windows->magnify.shared_memory=MagickFalse;
14964   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14965     resource_info->client_name);
14966   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14967     resource_name,"geometry",(char *) NULL);
14968   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14969     resource_info->magnify);
14970   if (windows->magnify.cursor != (Cursor) NULL)
14971     (void) XFreeCursor(display,windows->magnify.cursor);
14972   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14973     map_info->colormap,resource_info->background_color,
14974     resource_info->foreground_color);
14975   if (windows->magnify.cursor == (Cursor) NULL)
14976     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14977       display_image->filename);
14978   windows->magnify.width=MagnifySize;
14979   windows->magnify.height=MagnifySize;
14980   windows->magnify.flags|=PPosition;
14981   windows->magnify.min_width=MagnifySize;
14982   windows->magnify.min_height=MagnifySize;
14983   windows->magnify.width_inc=MagnifySize;
14984   windows->magnify.height_inc=MagnifySize;
14985   windows->magnify.data=resource_info->magnify;
14986   windows->magnify.attributes.cursor=windows->magnify.cursor;
14987   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14988     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14989     StructureNotifyMask;
14990   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14991   manager_hints->input=MagickTrue;
14992   manager_hints->initial_state=NormalState;
14993   manager_hints->window_group=windows->image.id;
14994   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14995     &windows->magnify);
14996   if (display_image->debug != MagickFalse)
14997     (void) LogMagickEvent(X11Event,GetMagickModule(),
14998       "Window id: 0x%lx (magnify)",windows->magnify.id);
14999   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
15000   /*
15001     Initialize panning window.
15002   */
15003   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
15004     resource_info,&windows->pan);
15005   (void) CloneString(&windows->pan.name,"Pan Icon");
15006   windows->pan.width=windows->icon.width;
15007   windows->pan.height=windows->icon.height;
15008   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
15009     resource_info->client_name);
15010   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15011     resource_name,"geometry",(char *) NULL);
15012   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15013     &windows->pan.width,&windows->pan.height);
15014   windows->pan.flags|=PPosition;
15015   windows->pan.immutable=MagickTrue;
15016   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15017     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15018     StructureNotifyMask;
15019   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15020   manager_hints->input=MagickFalse;
15021   manager_hints->initial_state=NormalState;
15022   manager_hints->window_group=windows->image.id;
15023   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15024     &windows->pan);
15025   if (display_image->debug != MagickFalse)
15026     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15027       windows->pan.id);
15028   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15029   if (windows->info.mapped != MagickFalse)
15030     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15031   if ((windows->image.mapped == MagickFalse) ||
15032       (windows->backdrop.id != (Window) NULL))
15033     (void) XMapWindow(display,windows->image.id);
15034   /*
15035     Set our progress monitor and warning handlers.
15036   */
15037   if (warning_handler == (WarningHandler) NULL)
15038     {
15039       warning_handler=resource_info->display_warnings ?
15040         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15041       warning_handler=resource_info->display_warnings ?
15042         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15043     }
15044   /*
15045     Initialize Image and Magnify X images.
15046   */
15047   windows->image.x=0;
15048   windows->image.y=0;
15049   windows->magnify.shape=MagickFalse;
15050   width=(unsigned int) display_image->columns;
15051   height=(unsigned int) display_image->rows;
15052   if ((display_image->columns != width) || (display_image->rows != height))
15053     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15054       display_image->filename);
15055   status=XMakeImage(display,resource_info,&windows->image,display_image,
15056     width,height,exception);
15057   if (status == MagickFalse)
15058     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15059       display_image->filename);
15060   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15061     windows->magnify.width,windows->magnify.height,exception);
15062   if (status == MagickFalse)
15063     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15064       display_image->filename);
15065   if (windows->magnify.mapped != MagickFalse)
15066     (void) XMapRaised(display,windows->magnify.id);
15067   if (windows->pan.mapped != MagickFalse)
15068     (void) XMapRaised(display,windows->pan.id);
15069   windows->image.window_changes.width=(int) display_image->columns;
15070   windows->image.window_changes.height=(int) display_image->rows;
15071   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15072   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15073   (void) XSync(display,MagickFalse);
15074   /*
15075     Respond to events.
15076   */
15077   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15078   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15079   update_time=0;
15080   if (resource_info->update != MagickFalse)
15081     {
15082       MagickBooleanType
15083         status;
15084
15085       /*
15086         Determine when file data was last modified.
15087       */
15088       status=GetPathAttributes(display_image->filename,&attributes);
15089       if (status != MagickFalse)
15090         update_time=attributes.st_mtime;
15091     }
15092   *state&=(~FormerImageState);
15093   *state&=(~MontageImageState);
15094   *state&=(~NextImageState);
15095   do
15096   {
15097     /*
15098       Handle a window event.
15099     */
15100     if (windows->image.mapped != MagickFalse)
15101       if ((display_image->delay != 0) || (resource_info->update != 0))
15102         {
15103           if (timer < time((time_t *) NULL))
15104             {
15105               if (resource_info->update == MagickFalse)
15106                 *state|=NextImageState | ExitState;
15107               else
15108                 {
15109                   MagickBooleanType
15110                     status;
15111
15112                   /*
15113                     Determine if image file was modified.
15114                   */
15115                   status=GetPathAttributes(display_image->filename,&attributes);
15116                   if (status != MagickFalse)
15117                     if (update_time != attributes.st_mtime)
15118                       {
15119                         /*
15120                           Redisplay image.
15121                         */
15122                         (void) FormatLocaleString(
15123                           resource_info->image_info->filename,MaxTextExtent,
15124                           "%s:%s",display_image->magick,
15125                           display_image->filename);
15126                         nexus=ReadImage(resource_info->image_info,exception);
15127                         if (nexus != (Image *) NULL)
15128                           {
15129                             nexus=DestroyImage(nexus);
15130                             *state|=NextImageState | ExitState;
15131                           }
15132                       }
15133                   delay=display_image->delay/MagickMax(
15134                     display_image->ticks_per_second,1L);
15135                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15136                 }
15137             }
15138           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15139             {
15140               /*
15141                 Do not block if delay > 0.
15142               */
15143               XDelay(display,SuspendTime << 2);
15144               continue;
15145             }
15146         }
15147     timestamp=time((time_t *) NULL);
15148     (void) XNextEvent(display,&event);
15149     if (windows->image.stasis == MagickFalse)
15150       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15151         MagickTrue : MagickFalse;
15152     if (windows->magnify.stasis == MagickFalse)
15153       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15154         MagickTrue : MagickFalse;
15155     if (event.xany.window == windows->command.id)
15156       {
15157         /*
15158           Select a command from the Command widget.
15159         */
15160         id=XCommandWidget(display,windows,CommandMenu,&event);
15161         if (id < 0)
15162           continue;
15163         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15164         command_type=CommandMenus[id];
15165         if (id < MagickMenus)
15166           {
15167             /*
15168               Select a command from a pop-up menu.
15169             */
15170             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15171               command);
15172             if (entry < 0)
15173               continue;
15174             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15175             command_type=Commands[id][entry];
15176           }
15177         if (command_type != NullCommand)
15178           nexus=XMagickCommand(display,resource_info,windows,command_type,
15179             &display_image,exception);
15180         continue;
15181       }
15182     switch (event.type)
15183     {
15184       case ButtonPress:
15185       {
15186         if (display_image->debug != MagickFalse)
15187           (void) LogMagickEvent(X11Event,GetMagickModule(),
15188             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15189             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15190         if ((event.xbutton.button == Button3) &&
15191             (event.xbutton.state & Mod1Mask))
15192           {
15193             /*
15194               Convert Alt-Button3 to Button2.
15195             */
15196             event.xbutton.button=Button2;
15197             event.xbutton.state&=(~Mod1Mask);
15198           }
15199         if (event.xbutton.window == windows->backdrop.id)
15200           {
15201             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15202               event.xbutton.time);
15203             break;
15204           }
15205         if (event.xbutton.window == windows->image.id)
15206           {
15207             switch (event.xbutton.button)
15208             {
15209               case Button1:
15210               {
15211                 if (resource_info->immutable)
15212                   {
15213                     /*
15214                       Select a command from the Virtual menu.
15215                     */
15216                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15217                       command);
15218                     if (entry >= 0)
15219                       nexus=XMagickCommand(display,resource_info,windows,
15220                         VirtualCommands[entry],&display_image,exception);
15221                     break;
15222                   }
15223                 /*
15224                   Map/unmap Command widget.
15225                 */
15226                 if (windows->command.mapped != MagickFalse)
15227                   (void) XWithdrawWindow(display,windows->command.id,
15228                     windows->command.screen);
15229                 else
15230                   {
15231                     (void) XCommandWidget(display,windows,CommandMenu,
15232                       (XEvent *) NULL);
15233                     (void) XMapRaised(display,windows->command.id);
15234                   }
15235                 break;
15236               }
15237               case Button2:
15238               {
15239                 /*
15240                   User pressed the image magnify button.
15241                 */
15242                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15243                   &display_image,exception);
15244                 XMagnifyImage(display,windows,&event,exception);
15245                 break;
15246               }
15247               case Button3:
15248               {
15249                 if (resource_info->immutable)
15250                   {
15251                     /*
15252                       Select a command from the Virtual menu.
15253                     */
15254                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15255                       command);
15256                     if (entry >= 0)
15257                       nexus=XMagickCommand(display,resource_info,windows,
15258                         VirtualCommands[entry],&display_image,exception);
15259                     break;
15260                   }
15261                 if (display_image->montage != (char *) NULL)
15262                   {
15263                     /*
15264                       Open or delete a tile from a visual image directory.
15265                     */
15266                     nexus=XTileImage(display,resource_info,windows,
15267                       display_image,&event,exception);
15268                     if (nexus != (Image *) NULL)
15269                       *state|=MontageImageState | NextImageState | ExitState;
15270                     vid_info.x=(short int) windows->image.x;
15271                     vid_info.y=(short int) windows->image.y;
15272                     break;
15273                   }
15274                 /*
15275                   Select a command from the Short Cuts menu.
15276                 */
15277                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15278                   command);
15279                 if (entry >= 0)
15280                   nexus=XMagickCommand(display,resource_info,windows,
15281                     ShortCutsCommands[entry],&display_image,exception);
15282                 break;
15283               }
15284               case Button4:
15285               {
15286                 /*
15287                   Wheel up.
15288                 */
15289                 XTranslateImage(display,windows,*image,XK_Up);
15290                 break;
15291               }
15292               case Button5:
15293               {
15294                 /*
15295                   Wheel down.
15296                 */
15297                 XTranslateImage(display,windows,*image,XK_Down);
15298                 break;
15299               }
15300               default:
15301                 break;
15302             }
15303             break;
15304           }
15305         if (event.xbutton.window == windows->magnify.id)
15306           {
15307             int
15308               factor;
15309
15310             static const char
15311               *MagnifyMenu[] =
15312               {
15313                 "2",
15314                 "4",
15315                 "5",
15316                 "6",
15317                 "7",
15318                 "8",
15319                 "9",
15320                 "3",
15321                 (char *) NULL,
15322               };
15323
15324             static KeySym
15325               MagnifyCommands[] =
15326               {
15327                 XK_2,
15328                 XK_4,
15329                 XK_5,
15330                 XK_6,
15331                 XK_7,
15332                 XK_8,
15333                 XK_9,
15334                 XK_3
15335               };
15336
15337             /*
15338               Select a magnify factor from the pop-up menu.
15339             */
15340             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15341             if (factor >= 0)
15342               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15343                 exception);
15344             break;
15345           }
15346         if (event.xbutton.window == windows->pan.id)
15347           {
15348             switch (event.xbutton.button)
15349             {
15350               case Button4:
15351               {
15352                 /*
15353                   Wheel up.
15354                 */
15355                 XTranslateImage(display,windows,*image,XK_Up);
15356                 break;
15357               }
15358               case Button5:
15359               {
15360                 /*
15361                   Wheel down.
15362                 */
15363                 XTranslateImage(display,windows,*image,XK_Down);
15364                 break;
15365               }
15366               default:
15367               {
15368                 XPanImage(display,windows,&event,exception);
15369                 break;
15370               }
15371             }
15372             break;
15373           }
15374         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15375           1L);
15376         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15377         break;
15378       }
15379       case ButtonRelease:
15380       {
15381         if (display_image->debug != MagickFalse)
15382           (void) LogMagickEvent(X11Event,GetMagickModule(),
15383             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15384             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15385         break;
15386       }
15387       case ClientMessage:
15388       {
15389         if (display_image->debug != MagickFalse)
15390           (void) LogMagickEvent(X11Event,GetMagickModule(),
15391             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15392             event.xclient.message_type,event.xclient.format,(unsigned long)
15393             event.xclient.data.l[0]);
15394         if (event.xclient.message_type == windows->im_protocols)
15395           {
15396             if (*event.xclient.data.l == (long) windows->im_update_widget)
15397               {
15398                 (void) CloneString(&windows->command.name,MagickTitle);
15399                 windows->command.data=MagickMenus;
15400                 (void) XCommandWidget(display,windows,CommandMenu,
15401                   (XEvent *) NULL);
15402                 break;
15403               }
15404             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15405               {
15406                 /*
15407                   Update graphic context and window colormap.
15408                 */
15409                 for (i=0; i < (int) number_windows; i++)
15410                 {
15411                   if (magick_windows[i]->id == windows->icon.id)
15412                     continue;
15413                   context_values.background=pixel->background_color.pixel;
15414                   context_values.foreground=pixel->foreground_color.pixel;
15415                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15416                     context_mask,&context_values);
15417                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15418                     context_mask,&context_values);
15419                   context_values.background=pixel->foreground_color.pixel;
15420                   context_values.foreground=pixel->background_color.pixel;
15421                   context_values.plane_mask=context_values.background ^
15422                     context_values.foreground;
15423                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15424                     (size_t) (context_mask | GCPlaneMask),
15425                     &context_values);
15426                   magick_windows[i]->attributes.background_pixel=
15427                     pixel->background_color.pixel;
15428                   magick_windows[i]->attributes.border_pixel=
15429                     pixel->border_color.pixel;
15430                   magick_windows[i]->attributes.colormap=map_info->colormap;
15431                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15432                     (unsigned long) magick_windows[i]->mask,
15433                     &magick_windows[i]->attributes);
15434                 }
15435                 if (windows->pan.mapped != MagickFalse)
15436                   {
15437                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15438                       windows->pan.pixmap);
15439                     (void) XClearWindow(display,windows->pan.id);
15440                     XDrawPanRectangle(display,windows);
15441                   }
15442                 if (windows->backdrop.id != (Window) NULL)
15443                   (void) XInstallColormap(display,map_info->colormap);
15444                 break;
15445               }
15446             if (*event.xclient.data.l == (long) windows->im_former_image)
15447               {
15448                 *state|=FormerImageState | ExitState;
15449                 break;
15450               }
15451             if (*event.xclient.data.l == (long) windows->im_next_image)
15452               {
15453                 *state|=NextImageState | ExitState;
15454                 break;
15455               }
15456             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15457               {
15458                 *state|=RetainColorsState;
15459                 break;
15460               }
15461             if (*event.xclient.data.l == (long) windows->im_exit)
15462               {
15463                 *state|=ExitState;
15464                 break;
15465               }
15466             break;
15467           }
15468         if (event.xclient.message_type == windows->dnd_protocols)
15469           {
15470             Atom
15471               selection,
15472               type;
15473
15474             int
15475               format,
15476               status;
15477
15478             unsigned char
15479               *data;
15480
15481             unsigned long
15482               after,
15483               length;
15484
15485             /*
15486               Display image named by the Drag-and-Drop selection.
15487             */
15488             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15489               break;
15490             selection=XInternAtom(display,"DndSelection",MagickFalse);
15491             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15492               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15493               &length,&after,&data);
15494             if ((status != Success) || (length == 0))
15495               break;
15496             if (*event.xclient.data.l == 2)
15497               {
15498                 /*
15499                   Offix DND.
15500                 */
15501                 (void) CopyMagickString(resource_info->image_info->filename,
15502                   (char *) data,MaxTextExtent);
15503               }
15504             else
15505               {
15506                 /*
15507                   XDND.
15508                 */
15509                 if (strncmp((char *) data, "file:", 5) != 0)
15510                   {
15511                     (void) XFree((void *) data);
15512                     break;
15513                   }
15514                 (void) CopyMagickString(resource_info->image_info->filename,
15515                   ((char *) data)+5,MaxTextExtent);
15516               }
15517             nexus=ReadImage(resource_info->image_info,exception);
15518             CatchException(exception);
15519             if (nexus != (Image *) NULL)
15520               *state|=NextImageState | ExitState;
15521             (void) XFree((void *) data);
15522             break;
15523           }
15524         /*
15525           If client window delete message, exit.
15526         */
15527         if (event.xclient.message_type != windows->wm_protocols)
15528           break;
15529         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15530           break;
15531         (void) XWithdrawWindow(display,event.xclient.window,
15532           visual_info->screen);
15533         if (event.xclient.window == windows->image.id)
15534           {
15535             *state|=ExitState;
15536             break;
15537           }
15538         if (event.xclient.window == windows->pan.id)
15539           {
15540             /*
15541               Restore original image size when pan window is deleted.
15542             */
15543             windows->image.window_changes.width=windows->image.ximage->width;
15544             windows->image.window_changes.height=windows->image.ximage->height;
15545             (void) XConfigureImage(display,resource_info,windows,
15546               display_image,exception);
15547           }
15548         break;
15549       }
15550       case ConfigureNotify:
15551       {
15552         if (display_image->debug != MagickFalse)
15553           (void) LogMagickEvent(X11Event,GetMagickModule(),
15554             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15555             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15556             event.xconfigure.y,event.xconfigure.send_event);
15557         if (event.xconfigure.window == windows->image.id)
15558           {
15559             /*
15560               Image window has a new configuration.
15561             */
15562             if (event.xconfigure.send_event != 0)
15563               {
15564                 XWindowChanges
15565                   window_changes;
15566
15567                 /*
15568                   Position the transient windows relative of the Image window.
15569                 */
15570                 if (windows->command.geometry == (char *) NULL)
15571                   if (windows->command.mapped == MagickFalse)
15572                     {
15573                       windows->command.x=event.xconfigure.x-
15574                         windows->command.width-25;
15575                       windows->command.y=event.xconfigure.y;
15576                       XConstrainWindowPosition(display,&windows->command);
15577                       window_changes.x=windows->command.x;
15578                       window_changes.y=windows->command.y;
15579                       (void) XReconfigureWMWindow(display,windows->command.id,
15580                         windows->command.screen,(unsigned int) (CWX | CWY),
15581                         &window_changes);
15582                     }
15583                 if (windows->widget.geometry == (char *) NULL)
15584                   if (windows->widget.mapped == MagickFalse)
15585                     {
15586                       windows->widget.x=event.xconfigure.x+
15587                         event.xconfigure.width/10;
15588                       windows->widget.y=event.xconfigure.y+
15589                         event.xconfigure.height/10;
15590                       XConstrainWindowPosition(display,&windows->widget);
15591                       window_changes.x=windows->widget.x;
15592                       window_changes.y=windows->widget.y;
15593                       (void) XReconfigureWMWindow(display,windows->widget.id,
15594                         windows->widget.screen,(unsigned int) (CWX | CWY),
15595                         &window_changes);
15596                     }
15597                 if (windows->magnify.geometry == (char *) NULL)
15598                   if (windows->magnify.mapped == MagickFalse)
15599                     {
15600                       windows->magnify.x=event.xconfigure.x+
15601                         event.xconfigure.width+25;
15602                       windows->magnify.y=event.xconfigure.y;
15603                       XConstrainWindowPosition(display,&windows->magnify);
15604                       window_changes.x=windows->magnify.x;
15605                       window_changes.y=windows->magnify.y;
15606                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15607                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15608                         &window_changes);
15609                     }
15610                 if (windows->pan.geometry == (char *) NULL)
15611                   if (windows->pan.mapped == MagickFalse)
15612                     {
15613                       windows->pan.x=event.xconfigure.x+
15614                         event.xconfigure.width+25;
15615                       windows->pan.y=event.xconfigure.y+
15616                         windows->magnify.height+50;
15617                       XConstrainWindowPosition(display,&windows->pan);
15618                       window_changes.x=windows->pan.x;
15619                       window_changes.y=windows->pan.y;
15620                       (void) XReconfigureWMWindow(display,windows->pan.id,
15621                         windows->pan.screen,(unsigned int) (CWX | CWY),
15622                         &window_changes);
15623                     }
15624               }
15625             if ((event.xconfigure.width == (int) windows->image.width) &&
15626                 (event.xconfigure.height == (int) windows->image.height))
15627               break;
15628             windows->image.width=(unsigned int) event.xconfigure.width;
15629             windows->image.height=(unsigned int) event.xconfigure.height;
15630             windows->image.x=0;
15631             windows->image.y=0;
15632             if (display_image->montage != (char *) NULL)
15633               {
15634                 windows->image.x=vid_info.x;
15635                 windows->image.y=vid_info.y;
15636               }
15637             if ((windows->image.mapped != MagickFalse) &&
15638                 (windows->image.stasis != MagickFalse))
15639               {
15640                 /*
15641                   Update image window configuration.
15642                 */
15643                 windows->image.window_changes.width=event.xconfigure.width;
15644                 windows->image.window_changes.height=event.xconfigure.height;
15645                 (void) XConfigureImage(display,resource_info,windows,
15646                   display_image,exception);
15647               }
15648             /*
15649               Update pan window configuration.
15650             */
15651             if ((event.xconfigure.width < windows->image.ximage->width) ||
15652                 (event.xconfigure.height < windows->image.ximage->height))
15653               {
15654                 (void) XMapRaised(display,windows->pan.id);
15655                 XDrawPanRectangle(display,windows);
15656               }
15657             else
15658               if (windows->pan.mapped != MagickFalse)
15659                 (void) XWithdrawWindow(display,windows->pan.id,
15660                   windows->pan.screen);
15661             break;
15662           }
15663         if (event.xconfigure.window == windows->magnify.id)
15664           {
15665             unsigned int
15666               magnify;
15667
15668             /*
15669               Magnify window has a new configuration.
15670             */
15671             windows->magnify.width=(unsigned int) event.xconfigure.width;
15672             windows->magnify.height=(unsigned int) event.xconfigure.height;
15673             if (windows->magnify.mapped == MagickFalse)
15674               break;
15675             magnify=1;
15676             while ((int) magnify <= event.xconfigure.width)
15677               magnify<<=1;
15678             while ((int) magnify <= event.xconfigure.height)
15679               magnify<<=1;
15680             magnify>>=1;
15681             if (((int) magnify != event.xconfigure.width) ||
15682                 ((int) magnify != event.xconfigure.height))
15683               {
15684                 window_changes.width=(int) magnify;
15685                 window_changes.height=(int) magnify;
15686                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15687                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15688                   &window_changes);
15689                 break;
15690               }
15691             if ((windows->magnify.mapped != MagickFalse) &&
15692                 (windows->magnify.stasis != MagickFalse))
15693               {
15694                 status=XMakeImage(display,resource_info,&windows->magnify,
15695                   display_image,windows->magnify.width,windows->magnify.height,
15696                   exception);
15697                 XMakeMagnifyImage(display,windows,exception);
15698               }
15699             break;
15700           }
15701         if ((windows->magnify.mapped != MagickFalse) &&
15702             (event.xconfigure.window == windows->pan.id))
15703           {
15704             /*
15705               Pan icon window has a new configuration.
15706             */
15707             if (event.xconfigure.send_event != 0)
15708               {
15709                 windows->pan.x=event.xconfigure.x;
15710                 windows->pan.y=event.xconfigure.y;
15711               }
15712             windows->pan.width=(unsigned int) event.xconfigure.width;
15713             windows->pan.height=(unsigned int) event.xconfigure.height;
15714             break;
15715           }
15716         if (event.xconfigure.window == windows->icon.id)
15717           {
15718             /*
15719               Icon window has a new configuration.
15720             */
15721             windows->icon.width=(unsigned int) event.xconfigure.width;
15722             windows->icon.height=(unsigned int) event.xconfigure.height;
15723             break;
15724           }
15725         break;
15726       }
15727       case DestroyNotify:
15728       {
15729         /*
15730           Group leader has exited.
15731         */
15732         if (display_image->debug != MagickFalse)
15733           (void) LogMagickEvent(X11Event,GetMagickModule(),
15734             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15735         if (event.xdestroywindow.window == windows->group_leader.id)
15736           {
15737             *state|=ExitState;
15738             break;
15739           }
15740         break;
15741       }
15742       case EnterNotify:
15743       {
15744         /*
15745           Selectively install colormap.
15746         */
15747         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15748           if (event.xcrossing.mode != NotifyUngrab)
15749             XInstallColormap(display,map_info->colormap);
15750         break;
15751       }
15752       case Expose:
15753       {
15754         if (display_image->debug != MagickFalse)
15755           (void) LogMagickEvent(X11Event,GetMagickModule(),
15756             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15757             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15758             event.xexpose.y);
15759         /*
15760           Refresh windows that are now exposed.
15761         */
15762         if ((event.xexpose.window == windows->image.id) &&
15763             (windows->image.mapped != MagickFalse))
15764           {
15765             XRefreshWindow(display,&windows->image,&event);
15766             delay=display_image->delay/MagickMax(
15767               display_image->ticks_per_second,1L);
15768             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15769             break;
15770           }
15771         if ((event.xexpose.window == windows->magnify.id) &&
15772             (windows->magnify.mapped != MagickFalse))
15773           {
15774             XMakeMagnifyImage(display,windows,exception);
15775             break;
15776           }
15777         if (event.xexpose.window == windows->pan.id)
15778           {
15779             XDrawPanRectangle(display,windows);
15780             break;
15781           }
15782         if (event.xexpose.window == windows->icon.id)
15783           {
15784             XRefreshWindow(display,&windows->icon,&event);
15785             break;
15786           }
15787         break;
15788       }
15789       case KeyPress:
15790       {
15791         int
15792           length;
15793
15794         /*
15795           Respond to a user key press.
15796         */
15797         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15798           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15799         *(command+length)='\0';
15800         if (display_image->debug != MagickFalse)
15801           (void) LogMagickEvent(X11Event,GetMagickModule(),
15802             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15803             key_symbol,command);
15804         if (event.xkey.window == windows->image.id)
15805           {
15806             command_type=XImageWindowCommand(display,resource_info,windows,
15807               event.xkey.state,key_symbol,&display_image,exception);
15808             if (command_type != NullCommand)
15809               nexus=XMagickCommand(display,resource_info,windows,command_type,
15810                 &display_image,exception);
15811           }
15812         if (event.xkey.window == windows->magnify.id)
15813           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15814             exception);
15815         if (event.xkey.window == windows->pan.id)
15816           {
15817             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15818               (void) XWithdrawWindow(display,windows->pan.id,
15819                 windows->pan.screen);
15820             else
15821               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15822                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15823                   "Help Viewer - Image Pan",ImagePanHelp);
15824               else
15825                 XTranslateImage(display,windows,*image,key_symbol);
15826           }
15827         delay=display_image->delay/MagickMax(
15828           display_image->ticks_per_second,1L);
15829         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15830         break;
15831       }
15832       case KeyRelease:
15833       {
15834         /*
15835           Respond to a user key release.
15836         */
15837         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15838           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15839         if (display_image->debug != MagickFalse)
15840           (void) LogMagickEvent(X11Event,GetMagickModule(),
15841             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15842         break;
15843       }
15844       case LeaveNotify:
15845       {
15846         /*
15847           Selectively uninstall colormap.
15848         */
15849         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15850           if (event.xcrossing.mode != NotifyUngrab)
15851             XUninstallColormap(display,map_info->colormap);
15852         break;
15853       }
15854       case MapNotify:
15855       {
15856         if (display_image->debug != MagickFalse)
15857           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15858             event.xmap.window);
15859         if (event.xmap.window == windows->backdrop.id)
15860           {
15861             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15862               CurrentTime);
15863             windows->backdrop.mapped=MagickTrue;
15864             break;
15865           }
15866         if (event.xmap.window == windows->image.id)
15867           {
15868             if (windows->backdrop.id != (Window) NULL)
15869               (void) XInstallColormap(display,map_info->colormap);
15870             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15871               {
15872                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15873                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15874               }
15875             if (((int) windows->image.width < windows->image.ximage->width) ||
15876                 ((int) windows->image.height < windows->image.ximage->height))
15877               (void) XMapRaised(display,windows->pan.id);
15878             windows->image.mapped=MagickTrue;
15879             break;
15880           }
15881         if (event.xmap.window == windows->magnify.id)
15882           {
15883             XMakeMagnifyImage(display,windows,exception);
15884             windows->magnify.mapped=MagickTrue;
15885             (void) XWithdrawWindow(display,windows->info.id,
15886               windows->info.screen);
15887             break;
15888           }
15889         if (event.xmap.window == windows->pan.id)
15890           {
15891             XMakePanImage(display,resource_info,windows,display_image,
15892               exception);
15893             windows->pan.mapped=MagickTrue;
15894             break;
15895           }
15896         if (event.xmap.window == windows->info.id)
15897           {
15898             windows->info.mapped=MagickTrue;
15899             break;
15900           }
15901         if (event.xmap.window == windows->icon.id)
15902           {
15903             MagickBooleanType
15904               taint;
15905
15906             /*
15907               Create an icon image.
15908             */
15909             taint=display_image->taint;
15910             XMakeStandardColormap(display,icon_visual,icon_resources,
15911               display_image,icon_map,icon_pixel,exception);
15912             (void) XMakeImage(display,icon_resources,&windows->icon,
15913               display_image,windows->icon.width,windows->icon.height,
15914               exception);
15915             display_image->taint=taint;
15916             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15917               windows->icon.pixmap);
15918             (void) XClearWindow(display,windows->icon.id);
15919             (void) XWithdrawWindow(display,windows->info.id,
15920               windows->info.screen);
15921             windows->icon.mapped=MagickTrue;
15922             break;
15923           }
15924         if (event.xmap.window == windows->command.id)
15925           {
15926             windows->command.mapped=MagickTrue;
15927             break;
15928           }
15929         if (event.xmap.window == windows->popup.id)
15930           {
15931             windows->popup.mapped=MagickTrue;
15932             break;
15933           }
15934         if (event.xmap.window == windows->widget.id)
15935           {
15936             windows->widget.mapped=MagickTrue;
15937             break;
15938           }
15939         break;
15940       }
15941       case MappingNotify:
15942       {
15943         (void) XRefreshKeyboardMapping(&event.xmapping);
15944         break;
15945       }
15946       case NoExpose:
15947         break;
15948       case PropertyNotify:
15949       {
15950         Atom
15951           type;
15952
15953         int
15954           format,
15955           status;
15956
15957         unsigned char
15958           *data;
15959
15960         unsigned long
15961           after,
15962           length;
15963
15964         if (display_image->debug != MagickFalse)
15965           (void) LogMagickEvent(X11Event,GetMagickModule(),
15966             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15967             event.xproperty.atom,event.xproperty.state);
15968         if (event.xproperty.atom != windows->im_remote_command)
15969           break;
15970         /*
15971           Display image named by the remote command protocol.
15972         */
15973         status=XGetWindowProperty(display,event.xproperty.window,
15974           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15975           AnyPropertyType,&type,&format,&length,&after,&data);
15976         if ((status != Success) || (length == 0))
15977           break;
15978         if (LocaleCompare((char *) data,"-quit") == 0)
15979           {
15980             XClientMessage(display,windows->image.id,windows->im_protocols,
15981               windows->im_exit,CurrentTime);
15982             (void) XFree((void *) data);
15983             break;
15984           }
15985         (void) CopyMagickString(resource_info->image_info->filename,
15986           (char *) data,MaxTextExtent);
15987         (void) XFree((void *) data);
15988         nexus=ReadImage(resource_info->image_info,exception);
15989         CatchException(exception);
15990         if (nexus != (Image *) NULL)
15991           *state|=NextImageState | ExitState;
15992         break;
15993       }
15994       case ReparentNotify:
15995       {
15996         if (display_image->debug != MagickFalse)
15997           (void) LogMagickEvent(X11Event,GetMagickModule(),
15998             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15999             event.xreparent.window);
16000         break;
16001       }
16002       case UnmapNotify:
16003       {
16004         if (display_image->debug != MagickFalse)
16005           (void) LogMagickEvent(X11Event,GetMagickModule(),
16006             "Unmap Notify: 0x%lx",event.xunmap.window);
16007         if (event.xunmap.window == windows->backdrop.id)
16008           {
16009             windows->backdrop.mapped=MagickFalse;
16010             break;
16011           }
16012         if (event.xunmap.window == windows->image.id)
16013           {
16014             windows->image.mapped=MagickFalse;
16015             break;
16016           }
16017         if (event.xunmap.window == windows->magnify.id)
16018           {
16019             windows->magnify.mapped=MagickFalse;
16020             break;
16021           }
16022         if (event.xunmap.window == windows->pan.id)
16023           {
16024             windows->pan.mapped=MagickFalse;
16025             break;
16026           }
16027         if (event.xunmap.window == windows->info.id)
16028           {
16029             windows->info.mapped=MagickFalse;
16030             break;
16031           }
16032         if (event.xunmap.window == windows->icon.id)
16033           {
16034             if (map_info->colormap == icon_map->colormap)
16035               XConfigureImageColormap(display,resource_info,windows,
16036                 display_image,exception);
16037             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16038               icon_pixel);
16039             windows->icon.mapped=MagickFalse;
16040             break;
16041           }
16042         if (event.xunmap.window == windows->command.id)
16043           {
16044             windows->command.mapped=MagickFalse;
16045             break;
16046           }
16047         if (event.xunmap.window == windows->popup.id)
16048           {
16049             if (windows->backdrop.id != (Window) NULL)
16050               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16051                 CurrentTime);
16052             windows->popup.mapped=MagickFalse;
16053             break;
16054           }
16055         if (event.xunmap.window == windows->widget.id)
16056           {
16057             if (windows->backdrop.id != (Window) NULL)
16058               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16059                 CurrentTime);
16060             windows->widget.mapped=MagickFalse;
16061             break;
16062           }
16063         break;
16064       }
16065       default:
16066       {
16067         if (display_image->debug != MagickFalse)
16068           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16069             event.type);
16070         break;
16071       }
16072     }
16073   } while (!(*state & ExitState));
16074   if ((*state & ExitState) == 0)
16075     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16076       &display_image,exception);
16077   else
16078     if (resource_info->confirm_edit != MagickFalse)
16079       {
16080         /*
16081           Query user if image has changed.
16082         */
16083         if ((resource_info->immutable == MagickFalse) &&
16084             (display_image->taint != MagickFalse))
16085           {
16086             int
16087               status;
16088
16089             status=XConfirmWidget(display,windows,"Your image changed.",
16090               "Do you want to save it");
16091             if (status == 0)
16092               *state&=(~ExitState);
16093             else
16094               if (status > 0)
16095                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16096                   &display_image,exception);
16097           }
16098       }
16099   if ((windows->visual_info->klass == GrayScale) ||
16100       (windows->visual_info->klass == PseudoColor) ||
16101       (windows->visual_info->klass == DirectColor))
16102     {
16103       /*
16104         Withdraw pan and Magnify window.
16105       */
16106       if (windows->info.mapped != MagickFalse)
16107         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16108       if (windows->magnify.mapped != MagickFalse)
16109         (void) XWithdrawWindow(display,windows->magnify.id,
16110           windows->magnify.screen);
16111       if (windows->command.mapped != MagickFalse)
16112         (void) XWithdrawWindow(display,windows->command.id,
16113           windows->command.screen);
16114     }
16115   if (windows->pan.mapped != MagickFalse)
16116     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16117   if (resource_info->backdrop == MagickFalse)
16118     if (windows->backdrop.mapped)
16119       {
16120         (void) XWithdrawWindow(display,windows->backdrop.id,
16121           windows->backdrop.screen);
16122         (void) XDestroyWindow(display,windows->backdrop.id);
16123         windows->backdrop.id=(Window) NULL;
16124         (void) XWithdrawWindow(display,windows->image.id,
16125           windows->image.screen);
16126         (void) XDestroyWindow(display,windows->image.id);
16127         windows->image.id=(Window) NULL;
16128       }
16129   XSetCursorState(display,windows,MagickTrue);
16130   XCheckRefreshWindows(display,windows);
16131   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16132     *state&=(~ExitState);
16133   if (*state & ExitState)
16134     {
16135       /*
16136         Free Standard Colormap.
16137       */
16138       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16139       if (resource_info->map_type == (char *) NULL)
16140         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16141       /*
16142         Free X resources.
16143       */
16144       if (resource_info->copy_image != (Image *) NULL)
16145         {
16146           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16147           resource_info->copy_image=NewImageList();
16148         }
16149       DestroyXResources();
16150     }
16151   (void) XSync(display,MagickFalse);
16152   /*
16153     Restore our progress monitor and warning handlers.
16154   */
16155   (void) SetErrorHandler(warning_handler);
16156   (void) SetWarningHandler(warning_handler);
16157   /*
16158     Change to home directory.
16159   */
16160   directory=getcwd(working_directory,MaxTextExtent);
16161   (void) directory;
16162   {
16163     int
16164       status;
16165
16166     status=chdir(resource_info->home_directory);
16167     if (status == -1)
16168       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16169         "UnableToOpenFile","%s",resource_info->home_directory);
16170   }
16171   *image=display_image;
16172   return(nexus);
16173 }
16174 #else
16175 \f
16176 /*
16177 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16178 %                                                                             %
16179 %                                                                             %
16180 %                                                                             %
16181 +   D i s p l a y I m a g e s                                                 %
16182 %                                                                             %
16183 %                                                                             %
16184 %                                                                             %
16185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16186 %
16187 %  DisplayImages() displays an image sequence to any X window screen.  It
16188 %  returns a value other than 0 if successful.  Check the exception member
16189 %  of image to determine the reason for any failure.
16190 %
16191 %  The format of the DisplayImages method is:
16192 %
16193 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16194 %        Image *images,ExceptionInfo *exception)
16195 %
16196 %  A description of each parameter follows:
16197 %
16198 %    o image_info: the image info.
16199 %
16200 %    o image: the image.
16201 %
16202 %    o exception: return any errors or warnings in this structure.
16203 %
16204 */
16205 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16206   Image *image,ExceptionInfo *exception)
16207 {
16208   assert(image_info != (const ImageInfo *) NULL);
16209   assert(image_info->signature == MagickSignature);
16210   assert(image != (Image *) NULL);
16211   assert(image->signature == MagickSignature);
16212   if (image->debug != MagickFalse)
16213     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16214   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16215     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16216   return(MagickFalse);
16217 }
16218 \f
16219 /*
16220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16221 %                                                                             %
16222 %                                                                             %
16223 %                                                                             %
16224 +   R e m o t e D i s p l a y C o m m a n d                                   %
16225 %                                                                             %
16226 %                                                                             %
16227 %                                                                             %
16228 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16229 %
16230 %  RemoteDisplayCommand() encourages a remote display program to display the
16231 %  specified image filename.
16232 %
16233 %  The format of the RemoteDisplayCommand method is:
16234 %
16235 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16236 %        const char *window,const char *filename,ExceptionInfo *exception)
16237 %
16238 %  A description of each parameter follows:
16239 %
16240 %    o image_info: the image info.
16241 %
16242 %    o window: Specifies the name or id of an X window.
16243 %
16244 %    o filename: the name of the image filename to display.
16245 %
16246 %    o exception: return any errors or warnings in this structure.
16247 %
16248 */
16249 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16250   const char *window,const char *filename,ExceptionInfo *exception)
16251 {
16252   assert(image_info != (const ImageInfo *) NULL);
16253   assert(image_info->signature == MagickSignature);
16254   assert(filename != (char *) NULL);
16255   (void) window;
16256   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16257   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16258     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16259   return(MagickFalse);
16260 }
16261 #endif