]> granicus.if.org Git - imagemagick/blob - MagickCore/display.c
5a6f61379572feaa7be525b5c738d612f4372076
[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 %                                  Cristy                                     %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/attribute.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/cache.h"
47 #include "MagickCore/cache-private.h"
48 #include "MagickCore/channel.h"
49 #include "MagickCore/client.h"
50 #include "MagickCore/color.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/composite.h"
53 #include "MagickCore/constitute.h"
54 #include "MagickCore/decorate.h"
55 #include "MagickCore/delegate.h"
56 #include "MagickCore/display.h"
57 #include "MagickCore/display-private.h"
58 #include "MagickCore/distort.h"
59 #include "MagickCore/draw.h"
60 #include "MagickCore/effect.h"
61 #include "MagickCore/enhance.h"
62 #include "MagickCore/exception.h"
63 #include "MagickCore/exception-private.h"
64 #include "MagickCore/fx.h"
65 #include "MagickCore/geometry.h"
66 #include "MagickCore/image.h"
67 #include "MagickCore/image-private.h"
68 #include "MagickCore/list.h"
69 #include "MagickCore/log.h"
70 #include "MagickCore/magick.h"
71 #include "MagickCore/memory_.h"
72 #include "MagickCore/monitor.h"
73 #include "MagickCore/monitor-private.h"
74 #include "MagickCore/montage.h"
75 #include "MagickCore/nt-base-private.h"
76 #include "MagickCore/option.h"
77 #include "MagickCore/paint.h"
78 #include "MagickCore/pixel.h"
79 #include "MagickCore/pixel-accessor.h"
80 #include "MagickCore/PreRvIcccm.h"
81 #include "MagickCore/property.h"
82 #include "MagickCore/quantum.h"
83 #include "MagickCore/quantum-private.h"
84 #include "MagickCore/resize.h"
85 #include "MagickCore/resource_.h"
86 #include "MagickCore/shear.h"
87 #include "MagickCore/segment.h"
88 #include "MagickCore/statistic.h"
89 #include "MagickCore/string_.h"
90 #include "MagickCore/string-private.h"
91 #include "MagickCore/transform.h"
92 #include "MagickCore/threshold.h"
93 #include "MagickCore/utility.h"
94 #include "MagickCore/utility-private.h"
95 #include "MagickCore/version.h"
96 #include "MagickCore/widget.h"
97 #include "MagickCore/widget-private.h"
98 #include "MagickCore/xwindow.h"
99 #include "MagickCore/xwindow-private.h"
100 \f
101 #if defined(MAGICKCORE_X11_DELEGATE)
102 /*
103   Define declarations.
104 */
105 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
106 \f
107 /*
108   Constant declarations.
109 */
110 static const unsigned char
111   HighlightBitmap[8] =
112   {
113     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
114   },
115   OpaqueBitmap[8] =
116   {
117     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
118   },
119   ShadowBitmap[8] =
120   {
121     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
122   };
123
124 static const char
125   *PageSizes[] =
126   {
127     "Letter",
128     "Tabloid",
129     "Ledger",
130     "Legal",
131     "Statement",
132     "Executive",
133     "A3",
134     "A4",
135     "A5",
136     "B4",
137     "B5",
138     "Folio",
139     "Quarto",
140     "10x14",
141     (char *) NULL
142   };
143 \f
144 /*
145   Help widget declarations.
146 */
147 static const char
148   *ImageAnnotateHelp[] =
149   {
150     "In annotate mode, the Command widget has these options:",
151     "",
152     "    Font Name",
153     "      fixed",
154     "      variable",
155     "      5x8",
156     "      6x10",
157     "      7x13bold",
158     "      8x13bold",
159     "      9x15bold",
160     "      10x20",
161     "      12x24",
162     "      Browser...",
163     "    Font Color",
164     "      black",
165     "      blue",
166     "      cyan",
167     "      green",
168     "      gray",
169     "      red",
170     "      magenta",
171     "      yellow",
172     "      white",
173     "      transparent",
174     "      Browser...",
175     "    Font Color",
176     "      black",
177     "      blue",
178     "      cyan",
179     "      green",
180     "      gray",
181     "      red",
182     "      magenta",
183     "      yellow",
184     "      white",
185     "      transparent",
186     "      Browser...",
187     "    Rotate Text",
188     "      -90",
189     "      -45",
190     "      -30",
191     "      0",
192     "      30",
193     "      45",
194     "      90",
195     "      180",
196     "      Dialog...",
197     "    Help",
198     "    Dismiss",
199     "",
200     "Choose a font name from the Font Name sub-menu.  Additional",
201     "font names can be specified with the font browser.  You can",
202     "change the menu names by setting the X resources font1",
203     "through font9.",
204     "",
205     "Choose a font color from the Font Color sub-menu.",
206     "Additional font colors can be specified with the color",
207     "browser.  You can change the menu colors by setting the X",
208     "resources pen1 through pen9.",
209     "",
210     "If you select the color browser and press Grab, you can",
211     "choose the font color by moving the pointer to the desired",
212     "color on the screen and press any button.",
213     "",
214     "If you choose to rotate the text, choose Rotate Text from the",
215     "menu and select an angle.  Typically you will only want to",
216     "rotate one line of text at a time.  Depending on the angle you",
217     "choose, subsequent lines may end up overwriting each other.",
218     "",
219     "Choosing a font and its color is optional.  The default font",
220     "is fixed and the default color is black.  However, you must",
221     "choose a location to begin entering text and press button 1.",
222     "An underscore character will appear at the location of the",
223     "pointer.  The cursor changes to a pencil to indicate you are",
224     "in text mode.  To exit immediately, press Dismiss.",
225     "",
226     "In text mode, any key presses will display the character at",
227     "the location of the underscore and advance the underscore",
228     "cursor.  Enter your text and once completed press Apply to",
229     "finish your image annotation.  To correct errors press BACK",
230     "SPACE.  To delete an entire line of text, press DELETE.  Any",
231     "text that exceeds the boundaries of the image window is",
232     "automagically continued onto the next line.",
233     "",
234     "The actual color you request for the font is saved in the",
235     "image.  However, the color that appears in your image window",
236     "may be different.  For example, on a monochrome screen the",
237     "text will appear black or white even if you choose the color",
238     "red as the font color.  However, the image saved to a file",
239     "with -write is written with red lettering.  To assure the",
240     "correct color text in the final image, any PseudoClass image",
241     "is promoted to DirectClass (see miff(5)).  To force a",
242     "PseudoClass image to remain PseudoClass, use -colors.",
243     (char *) NULL,
244   },
245   *ImageChopHelp[] =
246   {
247     "In chop mode, the Command widget has these options:",
248     "",
249     "    Direction",
250     "      horizontal",
251     "      vertical",
252     "    Help",
253     "    Dismiss",
254     "",
255     "If the you choose the horizontal direction (this the",
256     "default), the area of the image between the two horizontal",
257     "endpoints of the chop line is removed.  Otherwise, the area",
258     "of the image between the two vertical endpoints of the chop",
259     "line is removed.",
260     "",
261     "Select a location within the image window to begin your chop,",
262     "press and hold any button.  Next, move the pointer to",
263     "another location in the image.  As you move a line will",
264     "connect the initial location and the pointer.  When you",
265     "release the button, the area within the image to chop is",
266     "determined by which direction you choose from the Command",
267     "widget.",
268     "",
269     "To cancel the image chopping, move the pointer back to the",
270     "starting point of the line and release the button.",
271     (char *) NULL,
272   },
273   *ImageColorEditHelp[] =
274   {
275     "In color edit mode, the Command widget has these options:",
276     "",
277     "    Method",
278     "      point",
279     "      replace",
280     "      floodfill",
281     "      filltoborder",
282     "      reset",
283     "    Pixel Color",
284     "      black",
285     "      blue",
286     "      cyan",
287     "      green",
288     "      gray",
289     "      red",
290     "      magenta",
291     "      yellow",
292     "      white",
293     "      Browser...",
294     "    Border Color",
295     "      black",
296     "      blue",
297     "      cyan",
298     "      green",
299     "      gray",
300     "      red",
301     "      magenta",
302     "      yellow",
303     "      white",
304     "      Browser...",
305     "    Fuzz",
306     "      0%",
307     "      2%",
308     "      5%",
309     "      10%",
310     "      15%",
311     "      Dialog...",
312     "    Undo",
313     "    Help",
314     "    Dismiss",
315     "",
316     "Choose a color editing method from the Method sub-menu",
317     "of the Command widget.  The point method recolors any pixel",
318     "selected with the pointer until the button is released.  The",
319     "replace method recolors any pixel that matches the color of",
320     "the pixel you select with a button press.  Floodfill recolors",
321     "any pixel that matches the color of the pixel you select with",
322     "a button press and is a neighbor.  Whereas filltoborder recolors",
323     "any neighbor pixel that is not the border color.  Finally reset",
324     "changes the entire image to the designated color.",
325     "",
326     "Next, choose a pixel color from the Pixel Color sub-menu.",
327     "Additional pixel colors can be specified with the color",
328     "browser.  You can change the menu colors by setting the X",
329     "resources pen1 through pen9.",
330     "",
331     "Now press button 1 to select a pixel within the image window",
332     "to change its color.  Additional pixels may be recolored as",
333     "prescribed by the method you choose.",
334     "",
335     "If the Magnify widget is mapped, it can be helpful in positioning",
336     "your pointer within the image (refer to button 2).",
337     "",
338     "The actual color you request for the pixels is saved in the",
339     "image.  However, the color that appears in your image window",
340     "may be different.  For example, on a monochrome screen the",
341     "pixel will appear black or white even if you choose the",
342     "color red as the pixel color.  However, the image saved to a",
343     "file with -write is written with red pixels.  To assure the",
344     "correct color text in the final image, any PseudoClass image",
345     "is promoted to DirectClass (see miff(5)).  To force a",
346     "PseudoClass image to remain PseudoClass, use -colors.",
347     (char *) NULL,
348   },
349   *ImageCompositeHelp[] =
350   {
351     "First a widget window is displayed requesting you to enter an",
352     "image name. Press Composite, Grab or type a file name.",
353     "Press Cancel if you choose not to create a composite image.",
354     "When you choose Grab, move the pointer to the desired window",
355     "and press any button.",
356     "",
357     "If the Composite image does not have any matte information,",
358     "you are informed and the file browser is displayed again.",
359     "Enter the name of a mask image.  The image is typically",
360     "grayscale and the same size as the composite image.  If the",
361     "image is not grayscale, it is converted to grayscale and the",
362     "resulting intensities are used as matte information.",
363     "",
364     "A small window appears showing the location of the cursor in",
365     "the image window. You are now in composite mode.  To exit",
366     "immediately, press Dismiss.  In composite mode, the Command",
367     "widget has these options:",
368     "",
369     "    Operators",
370     "      Over",
371     "      In",
372     "      Out",
373     "      Atop",
374     "      Xor",
375     "      Plus",
376     "      Minus",
377     "      Add",
378     "      Subtract",
379     "      Difference",
380     "      Multiply",
381     "      Bumpmap",
382     "      Copy",
383     "      CopyRed",
384     "      CopyGreen",
385     "      CopyBlue",
386     "      CopyOpacity",
387     "      Clear",
388     "    Dissolve",
389     "    Displace",
390     "    Help",
391     "    Dismiss",
392     "",
393     "Choose a composite operation from the Operators sub-menu of",
394     "the Command widget.  How each operator behaves is described",
395     "below.  Image window is the image currently displayed on",
396     "your X server and image is the image obtained with the File",
397     "Browser widget.",
398     "",
399     "Over     The result is the union of the two image shapes,",
400     "         with image obscuring image window in the region of",
401     "         overlap.",
402     "",
403     "In       The result is simply image cut by the shape of",
404     "         image window.  None of the image data of image",
405     "         window is in the result.",
406     "",
407     "Out      The resulting image is image with the shape of",
408     "         image window cut out.",
409     "",
410     "Atop     The result is the same shape as image image window,",
411     "         with image obscuring image window where the image",
412     "         shapes overlap.  Note this differs from over",
413     "         because the portion of image outside image window's",
414     "         shape does not appear in the result.",
415     "",
416     "Xor      The result is the image data from both image and",
417     "         image window that is outside the overlap region.",
418     "         The overlap region is blank.",
419     "",
420     "Plus     The result is just the sum of the image data.",
421     "         Output values are cropped to QuantumRange (no overflow).",
422     "",
423     "Minus    The result of image - image window, with underflow",
424     "         cropped to zero.",
425     "",
426     "Add      The result of image + image window, with overflow",
427     "         wrapping around (mod 256).",
428     "",
429     "Subtract The result of image - image window, with underflow",
430     "         wrapping around (mod 256).  The add and subtract",
431     "         operators can be used to perform reversible",
432     "         transformations.",
433     "",
434     "Difference",
435     "         The result of abs(image - image window).  This",
436     "         useful for comparing two very similar images.",
437     "",
438     "Multiply",
439     "         The result of image * image window.  This",
440     "         useful for the creation of drop-shadows.",
441     "",
442     "Bumpmap  The result of surface normals from image * image",
443     "         window.",
444     "",
445     "Copy     The resulting image is image window replaced with",
446     "         image.  Here the matte information is ignored.",
447     "",
448     "CopyRed  The red layer of the image window is replace with",
449     "         the red layer of the image.  The other layers are",
450     "         untouched.",
451     "",
452     "CopyGreen",
453     "         The green layer of the image window is replace with",
454     "         the green layer of the image.  The other layers are",
455     "         untouched.",
456     "",
457     "CopyBlue The blue layer of the image window is replace with",
458     "         the blue layer of the image.  The other layers are",
459     "         untouched.",
460     "",
461     "CopyOpacity",
462     "         The matte layer of the image window is replace with",
463     "         the matte layer of the image.  The other layers are",
464     "         untouched.",
465     "",
466     "The image compositor requires a matte, or alpha channel in",
467     "the image for some operations.  This extra channel usually",
468     "defines a mask which represents a sort of a cookie-cutter",
469     "for the image.  This the case when matte is opaque (full",
470     "coverage) for pixels inside the shape, zero outside, and",
471     "between 0 and QuantumRange on the boundary.  If image does not",
472     "have a matte channel, it is initialized with 0 for any pixel",
473     "matching in color to pixel location (0,0), otherwise QuantumRange.",
474     "",
475     "If you choose Dissolve, the composite operator becomes Over.  The",
476     "image matte channel percent transparency is initialized to factor.",
477     "The image window is initialized to (100-factor). Where factor is the",
478     "value you specify in the Dialog widget.",
479     "",
480     "Displace shifts the image pixels as defined by a displacement",
481     "map.  With this option, image is used as a displacement map.",
482     "Black, within the displacement map, is a maximum positive",
483     "displacement.  White is a maximum negative displacement and",
484     "middle gray is neutral.  The displacement is scaled to determine",
485     "the pixel shift.  By default, the displacement applies in both the",
486     "horizontal and vertical directions.  However, if you specify a mask,",
487     "image is the horizontal X displacement and mask the vertical Y",
488     "displacement.",
489     "",
490     "Note that matte information for image window is not retained",
491     "for colormapped X server visuals (e.g. StaticColor,",
492     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
493     "behavior may require a TrueColor or DirectColor visual or a",
494     "Standard Colormap.",
495     "",
496     "Choosing a composite operator is optional.  The default",
497     "operator is replace.  However, you must choose a location to",
498     "composite your image and press button 1.  Press and hold the",
499     "button before releasing and an outline of the image will",
500     "appear to help you identify your location.",
501     "",
502     "The actual colors of the composite image is saved.  However,",
503     "the color that appears in image window may be different.",
504     "For example, on a monochrome screen image window will appear",
505     "black or white even though your composited image may have",
506     "many colors.  If the image is saved to a file it is written",
507     "with the correct colors.  To assure the correct colors are",
508     "saved in the final image, any PseudoClass image is promoted",
509     "to DirectClass (see miff(5)).  To force a PseudoClass image",
510     "to remain PseudoClass, use -colors.",
511     (char *) NULL,
512   },
513   *ImageCutHelp[] =
514   {
515     "In cut mode, the Command widget has these options:",
516     "",
517     "    Help",
518     "    Dismiss",
519     "",
520     "To define a cut region, press button 1 and drag.  The",
521     "cut region is defined by a highlighted rectangle that",
522     "expands or contracts as it follows the pointer.  Once you",
523     "are satisfied with the cut region, release the button.",
524     "You are now in rectify mode.  In rectify mode, the Command",
525     "widget has these options:",
526     "",
527     "    Cut",
528     "    Help",
529     "    Dismiss",
530     "",
531     "You can make adjustments by moving the pointer to one of the",
532     "cut rectangle corners, pressing a button, and dragging.",
533     "Finally, press Cut to commit your copy region.  To",
534     "exit without cutting the image, press Dismiss.",
535     (char *) NULL,
536   },
537   *ImageCopyHelp[] =
538   {
539     "In copy mode, the Command widget has these options:",
540     "",
541     "    Help",
542     "    Dismiss",
543     "",
544     "To define a copy region, press button 1 and drag.  The",
545     "copy region is defined by a highlighted rectangle that",
546     "expands or contracts as it follows the pointer.  Once you",
547     "are satisfied with the copy region, release the button.",
548     "You are now in rectify mode.  In rectify mode, the Command",
549     "widget has these options:",
550     "",
551     "    Copy",
552     "    Help",
553     "    Dismiss",
554     "",
555     "You can make adjustments by moving the pointer to one of the",
556     "copy rectangle corners, pressing a button, and dragging.",
557     "Finally, press Copy to commit your copy region.  To",
558     "exit without copying the image, press Dismiss.",
559     (char *) NULL,
560   },
561   *ImageCropHelp[] =
562   {
563     "In crop mode, the Command widget has these options:",
564     "",
565     "    Help",
566     "    Dismiss",
567     "",
568     "To define a cropping region, press button 1 and drag.  The",
569     "cropping region is defined by a highlighted rectangle that",
570     "expands or contracts as it follows the pointer.  Once you",
571     "are satisfied with the cropping region, release the button.",
572     "You are now in rectify mode.  In rectify mode, the Command",
573     "widget has these options:",
574     "",
575     "    Crop",
576     "    Help",
577     "    Dismiss",
578     "",
579     "You can make adjustments by moving the pointer to one of the",
580     "cropping rectangle corners, pressing a button, and dragging.",
581     "Finally, press Crop to commit your cropping region.  To",
582     "exit without cropping the image, press Dismiss.",
583     (char *) NULL,
584   },
585   *ImageDrawHelp[] =
586   {
587     "The cursor changes to a crosshair to indicate you are in",
588     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
589     "the Command widget has these options:",
590     "",
591     "    Element",
592     "      point",
593     "      line",
594     "      rectangle",
595     "      fill rectangle",
596     "      circle",
597     "      fill circle",
598     "      ellipse",
599     "      fill ellipse",
600     "      polygon",
601     "      fill polygon",
602     "    Color",
603     "      black",
604     "      blue",
605     "      cyan",
606     "      green",
607     "      gray",
608     "      red",
609     "      magenta",
610     "      yellow",
611     "      white",
612     "      transparent",
613     "      Browser...",
614     "    Stipple",
615     "      Brick",
616     "      Diagonal",
617     "      Scales",
618     "      Vertical",
619     "      Wavy",
620     "      Translucent",
621     "      Opaque",
622     "      Open...",
623     "    Width",
624     "      1",
625     "      2",
626     "      4",
627     "      8",
628     "      16",
629     "      Dialog...",
630     "    Undo",
631     "    Help",
632     "    Dismiss",
633     "",
634     "Choose a drawing primitive from the Element sub-menu.",
635     "",
636     "Choose a color from the Color sub-menu.  Additional",
637     "colors can be specified with the color browser.",
638     "",
639     "If you choose the color browser and press Grab, you can",
640     "select the color by moving the pointer to the desired",
641     "color on the screen and press any button.  The transparent",
642     "color updates the image matte channel and is useful for",
643     "image compositing.",
644     "",
645     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
646     "Additional stipples can be specified with the file browser.",
647     "Stipples obtained from the file browser must be on disk in the",
648     "X11 bitmap format.",
649     "",
650     "Choose a width, if appropriate, from the Width sub-menu.  To",
651     "choose a specific width select the Dialog widget.",
652     "",
653     "Choose a point in the Image window and press button 1 and",
654     "hold.  Next, move the pointer to another location in the",
655     "image.  As you move, a line connects the initial location and",
656     "the pointer.  When you release the button, the image is",
657     "updated with the primitive you just drew.  For polygons, the",
658     "image is updated when you press and release the button without",
659     "moving the pointer.",
660     "",
661     "To cancel image drawing, move the pointer back to the",
662     "starting point of the line and release the button.",
663     (char *) NULL,
664   },
665   *DisplayHelp[] =
666   {
667     "BUTTONS",
668     "  The effects of each button press is described below.  Three",
669     "  buttons are required.  If you have a two button mouse,",
670     "  button 1 and 3 are returned.  Press ALT and button 3 to",
671     "  simulate button 2.",
672     "",
673     "  1    Press this button to map or unmap the Command widget.",
674     "",
675     "  2    Press and drag to define a region of the image to",
676     "       magnify.",
677     "",
678     "  3    Press and drag to choose from a select set of commands.",
679     "       This button behaves differently if the image being",
680     "       displayed is a visual image directory.  Here, choose a",
681     "       particular tile of the directory and press this button and",
682     "       drag to select a command from a pop-up menu.  Choose from",
683     "       these menu items:",
684     "",
685     "           Open",
686     "           Next",
687     "           Former",
688     "           Delete",
689     "           Update",
690     "",
691     "       If you choose Open, the image represented by the tile is",
692     "       displayed.  To return to the visual image directory, choose",
693     "       Next from the Command widget.  Next and Former moves to the",
694     "       next or former image respectively.  Choose Delete to delete",
695     "       a particular image tile.  Finally, choose Update to",
696     "       synchronize all the image tiles with their respective",
697     "       images.",
698     "",
699     "COMMAND WIDGET",
700     "  The Command widget lists a number of sub-menus and commands.",
701     "  They are",
702     "",
703     "      File",
704     "        Open...",
705     "        Next",
706     "        Former",
707     "        Select...",
708     "        Save...",
709     "        Print...",
710     "        Delete...",
711     "        New...",
712     "        Visual Directory...",
713     "        Quit",
714     "      Edit",
715     "        Undo",
716     "        Redo",
717     "        Cut",
718     "        Copy",
719     "        Paste",
720     "      View",
721     "        Half Size",
722     "        Original Size",
723     "        Double Size",
724     "        Resize...",
725     "        Apply",
726     "        Refresh",
727     "        Restore",
728     "      Transform",
729     "        Crop",
730     "        Chop",
731     "        Flop",
732     "        Flip",
733     "        Rotate Right",
734     "        Rotate Left",
735     "        Rotate...",
736     "        Shear...",
737     "        Roll...",
738     "        Trim Edges",
739     "      Enhance",
740     "        Brightness...",
741     "        Saturation...",
742     "        Hue...",
743     "        Gamma...",
744     "        Sharpen...",
745     "        Dull",
746     "        Contrast Stretch...",
747     "        Sigmoidal Contrast...",
748     "        Normalize",
749     "        Equalize",
750     "        Negate",
751     "        Grayscale",
752     "        Map...",
753     "        Quantize...",
754     "      Effects",
755     "        Despeckle",
756     "        Emboss",
757     "        Reduce Noise",
758     "        Add Noise",
759     "        Sharpen...",
760     "        Blur...",
761     "        Threshold...",
762     "        Edge Detect...",
763     "        Spread...",
764     "        Shade...",
765     "        Painting...",
766     "        Segment...",
767     "      F/X",
768     "        Solarize...",
769     "        Sepia Tone...",
770     "        Swirl...",
771     "        Implode...",
772     "        Vignette...",
773     "        Wave...",
774     "        Oil Painting...",
775     "        Charcoal Drawing...",
776     "      Image Edit",
777     "        Annotate...",
778     "        Draw...",
779     "        Color...",
780     "        Matte...",
781     "        Composite...",
782     "        Add Border...",
783     "        Add Frame...",
784     "        Comment...",
785     "        Launch...",
786     "        Region of Interest...",
787     "      Miscellany",
788     "        Image Info",
789     "        Zoom Image",
790     "        Show Preview...",
791     "        Show Histogram",
792     "        Show Matte",
793     "        Background...",
794     "        Slide Show",
795     "        Preferences...",
796     "      Help",
797     "        Overview",
798     "        Browse Documentation",
799     "        About Display",
800     "",
801     "  Menu items with a indented triangle have a sub-menu.  They",
802     "  are represented above as the indented items.  To access a",
803     "  sub-menu item, move the pointer to the appropriate menu and",
804     "  press a button and drag.  When you find the desired sub-menu",
805     "  item, release the button and the command is executed.  Move",
806     "  the pointer away from the sub-menu if you decide not to",
807     "  execute a particular command.",
808     "",
809     "KEYBOARD ACCELERATORS",
810     "  Accelerators are one or two key presses that effect a",
811     "  particular command.  The keyboard accelerators that",
812     "  display(1) understands is:",
813     "",
814     "  Ctl+O     Press to open an image from a file.",
815     "",
816     "  space     Press to display the next image.",
817     "",
818     "            If the image is a multi-paged document such as a Postscript",
819     "            document, you can skip ahead several pages by preceding",
820     "            this command with a number.  For example to display the",
821     "            third page beyond the current page, press 3<space>.",
822     "",
823     "  backspace Press to display the former image.",
824     "",
825     "            If the image is a multi-paged document such as a Postscript",
826     "            document, you can skip behind several pages by preceding",
827     "            this command with a number.  For example to display the",
828     "            third page preceding the current page, press 3<backspace>.",
829     "",
830     "  Ctl+S     Press to write the image to a file.",
831     "",
832     "  Ctl+P     Press to print the image to a Postscript printer.",
833     "",
834     "  Ctl+D     Press to delete an image file.",
835     "",
836     "  Ctl+N     Press to create a blank canvas.",
837     "",
838     "  Ctl+Q     Press to discard all images and exit program.",
839     "",
840     "  Ctl+Z     Press to undo last image transformation.",
841     "",
842     "  Ctl+R     Press to redo last image transformation.",
843     "",
844     "  Ctl+X     Press to cut a region of the image.",
845     "",
846     "  Ctl+C     Press to copy a region of the image.",
847     "",
848     "  Ctl+V     Press to paste a region to the image.",
849     "",
850     "  <         Press to half the image size.",
851     "",
852     "  -         Press to return to the original image size.",
853     "",
854     "  >         Press to double the image size.",
855     "",
856     "  %         Press to resize the image to a width and height you",
857     "            specify.",
858     "",
859     "Cmd-A       Press to make any image transformations permanent."
860     "",
861     "            By default, any image size transformations are applied",
862     "            to the original image to create the image displayed on",
863     "            the X server.  However, the transformations are not",
864     "            permanent (i.e. the original image does not change",
865     "            size only the X image does).  For example, if you",
866     "            press > the X image will appear to double in size,",
867     "            but the original image will in fact remain the same size.",
868     "            To force the original image to double in size, press >",
869     "            followed by Cmd-A.",
870     "",
871     "  @         Press to refresh the image window.",
872     "",
873     "  C         Press to cut out a rectangular region of the image.",
874     "",
875     "  [         Press to chop the image.",
876     "",
877     "  H         Press to flop image in the horizontal direction.",
878     "",
879     "  V         Press to flip image in the vertical direction.",
880     "",
881     "  /         Press to rotate the image 90 degrees clockwise.",
882     "",
883     " \\         Press to rotate the image 90 degrees counter-clockwise.",
884     "",
885     "  *         Press to rotate the image the number of degrees you",
886     "            specify.",
887     "",
888     "  S         Press to shear the image the number of degrees you",
889     "            specify.",
890     "",
891     "  R         Press to roll the image.",
892     "",
893     "  T         Press to trim the image edges.",
894     "",
895     "  Shft-H    Press to vary the image hue.",
896     "",
897     "  Shft-S    Press to vary the color saturation.",
898     "",
899     "  Shft-L    Press to vary the color brightness.",
900     "",
901     "  Shft-G    Press to gamma correct the image.",
902     "",
903     "  Shft-C    Press to sharpen the image contrast.",
904     "",
905     "  Shft-Z    Press to dull the image contrast.",
906     "",
907     "  =         Press to perform histogram equalization on the image.",
908     "",
909     "  Shft-N    Press to perform histogram normalization on the image.",
910     "",
911     "  Shft-~    Press to negate the colors of the image.",
912     "",
913     "  .         Press to convert the image colors to gray.",
914     "",
915     "  Shft-#    Press to set the maximum number of unique colors in the",
916     "            image.",
917     "",
918     "  F2        Press to reduce the speckles in an image.",
919     "",
920     "  F3        Press to eliminate peak noise from an image.",
921     "",
922     "  F4        Press to add noise to an image.",
923     "",
924     "  F5        Press to sharpen an image.",
925     "",
926     "  F6        Press to delete an image file.",
927     "",
928     "  F7        Press to threshold the image.",
929     "",
930     "  F8        Press to detect edges within an image.",
931     "",
932     "  F9        Press to emboss an image.",
933     "",
934     "  F10       Press to displace pixels by a random amount.",
935     "",
936     "  F11       Press to negate all pixels above the threshold level.",
937     "",
938     "  F12       Press to shade the image using a distant light source.",
939     "",
940     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
941     "",
942     "  F14       Press to segment the image by color.",
943     "",
944     "  Meta-S    Press to swirl image pixels about the center.",
945     "",
946     "  Meta-I    Press to implode image pixels about the center.",
947     "",
948     "  Meta-W    Press to alter an image along a sine wave.",
949     "",
950     "  Meta-P    Press to simulate an oil painting.",
951     "",
952     "  Meta-C    Press to simulate a charcoal drawing.",
953     "",
954     "  Alt-A     Press to annotate the image with text.",
955     "",
956     "  Alt-D     Press to draw on an image.",
957     "",
958     "  Alt-P     Press to edit an image pixel color.",
959     "",
960     "  Alt-M     Press to edit the image matte information.",
961     "",
962     "  Alt-V     Press to composite the image with another.",
963     "",
964     "  Alt-B     Press to add a border to the image.",
965     "",
966     "  Alt-F     Press to add an ornamental border to the image.",
967     "",
968     "  Alt-Shft-!",
969     "            Press to add an image comment.",
970     "",
971     "  Ctl-A     Press to apply image processing techniques to a region",
972     "            of interest.",
973     "",
974     "  Shft-?    Press to display information about the image.",
975     "",
976     "  Shft-+    Press to map the zoom image window.",
977     "",
978     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
979     "",
980     "  F1        Press to display helpful information about display(1).",
981     "",
982     "  Find      Press to browse documentation about ImageMagick.",
983     "",
984     "  1-9       Press to change the level of magnification.",
985     "",
986     "  Use the arrow keys to move the image one pixel up, down,",
987     "  left, or right within the magnify window.  Be sure to first",
988     "  map the magnify window by pressing button 2.",
989     "",
990     "  Press ALT and one of the arrow keys to trim off one pixel",
991     "  from any side of the image.",
992     (char *) NULL,
993   },
994   *ImageMatteEditHelp[] =
995   {
996     "Matte information within an image is useful for some",
997     "operations such as image compositing (See IMAGE",
998     "COMPOSITING).  This extra channel usually defines a mask",
999     "which represents a sort of a cookie-cutter for the image.",
1000     "This the case when matte is opaque (full coverage) for",
1001     "pixels inside the shape, zero outside, and between 0 and",
1002     "QuantumRange on the boundary.",
1003     "",
1004     "A small window appears showing the location of the cursor in",
1005     "the image window. You are now in matte edit mode.  To exit",
1006     "immediately, press Dismiss.  In matte edit mode, the Command",
1007     "widget has these options:",
1008     "",
1009     "    Method",
1010     "      point",
1011     "      replace",
1012     "      floodfill",
1013     "      filltoborder",
1014     "      reset",
1015     "    Border Color",
1016     "      black",
1017     "      blue",
1018     "      cyan",
1019     "      green",
1020     "      gray",
1021     "      red",
1022     "      magenta",
1023     "      yellow",
1024     "      white",
1025     "      Browser...",
1026     "    Fuzz",
1027     "      0%",
1028     "      2%",
1029     "      5%",
1030     "      10%",
1031     "      15%",
1032     "      Dialog...",
1033     "    Matte",
1034     "      Opaque",
1035     "      Transparent",
1036     "      Dialog...",
1037     "    Undo",
1038     "    Help",
1039     "    Dismiss",
1040     "",
1041     "Choose a matte editing method from the Method sub-menu of",
1042     "the Command widget.  The point method changes the matte value",
1043     "of any pixel selected with the pointer until the button is",
1044     "is released.  The replace method changes the matte value of",
1045     "any pixel that matches the color of the pixel you select with",
1046     "a button press.  Floodfill changes the matte value of any pixel",
1047     "that matches the color of the pixel you select with a button",
1048     "press and is a neighbor.  Whereas filltoborder changes the matte",
1049     "value any neighbor pixel that is not the border color.  Finally",
1050     "reset changes the entire image to the designated matte value.",
1051     "",
1052     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1053     "select the Dialog entry.  Here a dialog appears requesting a matte",
1054     "value.  The value you select is assigned as the opacity value of the",
1055     "selected pixel or pixels.",
1056     "",
1057     "Now, press any button to select a pixel within the image",
1058     "window to change its matte value.",
1059     "",
1060     "If the Magnify widget is mapped, it can be helpful in positioning",
1061     "your pointer within the image (refer to button 2).",
1062     "",
1063     "Matte information is only valid in a DirectClass image.",
1064     "Therefore, any PseudoClass image is promoted to DirectClass",
1065     "(see miff(5)).  Note that matte information for PseudoClass",
1066     "is not retained for colormapped X server visuals (e.g.",
1067     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1068     "immediately save your image to a file (refer to Write).",
1069     "Correct matte editing behavior may require a TrueColor or",
1070     "DirectColor visual or a Standard Colormap.",
1071     (char *) NULL,
1072   },
1073   *ImagePanHelp[] =
1074   {
1075     "When an image exceeds the width or height of the X server",
1076     "screen, display maps a small panning icon.  The rectangle",
1077     "within the panning icon shows the area that is currently",
1078     "displayed in the image window.  To pan about the image,",
1079     "press any button and drag the pointer within the panning",
1080     "icon.  The pan rectangle moves with the pointer and the",
1081     "image window is updated to reflect the location of the",
1082     "rectangle within the panning icon.  When you have selected",
1083     "the area of the image you wish to view, release the button.",
1084     "",
1085     "Use the arrow keys to pan the image one pixel up, down,",
1086     "left, or right within the image window.",
1087     "",
1088     "The panning icon is withdrawn if the image becomes smaller",
1089     "than the dimensions of the X server screen.",
1090     (char *) NULL,
1091   },
1092   *ImagePasteHelp[] =
1093   {
1094     "A small window appears showing the location of the cursor in",
1095     "the image window. You are now in paste mode.  To exit",
1096     "immediately, press Dismiss.  In paste mode, the Command",
1097     "widget has these options:",
1098     "",
1099     "    Operators",
1100     "      over",
1101     "      in",
1102     "      out",
1103     "      atop",
1104     "      xor",
1105     "      plus",
1106     "      minus",
1107     "      add",
1108     "      subtract",
1109     "      difference",
1110     "      replace",
1111     "    Help",
1112     "    Dismiss",
1113     "",
1114     "Choose a composite operation from the Operators sub-menu of",
1115     "the Command widget.  How each operator behaves is described",
1116     "below.  Image window is the image currently displayed on",
1117     "your X server and image is the image obtained with the File",
1118     "Browser widget.",
1119     "",
1120     "Over     The result is the union of the two image shapes,",
1121     "         with image obscuring image window in the region of",
1122     "         overlap.",
1123     "",
1124     "In       The result is simply image cut by the shape of",
1125     "         image window.  None of the image data of image",
1126     "         window is in the result.",
1127     "",
1128     "Out      The resulting image is image with the shape of",
1129     "         image window cut out.",
1130     "",
1131     "Atop     The result is the same shape as image image window,",
1132     "         with image obscuring image window where the image",
1133     "         shapes overlap.  Note this differs from over",
1134     "         because the portion of image outside image window's",
1135     "         shape does not appear in the result.",
1136     "",
1137     "Xor      The result is the image data from both image and",
1138     "         image window that is outside the overlap region.",
1139     "         The overlap region is blank.",
1140     "",
1141     "Plus     The result is just the sum of the image data.",
1142     "         Output values are cropped to QuantumRange (no overflow).",
1143     "         This operation is independent of the matte",
1144     "         channels.",
1145     "",
1146     "Minus    The result of image - image window, with underflow",
1147     "         cropped to zero.",
1148     "",
1149     "Add      The result of image + image window, with overflow",
1150     "         wrapping around (mod 256).",
1151     "",
1152     "Subtract The result of image - image window, with underflow",
1153     "         wrapping around (mod 256).  The add and subtract",
1154     "         operators can be used to perform reversible",
1155     "         transformations.",
1156     "",
1157     "Difference",
1158     "         The result of abs(image - image window).  This",
1159     "         useful for comparing two very similar images.",
1160     "",
1161     "Copy     The resulting image is image window replaced with",
1162     "         image.  Here the matte information is ignored.",
1163     "",
1164     "CopyRed  The red layer of the image window is replace with",
1165     "         the red layer of the image.  The other layers are",
1166     "         untouched.",
1167     "",
1168     "CopyGreen",
1169     "         The green layer of the image window is replace with",
1170     "         the green layer of the image.  The other layers are",
1171     "         untouched.",
1172     "",
1173     "CopyBlue The blue layer of the image window is replace with",
1174     "         the blue layer of the image.  The other layers are",
1175     "         untouched.",
1176     "",
1177     "CopyOpacity",
1178     "         The matte layer of the image window is replace with",
1179     "         the matte layer of the image.  The other layers are",
1180     "         untouched.",
1181     "",
1182     "The image compositor requires a matte, or alpha channel in",
1183     "the image for some operations.  This extra channel usually",
1184     "defines a mask which represents a sort of a cookie-cutter",
1185     "for the image.  This the case when matte is opaque (full",
1186     "coverage) for pixels inside the shape, zero outside, and",
1187     "between 0 and QuantumRange on the boundary.  If image does not",
1188     "have a matte channel, it is initialized with 0 for any pixel",
1189     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1190     "",
1191     "Note that matte information for image window is not retained",
1192     "for colormapped X server visuals (e.g. StaticColor,",
1193     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1194     "behavior may require a TrueColor or DirectColor visual or a",
1195     "Standard Colormap.",
1196     "",
1197     "Choosing a composite operator is optional.  The default",
1198     "operator is replace.  However, you must choose a location to",
1199     "paste your image and press button 1.  Press and hold the",
1200     "button before releasing and an outline of the image will",
1201     "appear to help you identify your location.",
1202     "",
1203     "The actual colors of the pasted image is saved.  However,",
1204     "the color that appears in image window may be different.",
1205     "For example, on a monochrome screen image window will appear",
1206     "black or white even though your pasted image may have",
1207     "many colors.  If the image is saved to a file it is written",
1208     "with the correct colors.  To assure the correct colors are",
1209     "saved in the final image, any PseudoClass image is promoted",
1210     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1211     "to remain PseudoClass, use -colors.",
1212     (char *) NULL,
1213   },
1214   *ImageROIHelp[] =
1215   {
1216     "In region of interest mode, the Command widget has these",
1217     "options:",
1218     "",
1219     "    Help",
1220     "    Dismiss",
1221     "",
1222     "To define a region of interest, press button 1 and drag.",
1223     "The region of interest is defined by a highlighted rectangle",
1224     "that expands or contracts as it follows the pointer.  Once",
1225     "you are satisfied with the region of interest, release the",
1226     "button.  You are now in apply mode.  In apply mode the",
1227     "Command widget has these options:",
1228     "",
1229     "      File",
1230     "        Save...",
1231     "        Print...",
1232     "      Edit",
1233     "        Undo",
1234     "        Redo",
1235     "      Transform",
1236     "        Flop",
1237     "        Flip",
1238     "        Rotate Right",
1239     "        Rotate Left",
1240     "      Enhance",
1241     "        Hue...",
1242     "        Saturation...",
1243     "        Brightness...",
1244     "        Gamma...",
1245     "        Spiff",
1246     "        Dull",
1247     "        Contrast Stretch",
1248     "        Sigmoidal Contrast...",
1249     "        Normalize",
1250     "        Equalize",
1251     "        Negate",
1252     "        Grayscale",
1253     "        Map...",
1254     "        Quantize...",
1255     "      Effects",
1256     "        Despeckle",
1257     "        Emboss",
1258     "        Reduce Noise",
1259     "        Sharpen...",
1260     "        Blur...",
1261     "        Threshold...",
1262     "        Edge Detect...",
1263     "        Spread...",
1264     "        Shade...",
1265     "        Raise...",
1266     "        Segment...",
1267     "      F/X",
1268     "        Solarize...",
1269     "        Sepia Tone...",
1270     "        Swirl...",
1271     "        Implode...",
1272     "        Vignette...",
1273     "        Wave...",
1274     "        Oil Painting...",
1275     "        Charcoal Drawing...",
1276     "      Miscellany",
1277     "        Image Info",
1278     "        Zoom Image",
1279     "        Show Preview...",
1280     "        Show Histogram",
1281     "        Show Matte",
1282     "      Help",
1283     "      Dismiss",
1284     "",
1285     "You can make adjustments to the region of interest by moving",
1286     "the pointer to one of the rectangle corners, pressing a",
1287     "button, and dragging.  Finally, choose an image processing",
1288     "technique from the Command widget.  You can choose more than",
1289     "one image processing technique to apply to an area.",
1290     "Alternatively, you can move the region of interest before",
1291     "applying another image processing technique.  To exit, press",
1292     "Dismiss.",
1293     (char *) NULL,
1294   },
1295   *ImageRotateHelp[] =
1296   {
1297     "In rotate mode, the Command widget has these options:",
1298     "",
1299     "    Pixel Color",
1300     "      black",
1301     "      blue",
1302     "      cyan",
1303     "      green",
1304     "      gray",
1305     "      red",
1306     "      magenta",
1307     "      yellow",
1308     "      white",
1309     "      Browser...",
1310     "    Direction",
1311     "      horizontal",
1312     "      vertical",
1313     "    Help",
1314     "    Dismiss",
1315     "",
1316     "Choose a background color from the Pixel Color sub-menu.",
1317     "Additional background colors can be specified with the color",
1318     "browser.  You can change the menu colors by setting the X",
1319     "resources pen1 through pen9.",
1320     "",
1321     "If you choose the color browser and press Grab, you can",
1322     "select the background color by moving the pointer to the",
1323     "desired color on the screen and press any button.",
1324     "",
1325     "Choose a point in the image window and press this button and",
1326     "hold.  Next, move the pointer to another location in the",
1327     "image.  As you move a line connects the initial location and",
1328     "the pointer.  When you release the button, the degree of",
1329     "image rotation is determined by the slope of the line you",
1330     "just drew.  The slope is relative to the direction you",
1331     "choose from the Direction sub-menu of the Command widget.",
1332     "",
1333     "To cancel the image rotation, move the pointer back to the",
1334     "starting point of the line and release the button.",
1335     (char *) NULL,
1336   };
1337 \f
1338 /*
1339   Enumeration declarations.
1340 */
1341 typedef enum
1342 {
1343   CopyMode,
1344   CropMode,
1345   CutMode
1346 } ClipboardMode;
1347
1348 typedef enum
1349 {
1350   OpenCommand,
1351   NextCommand,
1352   FormerCommand,
1353   SelectCommand,
1354   SaveCommand,
1355   PrintCommand,
1356   DeleteCommand,
1357   NewCommand,
1358   VisualDirectoryCommand,
1359   QuitCommand,
1360   UndoCommand,
1361   RedoCommand,
1362   CutCommand,
1363   CopyCommand,
1364   PasteCommand,
1365   HalfSizeCommand,
1366   OriginalSizeCommand,
1367   DoubleSizeCommand,
1368   ResizeCommand,
1369   ApplyCommand,
1370   RefreshCommand,
1371   RestoreCommand,
1372   CropCommand,
1373   ChopCommand,
1374   FlopCommand,
1375   FlipCommand,
1376   RotateRightCommand,
1377   RotateLeftCommand,
1378   RotateCommand,
1379   ShearCommand,
1380   RollCommand,
1381   TrimCommand,
1382   HueCommand,
1383   SaturationCommand,
1384   BrightnessCommand,
1385   GammaCommand,
1386   SpiffCommand,
1387   DullCommand,
1388   ContrastStretchCommand,
1389   SigmoidalContrastCommand,
1390   NormalizeCommand,
1391   EqualizeCommand,
1392   NegateCommand,
1393   GrayscaleCommand,
1394   MapCommand,
1395   QuantizeCommand,
1396   DespeckleCommand,
1397   EmbossCommand,
1398   ReduceNoiseCommand,
1399   AddNoiseCommand,
1400   SharpenCommand,
1401   BlurCommand,
1402   ThresholdCommand,
1403   EdgeDetectCommand,
1404   SpreadCommand,
1405   ShadeCommand,
1406   RaiseCommand,
1407   SegmentCommand,
1408   SolarizeCommand,
1409   SepiaToneCommand,
1410   SwirlCommand,
1411   ImplodeCommand,
1412   VignetteCommand,
1413   WaveCommand,
1414   OilPaintCommand,
1415   CharcoalDrawCommand,
1416   AnnotateCommand,
1417   DrawCommand,
1418   ColorCommand,
1419   MatteCommand,
1420   CompositeCommand,
1421   AddBorderCommand,
1422   AddFrameCommand,
1423   CommentCommand,
1424   LaunchCommand,
1425   RegionofInterestCommand,
1426   ROIHelpCommand,
1427   ROIDismissCommand,
1428   InfoCommand,
1429   ZoomCommand,
1430   ShowPreviewCommand,
1431   ShowHistogramCommand,
1432   ShowMatteCommand,
1433   BackgroundCommand,
1434   SlideShowCommand,
1435   PreferencesCommand,
1436   HelpCommand,
1437   BrowseDocumentationCommand,
1438   VersionCommand,
1439   SaveToUndoBufferCommand,
1440   FreeBuffersCommand,
1441   NullCommand
1442 } CommandType;
1443
1444 typedef enum
1445 {
1446   AnnotateNameCommand,
1447   AnnotateFontColorCommand,
1448   AnnotateBackgroundColorCommand,
1449   AnnotateRotateCommand,
1450   AnnotateHelpCommand,
1451   AnnotateDismissCommand,
1452   TextHelpCommand,
1453   TextApplyCommand,
1454   ChopDirectionCommand,
1455   ChopHelpCommand,
1456   ChopDismissCommand,
1457   HorizontalChopCommand,
1458   VerticalChopCommand,
1459   ColorEditMethodCommand,
1460   ColorEditColorCommand,
1461   ColorEditBorderCommand,
1462   ColorEditFuzzCommand,
1463   ColorEditUndoCommand,
1464   ColorEditHelpCommand,
1465   ColorEditDismissCommand,
1466   CompositeOperatorsCommand,
1467   CompositeDissolveCommand,
1468   CompositeDisplaceCommand,
1469   CompositeHelpCommand,
1470   CompositeDismissCommand,
1471   CropHelpCommand,
1472   CropDismissCommand,
1473   RectifyCopyCommand,
1474   RectifyHelpCommand,
1475   RectifyDismissCommand,
1476   DrawElementCommand,
1477   DrawColorCommand,
1478   DrawStippleCommand,
1479   DrawWidthCommand,
1480   DrawUndoCommand,
1481   DrawHelpCommand,
1482   DrawDismissCommand,
1483   MatteEditMethod,
1484   MatteEditBorderCommand,
1485   MatteEditFuzzCommand,
1486   MatteEditValueCommand,
1487   MatteEditUndoCommand,
1488   MatteEditHelpCommand,
1489   MatteEditDismissCommand,
1490   PasteOperatorsCommand,
1491   PasteHelpCommand,
1492   PasteDismissCommand,
1493   RotateColorCommand,
1494   RotateDirectionCommand,
1495   RotateCropCommand,
1496   RotateSharpenCommand,
1497   RotateHelpCommand,
1498   RotateDismissCommand,
1499   HorizontalRotateCommand,
1500   VerticalRotateCommand,
1501   TileLoadCommand,
1502   TileNextCommand,
1503   TileFormerCommand,
1504   TileDeleteCommand,
1505   TileUpdateCommand
1506 } ModeType;
1507 \f
1508 /*
1509   Stipples.
1510 */
1511 #define BricksWidth  20
1512 #define BricksHeight  20
1513 #define DiagonalWidth  16
1514 #define DiagonalHeight  16
1515 #define HighlightWidth  8
1516 #define HighlightHeight  8
1517 #define OpaqueWidth  8
1518 #define OpaqueHeight  8
1519 #define ScalesWidth  16
1520 #define ScalesHeight  16
1521 #define ShadowWidth  8
1522 #define ShadowHeight  8
1523 #define VerticalWidth  16
1524 #define VerticalHeight  16
1525 #define WavyWidth  16
1526 #define WavyHeight  16
1527 \f
1528 /*
1529   Constant declaration.
1530 */
1531 static const int
1532   RoiDelta = 8;
1533
1534 static const unsigned char
1535   BricksBitmap[] =
1536   {
1537     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1538     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1539     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1540     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1541     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1542   },
1543   DiagonalBitmap[] =
1544   {
1545     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1546     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1547     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1548   },
1549   ScalesBitmap[] =
1550   {
1551     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1552     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1553     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1554   },
1555   VerticalBitmap[] =
1556   {
1557     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1558     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1559     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1560   },
1561   WavyBitmap[] =
1562   {
1563     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1564     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1565     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1566   };
1567 \f
1568 /*
1569   Function prototypes.
1570 */
1571 static CommandType
1572   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1573     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1574
1575 static Image
1576   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1577     Image **,ExceptionInfo *),
1578   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1579   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1580     ExceptionInfo *),
1581   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1582     ExceptionInfo *);
1583
1584 static MagickBooleanType
1585   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1586     ExceptionInfo *),
1587   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1588     ExceptionInfo *),
1589   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1590     ExceptionInfo *),
1591   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1592     ExceptionInfo *),
1593   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1594     ExceptionInfo *),
1595   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1596     ExceptionInfo *),
1597   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1599     ExceptionInfo *),
1600   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1601     ExceptionInfo *),
1602   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1603   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1604   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1605     ExceptionInfo *),
1606   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1607   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1608   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1609
1610 static void
1611   XDrawPanRectangle(Display *,XWindows *),
1612   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1613     ExceptionInfo *),
1614   XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1615   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1616   XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1617   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1618     const KeySym,ExceptionInfo *),
1619   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1620   XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1621   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1622 \f
1623 /*
1624 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1625 %                                                                             %
1626 %                                                                             %
1627 %                                                                             %
1628 %   D i s p l a y I m a g e s                                                 %
1629 %                                                                             %
1630 %                                                                             %
1631 %                                                                             %
1632 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1633 %
1634 %  DisplayImages() displays an image sequence to any X window screen.  It
1635 %  returns a value other than 0 if successful.  Check the exception member
1636 %  of image to determine the reason for any failure.
1637 %
1638 %  The format of the DisplayImages method is:
1639 %
1640 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1641 %        Image *images,ExceptionInfo *exception)
1642 %
1643 %  A description of each parameter follows:
1644 %
1645 %    o image_info: the image info.
1646 %
1647 %    o image: the image.
1648 %
1649 %    o exception: return any errors or warnings in this structure.
1650 %
1651 */
1652 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1653   Image *images,ExceptionInfo *exception)
1654 {
1655   char
1656     *argv[1];
1657
1658   Display
1659     *display;
1660
1661   Image
1662     *image;
1663
1664   register ssize_t
1665     i;
1666
1667   size_t
1668     state;
1669
1670   XrmDatabase
1671     resource_database;
1672
1673   XResourceInfo
1674     resource_info;
1675
1676   assert(image_info != (const ImageInfo *) NULL);
1677   assert(image_info->signature == MagickSignature);
1678   assert(images != (Image *) NULL);
1679   assert(images->signature == MagickSignature);
1680   if (IfMagickTrue(images->debug) )
1681     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1682   display=XOpenDisplay(image_info->server_name);
1683   if (display == (Display *) NULL)
1684     {
1685       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1686         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1687       return(MagickFalse);
1688     }
1689   if (exception->severity != UndefinedException)
1690     CatchException(exception);
1691   (void) XSetErrorHandler(XError);
1692   resource_database=XGetResourceDatabase(display,GetClientName());
1693   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1694   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1695   if (image_info->page != (char *) NULL)
1696     resource_info.image_geometry=AcquireString(image_info->page);
1697   resource_info.immutable=MagickTrue;
1698   argv[0]=AcquireString(GetClientName());
1699   state=DefaultState;
1700   for (i=0; (state & ExitState) == 0; i++)
1701   {
1702     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1703       break;
1704     image=GetImageFromList(images,i % GetImageListLength(images));
1705     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1706   }
1707   (void) SetErrorHandler((ErrorHandler) NULL);
1708   (void) SetWarningHandler((WarningHandler) NULL);
1709   argv[0]=DestroyString(argv[0]);
1710   (void) XCloseDisplay(display);
1711   XDestroyResourceInfo(&resource_info);
1712   if (exception->severity != UndefinedException)
1713     return(MagickFalse);
1714   return(MagickTrue);
1715 }
1716 \f
1717 /*
1718 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1719 %                                                                             %
1720 %                                                                             %
1721 %                                                                             %
1722 %   R e m o t e D i s p l a y C o m m a n d                                   %
1723 %                                                                             %
1724 %                                                                             %
1725 %                                                                             %
1726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1727 %
1728 %  RemoteDisplayCommand() encourages a remote display program to display the
1729 %  specified image filename.
1730 %
1731 %  The format of the RemoteDisplayCommand method is:
1732 %
1733 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1734 %        const char *window,const char *filename,ExceptionInfo *exception)
1735 %
1736 %  A description of each parameter follows:
1737 %
1738 %    o image_info: the image info.
1739 %
1740 %    o window: Specifies the name or id of an X window.
1741 %
1742 %    o filename: the name of the image filename to display.
1743 %
1744 %    o exception: return any errors or warnings in this structure.
1745 %
1746 */
1747 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1748   const char *window,const char *filename,ExceptionInfo *exception)
1749 {
1750   Display
1751     *display;
1752
1753   MagickStatusType
1754     status;
1755
1756   assert(image_info != (const ImageInfo *) NULL);
1757   assert(image_info->signature == MagickSignature);
1758   assert(filename != (char *) NULL);
1759   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1760   display=XOpenDisplay(image_info->server_name);
1761   if (display == (Display *) NULL)
1762     {
1763       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1764         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1765       return(MagickFalse);
1766     }
1767   (void) XSetErrorHandler(XError);
1768   status=XRemoteCommand(display,window,filename);
1769   (void) XCloseDisplay(display);
1770   return(IsMagickTrue(status));
1771 }
1772 \f
1773 /*
1774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1775 %                                                                             %
1776 %                                                                             %
1777 %                                                                             %
1778 +   X A n n o t a t e E d i t I m a g e                                       %
1779 %                                                                             %
1780 %                                                                             %
1781 %                                                                             %
1782 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1783 %
1784 %  XAnnotateEditImage() annotates the image with text.
1785 %
1786 %  The format of the XAnnotateEditImage method is:
1787 %
1788 %      MagickBooleanType XAnnotateEditImage(Display *display,
1789 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1790 %        ExceptionInfo *exception)
1791 %
1792 %  A description of each parameter follows:
1793 %
1794 %    o display: Specifies a connection to an X server;  returned from
1795 %      XOpenDisplay.
1796 %
1797 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1798 %
1799 %    o windows: Specifies a pointer to a XWindows structure.
1800 %
1801 %    o image: the image; returned from ReadImage.
1802 %
1803 */
1804
1805 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1806 {
1807   if (x > y)
1808     return(x);
1809   return(y);
1810 }
1811
1812 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1813 {
1814   if (x < y)
1815     return(x);
1816   return(y);
1817 }
1818
1819 static MagickBooleanType XAnnotateEditImage(Display *display,
1820   XResourceInfo *resource_info,XWindows *windows,Image *image,
1821   ExceptionInfo *exception)
1822 {
1823   static const char
1824     *AnnotateMenu[] =
1825     {
1826       "Font Name",
1827       "Font Color",
1828       "Box Color",
1829       "Rotate Text",
1830       "Help",
1831       "Dismiss",
1832       (char *) NULL
1833     },
1834     *TextMenu[] =
1835     {
1836       "Help",
1837       "Apply",
1838       (char *) NULL
1839     };
1840
1841   static const ModeType
1842     AnnotateCommands[] =
1843     {
1844       AnnotateNameCommand,
1845       AnnotateFontColorCommand,
1846       AnnotateBackgroundColorCommand,
1847       AnnotateRotateCommand,
1848       AnnotateHelpCommand,
1849       AnnotateDismissCommand
1850     },
1851     TextCommands[] =
1852     {
1853       TextHelpCommand,
1854       TextApplyCommand
1855     };
1856
1857   static MagickBooleanType
1858     transparent_box = MagickTrue,
1859     transparent_pen = MagickFalse;
1860
1861   static double
1862     degrees = 0.0;
1863
1864   static unsigned int
1865     box_id = MaxNumberPens-2,
1866     font_id = 0,
1867     pen_id = 0;
1868
1869   char
1870     command[MaxTextExtent],
1871     text[MaxTextExtent];
1872
1873   const char
1874     *ColorMenu[MaxNumberPens+1];
1875
1876   Cursor
1877     cursor;
1878
1879   GC
1880     annotate_context;
1881
1882   int
1883     id,
1884     pen_number,
1885     status,
1886     x,
1887     y;
1888
1889   KeySym
1890     key_symbol;
1891
1892   register char
1893     *p;
1894
1895   register ssize_t
1896     i;
1897
1898   unsigned int
1899     height,
1900     width;
1901
1902   size_t
1903     state;
1904
1905   XAnnotateInfo
1906     *annotate_info,
1907     *previous_info;
1908
1909   XColor
1910     color;
1911
1912   XFontStruct
1913     *font_info;
1914
1915   XEvent
1916     event,
1917     text_event;
1918
1919   /*
1920     Map Command widget.
1921   */
1922   (void) CloneString(&windows->command.name,"Annotate");
1923   windows->command.data=4;
1924   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1925   (void) XMapRaised(display,windows->command.id);
1926   XClientMessage(display,windows->image.id,windows->im_protocols,
1927     windows->im_update_widget,CurrentTime);
1928   /*
1929     Track pointer until button 1 is pressed.
1930   */
1931   XQueryPosition(display,windows->image.id,&x,&y);
1932   (void) XSelectInput(display,windows->image.id,
1933     windows->image.attributes.event_mask | PointerMotionMask);
1934   cursor=XCreateFontCursor(display,XC_left_side);
1935   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1936   state=DefaultState;
1937   do
1938   {
1939     if (IfMagickTrue(windows->info.mapped) )
1940       {
1941         /*
1942           Display pointer position.
1943         */
1944         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1945           x+windows->image.x,y+windows->image.y);
1946         XInfoWidget(display,windows,text);
1947       }
1948     /*
1949       Wait for next event.
1950     */
1951     XScreenEvent(display,windows,&event,exception);
1952     if (event.xany.window == windows->command.id)
1953       {
1954         /*
1955           Select a command from the Command widget.
1956         */
1957         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1958         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1959         if (id < 0)
1960           continue;
1961         switch (AnnotateCommands[id])
1962         {
1963           case AnnotateNameCommand:
1964           {
1965             const char
1966               *FontMenu[MaxNumberFonts];
1967
1968             int
1969               font_number;
1970
1971             /*
1972               Initialize menu selections.
1973             */
1974             for (i=0; i < MaxNumberFonts; i++)
1975               FontMenu[i]=resource_info->font_name[i];
1976             FontMenu[MaxNumberFonts-2]="Browser...";
1977             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1978             /*
1979               Select a font name from the pop-up menu.
1980             */
1981             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1982               (const char **) FontMenu,command);
1983             if (font_number < 0)
1984               break;
1985             if (font_number == (MaxNumberFonts-2))
1986               {
1987                 static char
1988                   font_name[MaxTextExtent] = "fixed";
1989
1990                 /*
1991                   Select a font name from a browser.
1992                 */
1993                 resource_info->font_name[font_number]=font_name;
1994                 XFontBrowserWidget(display,windows,"Select",font_name);
1995                 if (*font_name == '\0')
1996                   break;
1997               }
1998             /*
1999               Initialize font info.
2000             */
2001             font_info=XLoadQueryFont(display,resource_info->font_name[
2002               font_number]);
2003             if (font_info == (XFontStruct *) NULL)
2004               {
2005                 XNoticeWidget(display,windows,"Unable to load font:",
2006                   resource_info->font_name[font_number]);
2007                 break;
2008               }
2009             font_id=(unsigned int) font_number;
2010             (void) XFreeFont(display,font_info);
2011             break;
2012           }
2013           case AnnotateFontColorCommand:
2014           {
2015             /*
2016               Initialize menu selections.
2017             */
2018             for (i=0; i < (int) (MaxNumberPens-2); i++)
2019               ColorMenu[i]=resource_info->pen_colors[i];
2020             ColorMenu[MaxNumberPens-2]="transparent";
2021             ColorMenu[MaxNumberPens-1]="Browser...";
2022             ColorMenu[MaxNumberPens]=(const char *) NULL;
2023             /*
2024               Select a pen color from the pop-up menu.
2025             */
2026             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2027               (const char **) ColorMenu,command);
2028             if (pen_number < 0)
2029               break;
2030             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2031               MagickFalse;
2032             if (IfMagickTrue(transparent_pen) )
2033               break;
2034             if (pen_number == (MaxNumberPens-1))
2035               {
2036                 static char
2037                   color_name[MaxTextExtent] = "gray";
2038
2039                 /*
2040                   Select a pen color from a dialog.
2041                 */
2042                 resource_info->pen_colors[pen_number]=color_name;
2043                 XColorBrowserWidget(display,windows,"Select",color_name);
2044                 if (*color_name == '\0')
2045                   break;
2046               }
2047             /*
2048               Set pen color.
2049             */
2050             (void) XParseColor(display,windows->map_info->colormap,
2051               resource_info->pen_colors[pen_number],&color);
2052             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2053               (unsigned int) MaxColors,&color);
2054             windows->pixel_info->pen_colors[pen_number]=color;
2055             pen_id=(unsigned int) pen_number;
2056             break;
2057           }
2058           case AnnotateBackgroundColorCommand:
2059           {
2060             /*
2061               Initialize menu selections.
2062             */
2063             for (i=0; i < (int) (MaxNumberPens-2); i++)
2064               ColorMenu[i]=resource_info->pen_colors[i];
2065             ColorMenu[MaxNumberPens-2]="transparent";
2066             ColorMenu[MaxNumberPens-1]="Browser...";
2067             ColorMenu[MaxNumberPens]=(const char *) NULL;
2068             /*
2069               Select a pen color from the pop-up menu.
2070             */
2071             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2072               (const char **) ColorMenu,command);
2073             if (pen_number < 0)
2074               break;
2075             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2076               MagickFalse;
2077             if (IfMagickTrue(transparent_box) )
2078               break;
2079             if (pen_number == (MaxNumberPens-1))
2080               {
2081                 static char
2082                   color_name[MaxTextExtent] = "gray";
2083
2084                 /*
2085                   Select a pen color from a dialog.
2086                 */
2087                 resource_info->pen_colors[pen_number]=color_name;
2088                 XColorBrowserWidget(display,windows,"Select",color_name);
2089                 if (*color_name == '\0')
2090                   break;
2091               }
2092             /*
2093               Set pen color.
2094             */
2095             (void) XParseColor(display,windows->map_info->colormap,
2096               resource_info->pen_colors[pen_number],&color);
2097             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2098               (unsigned int) MaxColors,&color);
2099             windows->pixel_info->pen_colors[pen_number]=color;
2100             box_id=(unsigned int) pen_number;
2101             break;
2102           }
2103           case AnnotateRotateCommand:
2104           {
2105             int
2106               entry;
2107
2108             static char
2109               angle[MaxTextExtent] = "30.0";
2110
2111             static const char
2112               *RotateMenu[] =
2113               {
2114                 "-90",
2115                 "-45",
2116                 "-30",
2117                 "0",
2118                 "30",
2119                 "45",
2120                 "90",
2121                 "180",
2122                 "Dialog...",
2123                 (char *) NULL,
2124               };
2125
2126             /*
2127               Select a command from the pop-up menu.
2128             */
2129             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2130               command);
2131             if (entry < 0)
2132               break;
2133             if (entry != 8)
2134               {
2135                 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2136                 break;
2137               }
2138             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2139               angle);
2140             if (*angle == '\0')
2141               break;
2142             degrees=StringToDouble(angle,(char **) NULL);
2143             break;
2144           }
2145           case AnnotateHelpCommand:
2146           {
2147             XTextViewWidget(display,resource_info,windows,MagickFalse,
2148               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2149             break;
2150           }
2151           case AnnotateDismissCommand:
2152           {
2153             /*
2154               Prematurely exit.
2155             */
2156             state|=EscapeState;
2157             state|=ExitState;
2158             break;
2159           }
2160           default:
2161             break;
2162         }
2163         continue;
2164       }
2165     switch (event.type)
2166     {
2167       case ButtonPress:
2168       {
2169         if (event.xbutton.button != Button1)
2170           break;
2171         if (event.xbutton.window != windows->image.id)
2172           break;
2173         /*
2174           Change to text entering mode.
2175         */
2176         x=event.xbutton.x;
2177         y=event.xbutton.y;
2178         state|=ExitState;
2179         break;
2180       }
2181       case ButtonRelease:
2182         break;
2183       case Expose:
2184         break;
2185       case KeyPress:
2186       {
2187         if (event.xkey.window != windows->image.id)
2188           break;
2189         /*
2190           Respond to a user key press.
2191         */
2192         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2193           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2194         switch ((int) key_symbol)
2195         {
2196           case XK_Escape:
2197           case XK_F20:
2198           {
2199             /*
2200               Prematurely exit.
2201             */
2202             state|=EscapeState;
2203             state|=ExitState;
2204             break;
2205           }
2206           case XK_F1:
2207           case XK_Help:
2208           {
2209             XTextViewWidget(display,resource_info,windows,MagickFalse,
2210               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2211             break;
2212           }
2213           default:
2214           {
2215             (void) XBell(display,0);
2216             break;
2217           }
2218         }
2219         break;
2220       }
2221       case MotionNotify:
2222       {
2223         /*
2224           Map and unmap Info widget as cursor crosses its boundaries.
2225         */
2226         x=event.xmotion.x;
2227         y=event.xmotion.y;
2228         if (IfMagickTrue(windows->info.mapped) )
2229           {
2230             if ((x < (int) (windows->info.x+windows->info.width)) &&
2231                 (y < (int) (windows->info.y+windows->info.height)))
2232               (void) XWithdrawWindow(display,windows->info.id,
2233                 windows->info.screen);
2234           }
2235         else
2236           if ((x > (int) (windows->info.x+windows->info.width)) ||
2237               (y > (int) (windows->info.y+windows->info.height)))
2238             (void) XMapWindow(display,windows->info.id);
2239         break;
2240       }
2241       default:
2242         break;
2243     }
2244   } while ((state & ExitState) == 0);
2245   (void) XSelectInput(display,windows->image.id,
2246     windows->image.attributes.event_mask);
2247   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2248   if ((state & EscapeState) != 0)
2249     return(MagickTrue);
2250   /*
2251     Set font info and check boundary conditions.
2252   */
2253   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2254   if (font_info == (XFontStruct *) NULL)
2255     {
2256       XNoticeWidget(display,windows,"Unable to load font:",
2257         resource_info->font_name[font_id]);
2258       font_info=windows->font_info;
2259     }
2260   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2261     x=(int) windows->image.width-font_info->max_bounds.width;
2262   if (y < (int) (font_info->ascent+font_info->descent))
2263     y=(int) font_info->ascent+font_info->descent;
2264   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2265       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2266     return(MagickFalse);
2267   /*
2268     Initialize annotate structure.
2269   */
2270   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2271   if (annotate_info == (XAnnotateInfo *) NULL)
2272     return(MagickFalse);
2273   XGetAnnotateInfo(annotate_info);
2274   annotate_info->x=x;
2275   annotate_info->y=y;
2276   if (IfMagickFalse(transparent_box) && IfMagickFalse(transparent_pen))
2277     annotate_info->stencil=OpaqueStencil;
2278   else
2279     if (IfMagickFalse(transparent_box) )
2280       annotate_info->stencil=BackgroundStencil;
2281     else
2282       annotate_info->stencil=ForegroundStencil;
2283   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2284   annotate_info->degrees=degrees;
2285   annotate_info->font_info=font_info;
2286   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2287     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2288     sizeof(*annotate_info->text));
2289   if (annotate_info->text == (char *) NULL)
2290     return(MagickFalse);
2291   /*
2292     Create cursor and set graphic context.
2293   */
2294   cursor=XCreateFontCursor(display,XC_pencil);
2295   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2296   annotate_context=windows->image.annotate_context;
2297   (void) XSetFont(display,annotate_context,font_info->fid);
2298   (void) XSetBackground(display,annotate_context,
2299     windows->pixel_info->pen_colors[box_id].pixel);
2300   (void) XSetForeground(display,annotate_context,
2301     windows->pixel_info->pen_colors[pen_id].pixel);
2302   /*
2303     Begin annotating the image with text.
2304   */
2305   (void) CloneString(&windows->command.name,"Text");
2306   windows->command.data=0;
2307   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2308   state=DefaultState;
2309   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2310   text_event.xexpose.width=(int) font_info->max_bounds.width;
2311   text_event.xexpose.height=font_info->max_bounds.ascent+
2312     font_info->max_bounds.descent;
2313   p=annotate_info->text;
2314   do
2315   {
2316     /*
2317       Display text cursor.
2318     */
2319     *p='\0';
2320     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2321     /*
2322       Wait for next event.
2323     */
2324     XScreenEvent(display,windows,&event,exception);
2325     if (event.xany.window == windows->command.id)
2326       {
2327         /*
2328           Select a command from the Command widget.
2329         */
2330         (void) XSetBackground(display,annotate_context,
2331           windows->pixel_info->background_color.pixel);
2332         (void) XSetForeground(display,annotate_context,
2333           windows->pixel_info->foreground_color.pixel);
2334         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2335         (void) XSetBackground(display,annotate_context,
2336           windows->pixel_info->pen_colors[box_id].pixel);
2337         (void) XSetForeground(display,annotate_context,
2338           windows->pixel_info->pen_colors[pen_id].pixel);
2339         if (id < 0)
2340           continue;
2341         switch (TextCommands[id])
2342         {
2343           case TextHelpCommand:
2344           {
2345             XTextViewWidget(display,resource_info,windows,MagickFalse,
2346               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2347             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2348             break;
2349           }
2350           case TextApplyCommand:
2351           {
2352             /*
2353               Finished annotating.
2354             */
2355             annotate_info->width=(unsigned int) XTextWidth(font_info,
2356               annotate_info->text,(int) strlen(annotate_info->text));
2357             XRefreshWindow(display,&windows->image,&text_event);
2358             state|=ExitState;
2359             break;
2360           }
2361           default:
2362             break;
2363         }
2364         continue;
2365       }
2366     /*
2367       Erase text cursor.
2368     */
2369     text_event.xexpose.x=x;
2370     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2371     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2372       (unsigned int) text_event.xexpose.width,(unsigned int)
2373       text_event.xexpose.height,MagickFalse);
2374     XRefreshWindow(display,&windows->image,&text_event);
2375     switch (event.type)
2376     {
2377       case ButtonPress:
2378       {
2379         if (event.xbutton.window != windows->image.id)
2380           break;
2381         if (event.xbutton.button == Button2)
2382           {
2383             /*
2384               Request primary selection.
2385             */
2386             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2387               windows->image.id,CurrentTime);
2388             break;
2389           }
2390         break;
2391       }
2392       case Expose:
2393       {
2394         if (event.xexpose.count == 0)
2395           {
2396             XAnnotateInfo
2397               *text_info;
2398
2399             /*
2400               Refresh Image window.
2401             */
2402             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2403             text_info=annotate_info;
2404             while (text_info != (XAnnotateInfo *) NULL)
2405             {
2406               if (annotate_info->stencil == ForegroundStencil)
2407                 (void) XDrawString(display,windows->image.id,annotate_context,
2408                   text_info->x,text_info->y,text_info->text,
2409                   (int) strlen(text_info->text));
2410               else
2411                 (void) XDrawImageString(display,windows->image.id,
2412                   annotate_context,text_info->x,text_info->y,text_info->text,
2413                   (int) strlen(text_info->text));
2414               text_info=text_info->previous;
2415             }
2416             (void) XDrawString(display,windows->image.id,annotate_context,
2417               x,y,"_",1);
2418           }
2419         break;
2420       }
2421       case KeyPress:
2422       {
2423         int
2424           length;
2425
2426         if (event.xkey.window != windows->image.id)
2427           break;
2428         /*
2429           Respond to a user key press.
2430         */
2431         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2432           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2433         *(command+length)='\0';
2434         if (((event.xkey.state & ControlMask) != 0) ||
2435             ((event.xkey.state & Mod1Mask) != 0))
2436           state|=ModifierState;
2437         if ((state & ModifierState) != 0)
2438           switch ((int) key_symbol)
2439           {
2440             case XK_u:
2441             case XK_U:
2442             {
2443               key_symbol=DeleteCommand;
2444               break;
2445             }
2446             default:
2447               break;
2448           }
2449         switch ((int) key_symbol)
2450         {
2451           case XK_BackSpace:
2452           {
2453             /*
2454               Erase one character.
2455             */
2456             if (p == annotate_info->text)
2457               {
2458                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2459                   break;
2460                 else
2461                   {
2462                     /*
2463                       Go to end of the previous line of text.
2464                     */
2465                     annotate_info=annotate_info->previous;
2466                     p=annotate_info->text;
2467                     x=annotate_info->x+annotate_info->width;
2468                     y=annotate_info->y;
2469                     if (annotate_info->width != 0)
2470                       p+=strlen(annotate_info->text);
2471                     break;
2472                   }
2473               }
2474             p--;
2475             x-=XTextWidth(font_info,p,1);
2476             text_event.xexpose.x=x;
2477             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2478             XRefreshWindow(display,&windows->image,&text_event);
2479             break;
2480           }
2481           case XK_bracketleft:
2482           {
2483             key_symbol=XK_Escape;
2484             break;
2485           }
2486           case DeleteCommand:
2487           {
2488             /*
2489               Erase the entire line of text.
2490             */
2491             while (p != annotate_info->text)
2492             {
2493               p--;
2494               x-=XTextWidth(font_info,p,1);
2495               text_event.xexpose.x=x;
2496               XRefreshWindow(display,&windows->image,&text_event);
2497             }
2498             break;
2499           }
2500           case XK_Escape:
2501           case XK_F20:
2502           {
2503             /*
2504               Finished annotating.
2505             */
2506             annotate_info->width=(unsigned int) XTextWidth(font_info,
2507               annotate_info->text,(int) strlen(annotate_info->text));
2508             XRefreshWindow(display,&windows->image,&text_event);
2509             state|=ExitState;
2510             break;
2511           }
2512           default:
2513           {
2514             /*
2515               Draw a single character on the Image window.
2516             */
2517             if ((state & ModifierState) != 0)
2518               break;
2519             if (*command == '\0')
2520               break;
2521             *p=(*command);
2522             if (annotate_info->stencil == ForegroundStencil)
2523               (void) XDrawString(display,windows->image.id,annotate_context,
2524                 x,y,p,1);
2525             else
2526               (void) XDrawImageString(display,windows->image.id,
2527                 annotate_context,x,y,p,1);
2528             x+=XTextWidth(font_info,p,1);
2529             p++;
2530             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2531               break;
2532           }
2533           case XK_Return:
2534           case XK_KP_Enter:
2535           {
2536             /*
2537               Advance to the next line of text.
2538             */
2539             *p='\0';
2540             annotate_info->width=(unsigned int) XTextWidth(font_info,
2541               annotate_info->text,(int) strlen(annotate_info->text));
2542             if (annotate_info->next != (XAnnotateInfo *) NULL)
2543               {
2544                 /*
2545                   Line of text already exists.
2546                 */
2547                 annotate_info=annotate_info->next;
2548                 x=annotate_info->x;
2549                 y=annotate_info->y;
2550                 p=annotate_info->text;
2551                 break;
2552               }
2553             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2554               sizeof(*annotate_info->next));
2555             if (annotate_info->next == (XAnnotateInfo *) NULL)
2556               return(MagickFalse);
2557             *annotate_info->next=(*annotate_info);
2558             annotate_info->next->previous=annotate_info;
2559             annotate_info=annotate_info->next;
2560             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2561               windows->image.width/MagickMax((ssize_t)
2562               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2563             if (annotate_info->text == (char *) NULL)
2564               return(MagickFalse);
2565             annotate_info->y+=annotate_info->height;
2566             if (annotate_info->y > (int) windows->image.height)
2567               annotate_info->y=(int) annotate_info->height;
2568             annotate_info->next=(XAnnotateInfo *) NULL;
2569             x=annotate_info->x;
2570             y=annotate_info->y;
2571             p=annotate_info->text;
2572             break;
2573           }
2574         }
2575         break;
2576       }
2577       case KeyRelease:
2578       {
2579         /*
2580           Respond to a user key release.
2581         */
2582         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2583           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2584         state&=(~ModifierState);
2585         break;
2586       }
2587       case SelectionNotify:
2588       {
2589         Atom
2590           type;
2591
2592         int
2593           format;
2594
2595         unsigned char
2596           *data;
2597
2598         unsigned long
2599           after,
2600           length;
2601
2602         /*
2603           Obtain response from primary selection.
2604         */
2605         if (event.xselection.property == (Atom) None)
2606           break;
2607         status=XGetWindowProperty(display,event.xselection.requestor,
2608           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2609           &type,&format,&length,&after,&data);
2610         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2611             (length == 0))
2612           break;
2613         /*
2614           Annotate Image window with primary selection.
2615         */
2616         for (i=0; i < (ssize_t) length; i++)
2617         {
2618           if ((char) data[i] != '\n')
2619             {
2620               /*
2621                 Draw a single character on the Image window.
2622               */
2623               *p=(char) data[i];
2624               (void) XDrawString(display,windows->image.id,annotate_context,
2625                 x,y,p,1);
2626               x+=XTextWidth(font_info,p,1);
2627               p++;
2628               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2629                 continue;
2630             }
2631           /*
2632             Advance to the next line of text.
2633           */
2634           *p='\0';
2635           annotate_info->width=(unsigned int) XTextWidth(font_info,
2636             annotate_info->text,(int) strlen(annotate_info->text));
2637           if (annotate_info->next != (XAnnotateInfo *) NULL)
2638             {
2639               /*
2640                 Line of text already exists.
2641               */
2642               annotate_info=annotate_info->next;
2643               x=annotate_info->x;
2644               y=annotate_info->y;
2645               p=annotate_info->text;
2646               continue;
2647             }
2648           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2649             sizeof(*annotate_info->next));
2650           if (annotate_info->next == (XAnnotateInfo *) NULL)
2651             return(MagickFalse);
2652           *annotate_info->next=(*annotate_info);
2653           annotate_info->next->previous=annotate_info;
2654           annotate_info=annotate_info->next;
2655           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2656             windows->image.width/MagickMax((ssize_t)
2657             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2658           if (annotate_info->text == (char *) NULL)
2659             return(MagickFalse);
2660           annotate_info->y+=annotate_info->height;
2661           if (annotate_info->y > (int) windows->image.height)
2662             annotate_info->y=(int) annotate_info->height;
2663           annotate_info->next=(XAnnotateInfo *) NULL;
2664           x=annotate_info->x;
2665           y=annotate_info->y;
2666           p=annotate_info->text;
2667         }
2668         (void) XFree((void *) data);
2669         break;
2670       }
2671       default:
2672         break;
2673     }
2674   } while ((state & ExitState) == 0);
2675   (void) XFreeCursor(display,cursor);
2676   /*
2677     Annotation is relative to image configuration.
2678   */
2679   width=(unsigned int) image->columns;
2680   height=(unsigned int) image->rows;
2681   x=0;
2682   y=0;
2683   if (windows->image.crop_geometry != (char *) NULL)
2684     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2685   /*
2686     Initialize annotated image.
2687   */
2688   XSetCursorState(display,windows,MagickTrue);
2689   XCheckRefreshWindows(display,windows);
2690   while (annotate_info != (XAnnotateInfo *) NULL)
2691   {
2692     if (annotate_info->width == 0)
2693       {
2694         /*
2695           No text on this line--  go to the next line of text.
2696         */
2697         previous_info=annotate_info->previous;
2698         annotate_info->text=(char *)
2699           RelinquishMagickMemory(annotate_info->text);
2700         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2701         annotate_info=previous_info;
2702         continue;
2703       }
2704     /*
2705       Determine pixel index for box and pen color.
2706     */
2707     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2708     if (windows->pixel_info->colors != 0)
2709       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2710         if (windows->pixel_info->pixels[i] ==
2711             windows->pixel_info->pen_colors[box_id].pixel)
2712           {
2713             windows->pixel_info->box_index=(unsigned short) i;
2714             break;
2715           }
2716     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2717     if (windows->pixel_info->colors != 0)
2718       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2719         if (windows->pixel_info->pixels[i] ==
2720             windows->pixel_info->pen_colors[pen_id].pixel)
2721           {
2722             windows->pixel_info->pen_index=(unsigned short) i;
2723             break;
2724           }
2725     /*
2726       Define the annotate geometry string.
2727     */
2728     annotate_info->x=(int)
2729       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2730     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2731       windows->image.y)/windows->image.ximage->height;
2732     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2733       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2734       height*annotate_info->height/windows->image.ximage->height,
2735       annotate_info->x+x,annotate_info->y+y);
2736     /*
2737       Annotate image with text.
2738     */
2739     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2740       exception);
2741     if (status == 0)
2742       return(MagickFalse);
2743     /*
2744       Free up memory.
2745     */
2746     previous_info=annotate_info->previous;
2747     annotate_info->text=DestroyString(annotate_info->text);
2748     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2749     annotate_info=previous_info;
2750   }
2751   (void) XSetForeground(display,annotate_context,
2752     windows->pixel_info->foreground_color.pixel);
2753   (void) XSetBackground(display,annotate_context,
2754     windows->pixel_info->background_color.pixel);
2755   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2756   XSetCursorState(display,windows,MagickFalse);
2757   (void) XFreeFont(display,font_info);
2758   /*
2759     Update image configuration.
2760   */
2761   XConfigureImageColormap(display,resource_info,windows,image,exception);
2762   (void) XConfigureImage(display,resource_info,windows,image,exception);
2763   return(MagickTrue);
2764 }
2765 \f
2766 /*
2767 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2768 %                                                                             %
2769 %                                                                             %
2770 %                                                                             %
2771 +   X B a c k g r o u n d I m a g e                                           %
2772 %                                                                             %
2773 %                                                                             %
2774 %                                                                             %
2775 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2776 %
2777 %  XBackgroundImage() displays the image in the background of a window.
2778 %
2779 %  The format of the XBackgroundImage method is:
2780 %
2781 %      MagickBooleanType XBackgroundImage(Display *display,
2782 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2783 %        ExceptionInfo *exception)
2784 %
2785 %  A description of each parameter follows:
2786 %
2787 %    o display: Specifies a connection to an X server; returned from
2788 %      XOpenDisplay.
2789 %
2790 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2791 %
2792 %    o windows: Specifies a pointer to a XWindows structure.
2793 %
2794 %    o image: the image.
2795 %
2796 %    o exception: return any errors or warnings in this structure.
2797 %
2798 */
2799 static MagickBooleanType XBackgroundImage(Display *display,
2800   XResourceInfo *resource_info,XWindows *windows,Image **image,
2801   ExceptionInfo *exception)
2802 {
2803 #define BackgroundImageTag  "Background/Image"
2804
2805   int
2806     status;
2807
2808   static char
2809     window_id[MaxTextExtent] = "root";
2810
2811   XResourceInfo
2812     background_resources;
2813
2814   /*
2815     Put image in background.
2816   */
2817   status=XDialogWidget(display,windows,"Background",
2818     "Enter window id (id 0x00 selects window with pointer):",window_id);
2819   if (*window_id == '\0')
2820     return(MagickFalse);
2821   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2822     exception);
2823   XInfoWidget(display,windows,BackgroundImageTag);
2824   XSetCursorState(display,windows,MagickTrue);
2825   XCheckRefreshWindows(display,windows);
2826   background_resources=(*resource_info);
2827   background_resources.window_id=window_id;
2828   background_resources.backdrop=IsMagickTrue(status);
2829   status=XDisplayBackgroundImage(display,&background_resources,*image,
2830     exception);
2831   if (IfMagickTrue(status))
2832     XClientMessage(display,windows->image.id,windows->im_protocols,
2833       windows->im_retain_colors,CurrentTime);
2834   XSetCursorState(display,windows,MagickFalse);
2835   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2836     exception);
2837   return(MagickTrue);
2838 }
2839 \f
2840 /*
2841 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2842 %                                                                             %
2843 %                                                                             %
2844 %                                                                             %
2845 +   X C h o p I m a g e                                                       %
2846 %                                                                             %
2847 %                                                                             %
2848 %                                                                             %
2849 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2850 %
2851 %  XChopImage() chops the X image.
2852 %
2853 %  The format of the XChopImage method is:
2854 %
2855 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2856 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2857 %
2858 %  A description of each parameter follows:
2859 %
2860 %    o display: Specifies a connection to an X server; returned from
2861 %      XOpenDisplay.
2862 %
2863 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2864 %
2865 %    o windows: Specifies a pointer to a XWindows structure.
2866 %
2867 %    o image: the image.
2868 %
2869 %    o exception: return any errors or warnings in this structure.
2870 %
2871 */
2872 static MagickBooleanType XChopImage(Display *display,
2873   XResourceInfo *resource_info,XWindows *windows,Image **image,
2874   ExceptionInfo *exception)
2875 {
2876   static const char
2877     *ChopMenu[] =
2878     {
2879       "Direction",
2880       "Help",
2881       "Dismiss",
2882       (char *) NULL
2883     };
2884
2885   static ModeType
2886     direction = HorizontalChopCommand;
2887
2888   static const ModeType
2889     ChopCommands[] =
2890     {
2891       ChopDirectionCommand,
2892       ChopHelpCommand,
2893       ChopDismissCommand
2894     },
2895     DirectionCommands[] =
2896     {
2897       HorizontalChopCommand,
2898       VerticalChopCommand
2899     };
2900
2901   char
2902     text[MaxTextExtent];
2903
2904   Image
2905     *chop_image;
2906
2907   int
2908     id,
2909     x,
2910     y;
2911
2912   double
2913     scale_factor;
2914
2915   RectangleInfo
2916     chop_info;
2917
2918   unsigned int
2919     distance,
2920     height,
2921     width;
2922
2923   size_t
2924     state;
2925
2926   XEvent
2927     event;
2928
2929   XSegment
2930     segment_info;
2931
2932   /*
2933     Map Command widget.
2934   */
2935   (void) CloneString(&windows->command.name,"Chop");
2936   windows->command.data=1;
2937   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2938   (void) XMapRaised(display,windows->command.id);
2939   XClientMessage(display,windows->image.id,windows->im_protocols,
2940     windows->im_update_widget,CurrentTime);
2941   /*
2942     Track pointer until button 1 is pressed.
2943   */
2944   XQueryPosition(display,windows->image.id,&x,&y);
2945   (void) XSelectInput(display,windows->image.id,
2946     windows->image.attributes.event_mask | PointerMotionMask);
2947   state=DefaultState;
2948   do
2949   {
2950     if (IfMagickTrue(windows->info.mapped) )
2951       {
2952         /*
2953           Display pointer position.
2954         */
2955         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2956           x+windows->image.x,y+windows->image.y);
2957         XInfoWidget(display,windows,text);
2958       }
2959     /*
2960       Wait for next event.
2961     */
2962     XScreenEvent(display,windows,&event,exception);
2963     if (event.xany.window == windows->command.id)
2964       {
2965         /*
2966           Select a command from the Command widget.
2967         */
2968         id=XCommandWidget(display,windows,ChopMenu,&event);
2969         if (id < 0)
2970           continue;
2971         switch (ChopCommands[id])
2972         {
2973           case ChopDirectionCommand:
2974           {
2975             char
2976               command[MaxTextExtent];
2977
2978             static const char
2979               *Directions[] =
2980               {
2981                 "horizontal",
2982                 "vertical",
2983                 (char *) NULL,
2984               };
2985
2986             /*
2987               Select a command from the pop-up menu.
2988             */
2989             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2990             if (id >= 0)
2991               direction=DirectionCommands[id];
2992             break;
2993           }
2994           case ChopHelpCommand:
2995           {
2996             XTextViewWidget(display,resource_info,windows,MagickFalse,
2997               "Help Viewer - Image Chop",ImageChopHelp);
2998             break;
2999           }
3000           case ChopDismissCommand:
3001           {
3002             /*
3003               Prematurely exit.
3004             */
3005             state|=EscapeState;
3006             state|=ExitState;
3007             break;
3008           }
3009           default:
3010             break;
3011         }
3012         continue;
3013       }
3014     switch (event.type)
3015     {
3016       case ButtonPress:
3017       {
3018         if (event.xbutton.button != Button1)
3019           break;
3020         if (event.xbutton.window != windows->image.id)
3021           break;
3022         /*
3023           User has committed to start point of chopping line.
3024         */
3025         segment_info.x1=(short int) event.xbutton.x;
3026         segment_info.x2=(short int) event.xbutton.x;
3027         segment_info.y1=(short int) event.xbutton.y;
3028         segment_info.y2=(short int) event.xbutton.y;
3029         state|=ExitState;
3030         break;
3031       }
3032       case ButtonRelease:
3033         break;
3034       case Expose:
3035         break;
3036       case KeyPress:
3037       {
3038         char
3039           command[MaxTextExtent];
3040
3041         KeySym
3042           key_symbol;
3043
3044         if (event.xkey.window != windows->image.id)
3045           break;
3046         /*
3047           Respond to a user key press.
3048         */
3049         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3050           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3051         switch ((int) key_symbol)
3052         {
3053           case XK_Escape:
3054           case XK_F20:
3055           {
3056             /*
3057               Prematurely exit.
3058             */
3059             state|=EscapeState;
3060             state|=ExitState;
3061             break;
3062           }
3063           case XK_F1:
3064           case XK_Help:
3065           {
3066             (void) XSetFunction(display,windows->image.highlight_context,
3067               GXcopy);
3068             XTextViewWidget(display,resource_info,windows,MagickFalse,
3069               "Help Viewer - Image Chop",ImageChopHelp);
3070             (void) XSetFunction(display,windows->image.highlight_context,
3071               GXinvert);
3072             break;
3073           }
3074           default:
3075           {
3076             (void) XBell(display,0);
3077             break;
3078           }
3079         }
3080         break;
3081       }
3082       case MotionNotify:
3083       {
3084         /*
3085           Map and unmap Info widget as text cursor crosses its boundaries.
3086         */
3087         x=event.xmotion.x;
3088         y=event.xmotion.y;
3089         if (IfMagickTrue(windows->info.mapped) )
3090           {
3091             if ((x < (int) (windows->info.x+windows->info.width)) &&
3092                 (y < (int) (windows->info.y+windows->info.height)))
3093               (void) XWithdrawWindow(display,windows->info.id,
3094                 windows->info.screen);
3095           }
3096         else
3097           if ((x > (int) (windows->info.x+windows->info.width)) ||
3098               (y > (int) (windows->info.y+windows->info.height)))
3099             (void) XMapWindow(display,windows->info.id);
3100       }
3101     }
3102   } while ((state & ExitState) == 0);
3103   (void) XSelectInput(display,windows->image.id,
3104     windows->image.attributes.event_mask);
3105   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3106   if ((state & EscapeState) != 0)
3107     return(MagickTrue);
3108   /*
3109     Draw line as pointer moves until the mouse button is released.
3110   */
3111   chop_info.width=0;
3112   chop_info.height=0;
3113   chop_info.x=0;
3114   chop_info.y=0;
3115   distance=0;
3116   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3117   state=DefaultState;
3118   do
3119   {
3120     if (distance > 9)
3121       {
3122         /*
3123           Display info and draw chopping line.
3124         */
3125         if (IfMagickFalse(windows->info.mapped) )
3126           (void) XMapWindow(display,windows->info.id);
3127         (void) FormatLocaleString(text,MaxTextExtent,
3128           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3129           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3130         XInfoWidget(display,windows,text);
3131         XHighlightLine(display,windows->image.id,
3132           windows->image.highlight_context,&segment_info);
3133       }
3134     else
3135       if (IfMagickTrue(windows->info.mapped) )
3136         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3137     /*
3138       Wait for next event.
3139     */
3140     XScreenEvent(display,windows,&event,exception);
3141     if (distance > 9)
3142       XHighlightLine(display,windows->image.id,
3143         windows->image.highlight_context,&segment_info);
3144     switch (event.type)
3145     {
3146       case ButtonPress:
3147       {
3148         segment_info.x2=(short int) event.xmotion.x;
3149         segment_info.y2=(short int) event.xmotion.y;
3150         break;
3151       }
3152       case ButtonRelease:
3153       {
3154         /*
3155           User has committed to chopping line.
3156         */
3157         segment_info.x2=(short int) event.xbutton.x;
3158         segment_info.y2=(short int) event.xbutton.y;
3159         state|=ExitState;
3160         break;
3161       }
3162       case Expose:
3163         break;
3164       case MotionNotify:
3165       {
3166         segment_info.x2=(short int) event.xmotion.x;
3167         segment_info.y2=(short int) event.xmotion.y;
3168       }
3169       default:
3170         break;
3171     }
3172     /*
3173       Check boundary conditions.
3174     */
3175     if (segment_info.x2 < 0)
3176       segment_info.x2=0;
3177     else
3178       if (segment_info.x2 > windows->image.ximage->width)
3179         segment_info.x2=windows->image.ximage->width;
3180     if (segment_info.y2 < 0)
3181       segment_info.y2=0;
3182     else
3183       if (segment_info.y2 > windows->image.ximage->height)
3184         segment_info.y2=windows->image.ximage->height;
3185     distance=(unsigned int)
3186       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3187        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3188     /*
3189       Compute chopping geometry.
3190     */
3191     if (direction == HorizontalChopCommand)
3192       {
3193         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3194         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3195         chop_info.height=0;
3196         chop_info.y=0;
3197         if (segment_info.x1 > (int) segment_info.x2)
3198           {
3199             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3200             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3201           }
3202       }
3203     else
3204       {
3205         chop_info.width=0;
3206         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3207         chop_info.x=0;
3208         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3209         if (segment_info.y1 > segment_info.y2)
3210           {
3211             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3212             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3213           }
3214       }
3215   } while ((state & ExitState) == 0);
3216   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3217   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3218   if (distance <= 9)
3219     return(MagickTrue);
3220   /*
3221     Image chopping is relative to image configuration.
3222   */
3223   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3224     exception);
3225   XSetCursorState(display,windows,MagickTrue);
3226   XCheckRefreshWindows(display,windows);
3227   windows->image.window_changes.width=windows->image.ximage->width-
3228     (unsigned int) chop_info.width;
3229   windows->image.window_changes.height=windows->image.ximage->height-
3230     (unsigned int) chop_info.height;
3231   width=(unsigned int) (*image)->columns;
3232   height=(unsigned int) (*image)->rows;
3233   x=0;
3234   y=0;
3235   if (windows->image.crop_geometry != (char *) NULL)
3236     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3237   scale_factor=(double) width/windows->image.ximage->width;
3238   chop_info.x+=x;
3239   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3240   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3241   scale_factor=(double) height/windows->image.ximage->height;
3242   chop_info.y+=y;
3243   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3244   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3245   /*
3246     Chop image.
3247   */
3248   chop_image=ChopImage(*image,&chop_info,exception);
3249   XSetCursorState(display,windows,MagickFalse);
3250   if (chop_image == (Image *) NULL)
3251     return(MagickFalse);
3252   *image=DestroyImage(*image);
3253   *image=chop_image;
3254   /*
3255     Update image configuration.
3256   */
3257   XConfigureImageColormap(display,resource_info,windows,*image,exception);
3258   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3259   return(MagickTrue);
3260 }
3261 \f
3262 /*
3263 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3264 %                                                                             %
3265 %                                                                             %
3266 %                                                                             %
3267 +   X C o l o r E d i t I m a g e                                             %
3268 %                                                                             %
3269 %                                                                             %
3270 %                                                                             %
3271 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3272 %
3273 %  XColorEditImage() allows the user to interactively change the color of one
3274 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3275 %
3276 %  The format of the XColorEditImage method is:
3277 %
3278 %      MagickBooleanType XColorEditImage(Display *display,
3279 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3280 %          ExceptionInfo *exception)
3281 %
3282 %  A description of each parameter follows:
3283 %
3284 %    o display: Specifies a connection to an X server;  returned from
3285 %      XOpenDisplay.
3286 %
3287 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3288 %
3289 %    o windows: Specifies a pointer to a XWindows structure.
3290 %
3291 %    o image: the image; returned from ReadImage.
3292 %
3293 %    o exception: return any errors or warnings in this structure.
3294 %
3295 */
3296 static MagickBooleanType XColorEditImage(Display *display,
3297   XResourceInfo *resource_info,XWindows *windows,Image **image,
3298   ExceptionInfo *exception)
3299 {
3300   static const char
3301     *ColorEditMenu[] =
3302     {
3303       "Method",
3304       "Pixel Color",
3305       "Border Color",
3306       "Fuzz",
3307       "Undo",
3308       "Help",
3309       "Dismiss",
3310       (char *) NULL
3311     };
3312
3313   static const ModeType
3314     ColorEditCommands[] =
3315     {
3316       ColorEditMethodCommand,
3317       ColorEditColorCommand,
3318       ColorEditBorderCommand,
3319       ColorEditFuzzCommand,
3320       ColorEditUndoCommand,
3321       ColorEditHelpCommand,
3322       ColorEditDismissCommand
3323     };
3324
3325   static PaintMethod
3326     method = PointMethod;
3327
3328   static unsigned int
3329     pen_id = 0;
3330
3331   static XColor
3332     border_color = { 0, 0, 0, 0, 0, 0 };
3333
3334   char
3335     command[MaxTextExtent],
3336     text[MaxTextExtent];
3337
3338   Cursor
3339     cursor;
3340
3341   int
3342     entry,
3343     id,
3344     x,
3345     x_offset,
3346     y,
3347     y_offset;
3348
3349   register Quantum
3350     *q;
3351
3352   register ssize_t
3353     i;
3354
3355   unsigned int
3356     height,
3357     width;
3358
3359   size_t
3360     state;
3361
3362   XColor
3363     color;
3364
3365   XEvent
3366     event;
3367
3368   /*
3369     Map Command widget.
3370   */
3371   (void) CloneString(&windows->command.name,"Color Edit");
3372   windows->command.data=4;
3373   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3374   (void) XMapRaised(display,windows->command.id);
3375   XClientMessage(display,windows->image.id,windows->im_protocols,
3376     windows->im_update_widget,CurrentTime);
3377   /*
3378     Make cursor.
3379   */
3380   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3381     resource_info->background_color,resource_info->foreground_color);
3382   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3383   /*
3384     Track pointer until button 1 is pressed.
3385   */
3386   XQueryPosition(display,windows->image.id,&x,&y);
3387   (void) XSelectInput(display,windows->image.id,
3388     windows->image.attributes.event_mask | PointerMotionMask);
3389   state=DefaultState;
3390   do
3391   {
3392     if (IfMagickTrue(windows->info.mapped) )
3393       {
3394         /*
3395           Display pointer position.
3396         */
3397         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3398           x+windows->image.x,y+windows->image.y);
3399         XInfoWidget(display,windows,text);
3400       }
3401     /*
3402       Wait for next event.
3403     */
3404     XScreenEvent(display,windows,&event,exception);
3405     if (event.xany.window == windows->command.id)
3406       {
3407         /*
3408           Select a command from the Command widget.
3409         */
3410         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3411         if (id < 0)
3412           {
3413             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3414             continue;
3415           }
3416         switch (ColorEditCommands[id])
3417         {
3418           case ColorEditMethodCommand:
3419           {
3420             char
3421               **methods;
3422
3423             /*
3424               Select a method from the pop-up menu.
3425             */
3426             methods=(char **) GetCommandOptions(MagickMethodOptions);
3427             if (methods == (char **) NULL)
3428               break;
3429             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3430               (const char **) methods,command);
3431             if (entry >= 0)
3432               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3433                 MagickFalse,methods[entry]);
3434             methods=DestroyStringList(methods);
3435             break;
3436           }
3437           case ColorEditColorCommand:
3438           {
3439             const char
3440               *ColorMenu[MaxNumberPens];
3441
3442             int
3443               pen_number;
3444
3445             /*
3446               Initialize menu selections.
3447             */
3448             for (i=0; i < (int) (MaxNumberPens-2); i++)
3449               ColorMenu[i]=resource_info->pen_colors[i];
3450             ColorMenu[MaxNumberPens-2]="Browser...";
3451             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3452             /*
3453               Select a pen color from the pop-up menu.
3454             */
3455             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3456               (const char **) ColorMenu,command);
3457             if (pen_number < 0)
3458               break;
3459             if (pen_number == (MaxNumberPens-2))
3460               {
3461                 static char
3462                   color_name[MaxTextExtent] = "gray";
3463
3464                 /*
3465                   Select a pen color from a dialog.
3466                 */
3467                 resource_info->pen_colors[pen_number]=color_name;
3468                 XColorBrowserWidget(display,windows,"Select",color_name);
3469                 if (*color_name == '\0')
3470                   break;
3471               }
3472             /*
3473               Set pen color.
3474             */
3475             (void) XParseColor(display,windows->map_info->colormap,
3476               resource_info->pen_colors[pen_number],&color);
3477             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3478               (unsigned int) MaxColors,&color);
3479             windows->pixel_info->pen_colors[pen_number]=color;
3480             pen_id=(unsigned int) pen_number;
3481             break;
3482           }
3483           case ColorEditBorderCommand:
3484           {
3485             const char
3486               *ColorMenu[MaxNumberPens];
3487
3488             int
3489               pen_number;
3490
3491             /*
3492               Initialize menu selections.
3493             */
3494             for (i=0; i < (int) (MaxNumberPens-2); i++)
3495               ColorMenu[i]=resource_info->pen_colors[i];
3496             ColorMenu[MaxNumberPens-2]="Browser...";
3497             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3498             /*
3499               Select a pen color from the pop-up menu.
3500             */
3501             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3502               (const char **) ColorMenu,command);
3503             if (pen_number < 0)
3504               break;
3505             if (pen_number == (MaxNumberPens-2))
3506               {
3507                 static char
3508                   color_name[MaxTextExtent] = "gray";
3509
3510                 /*
3511                   Select a pen color from a dialog.
3512                 */
3513                 resource_info->pen_colors[pen_number]=color_name;
3514                 XColorBrowserWidget(display,windows,"Select",color_name);
3515                 if (*color_name == '\0')
3516                   break;
3517               }
3518             /*
3519               Set border color.
3520             */
3521             (void) XParseColor(display,windows->map_info->colormap,
3522               resource_info->pen_colors[pen_number],&border_color);
3523             break;
3524           }
3525           case ColorEditFuzzCommand:
3526           {
3527             static char
3528               fuzz[MaxTextExtent];
3529
3530             static const char
3531               *FuzzMenu[] =
3532               {
3533                 "0%",
3534                 "2%",
3535                 "5%",
3536                 "10%",
3537                 "15%",
3538                 "Dialog...",
3539                 (char *) NULL,
3540               };
3541
3542             /*
3543               Select a command from the pop-up menu.
3544             */
3545             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3546               command);
3547             if (entry < 0)
3548               break;
3549             if (entry != 5)
3550               {
3551                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3552                   QuantumRange+1.0);
3553                 break;
3554               }
3555             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3556             (void) XDialogWidget(display,windows,"Ok",
3557               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3558             if (*fuzz == '\0')
3559               break;
3560             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3561             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3562               1.0);
3563             break;
3564           }
3565           case ColorEditUndoCommand:
3566           {
3567             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3568               image,exception);
3569             break;
3570           }
3571           case ColorEditHelpCommand:
3572           default:
3573           {
3574             XTextViewWidget(display,resource_info,windows,MagickFalse,
3575               "Help Viewer - Image Annotation",ImageColorEditHelp);
3576             break;
3577           }
3578           case ColorEditDismissCommand:
3579           {
3580             /*
3581               Prematurely exit.
3582             */
3583             state|=EscapeState;
3584             state|=ExitState;
3585             break;
3586           }
3587         }
3588         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3589         continue;
3590       }
3591     switch (event.type)
3592     {
3593       case ButtonPress:
3594       {
3595         if (event.xbutton.button != Button1)
3596           break;
3597         if ((event.xbutton.window != windows->image.id) &&
3598             (event.xbutton.window != windows->magnify.id))
3599           break;
3600         /*
3601           exit loop.
3602         */
3603         x=event.xbutton.x;
3604         y=event.xbutton.y;
3605         (void) XMagickCommand(display,resource_info,windows,
3606           SaveToUndoBufferCommand,image,exception);
3607         state|=UpdateConfigurationState;
3608         break;
3609       }
3610       case ButtonRelease:
3611       {
3612         if (event.xbutton.button != Button1)
3613           break;
3614         if ((event.xbutton.window != windows->image.id) &&
3615             (event.xbutton.window != windows->magnify.id))
3616           break;
3617         /*
3618           Update colormap information.
3619         */
3620         x=event.xbutton.x;
3621         y=event.xbutton.y;
3622         XConfigureImageColormap(display,resource_info,windows,*image,exception);
3623         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3624         XInfoWidget(display,windows,text);
3625         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3626         state&=(~UpdateConfigurationState);
3627         break;
3628       }
3629       case Expose:
3630         break;
3631       case KeyPress:
3632       {
3633         KeySym
3634           key_symbol;
3635
3636         if (event.xkey.window == windows->magnify.id)
3637           {
3638             Window
3639               window;
3640
3641             window=windows->magnify.id;
3642             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3643           }
3644         if (event.xkey.window != windows->image.id)
3645           break;
3646         /*
3647           Respond to a user key press.
3648         */
3649         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3650           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3651         switch ((int) key_symbol)
3652         {
3653           case XK_Escape:
3654           case XK_F20:
3655           {
3656             /*
3657               Prematurely exit.
3658             */
3659             state|=ExitState;
3660             break;
3661           }
3662           case XK_F1:
3663           case XK_Help:
3664           {
3665             XTextViewWidget(display,resource_info,windows,MagickFalse,
3666               "Help Viewer - Image Annotation",ImageColorEditHelp);
3667             break;
3668           }
3669           default:
3670           {
3671             (void) XBell(display,0);
3672             break;
3673           }
3674         }
3675         break;
3676       }
3677       case MotionNotify:
3678       {
3679         /*
3680           Map and unmap Info widget as cursor crosses its boundaries.
3681         */
3682         x=event.xmotion.x;
3683         y=event.xmotion.y;
3684         if (IfMagickTrue(windows->info.mapped) )
3685           {
3686             if ((x < (int) (windows->info.x+windows->info.width)) &&
3687                 (y < (int) (windows->info.y+windows->info.height)))
3688               (void) XWithdrawWindow(display,windows->info.id,
3689                 windows->info.screen);
3690           }
3691         else
3692           if ((x > (int) (windows->info.x+windows->info.width)) ||
3693               (y > (int) (windows->info.y+windows->info.height)))
3694             (void) XMapWindow(display,windows->info.id);
3695         break;
3696       }
3697       default:
3698         break;
3699     }
3700     if (event.xany.window == windows->magnify.id)
3701       {
3702         x=windows->magnify.x-windows->image.x;
3703         y=windows->magnify.y-windows->image.y;
3704       }
3705     x_offset=x;
3706     y_offset=y;
3707     if ((state & UpdateConfigurationState) != 0)
3708       {
3709         CacheView
3710           *image_view;
3711
3712         int
3713           x,
3714           y;
3715
3716         /*
3717           Pixel edit is relative to image configuration.
3718         */
3719         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3720           MagickTrue);
3721         color=windows->pixel_info->pen_colors[pen_id];
3722         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3723         width=(unsigned int) (*image)->columns;
3724         height=(unsigned int) (*image)->rows;
3725         x=0;
3726         y=0;
3727         if (windows->image.crop_geometry != (char *) NULL)
3728           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3729             &width,&height);
3730         x_offset=(int)
3731           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3732         y_offset=(int)
3733           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3734         if ((x_offset < 0) || (y_offset < 0))
3735           continue;
3736         if ((x_offset >= (int) (*image)->columns) ||
3737             (y_offset >= (int) (*image)->rows))
3738           continue;
3739         image_view=AcquireAuthenticCacheView(*image,exception);
3740         switch (method)
3741         {
3742           case PointMethod:
3743           default:
3744           {
3745             /*
3746               Update color information using point algorithm.
3747             */
3748             if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3749               return(MagickFalse);
3750             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3751               (ssize_t) y_offset,1,1,exception);
3752             if (q == (Quantum *) NULL)
3753               break;
3754             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3755             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3756             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3757             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3758             break;
3759           }
3760           case ReplaceMethod:
3761           {
3762             PixelInfo
3763               pixel,
3764               target;
3765
3766             /*
3767               Update color information using replace algorithm.
3768             */
3769             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3770               x_offset,(ssize_t) y_offset,&target,exception);
3771             if ((*image)->storage_class == DirectClass)
3772               {
3773                 for (y=0; y < (int) (*image)->rows; y++)
3774                 {
3775                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3776                     (*image)->columns,1,exception);
3777                   if (q == (Quantum *) NULL)
3778                     break;
3779                   for (x=0; x < (int) (*image)->columns; x++)
3780                   {
3781                     GetPixelInfoPixel(*image,q,&pixel);
3782                     if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3783                       {
3784                         SetPixelRed(*image,ScaleShortToQuantum(
3785                           color.red),q);
3786                         SetPixelGreen(*image,ScaleShortToQuantum(
3787                           color.green),q);
3788                         SetPixelBlue(*image,ScaleShortToQuantum(
3789                           color.blue),q);
3790                       }
3791                     q+=GetPixelChannels(*image);
3792                   }
3793                   if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3794                     break;
3795                 }
3796               }
3797             else
3798               {
3799                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3800                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3801                     {
3802                       (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3803                         color.red);
3804                       (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3805                         color.green);
3806                       (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3807                         color.blue);
3808                     }
3809                 (void) SyncImage(*image,exception);
3810               }
3811             break;
3812           }
3813           case FloodfillMethod:
3814           case FillToBorderMethod:
3815           {
3816             DrawInfo
3817               *draw_info;
3818
3819             PixelInfo
3820               target;
3821
3822             /*
3823               Update color information using floodfill algorithm.
3824             */
3825             (void) GetOneVirtualPixelInfo(*image,
3826               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3827               y_offset,&target,exception);
3828             if (method == FillToBorderMethod)
3829               {
3830                 target.red=(double)
3831                   ScaleShortToQuantum(border_color.red);
3832                 target.green=(double)
3833                   ScaleShortToQuantum(border_color.green);
3834                 target.blue=(double)
3835                   ScaleShortToQuantum(border_color.blue);
3836               }
3837             draw_info=CloneDrawInfo(resource_info->image_info,
3838               (DrawInfo *) NULL);
3839             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3840               AllCompliance,&draw_info->fill,exception);
3841             (void) FloodfillPaintImage(*image,draw_info,&target,
3842               (ssize_t)x_offset,(ssize_t)y_offset,
3843               IsMagickFalse(method == FloodfillMethod),exception);
3844             draw_info=DestroyDrawInfo(draw_info);
3845             break;
3846           }
3847           case ResetMethod:
3848           {
3849             /*
3850               Update color information using reset algorithm.
3851             */
3852             if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3853               return(MagickFalse);
3854             for (y=0; y < (int) (*image)->rows; y++)
3855             {
3856               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3857                 (*image)->columns,1,exception);
3858               if (q == (Quantum *) NULL)
3859                 break;
3860               for (x=0; x < (int) (*image)->columns; x++)
3861               {
3862                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3863                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3864                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3865                 q+=GetPixelChannels(*image);
3866               }
3867               if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3868                 break;
3869             }
3870             break;
3871           }
3872         }
3873         image_view=DestroyCacheView(image_view);
3874         state&=(~UpdateConfigurationState);
3875       }
3876   } while ((state & ExitState) == 0);
3877   (void) XSelectInput(display,windows->image.id,
3878     windows->image.attributes.event_mask);
3879   XSetCursorState(display,windows,MagickFalse);
3880   (void) XFreeCursor(display,cursor);
3881   return(MagickTrue);
3882 }
3883 \f
3884 /*
3885 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3886 %                                                                             %
3887 %                                                                             %
3888 %                                                                             %
3889 +   X C o m p o s i t e I m a g e                                             %
3890 %                                                                             %
3891 %                                                                             %
3892 %                                                                             %
3893 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3894 %
3895 %  XCompositeImage() requests an image name from the user, reads the image and
3896 %  composites it with the X window image at a location the user chooses with
3897 %  the pointer.
3898 %
3899 %  The format of the XCompositeImage method is:
3900 %
3901 %      MagickBooleanType XCompositeImage(Display *display,
3902 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3903 %        ExceptionInfo *exception)
3904 %
3905 %  A description of each parameter follows:
3906 %
3907 %    o display: Specifies a connection to an X server;  returned from
3908 %      XOpenDisplay.
3909 %
3910 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3911 %
3912 %    o windows: Specifies a pointer to a XWindows structure.
3913 %
3914 %    o image: the image; returned from ReadImage.
3915 %
3916 %    o exception: return any errors or warnings in this structure.
3917 %
3918 */
3919 static MagickBooleanType XCompositeImage(Display *display,
3920   XResourceInfo *resource_info,XWindows *windows,Image *image,
3921   ExceptionInfo *exception)
3922 {
3923   static char
3924     displacement_geometry[MaxTextExtent] = "30x30",
3925     filename[MaxTextExtent] = "\0";
3926
3927   static const char
3928     *CompositeMenu[] =
3929     {
3930       "Operators",
3931       "Dissolve",
3932       "Displace",
3933       "Help",
3934       "Dismiss",
3935       (char *) NULL
3936     };
3937
3938   static CompositeOperator
3939     compose = CopyCompositeOp;
3940
3941   static const ModeType
3942     CompositeCommands[] =
3943     {
3944       CompositeOperatorsCommand,
3945       CompositeDissolveCommand,
3946       CompositeDisplaceCommand,
3947       CompositeHelpCommand,
3948       CompositeDismissCommand
3949     };
3950
3951   char
3952     text[MaxTextExtent];
3953
3954   Cursor
3955     cursor;
3956
3957   Image
3958     *composite_image;
3959
3960   int
3961     entry,
3962     id,
3963     x,
3964     y;
3965
3966   double
3967     blend,
3968     scale_factor;
3969
3970   RectangleInfo
3971     highlight_info,
3972     composite_info;
3973
3974   unsigned int
3975     height,
3976     width;
3977
3978   size_t
3979     state;
3980
3981   XEvent
3982     event;
3983
3984   /*
3985     Request image file name from user.
3986   */
3987   XFileBrowserWidget(display,windows,"Composite",filename);
3988   if (*filename == '\0')
3989     return(MagickTrue);
3990   /*
3991     Read image.
3992   */
3993   XSetCursorState(display,windows,MagickTrue);
3994   XCheckRefreshWindows(display,windows);
3995   (void) CopyMagickString(resource_info->image_info->filename,filename,
3996     MaxTextExtent);
3997   composite_image=ReadImage(resource_info->image_info,exception);
3998   CatchException(exception);
3999   XSetCursorState(display,windows,MagickFalse);
4000   if (composite_image == (Image *) NULL)
4001     return(MagickFalse);
4002   /*
4003     Map Command widget.
4004   */
4005   (void) CloneString(&windows->command.name,"Composite");
4006   windows->command.data=1;
4007   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4008   (void) XMapRaised(display,windows->command.id);
4009   XClientMessage(display,windows->image.id,windows->im_protocols,
4010     windows->im_update_widget,CurrentTime);
4011   /*
4012     Track pointer until button 1 is pressed.
4013   */
4014   XQueryPosition(display,windows->image.id,&x,&y);
4015   (void) XSelectInput(display,windows->image.id,
4016     windows->image.attributes.event_mask | PointerMotionMask);
4017   composite_info.x=(ssize_t) windows->image.x+x;
4018   composite_info.y=(ssize_t) windows->image.y+y;
4019   composite_info.width=0;
4020   composite_info.height=0;
4021   cursor=XCreateFontCursor(display,XC_ul_angle);
4022   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4023   blend=0.0;
4024   state=DefaultState;
4025   do
4026   {
4027     if (IfMagickTrue(windows->info.mapped) )
4028       {
4029         /*
4030           Display pointer position.
4031         */
4032         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4033           (long) composite_info.x,(long) composite_info.y);
4034         XInfoWidget(display,windows,text);
4035       }
4036     highlight_info=composite_info;
4037     highlight_info.x=composite_info.x-windows->image.x;
4038     highlight_info.y=composite_info.y-windows->image.y;
4039     XHighlightRectangle(display,windows->image.id,
4040       windows->image.highlight_context,&highlight_info);
4041     /*
4042       Wait for next event.
4043     */
4044     XScreenEvent(display,windows,&event,exception);
4045     XHighlightRectangle(display,windows->image.id,
4046       windows->image.highlight_context,&highlight_info);
4047     if (event.xany.window == windows->command.id)
4048       {
4049         /*
4050           Select a command from the Command widget.
4051         */
4052         id=XCommandWidget(display,windows,CompositeMenu,&event);
4053         if (id < 0)
4054           continue;
4055         switch (CompositeCommands[id])
4056         {
4057           case CompositeOperatorsCommand:
4058           {
4059             char
4060               command[MaxTextExtent],
4061               **operators;
4062
4063             /*
4064               Select a command from the pop-up menu.
4065             */
4066             operators=GetCommandOptions(MagickComposeOptions);
4067             if (operators == (char **) NULL)
4068               break;
4069             entry=XMenuWidget(display,windows,CompositeMenu[id],
4070               (const char **) operators,command);
4071             if (entry >= 0)
4072               compose=(CompositeOperator) ParseCommandOption(
4073                 MagickComposeOptions,MagickFalse,operators[entry]);
4074             operators=DestroyStringList(operators);
4075             break;
4076           }
4077           case CompositeDissolveCommand:
4078           {
4079             static char
4080               factor[MaxTextExtent] = "20.0";
4081
4082             /*
4083               Dissolve the two images a given percent.
4084             */
4085             (void) XSetFunction(display,windows->image.highlight_context,
4086               GXcopy);
4087             (void) XDialogWidget(display,windows,"Dissolve",
4088               "Enter the blend factor (0.0 - 99.9%):",factor);
4089             (void) XSetFunction(display,windows->image.highlight_context,
4090               GXinvert);
4091             if (*factor == '\0')
4092               break;
4093             blend=StringToDouble(factor,(char **) NULL);
4094             compose=DissolveCompositeOp;
4095             break;
4096           }
4097           case CompositeDisplaceCommand:
4098           {
4099             /*
4100               Get horizontal and vertical scale displacement geometry.
4101             */
4102             (void) XSetFunction(display,windows->image.highlight_context,
4103               GXcopy);
4104             (void) XDialogWidget(display,windows,"Displace",
4105               "Enter the horizontal and vertical scale:",displacement_geometry);
4106             (void) XSetFunction(display,windows->image.highlight_context,
4107               GXinvert);
4108             if (*displacement_geometry == '\0')
4109               break;
4110             compose=DisplaceCompositeOp;
4111             break;
4112           }
4113           case CompositeHelpCommand:
4114           {
4115             (void) XSetFunction(display,windows->image.highlight_context,
4116               GXcopy);
4117             XTextViewWidget(display,resource_info,windows,MagickFalse,
4118               "Help Viewer - Image Composite",ImageCompositeHelp);
4119             (void) XSetFunction(display,windows->image.highlight_context,
4120               GXinvert);
4121             break;
4122           }
4123           case CompositeDismissCommand:
4124           {
4125             /*
4126               Prematurely exit.
4127             */
4128             state|=EscapeState;
4129             state|=ExitState;
4130             break;
4131           }
4132           default:
4133             break;
4134         }
4135         continue;
4136       }
4137     switch (event.type)
4138     {
4139       case ButtonPress:
4140       {
4141         if (IfMagickTrue(image->debug) )
4142           (void) LogMagickEvent(X11Event,GetMagickModule(),
4143             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4144             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4145         if (event.xbutton.button != Button1)
4146           break;
4147         if (event.xbutton.window != windows->image.id)
4148           break;
4149         /*
4150           Change cursor.
4151         */
4152         composite_info.width=composite_image->columns;
4153         composite_info.height=composite_image->rows;
4154         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4155         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4156         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4157         break;
4158       }
4159       case ButtonRelease:
4160       {
4161         if (IfMagickTrue(image->debug) )
4162           (void) LogMagickEvent(X11Event,GetMagickModule(),
4163             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4164             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4165         if (event.xbutton.button != Button1)
4166           break;
4167         if (event.xbutton.window != windows->image.id)
4168           break;
4169         if ((composite_info.width != 0) && (composite_info.height != 0))
4170           {
4171             /*
4172               User has selected the location of the composite image.
4173             */
4174             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4175             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4176             state|=ExitState;
4177           }
4178         break;
4179       }
4180       case Expose:
4181         break;
4182       case KeyPress:
4183       {
4184         char
4185           command[MaxTextExtent];
4186
4187         KeySym
4188           key_symbol;
4189
4190         int
4191           length;
4192
4193         if (event.xkey.window != windows->image.id)
4194           break;
4195         /*
4196           Respond to a user key press.
4197         */
4198         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4199           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4200         *(command+length)='\0';
4201         if (IfMagickTrue(image->debug) )
4202           (void) LogMagickEvent(X11Event,GetMagickModule(),
4203             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4204         switch ((int) key_symbol)
4205         {
4206           case XK_Escape:
4207           case XK_F20:
4208           {
4209             /*
4210               Prematurely exit.
4211             */
4212             composite_image=DestroyImage(composite_image);
4213             state|=EscapeState;
4214             state|=ExitState;
4215             break;
4216           }
4217           case XK_F1:
4218           case XK_Help:
4219           {
4220             (void) XSetFunction(display,windows->image.highlight_context,
4221               GXcopy);
4222             XTextViewWidget(display,resource_info,windows,MagickFalse,
4223               "Help Viewer - Image Composite",ImageCompositeHelp);
4224             (void) XSetFunction(display,windows->image.highlight_context,
4225               GXinvert);
4226             break;
4227           }
4228           default:
4229           {
4230             (void) XBell(display,0);
4231             break;
4232           }
4233         }
4234         break;
4235       }
4236       case MotionNotify:
4237       {
4238         /*
4239           Map and unmap Info widget as text cursor crosses its boundaries.
4240         */
4241         x=event.xmotion.x;
4242         y=event.xmotion.y;
4243         if (IfMagickTrue(windows->info.mapped) )
4244           {
4245             if ((x < (int) (windows->info.x+windows->info.width)) &&
4246                 (y < (int) (windows->info.y+windows->info.height)))
4247               (void) XWithdrawWindow(display,windows->info.id,
4248                 windows->info.screen);
4249           }
4250         else
4251           if ((x > (int) (windows->info.x+windows->info.width)) ||
4252               (y > (int) (windows->info.y+windows->info.height)))
4253             (void) XMapWindow(display,windows->info.id);
4254         composite_info.x=(ssize_t) windows->image.x+x;
4255         composite_info.y=(ssize_t) windows->image.y+y;
4256         break;
4257       }
4258       default:
4259       {
4260         if (IfMagickTrue(image->debug) )
4261           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4262             event.type);
4263         break;
4264       }
4265     }
4266   } while ((state & ExitState) == 0);
4267   (void) XSelectInput(display,windows->image.id,
4268     windows->image.attributes.event_mask);
4269   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4270   XSetCursorState(display,windows,MagickFalse);
4271   (void) XFreeCursor(display,cursor);
4272   if ((state & EscapeState) != 0)
4273     return(MagickTrue);
4274   /*
4275     Image compositing is relative to image configuration.
4276   */
4277   XSetCursorState(display,windows,MagickTrue);
4278   XCheckRefreshWindows(display,windows);
4279   width=(unsigned int) image->columns;
4280   height=(unsigned int) image->rows;
4281   x=0;
4282   y=0;
4283   if (windows->image.crop_geometry != (char *) NULL)
4284     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4285   scale_factor=(double) width/windows->image.ximage->width;
4286   composite_info.x+=x;
4287   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4288   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4289   scale_factor=(double) height/windows->image.ximage->height;
4290   composite_info.y+=y;
4291   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4292   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4293   if ((composite_info.width != composite_image->columns) ||
4294       (composite_info.height != composite_image->rows))
4295     {
4296       Image
4297         *resize_image;
4298
4299       /*
4300         Scale composite image.
4301       */
4302       resize_image=ResizeImage(composite_image,composite_info.width,
4303         composite_info.height,composite_image->filter,exception);
4304       composite_image=DestroyImage(composite_image);
4305       if (resize_image == (Image *) NULL)
4306         {
4307           XSetCursorState(display,windows,MagickFalse);
4308           return(MagickFalse);
4309         }
4310       composite_image=resize_image;
4311     }
4312   if (compose == DisplaceCompositeOp)
4313     (void) SetImageArtifact(composite_image,"compose:args",
4314       displacement_geometry);
4315   if (blend != 0.0)
4316     {
4317       CacheView
4318         *image_view;
4319
4320       int
4321         y;
4322
4323       Quantum
4324         opacity;
4325
4326       register int
4327         x;
4328
4329       register Quantum
4330         *q;
4331
4332       /*
4333         Create mattes for blending.
4334       */
4335       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4336       opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4337         ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4338       if (IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
4339         return(MagickFalse);
4340       image->alpha_trait=BlendPixelTrait;
4341       image_view=AcquireAuthenticCacheView(image,exception);
4342       for (y=0; y < (int) image->rows; y++)
4343       {
4344         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4345           exception);
4346         if (q == (Quantum *) NULL)
4347           break;
4348         for (x=0; x < (int) image->columns; x++)
4349         {
4350           SetPixelAlpha(image,opacity,q);
4351           q+=GetPixelChannels(image);
4352         }
4353         if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
4354           break;
4355       }
4356       image_view=DestroyCacheView(image_view);
4357     }
4358   /*
4359     Composite image with X Image window.
4360   */
4361   (void) CompositeImage(image,composite_image,compose,MagickTrue,
4362     composite_info.x,composite_info.y,exception);
4363   composite_image=DestroyImage(composite_image);
4364   XSetCursorState(display,windows,MagickFalse);
4365   /*
4366     Update image configuration.
4367   */
4368   XConfigureImageColormap(display,resource_info,windows,image,exception);
4369   (void) XConfigureImage(display,resource_info,windows,image,exception);
4370   return(MagickTrue);
4371 }
4372 \f
4373 /*
4374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4375 %                                                                             %
4376 %                                                                             %
4377 %                                                                             %
4378 +   X C o n f i g u r e I m a g e                                             %
4379 %                                                                             %
4380 %                                                                             %
4381 %                                                                             %
4382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4383 %
4384 %  XConfigureImage() creates a new X image.  It also notifies the window
4385 %  manager of the new image size and configures the transient widows.
4386 %
4387 %  The format of the XConfigureImage method is:
4388 %
4389 %      MagickBooleanType XConfigureImage(Display *display,
4390 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4391 %        ExceptionInfo *exception)
4392 %
4393 %  A description of each parameter follows:
4394 %
4395 %    o display: Specifies a connection to an X server; returned from
4396 %      XOpenDisplay.
4397 %
4398 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4399 %
4400 %    o windows: Specifies a pointer to a XWindows structure.
4401 %
4402 %    o image: the image.
4403 %
4404 %    o exception: return any errors or warnings in this structure.
4405 %
4406 %    o exception: return any errors or warnings in this structure.
4407 %
4408 */
4409 static MagickBooleanType XConfigureImage(Display *display,
4410   XResourceInfo *resource_info,XWindows *windows,Image *image,
4411   ExceptionInfo *exception)
4412 {
4413   char
4414     geometry[MaxTextExtent];
4415
4416   MagickStatusType
4417     status;
4418
4419   size_t
4420     mask,
4421     height,
4422     width;
4423
4424   ssize_t
4425     x,
4426     y;
4427
4428   XSizeHints
4429     *size_hints;
4430
4431   XWindowChanges
4432     window_changes;
4433
4434   /*
4435     Dismiss if window dimensions are zero.
4436   */
4437   width=(unsigned int) windows->image.window_changes.width;
4438   height=(unsigned int) windows->image.window_changes.height;
4439   if (IfMagickTrue(image->debug) )
4440     (void) LogMagickEvent(X11Event,GetMagickModule(),
4441       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4442       windows->image.ximage->height,(double) width,(double) height);
4443   if ((width*height) == 0)
4444     return(MagickTrue);
4445   x=0;
4446   y=0;
4447   /*
4448     Resize image to fit Image window dimensions.
4449   */
4450   XSetCursorState(display,windows,MagickTrue);
4451   (void) XFlush(display);
4452   if (((int) width != windows->image.ximage->width) ||
4453       ((int) height != windows->image.ximage->height))
4454     image->taint=MagickTrue;
4455   windows->magnify.x=(int)
4456     width*windows->magnify.x/windows->image.ximage->width;
4457   windows->magnify.y=(int)
4458     height*windows->magnify.y/windows->image.ximage->height;
4459   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4460   windows->image.y=(int)
4461     (height*windows->image.y/windows->image.ximage->height);
4462   status=XMakeImage(display,resource_info,&windows->image,image,
4463     (unsigned int) width,(unsigned int) height,exception);
4464   if (IfMagickFalse(status) )
4465     XNoticeWidget(display,windows,"Unable to configure X image:",
4466       windows->image.name);
4467   /*
4468     Notify window manager of the new configuration.
4469   */
4470   if (resource_info->image_geometry != (char *) NULL)
4471     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4472       resource_info->image_geometry);
4473   else
4474     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4475       XDisplayWidth(display,windows->image.screen),
4476       XDisplayHeight(display,windows->image.screen));
4477   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4478   window_changes.width=(int) width;
4479   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4480     window_changes.width=XDisplayWidth(display,windows->image.screen);
4481   window_changes.height=(int) height;
4482   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4483     window_changes.height=XDisplayHeight(display,windows->image.screen);
4484   mask=(size_t) (CWWidth | CWHeight);
4485   if (resource_info->backdrop)
4486     {
4487       mask|=CWX | CWY;
4488       window_changes.x=(int)
4489         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4490       window_changes.y=(int)
4491         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4492     }
4493   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4494     (unsigned int) mask,&window_changes);
4495   (void) XClearWindow(display,windows->image.id);
4496   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4497   /*
4498     Update Magnify window configuration.
4499   */
4500   if (IfMagickTrue(windows->magnify.mapped) )
4501     XMakeMagnifyImage(display,windows,exception);
4502   windows->pan.crop_geometry=windows->image.crop_geometry;
4503   XBestIconSize(display,&windows->pan,image);
4504   while (((windows->pan.width << 1) < MaxIconSize) &&
4505          ((windows->pan.height << 1) < MaxIconSize))
4506   {
4507     windows->pan.width<<=1;
4508     windows->pan.height<<=1;
4509   }
4510   if (windows->pan.geometry != (char *) NULL)
4511     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4512       &windows->pan.width,&windows->pan.height);
4513   window_changes.width=(int) windows->pan.width;
4514   window_changes.height=(int) windows->pan.height;
4515   size_hints=XAllocSizeHints();
4516   if (size_hints != (XSizeHints *) NULL)
4517     {
4518       /*
4519         Set new size hints.
4520       */
4521       size_hints->flags=PSize | PMinSize | PMaxSize;
4522       size_hints->width=window_changes.width;
4523       size_hints->height=window_changes.height;
4524       size_hints->min_width=size_hints->width;
4525       size_hints->min_height=size_hints->height;
4526       size_hints->max_width=size_hints->width;
4527       size_hints->max_height=size_hints->height;
4528       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4529       (void) XFree((void *) size_hints);
4530     }
4531   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4532     (unsigned int) (CWWidth | CWHeight),&window_changes);
4533   /*
4534     Update icon window configuration.
4535   */
4536   windows->icon.crop_geometry=windows->image.crop_geometry;
4537   XBestIconSize(display,&windows->icon,image);
4538   window_changes.width=(int) windows->icon.width;
4539   window_changes.height=(int) windows->icon.height;
4540   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4541     (unsigned int) (CWWidth | CWHeight),&window_changes);
4542   XSetCursorState(display,windows,MagickFalse);
4543   return(IsMagickTrue(status));
4544 }
4545 \f
4546 /*
4547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4548 %                                                                             %
4549 %                                                                             %
4550 %                                                                             %
4551 +   X C r o p I m a g e                                                       %
4552 %                                                                             %
4553 %                                                                             %
4554 %                                                                             %
4555 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4556 %
4557 %  XCropImage() allows the user to select a region of the image and crop, copy,
4558 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4559 %  the image with XPasteImage.
4560 %
4561 %  The format of the XCropImage method is:
4562 %
4563 %      MagickBooleanType XCropImage(Display *display,
4564 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4565 %        const ClipboardMode mode,ExceptionInfo *exception)
4566 %
4567 %  A description of each parameter follows:
4568 %
4569 %    o display: Specifies a connection to an X server; returned from
4570 %      XOpenDisplay.
4571 %
4572 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4573 %
4574 %    o windows: Specifies a pointer to a XWindows structure.
4575 %
4576 %    o image: the image; returned from ReadImage.
4577 %
4578 %    o mode: This unsigned value specified whether the image should be
4579 %      cropped, copied, or cut.
4580 %
4581 %    o exception: return any errors or warnings in this structure.
4582 %
4583 */
4584 static MagickBooleanType XCropImage(Display *display,
4585   XResourceInfo *resource_info,XWindows *windows,Image *image,
4586   const ClipboardMode mode,ExceptionInfo *exception)
4587 {
4588   static const char
4589     *CropModeMenu[] =
4590     {
4591       "Help",
4592       "Dismiss",
4593       (char *) NULL
4594     },
4595     *RectifyModeMenu[] =
4596     {
4597       "Crop",
4598       "Help",
4599       "Dismiss",
4600       (char *) NULL
4601     };
4602
4603   static const ModeType
4604     CropCommands[] =
4605     {
4606       CropHelpCommand,
4607       CropDismissCommand
4608     },
4609     RectifyCommands[] =
4610     {
4611       RectifyCopyCommand,
4612       RectifyHelpCommand,
4613       RectifyDismissCommand
4614     };
4615
4616   CacheView
4617     *image_view;
4618
4619   char
4620     command[MaxTextExtent],
4621     text[MaxTextExtent];
4622
4623   Cursor
4624     cursor;
4625
4626   int
4627     id,
4628     x,
4629     y;
4630
4631   KeySym
4632     key_symbol;
4633
4634   Image
4635     *crop_image;
4636
4637   double
4638     scale_factor;
4639
4640   RectangleInfo
4641     crop_info,
4642     highlight_info;
4643
4644   register Quantum
4645     *q;
4646
4647   unsigned int
4648     height,
4649     width;
4650
4651   size_t
4652     state;
4653
4654   XEvent
4655     event;
4656
4657   /*
4658     Map Command widget.
4659   */
4660   switch (mode)
4661   {
4662     case CopyMode:
4663     {
4664       (void) CloneString(&windows->command.name,"Copy");
4665       break;
4666     }
4667     case CropMode:
4668     {
4669       (void) CloneString(&windows->command.name,"Crop");
4670       break;
4671     }
4672     case CutMode:
4673     {
4674       (void) CloneString(&windows->command.name,"Cut");
4675       break;
4676     }
4677   }
4678   RectifyModeMenu[0]=windows->command.name;
4679   windows->command.data=0;
4680   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4681   (void) XMapRaised(display,windows->command.id);
4682   XClientMessage(display,windows->image.id,windows->im_protocols,
4683     windows->im_update_widget,CurrentTime);
4684   /*
4685     Track pointer until button 1 is pressed.
4686   */
4687   XQueryPosition(display,windows->image.id,&x,&y);
4688   (void) XSelectInput(display,windows->image.id,
4689     windows->image.attributes.event_mask | PointerMotionMask);
4690   crop_info.x=(ssize_t) windows->image.x+x;
4691   crop_info.y=(ssize_t) windows->image.y+y;
4692   crop_info.width=0;
4693   crop_info.height=0;
4694   cursor=XCreateFontCursor(display,XC_fleur);
4695   state=DefaultState;
4696   do
4697   {
4698     if (IfMagickTrue(windows->info.mapped) )
4699       {
4700         /*
4701           Display pointer position.
4702         */
4703         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4704           (long) crop_info.x,(long) crop_info.y);
4705         XInfoWidget(display,windows,text);
4706       }
4707     /*
4708       Wait for next event.
4709     */
4710     XScreenEvent(display,windows,&event,exception);
4711     if (event.xany.window == windows->command.id)
4712       {
4713         /*
4714           Select a command from the Command widget.
4715         */
4716         id=XCommandWidget(display,windows,CropModeMenu,&event);
4717         if (id < 0)
4718           continue;
4719         switch (CropCommands[id])
4720         {
4721           case CropHelpCommand:
4722           {
4723             switch (mode)
4724             {
4725               case CopyMode:
4726               {
4727                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4728                   "Help Viewer - Image Copy",ImageCopyHelp);
4729                 break;
4730               }
4731               case CropMode:
4732               {
4733                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4734                   "Help Viewer - Image Crop",ImageCropHelp);
4735                 break;
4736               }
4737               case CutMode:
4738               {
4739                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4740                   "Help Viewer - Image Cut",ImageCutHelp);
4741                 break;
4742               }
4743             }
4744             break;
4745           }
4746           case CropDismissCommand:
4747           {
4748             /*
4749               Prematurely exit.
4750             */
4751             state|=EscapeState;
4752             state|=ExitState;
4753             break;
4754           }
4755           default:
4756             break;
4757         }
4758         continue;
4759       }
4760     switch (event.type)
4761     {
4762       case ButtonPress:
4763       {
4764         if (event.xbutton.button != Button1)
4765           break;
4766         if (event.xbutton.window != windows->image.id)
4767           break;
4768         /*
4769           Note first corner of cropping rectangle-- exit loop.
4770         */
4771         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4772         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4773         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4774         state|=ExitState;
4775         break;
4776       }
4777       case ButtonRelease:
4778         break;
4779       case Expose:
4780         break;
4781       case KeyPress:
4782       {
4783         if (event.xkey.window != windows->image.id)
4784           break;
4785         /*
4786           Respond to a user key press.
4787         */
4788         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4789           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4790         switch ((int) key_symbol)
4791         {
4792           case XK_Escape:
4793           case XK_F20:
4794           {
4795             /*
4796               Prematurely exit.
4797             */
4798             state|=EscapeState;
4799             state|=ExitState;
4800             break;
4801           }
4802           case XK_F1:
4803           case XK_Help:
4804           {
4805             switch (mode)
4806             {
4807               case CopyMode:
4808               {
4809                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4810                   "Help Viewer - Image Copy",ImageCopyHelp);
4811                 break;
4812               }
4813               case CropMode:
4814               {
4815                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4816                   "Help Viewer - Image Crop",ImageCropHelp);
4817                 break;
4818               }
4819               case CutMode:
4820               {
4821                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4822                   "Help Viewer - Image Cut",ImageCutHelp);
4823                 break;
4824               }
4825             }
4826             break;
4827           }
4828           default:
4829           {
4830             (void) XBell(display,0);
4831             break;
4832           }
4833         }
4834         break;
4835       }
4836       case MotionNotify:
4837       {
4838         if (event.xmotion.window != windows->image.id)
4839           break;
4840         /*
4841           Map and unmap Info widget as text cursor crosses its boundaries.
4842         */
4843         x=event.xmotion.x;
4844         y=event.xmotion.y;
4845         if (IfMagickTrue(windows->info.mapped) )
4846           {
4847             if ((x < (int) (windows->info.x+windows->info.width)) &&
4848                 (y < (int) (windows->info.y+windows->info.height)))
4849               (void) XWithdrawWindow(display,windows->info.id,
4850                 windows->info.screen);
4851           }
4852         else
4853           if ((x > (int) (windows->info.x+windows->info.width)) ||
4854               (y > (int) (windows->info.y+windows->info.height)))
4855             (void) XMapWindow(display,windows->info.id);
4856         crop_info.x=(ssize_t) windows->image.x+x;
4857         crop_info.y=(ssize_t) windows->image.y+y;
4858         break;
4859       }
4860       default:
4861         break;
4862     }
4863   } while ((state & ExitState) == 0);
4864   (void) XSelectInput(display,windows->image.id,
4865     windows->image.attributes.event_mask);
4866   if ((state & EscapeState) != 0)
4867     {
4868       /*
4869         User want to exit without cropping.
4870       */
4871       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4872       (void) XFreeCursor(display,cursor);
4873       return(MagickTrue);
4874     }
4875   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4876   do
4877   {
4878     /*
4879       Size rectangle as pointer moves until the mouse button is released.
4880     */
4881     x=(int) crop_info.x;
4882     y=(int) crop_info.y;
4883     crop_info.width=0;
4884     crop_info.height=0;
4885     state=DefaultState;
4886     do
4887     {
4888       highlight_info=crop_info;
4889       highlight_info.x=crop_info.x-windows->image.x;
4890       highlight_info.y=crop_info.y-windows->image.y;
4891       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4892         {
4893           /*
4894             Display info and draw cropping rectangle.
4895           */
4896           if (IfMagickFalse(windows->info.mapped) )
4897             (void) XMapWindow(display,windows->info.id);
4898           (void) FormatLocaleString(text,MaxTextExtent,
4899             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4900             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4901           XInfoWidget(display,windows,text);
4902           XHighlightRectangle(display,windows->image.id,
4903             windows->image.highlight_context,&highlight_info);
4904         }
4905       else
4906         if (IfMagickTrue(windows->info.mapped) )
4907           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4908       /*
4909         Wait for next event.
4910       */
4911       XScreenEvent(display,windows,&event,exception);
4912       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4913         XHighlightRectangle(display,windows->image.id,
4914           windows->image.highlight_context,&highlight_info);
4915       switch (event.type)
4916       {
4917         case ButtonPress:
4918         {
4919           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4920           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4921           break;
4922         }
4923         case ButtonRelease:
4924         {
4925           /*
4926             User has committed to cropping rectangle.
4927           */
4928           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4929           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4930           XSetCursorState(display,windows,MagickFalse);
4931           state|=ExitState;
4932           windows->command.data=0;
4933           (void) XCommandWidget(display,windows,RectifyModeMenu,
4934             (XEvent *) NULL);
4935           break;
4936         }
4937         case Expose:
4938           break;
4939         case MotionNotify:
4940         {
4941           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4942           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4943         }
4944         default:
4945           break;
4946       }
4947       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4948           ((state & ExitState) != 0))
4949         {
4950           /*
4951             Check boundary conditions.
4952           */
4953           if (crop_info.x < 0)
4954             crop_info.x=0;
4955           else
4956             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4957               crop_info.x=(ssize_t) windows->image.ximage->width;
4958           if ((int) crop_info.x < x)
4959             crop_info.width=(unsigned int) (x-crop_info.x);
4960           else
4961             {
4962               crop_info.width=(unsigned int) (crop_info.x-x);
4963               crop_info.x=(ssize_t) x;
4964             }
4965           if (crop_info.y < 0)
4966             crop_info.y=0;
4967           else
4968             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4969               crop_info.y=(ssize_t) windows->image.ximage->height;
4970           if ((int) crop_info.y < y)
4971             crop_info.height=(unsigned int) (y-crop_info.y);
4972           else
4973             {
4974               crop_info.height=(unsigned int) (crop_info.y-y);
4975               crop_info.y=(ssize_t) y;
4976             }
4977         }
4978     } while ((state & ExitState) == 0);
4979     /*
4980       Wait for user to grab a corner of the rectangle or press return.
4981     */
4982     state=DefaultState;
4983     (void) XMapWindow(display,windows->info.id);
4984     do
4985     {
4986       if (IfMagickTrue(windows->info.mapped) )
4987         {
4988           /*
4989             Display pointer position.
4990           */
4991           (void) FormatLocaleString(text,MaxTextExtent,
4992             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4993             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4994           XInfoWidget(display,windows,text);
4995         }
4996       highlight_info=crop_info;
4997       highlight_info.x=crop_info.x-windows->image.x;
4998       highlight_info.y=crop_info.y-windows->image.y;
4999       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
5000         {
5001           state|=EscapeState;
5002           state|=ExitState;
5003           break;
5004         }
5005       XHighlightRectangle(display,windows->image.id,
5006         windows->image.highlight_context,&highlight_info);
5007       XScreenEvent(display,windows,&event,exception);
5008       if (event.xany.window == windows->command.id)
5009         {
5010           /*
5011             Select a command from the Command widget.
5012           */
5013           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5014           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5015           (void) XSetFunction(display,windows->image.highlight_context,
5016             GXinvert);
5017           XHighlightRectangle(display,windows->image.id,
5018             windows->image.highlight_context,&highlight_info);
5019           if (id >= 0)
5020             switch (RectifyCommands[id])
5021             {
5022               case RectifyCopyCommand:
5023               {
5024                 state|=ExitState;
5025                 break;
5026               }
5027               case RectifyHelpCommand:
5028               {
5029                 (void) XSetFunction(display,windows->image.highlight_context,
5030                   GXcopy);
5031                 switch (mode)
5032                 {
5033                   case CopyMode:
5034                   {
5035                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5036                       "Help Viewer - Image Copy",ImageCopyHelp);
5037                     break;
5038                   }
5039                   case CropMode:
5040                   {
5041                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5042                       "Help Viewer - Image Crop",ImageCropHelp);
5043                     break;
5044                   }
5045                   case CutMode:
5046                   {
5047                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5048                       "Help Viewer - Image Cut",ImageCutHelp);
5049                     break;
5050                   }
5051                 }
5052                 (void) XSetFunction(display,windows->image.highlight_context,
5053                   GXinvert);
5054                 break;
5055               }
5056               case RectifyDismissCommand:
5057               {
5058                 /*
5059                   Prematurely exit.
5060                 */
5061                 state|=EscapeState;
5062                 state|=ExitState;
5063                 break;
5064               }
5065               default:
5066                 break;
5067             }
5068           continue;
5069         }
5070       XHighlightRectangle(display,windows->image.id,
5071         windows->image.highlight_context,&highlight_info);
5072       switch (event.type)
5073       {
5074         case ButtonPress:
5075         {
5076           if (event.xbutton.button != Button1)
5077             break;
5078           if (event.xbutton.window != windows->image.id)
5079             break;
5080           x=windows->image.x+event.xbutton.x;
5081           y=windows->image.y+event.xbutton.y;
5082           if ((x < (int) (crop_info.x+RoiDelta)) &&
5083               (x > (int) (crop_info.x-RoiDelta)) &&
5084               (y < (int) (crop_info.y+RoiDelta)) &&
5085               (y > (int) (crop_info.y-RoiDelta)))
5086             {
5087               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5088               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5089               state|=UpdateConfigurationState;
5090               break;
5091             }
5092           if ((x < (int) (crop_info.x+RoiDelta)) &&
5093               (x > (int) (crop_info.x-RoiDelta)) &&
5094               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5095               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5096             {
5097               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5098               state|=UpdateConfigurationState;
5099               break;
5100             }
5101           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5102               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5103               (y < (int) (crop_info.y+RoiDelta)) &&
5104               (y > (int) (crop_info.y-RoiDelta)))
5105             {
5106               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5107               state|=UpdateConfigurationState;
5108               break;
5109             }
5110           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5111               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5112               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5113               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5114             {
5115               state|=UpdateConfigurationState;
5116               break;
5117             }
5118         }
5119         case ButtonRelease:
5120         {
5121           if (event.xbutton.window == windows->pan.id)
5122             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5123                 (highlight_info.y != crop_info.y-windows->image.y))
5124               XHighlightRectangle(display,windows->image.id,
5125                 windows->image.highlight_context,&highlight_info);
5126           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5127             event.xbutton.time);
5128           break;
5129         }
5130         case Expose:
5131         {
5132           if (event.xexpose.window == windows->image.id)
5133             if (event.xexpose.count == 0)
5134               {
5135                 event.xexpose.x=(int) highlight_info.x;
5136                 event.xexpose.y=(int) highlight_info.y;
5137                 event.xexpose.width=(int) highlight_info.width;
5138                 event.xexpose.height=(int) highlight_info.height;
5139                 XRefreshWindow(display,&windows->image,&event);
5140               }
5141           if (event.xexpose.window == windows->info.id)
5142             if (event.xexpose.count == 0)
5143               XInfoWidget(display,windows,text);
5144           break;
5145         }
5146         case KeyPress:
5147         {
5148           if (event.xkey.window != windows->image.id)
5149             break;
5150           /*
5151             Respond to a user key press.
5152           */
5153           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5154             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5155           switch ((int) key_symbol)
5156           {
5157             case XK_Escape:
5158             case XK_F20:
5159               state|=EscapeState;
5160             case XK_Return:
5161             {
5162               state|=ExitState;
5163               break;
5164             }
5165             case XK_Home:
5166             case XK_KP_Home:
5167             {
5168               crop_info.x=(ssize_t) (windows->image.width/2L-
5169                 crop_info.width/2L);
5170               crop_info.y=(ssize_t) (windows->image.height/2L-
5171                 crop_info.height/2L);
5172               break;
5173             }
5174             case XK_Left:
5175             case XK_KP_Left:
5176             {
5177               crop_info.x--;
5178               break;
5179             }
5180             case XK_Up:
5181             case XK_KP_Up:
5182             case XK_Next:
5183             {
5184               crop_info.y--;
5185               break;
5186             }
5187             case XK_Right:
5188             case XK_KP_Right:
5189             {
5190               crop_info.x++;
5191               break;
5192             }
5193             case XK_Prior:
5194             case XK_Down:
5195             case XK_KP_Down:
5196             {
5197               crop_info.y++;
5198               break;
5199             }
5200             case XK_F1:
5201             case XK_Help:
5202             {
5203               (void) XSetFunction(display,windows->image.highlight_context,
5204                 GXcopy);
5205               switch (mode)
5206               {
5207                 case CopyMode:
5208                 {
5209                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5210                     "Help Viewer - Image Copy",ImageCopyHelp);
5211                   break;
5212                 }
5213                 case CropMode:
5214                 {
5215                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5216                     "Help Viewer - Image Cropg",ImageCropHelp);
5217                   break;
5218                 }
5219                 case CutMode:
5220                 {
5221                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5222                     "Help Viewer - Image Cutg",ImageCutHelp);
5223                   break;
5224                 }
5225               }
5226               (void) XSetFunction(display,windows->image.highlight_context,
5227                 GXinvert);
5228               break;
5229             }
5230             default:
5231             {
5232               (void) XBell(display,0);
5233               break;
5234             }
5235           }
5236           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5237             event.xkey.time);
5238           break;
5239         }
5240         case KeyRelease:
5241           break;
5242         case MotionNotify:
5243         {
5244           if (event.xmotion.window != windows->image.id)
5245             break;
5246           /*
5247             Map and unmap Info widget as text cursor crosses its boundaries.
5248           */
5249           x=event.xmotion.x;
5250           y=event.xmotion.y;
5251           if (IfMagickTrue(windows->info.mapped) )
5252             {
5253               if ((x < (int) (windows->info.x+windows->info.width)) &&
5254                   (y < (int) (windows->info.y+windows->info.height)))
5255                 (void) XWithdrawWindow(display,windows->info.id,
5256                   windows->info.screen);
5257             }
5258           else
5259             if ((x > (int) (windows->info.x+windows->info.width)) ||
5260                 (y > (int) (windows->info.y+windows->info.height)))
5261               (void) XMapWindow(display,windows->info.id);
5262           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5263           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5264           break;
5265         }
5266         case SelectionRequest:
5267         {
5268           XSelectionEvent
5269             notify;
5270
5271           XSelectionRequestEvent
5272             *request;
5273
5274           /*
5275             Set primary selection.
5276           */
5277           (void) FormatLocaleString(text,MaxTextExtent,
5278             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5279             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5280           request=(&(event.xselectionrequest));
5281           (void) XChangeProperty(request->display,request->requestor,
5282             request->property,request->target,8,PropModeReplace,
5283             (unsigned char *) text,(int) strlen(text));
5284           notify.type=SelectionNotify;
5285           notify.display=request->display;
5286           notify.requestor=request->requestor;
5287           notify.selection=request->selection;
5288           notify.target=request->target;
5289           notify.time=request->time;
5290           if (request->property == None)
5291             notify.property=request->target;
5292           else
5293             notify.property=request->property;
5294           (void) XSendEvent(request->display,request->requestor,False,0,
5295             (XEvent *) &notify);
5296         }
5297         default:
5298           break;
5299       }
5300       if ((state & UpdateConfigurationState) != 0)
5301         {
5302           (void) XPutBackEvent(display,&event);
5303           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5304           break;
5305         }
5306     } while ((state & ExitState) == 0);
5307   } while ((state & ExitState) == 0);
5308   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5309   XSetCursorState(display,windows,MagickFalse);
5310   if ((state & EscapeState) != 0)
5311     return(MagickTrue);
5312   if (mode == CropMode)
5313     if (((int) crop_info.width != windows->image.ximage->width) ||
5314         ((int) crop_info.height != windows->image.ximage->height))
5315       {
5316         /*
5317           Reconfigure Image window as defined by cropping rectangle.
5318         */
5319         XSetCropGeometry(display,windows,&crop_info,image);
5320         windows->image.window_changes.width=(int) crop_info.width;
5321         windows->image.window_changes.height=(int) crop_info.height;
5322         (void) XConfigureImage(display,resource_info,windows,image,exception);
5323         return(MagickTrue);
5324       }
5325   /*
5326     Copy image before applying image transforms.
5327   */
5328   XSetCursorState(display,windows,MagickTrue);
5329   XCheckRefreshWindows(display,windows);
5330   width=(unsigned int) image->columns;
5331   height=(unsigned int) image->rows;
5332   x=0;
5333   y=0;
5334   if (windows->image.crop_geometry != (char *) NULL)
5335     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5336   scale_factor=(double) width/windows->image.ximage->width;
5337   crop_info.x+=x;
5338   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5339   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5340   scale_factor=(double) height/windows->image.ximage->height;
5341   crop_info.y+=y;
5342   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5343   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5344   crop_image=CropImage(image,&crop_info,exception);
5345   XSetCursorState(display,windows,MagickFalse);
5346   if (crop_image == (Image *) NULL)
5347     return(MagickFalse);
5348   if (resource_info->copy_image != (Image *) NULL)
5349     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5350   resource_info->copy_image=crop_image;
5351   if (mode == CopyMode)
5352     {
5353       (void) XConfigureImage(display,resource_info,windows,image,exception);
5354       return(MagickTrue);
5355     }
5356   /*
5357     Cut image.
5358   */
5359   if (IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
5360     return(MagickFalse);
5361   image->alpha_trait=BlendPixelTrait;
5362   image_view=AcquireAuthenticCacheView(image,exception);
5363   for (y=0; y < (int) crop_info.height; y++)
5364   {
5365     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5366       crop_info.width,1,exception);
5367     if (q == (Quantum *) NULL)
5368       break;
5369     for (x=0; x < (int) crop_info.width; x++)
5370     {
5371       SetPixelAlpha(image,TransparentAlpha,q);
5372       q+=GetPixelChannels(image);
5373     }
5374     if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
5375       break;
5376   }
5377   image_view=DestroyCacheView(image_view);
5378   /*
5379     Update image configuration.
5380   */
5381   XConfigureImageColormap(display,resource_info,windows,image,exception);
5382   (void) XConfigureImage(display,resource_info,windows,image,exception);
5383   return(MagickTrue);
5384 }
5385 \f
5386 /*
5387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5388 %                                                                             %
5389 %                                                                             %
5390 %                                                                             %
5391 +   X D r a w I m a g e                                                       %
5392 %                                                                             %
5393 %                                                                             %
5394 %                                                                             %
5395 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5396 %
5397 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5398 %  the image.
5399 %
5400 %  The format of the XDrawEditImage method is:
5401 %
5402 %      MagickBooleanType XDrawEditImage(Display *display,
5403 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5404 %        ExceptionInfo *exception)
5405 %
5406 %  A description of each parameter follows:
5407 %
5408 %    o display: Specifies a connection to an X server; returned from
5409 %      XOpenDisplay.
5410 %
5411 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5412 %
5413 %    o windows: Specifies a pointer to a XWindows structure.
5414 %
5415 %    o image: the image.
5416 %
5417 %    o exception: return any errors or warnings in this structure.
5418 %
5419 */
5420 static MagickBooleanType XDrawEditImage(Display *display,
5421   XResourceInfo *resource_info,XWindows *windows,Image **image,
5422   ExceptionInfo *exception)
5423 {
5424   static const char
5425     *DrawMenu[] =
5426     {
5427       "Element",
5428       "Color",
5429       "Stipple",
5430       "Width",
5431       "Undo",
5432       "Help",
5433       "Dismiss",
5434       (char *) NULL
5435     };
5436
5437   static ElementType
5438     element = PointElement;
5439
5440   static const ModeType
5441     DrawCommands[] =
5442     {
5443       DrawElementCommand,
5444       DrawColorCommand,
5445       DrawStippleCommand,
5446       DrawWidthCommand,
5447       DrawUndoCommand,
5448       DrawHelpCommand,
5449       DrawDismissCommand
5450     };
5451
5452   static Pixmap
5453     stipple = (Pixmap) NULL;
5454
5455   static unsigned int
5456     pen_id = 0,
5457     line_width = 1;
5458
5459   char
5460     command[MaxTextExtent],
5461     text[MaxTextExtent];
5462
5463   Cursor
5464     cursor;
5465
5466   int
5467     entry,
5468     id,
5469     number_coordinates,
5470     x,
5471     y;
5472
5473   double
5474     degrees;
5475
5476   MagickStatusType
5477     status;
5478
5479   RectangleInfo
5480     rectangle_info;
5481
5482   register int
5483     i;
5484
5485   unsigned int
5486     distance,
5487     height,
5488     max_coordinates,
5489     width;
5490
5491   size_t
5492     state;
5493
5494   Window
5495     root_window;
5496
5497   XDrawInfo
5498     draw_info;
5499
5500   XEvent
5501     event;
5502
5503   XPoint
5504     *coordinate_info;
5505
5506   XSegment
5507     line_info;
5508
5509   /*
5510     Allocate polygon info.
5511   */
5512   max_coordinates=2048;
5513   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5514     sizeof(*coordinate_info));
5515   if (coordinate_info == (XPoint *) NULL)
5516     {
5517       (void) ThrowMagickException(exception,GetMagickModule(),
5518         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5519       return(MagickFalse);
5520     }
5521   /*
5522     Map Command widget.
5523   */
5524   (void) CloneString(&windows->command.name,"Draw");
5525   windows->command.data=4;
5526   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5527   (void) XMapRaised(display,windows->command.id);
5528   XClientMessage(display,windows->image.id,windows->im_protocols,
5529     windows->im_update_widget,CurrentTime);
5530   /*
5531     Wait for first button press.
5532   */
5533   root_window=XRootWindow(display,XDefaultScreen(display));
5534   draw_info.stencil=OpaqueStencil;
5535   status=MagickTrue;
5536   cursor=XCreateFontCursor(display,XC_tcross);
5537   for ( ; ; )
5538   {
5539     XQueryPosition(display,windows->image.id,&x,&y);
5540     (void) XSelectInput(display,windows->image.id,
5541       windows->image.attributes.event_mask | PointerMotionMask);
5542     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5543     state=DefaultState;
5544     do
5545     {
5546       if (IfMagickTrue(windows->info.mapped) )
5547         {
5548           /*
5549             Display pointer position.
5550           */
5551           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5552             x+windows->image.x,y+windows->image.y);
5553           XInfoWidget(display,windows,text);
5554         }
5555       /*
5556         Wait for next event.
5557       */
5558       XScreenEvent(display,windows,&event,exception);
5559       if (event.xany.window == windows->command.id)
5560         {
5561           /*
5562             Select a command from the Command widget.
5563           */
5564           id=XCommandWidget(display,windows,DrawMenu,&event);
5565           if (id < 0)
5566             continue;
5567           switch (DrawCommands[id])
5568           {
5569             case DrawElementCommand:
5570             {
5571               static const char
5572                 *Elements[] =
5573                 {
5574                   "point",
5575                   "line",
5576                   "rectangle",
5577                   "fill rectangle",
5578                   "circle",
5579                   "fill circle",
5580                   "ellipse",
5581                   "fill ellipse",
5582                   "polygon",
5583                   "fill polygon",
5584                   (char *) NULL,
5585                 };
5586
5587               /*
5588                 Select a command from the pop-up menu.
5589               */
5590               element=(ElementType) (XMenuWidget(display,windows,
5591                 DrawMenu[id],Elements,command)+1);
5592               break;
5593             }
5594             case DrawColorCommand:
5595             {
5596               const char
5597                 *ColorMenu[MaxNumberPens+1];
5598
5599               int
5600                 pen_number;
5601
5602               MagickBooleanType
5603                 transparent;
5604
5605               XColor
5606                 color;
5607
5608               /*
5609                 Initialize menu selections.
5610               */
5611               for (i=0; i < (int) (MaxNumberPens-2); i++)
5612                 ColorMenu[i]=resource_info->pen_colors[i];
5613               ColorMenu[MaxNumberPens-2]="transparent";
5614               ColorMenu[MaxNumberPens-1]="Browser...";
5615               ColorMenu[MaxNumberPens]=(char *) NULL;
5616               /*
5617                 Select a pen color from the pop-up menu.
5618               */
5619               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5620                 (const char **) ColorMenu,command);
5621               if (pen_number < 0)
5622                 break;
5623               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5624                 MagickFalse;
5625               if (IfMagickTrue(transparent) )
5626                 {
5627                   draw_info.stencil=TransparentStencil;
5628                   break;
5629                 }
5630               if (pen_number == (MaxNumberPens-1))
5631                 {
5632                   static char
5633                     color_name[MaxTextExtent] = "gray";
5634
5635                   /*
5636                     Select a pen color from a dialog.
5637                   */
5638                   resource_info->pen_colors[pen_number]=color_name;
5639                   XColorBrowserWidget(display,windows,"Select",color_name);
5640                   if (*color_name == '\0')
5641                     break;
5642                 }
5643               /*
5644                 Set pen color.
5645               */
5646               (void) XParseColor(display,windows->map_info->colormap,
5647                 resource_info->pen_colors[pen_number],&color);
5648               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5649                 (unsigned int) MaxColors,&color);
5650               windows->pixel_info->pen_colors[pen_number]=color;
5651               pen_id=(unsigned int) pen_number;
5652               draw_info.stencil=OpaqueStencil;
5653               break;
5654             }
5655             case DrawStippleCommand:
5656             {
5657               Image
5658                 *stipple_image;
5659
5660               ImageInfo
5661                 *image_info;
5662
5663               int
5664                 status;
5665
5666               static char
5667                 filename[MaxTextExtent] = "\0";
5668
5669               static const char
5670                 *StipplesMenu[] =
5671                 {
5672                   "Brick",
5673                   "Diagonal",
5674                   "Scales",
5675                   "Vertical",
5676                   "Wavy",
5677                   "Translucent",
5678                   "Opaque",
5679                   (char *) NULL,
5680                   (char *) NULL,
5681                 };
5682
5683               /*
5684                 Select a command from the pop-up menu.
5685               */
5686               StipplesMenu[7]="Open...";
5687               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5688                 command);
5689               if (entry < 0)
5690                 break;
5691               if (stipple != (Pixmap) NULL)
5692                 (void) XFreePixmap(display,stipple);
5693               stipple=(Pixmap) NULL;
5694               if (entry != 7)
5695                 {
5696                   switch (entry)
5697                   {
5698                     case 0:
5699                     {
5700                       stipple=XCreateBitmapFromData(display,root_window,
5701                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5702                       break;
5703                     }
5704                     case 1:
5705                     {
5706                       stipple=XCreateBitmapFromData(display,root_window,
5707                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5708                       break;
5709                     }
5710                     case 2:
5711                     {
5712                       stipple=XCreateBitmapFromData(display,root_window,
5713                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5714                       break;
5715                     }
5716                     case 3:
5717                     {
5718                       stipple=XCreateBitmapFromData(display,root_window,
5719                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5720                       break;
5721                     }
5722                     case 4:
5723                     {
5724                       stipple=XCreateBitmapFromData(display,root_window,
5725                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5726                       break;
5727                     }
5728                     case 5:
5729                     {
5730                       stipple=XCreateBitmapFromData(display,root_window,
5731                         (char *) HighlightBitmap,HighlightWidth,
5732                         HighlightHeight);
5733                       break;
5734                     }
5735                     case 6:
5736                     default:
5737                     {
5738                       stipple=XCreateBitmapFromData(display,root_window,
5739                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5740                       break;
5741                     }
5742                   }
5743                   break;
5744                 }
5745               XFileBrowserWidget(display,windows,"Stipple",filename);
5746               if (*filename == '\0')
5747                 break;
5748               /*
5749                 Read image.
5750               */
5751               XSetCursorState(display,windows,MagickTrue);
5752               XCheckRefreshWindows(display,windows);
5753               image_info=AcquireImageInfo();
5754               (void) CopyMagickString(image_info->filename,filename,
5755                 MaxTextExtent);
5756               stipple_image=ReadImage(image_info,exception);
5757               CatchException(exception);
5758               XSetCursorState(display,windows,MagickFalse);
5759               if (stipple_image == (Image *) NULL)
5760                 break;
5761               (void) AcquireUniqueFileResource(filename);
5762               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5763                 "xbm:%s",filename);
5764               (void) WriteImage(image_info,stipple_image,exception);
5765               stipple_image=DestroyImage(stipple_image);
5766               image_info=DestroyImageInfo(image_info);
5767               status=XReadBitmapFile(display,root_window,filename,&width,
5768                 &height,&stipple,&x,&y);
5769               (void) RelinquishUniqueFileResource(filename);
5770               if ((status != BitmapSuccess) != 0)
5771                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5772                   filename);
5773               break;
5774             }
5775             case DrawWidthCommand:
5776             {
5777               static char
5778                 width[MaxTextExtent] = "0";
5779
5780               static const char
5781                 *WidthsMenu[] =
5782                 {
5783                   "1",
5784                   "2",
5785                   "4",
5786                   "8",
5787                   "16",
5788                   "Dialog...",
5789                   (char *) NULL,
5790                 };
5791
5792               /*
5793                 Select a command from the pop-up menu.
5794               */
5795               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5796                 command);
5797               if (entry < 0)
5798                 break;
5799               if (entry != 5)
5800                 {
5801                   line_width=(unsigned int) StringToUnsignedLong(
5802                     WidthsMenu[entry]);
5803                   break;
5804                 }
5805               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5806                 width);
5807               if (*width == '\0')
5808                 break;
5809               line_width=(unsigned int) StringToUnsignedLong(width);
5810               break;
5811             }
5812             case DrawUndoCommand:
5813             {
5814               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5815                 image,exception);
5816               break;
5817             }
5818             case DrawHelpCommand:
5819             {
5820               XTextViewWidget(display,resource_info,windows,MagickFalse,
5821                 "Help Viewer - Image Rotation",ImageDrawHelp);
5822               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5823               break;
5824             }
5825             case DrawDismissCommand:
5826             {
5827               /*
5828                 Prematurely exit.
5829               */
5830               state|=EscapeState;
5831               state|=ExitState;
5832               break;
5833             }
5834             default:
5835               break;
5836           }
5837           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5838           continue;
5839         }
5840       switch (event.type)
5841       {
5842         case ButtonPress:
5843         {
5844           if (event.xbutton.button != Button1)
5845             break;
5846           if (event.xbutton.window != windows->image.id)
5847             break;
5848           /*
5849             exit loop.
5850           */
5851           x=event.xbutton.x;
5852           y=event.xbutton.y;
5853           state|=ExitState;
5854           break;
5855         }
5856         case ButtonRelease:
5857           break;
5858         case Expose:
5859           break;
5860         case KeyPress:
5861         {
5862           KeySym
5863             key_symbol;
5864
5865           if (event.xkey.window != windows->image.id)
5866             break;
5867           /*
5868             Respond to a user key press.
5869           */
5870           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5871             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5872           switch ((int) key_symbol)
5873           {
5874             case XK_Escape:
5875             case XK_F20:
5876             {
5877               /*
5878                 Prematurely exit.
5879               */
5880               state|=EscapeState;
5881               state|=ExitState;
5882               break;
5883             }
5884             case XK_F1:
5885             case XK_Help:
5886             {
5887               XTextViewWidget(display,resource_info,windows,MagickFalse,
5888                 "Help Viewer - Image Rotation",ImageDrawHelp);
5889               break;
5890             }
5891             default:
5892             {
5893               (void) XBell(display,0);
5894               break;
5895             }
5896           }
5897           break;
5898         }
5899         case MotionNotify:
5900         {
5901           /*
5902             Map and unmap Info widget as text cursor crosses its boundaries.
5903           */
5904           x=event.xmotion.x;
5905           y=event.xmotion.y;
5906           if (IfMagickTrue(windows->info.mapped) )
5907             {
5908               if ((x < (int) (windows->info.x+windows->info.width)) &&
5909                   (y < (int) (windows->info.y+windows->info.height)))
5910                 (void) XWithdrawWindow(display,windows->info.id,
5911                   windows->info.screen);
5912             }
5913           else
5914             if ((x > (int) (windows->info.x+windows->info.width)) ||
5915                 (y > (int) (windows->info.y+windows->info.height)))
5916               (void) XMapWindow(display,windows->info.id);
5917           break;
5918         }
5919       }
5920     } while ((state & ExitState) == 0);
5921     (void) XSelectInput(display,windows->image.id,
5922       windows->image.attributes.event_mask);
5923     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5924     if ((state & EscapeState) != 0)
5925       break;
5926     /*
5927       Draw element as pointer moves until the button is released.
5928     */
5929     distance=0;
5930     degrees=0.0;
5931     line_info.x1=x;
5932     line_info.y1=y;
5933     line_info.x2=x;
5934     line_info.y2=y;
5935     rectangle_info.x=(ssize_t) x;
5936     rectangle_info.y=(ssize_t) y;
5937     rectangle_info.width=0;
5938     rectangle_info.height=0;
5939     number_coordinates=1;
5940     coordinate_info->x=x;
5941     coordinate_info->y=y;
5942     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5943     state=DefaultState;
5944     do
5945     {
5946       switch (element)
5947       {
5948         case PointElement:
5949         default:
5950         {
5951           if (number_coordinates > 1)
5952             {
5953               (void) XDrawLines(display,windows->image.id,
5954                 windows->image.highlight_context,coordinate_info,
5955                 number_coordinates,CoordModeOrigin);
5956               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5957                 coordinate_info[number_coordinates-1].x,
5958                 coordinate_info[number_coordinates-1].y);
5959               XInfoWidget(display,windows,text);
5960             }
5961           break;
5962         }
5963         case LineElement:
5964         {
5965           if (distance > 9)
5966             {
5967               /*
5968                 Display angle of the line.
5969               */
5970               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5971                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5972               (void) FormatLocaleString(text,MaxTextExtent," %g",
5973                 (double) degrees);
5974               XInfoWidget(display,windows,text);
5975               XHighlightLine(display,windows->image.id,
5976                 windows->image.highlight_context,&line_info);
5977             }
5978           else
5979             if (IfMagickTrue(windows->info.mapped) )
5980               (void) XWithdrawWindow(display,windows->info.id,
5981                 windows->info.screen);
5982           break;
5983         }
5984         case RectangleElement:
5985         case FillRectangleElement:
5986         {
5987           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5988             {
5989               /*
5990                 Display info and draw drawing rectangle.
5991               */
5992               (void) FormatLocaleString(text,MaxTextExtent,
5993                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5994                 (double) rectangle_info.height,(double) rectangle_info.x,
5995                 (double) rectangle_info.y);
5996               XInfoWidget(display,windows,text);
5997               XHighlightRectangle(display,windows->image.id,
5998                 windows->image.highlight_context,&rectangle_info);
5999             }
6000           else
6001             if (IfMagickTrue(windows->info.mapped) )
6002               (void) XWithdrawWindow(display,windows->info.id,
6003                 windows->info.screen);
6004           break;
6005         }
6006         case CircleElement:
6007         case FillCircleElement:
6008         case EllipseElement:
6009         case FillEllipseElement:
6010         {
6011           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6012             {
6013               /*
6014                 Display info and draw drawing rectangle.
6015               */
6016               (void) FormatLocaleString(text,MaxTextExtent,
6017                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6018                 (double) rectangle_info.height,(double) rectangle_info.x,
6019                 (double) rectangle_info.y);
6020               XInfoWidget(display,windows,text);
6021               XHighlightEllipse(display,windows->image.id,
6022                 windows->image.highlight_context,&rectangle_info);
6023             }
6024           else
6025             if (IfMagickTrue(windows->info.mapped) )
6026               (void) XWithdrawWindow(display,windows->info.id,
6027                 windows->info.screen);
6028           break;
6029         }
6030         case PolygonElement:
6031         case FillPolygonElement:
6032         {
6033           if (number_coordinates > 1)
6034             (void) XDrawLines(display,windows->image.id,
6035               windows->image.highlight_context,coordinate_info,
6036               number_coordinates,CoordModeOrigin);
6037           if (distance > 9)
6038             {
6039               /*
6040                 Display angle of the line.
6041               */
6042               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6043                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6044               (void) FormatLocaleString(text,MaxTextExtent," %g",
6045                 (double) degrees);
6046               XInfoWidget(display,windows,text);
6047               XHighlightLine(display,windows->image.id,
6048                 windows->image.highlight_context,&line_info);
6049             }
6050           else
6051             if (IfMagickTrue(windows->info.mapped) )
6052               (void) XWithdrawWindow(display,windows->info.id,
6053                 windows->info.screen);
6054           break;
6055         }
6056       }
6057       /*
6058         Wait for next event.
6059       */
6060       XScreenEvent(display,windows,&event,exception);
6061       switch (element)
6062       {
6063         case PointElement:
6064         default:
6065         {
6066           if (number_coordinates > 1)
6067             (void) XDrawLines(display,windows->image.id,
6068               windows->image.highlight_context,coordinate_info,
6069               number_coordinates,CoordModeOrigin);
6070           break;
6071         }
6072         case LineElement:
6073         {
6074           if (distance > 9)
6075             XHighlightLine(display,windows->image.id,
6076               windows->image.highlight_context,&line_info);
6077           break;
6078         }
6079         case RectangleElement:
6080         case FillRectangleElement:
6081         {
6082           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6083             XHighlightRectangle(display,windows->image.id,
6084               windows->image.highlight_context,&rectangle_info);
6085           break;
6086         }
6087         case CircleElement:
6088         case FillCircleElement:
6089         case EllipseElement:
6090         case FillEllipseElement:
6091         {
6092           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6093             XHighlightEllipse(display,windows->image.id,
6094               windows->image.highlight_context,&rectangle_info);
6095           break;
6096         }
6097         case PolygonElement:
6098         case FillPolygonElement:
6099         {
6100           if (number_coordinates > 1)
6101             (void) XDrawLines(display,windows->image.id,
6102               windows->image.highlight_context,coordinate_info,
6103               number_coordinates,CoordModeOrigin);
6104           if (distance > 9)
6105             XHighlightLine(display,windows->image.id,
6106               windows->image.highlight_context,&line_info);
6107           break;
6108         }
6109       }
6110       switch (event.type)
6111       {
6112         case ButtonPress:
6113           break;
6114         case ButtonRelease:
6115         {
6116           /*
6117             User has committed to element.
6118           */
6119           line_info.x2=event.xbutton.x;
6120           line_info.y2=event.xbutton.y;
6121           rectangle_info.x=(ssize_t) event.xbutton.x;
6122           rectangle_info.y=(ssize_t) event.xbutton.y;
6123           coordinate_info[number_coordinates].x=event.xbutton.x;
6124           coordinate_info[number_coordinates].y=event.xbutton.y;
6125           if (((element != PolygonElement) &&
6126                (element != FillPolygonElement)) || (distance <= 9))
6127             {
6128               state|=ExitState;
6129               break;
6130             }
6131           number_coordinates++;
6132           if (number_coordinates < (int) max_coordinates)
6133             {
6134               line_info.x1=event.xbutton.x;
6135               line_info.y1=event.xbutton.y;
6136               break;
6137             }
6138           max_coordinates<<=1;
6139           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6140             max_coordinates,sizeof(*coordinate_info));
6141           if (coordinate_info == (XPoint *) NULL)
6142             (void) ThrowMagickException(exception,GetMagickModule(),
6143               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6144           break;
6145         }
6146         case Expose:
6147           break;
6148         case MotionNotify:
6149         {
6150           if (event.xmotion.window != windows->image.id)
6151             break;
6152           if (element != PointElement)
6153             {
6154               line_info.x2=event.xmotion.x;
6155               line_info.y2=event.xmotion.y;
6156               rectangle_info.x=(ssize_t) event.xmotion.x;
6157               rectangle_info.y=(ssize_t) event.xmotion.y;
6158               break;
6159             }
6160           coordinate_info[number_coordinates].x=event.xbutton.x;
6161           coordinate_info[number_coordinates].y=event.xbutton.y;
6162           number_coordinates++;
6163           if (number_coordinates < (int) max_coordinates)
6164             break;
6165           max_coordinates<<=1;
6166           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6167             max_coordinates,sizeof(*coordinate_info));
6168           if (coordinate_info == (XPoint *) NULL)
6169             (void) ThrowMagickException(exception,GetMagickModule(),
6170               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6171           break;
6172         }
6173         default:
6174           break;
6175       }
6176       /*
6177         Check boundary conditions.
6178       */
6179       if (line_info.x2 < 0)
6180         line_info.x2=0;
6181       else
6182         if (line_info.x2 > (int) windows->image.width)
6183           line_info.x2=(short) windows->image.width;
6184       if (line_info.y2 < 0)
6185         line_info.y2=0;
6186       else
6187         if (line_info.y2 > (int) windows->image.height)
6188           line_info.y2=(short) windows->image.height;
6189       distance=(unsigned int)
6190         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6191          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6192       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6193           ((state & ExitState) != 0))
6194         {
6195           if (rectangle_info.x < 0)
6196             rectangle_info.x=0;
6197           else
6198             if (rectangle_info.x > (ssize_t) windows->image.width)
6199               rectangle_info.x=(ssize_t) windows->image.width;
6200           if ((int) rectangle_info.x < x)
6201             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6202           else
6203             {
6204               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6205               rectangle_info.x=(ssize_t) x;
6206             }
6207           if (rectangle_info.y < 0)
6208             rectangle_info.y=0;
6209           else
6210             if (rectangle_info.y > (ssize_t) windows->image.height)
6211               rectangle_info.y=(ssize_t) windows->image.height;
6212           if ((int) rectangle_info.y < y)
6213             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6214           else
6215             {
6216               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6217               rectangle_info.y=(ssize_t) y;
6218             }
6219         }
6220     } while ((state & ExitState) == 0);
6221     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6222     if ((element == PointElement) || (element == PolygonElement) ||
6223         (element == FillPolygonElement))
6224       {
6225         /*
6226           Determine polygon bounding box.
6227         */
6228         rectangle_info.x=(ssize_t) coordinate_info->x;
6229         rectangle_info.y=(ssize_t) coordinate_info->y;
6230         x=coordinate_info->x;
6231         y=coordinate_info->y;
6232         for (i=1; i < number_coordinates; i++)
6233         {
6234           if (coordinate_info[i].x > x)
6235             x=coordinate_info[i].x;
6236           if (coordinate_info[i].y > y)
6237             y=coordinate_info[i].y;
6238           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6239             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6240           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6241             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6242         }
6243         rectangle_info.width=(size_t) (x-rectangle_info.x);
6244         rectangle_info.height=(size_t) (y-rectangle_info.y);
6245         for (i=0; i < number_coordinates; i++)
6246         {
6247           coordinate_info[i].x-=rectangle_info.x;
6248           coordinate_info[i].y-=rectangle_info.y;
6249         }
6250       }
6251     else
6252       if (distance <= 9)
6253         continue;
6254       else
6255         if ((element == RectangleElement) ||
6256             (element == CircleElement) || (element == EllipseElement))
6257           {
6258             rectangle_info.width--;
6259             rectangle_info.height--;
6260           }
6261     /*
6262       Drawing is relative to image configuration.
6263     */
6264     draw_info.x=(int) rectangle_info.x;
6265     draw_info.y=(int) rectangle_info.y;
6266     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6267       image,exception);
6268     width=(unsigned int) (*image)->columns;
6269     height=(unsigned int) (*image)->rows;
6270     x=0;
6271     y=0;
6272     if (windows->image.crop_geometry != (char *) NULL)
6273       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6274     draw_info.x+=windows->image.x-(line_width/2);
6275     if (draw_info.x < 0)
6276       draw_info.x=0;
6277     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6278     draw_info.y+=windows->image.y-(line_width/2);
6279     if (draw_info.y < 0)
6280       draw_info.y=0;
6281     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6282     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6283     if (draw_info.width > (unsigned int) (*image)->columns)
6284       draw_info.width=(unsigned int) (*image)->columns;
6285     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6286     if (draw_info.height > (unsigned int) (*image)->rows)
6287       draw_info.height=(unsigned int) (*image)->rows;
6288     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6289       width*draw_info.width/windows->image.ximage->width,
6290       height*draw_info.height/windows->image.ximage->height,
6291       draw_info.x+x,draw_info.y+y);
6292     /*
6293       Initialize drawing attributes.
6294     */
6295     draw_info.degrees=0.0;
6296     draw_info.element=element;
6297     draw_info.stipple=stipple;
6298     draw_info.line_width=line_width;
6299     draw_info.line_info=line_info;
6300     if (line_info.x1 > (int) (line_width/2))
6301       draw_info.line_info.x1=(short) line_width/2;
6302     if (line_info.y1 > (int) (line_width/2))
6303       draw_info.line_info.y1=(short) line_width/2;
6304     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6305     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6306     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6307       {
6308         draw_info.line_info.x2=(-draw_info.line_info.x2);
6309         draw_info.line_info.y2=(-draw_info.line_info.y2);
6310       }
6311     if (draw_info.line_info.x2 < 0)
6312       {
6313         draw_info.line_info.x2=(-draw_info.line_info.x2);
6314         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6315       }
6316     if (draw_info.line_info.y2 < 0)
6317       {
6318         draw_info.line_info.y2=(-draw_info.line_info.y2);
6319         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6320       }
6321     draw_info.rectangle_info=rectangle_info;
6322     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6323       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6324     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6325       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6326     draw_info.number_coordinates=(unsigned int) number_coordinates;
6327     draw_info.coordinate_info=coordinate_info;
6328     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6329     /*
6330       Draw element on image.
6331     */
6332     XSetCursorState(display,windows,MagickTrue);
6333     XCheckRefreshWindows(display,windows);
6334     status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6335     XSetCursorState(display,windows,MagickFalse);
6336     /*
6337       Update image colormap and return to image drawing.
6338     */
6339     XConfigureImageColormap(display,resource_info,windows,*image,exception);
6340     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6341   }
6342   XSetCursorState(display,windows,MagickFalse);
6343   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6344   return(IsMagickTrue(status));
6345 }
6346 \f
6347 /*
6348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6349 %                                                                             %
6350 %                                                                             %
6351 %                                                                             %
6352 +   X D r a w P a n R e c t a n g l e                                         %
6353 %                                                                             %
6354 %                                                                             %
6355 %                                                                             %
6356 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6357 %
6358 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6359 %  displays a zoom image and the rectangle shows which portion of the image is
6360 %  displayed in the Image window.
6361 %
6362 %  The format of the XDrawPanRectangle method is:
6363 %
6364 %      XDrawPanRectangle(Display *display,XWindows *windows)
6365 %
6366 %  A description of each parameter follows:
6367 %
6368 %    o display: Specifies a connection to an X server;  returned from
6369 %      XOpenDisplay.
6370 %
6371 %    o windows: Specifies a pointer to a XWindows structure.
6372 %
6373 */
6374 static void XDrawPanRectangle(Display *display,XWindows *windows)
6375 {
6376   double
6377     scale_factor;
6378
6379   RectangleInfo
6380     highlight_info;
6381
6382   /*
6383     Determine dimensions of the panning rectangle.
6384   */
6385   scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6386   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6387   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6388   scale_factor=(double)
6389     windows->pan.height/windows->image.ximage->height;
6390   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6391   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6392   /*
6393     Display the panning rectangle.
6394   */
6395   (void) XClearWindow(display,windows->pan.id);
6396   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6397     &highlight_info);
6398 }
6399 \f
6400 /*
6401 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6402 %                                                                             %
6403 %                                                                             %
6404 %                                                                             %
6405 +   X I m a g e C a c h e                                                     %
6406 %                                                                             %
6407 %                                                                             %
6408 %                                                                             %
6409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6410 %
6411 %  XImageCache() handles the creation, manipulation, and destruction of the
6412 %  image cache (undo and redo buffers).
6413 %
6414 %  The format of the XImageCache method is:
6415 %
6416 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6417 %        XWindows *windows,const CommandType command,Image **image,
6418 %        ExceptionInfo *exception)
6419 %
6420 %  A description of each parameter follows:
6421 %
6422 %    o display: Specifies a connection to an X server; returned from
6423 %      XOpenDisplay.
6424 %
6425 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6426 %
6427 %    o windows: Specifies a pointer to a XWindows structure.
6428 %
6429 %    o command: Specifies a command to perform.
6430 %
6431 %    o image: the image;  XImageCache may transform the image and return a new
6432 %      image pointer.
6433 %
6434 %    o exception: return any errors or warnings in this structure.
6435 %
6436 */
6437 static void XImageCache(Display *display,XResourceInfo *resource_info,
6438   XWindows *windows,const CommandType command,Image **image,
6439   ExceptionInfo *exception)
6440 {
6441   Image
6442     *cache_image;
6443
6444   static Image
6445     *redo_image = (Image *) NULL,
6446     *undo_image = (Image *) NULL;
6447
6448   switch (command)
6449   {
6450     case FreeBuffersCommand:
6451     {
6452       /*
6453         Free memory from the undo and redo cache.
6454       */
6455       while (undo_image != (Image *) NULL)
6456       {
6457         cache_image=undo_image;
6458         undo_image=GetPreviousImageInList(undo_image);
6459         cache_image->list=DestroyImage(cache_image->list);
6460         cache_image=DestroyImage(cache_image);
6461       }
6462       undo_image=NewImageList();
6463       if (redo_image != (Image *) NULL)
6464         redo_image=DestroyImage(redo_image);
6465       redo_image=NewImageList();
6466       return;
6467     }
6468     case UndoCommand:
6469     {
6470       char
6471         image_geometry[MaxTextExtent];
6472
6473       /*
6474         Undo the last image transformation.
6475       */
6476       if (undo_image == (Image *) NULL)
6477         {
6478           (void) XBell(display,0);
6479           return;
6480         }
6481       cache_image=undo_image;
6482       undo_image=GetPreviousImageInList(undo_image);
6483       windows->image.window_changes.width=(int) cache_image->columns;
6484       windows->image.window_changes.height=(int) cache_image->rows;
6485       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6486         windows->image.ximage->width,windows->image.ximage->height);
6487       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6488         exception);
6489       if (windows->image.crop_geometry != (char *) NULL)
6490         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6491           windows->image.crop_geometry);
6492       windows->image.crop_geometry=cache_image->geometry;
6493       if (redo_image != (Image *) NULL)
6494         redo_image=DestroyImage(redo_image);
6495       redo_image=(*image);
6496       *image=cache_image->list;
6497       cache_image=DestroyImage(cache_image);
6498       if (IfMagickTrue(windows->image.orphan) )
6499         return;
6500       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6501       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6502       return;
6503     }
6504     case CutCommand:
6505     case PasteCommand:
6506     case ApplyCommand:
6507     case HalfSizeCommand:
6508     case OriginalSizeCommand:
6509     case DoubleSizeCommand:
6510     case ResizeCommand:
6511     case TrimCommand:
6512     case CropCommand:
6513     case ChopCommand:
6514     case FlipCommand:
6515     case FlopCommand:
6516     case RotateRightCommand:
6517     case RotateLeftCommand:
6518     case RotateCommand:
6519     case ShearCommand:
6520     case RollCommand:
6521     case NegateCommand:
6522     case ContrastStretchCommand:
6523     case SigmoidalContrastCommand:
6524     case NormalizeCommand:
6525     case EqualizeCommand:
6526     case HueCommand:
6527     case SaturationCommand:
6528     case BrightnessCommand:
6529     case GammaCommand:
6530     case SpiffCommand:
6531     case DullCommand:
6532     case GrayscaleCommand:
6533     case MapCommand:
6534     case QuantizeCommand:
6535     case DespeckleCommand:
6536     case EmbossCommand:
6537     case ReduceNoiseCommand:
6538     case AddNoiseCommand:
6539     case SharpenCommand:
6540     case BlurCommand:
6541     case ThresholdCommand:
6542     case EdgeDetectCommand:
6543     case SpreadCommand:
6544     case ShadeCommand:
6545     case RaiseCommand:
6546     case SegmentCommand:
6547     case SolarizeCommand:
6548     case SepiaToneCommand:
6549     case SwirlCommand:
6550     case ImplodeCommand:
6551     case VignetteCommand:
6552     case WaveCommand:
6553     case OilPaintCommand:
6554     case CharcoalDrawCommand:
6555     case AnnotateCommand:
6556     case AddBorderCommand:
6557     case AddFrameCommand:
6558     case CompositeCommand:
6559     case CommentCommand:
6560     case LaunchCommand:
6561     case RegionofInterestCommand:
6562     case SaveToUndoBufferCommand:
6563     case RedoCommand:
6564     {
6565       Image
6566         *previous_image;
6567
6568       ssize_t
6569         bytes;
6570
6571       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6572       if (undo_image != (Image *) NULL)
6573         {
6574           /*
6575             Ensure the undo cache has enough memory available.
6576           */
6577           previous_image=undo_image;
6578           while (previous_image != (Image *) NULL)
6579           {
6580             bytes+=previous_image->list->columns*previous_image->list->rows*
6581               sizeof(PixelInfo);
6582             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6583               {
6584                 previous_image=GetPreviousImageInList(previous_image);
6585                 continue;
6586               }
6587             bytes-=previous_image->list->columns*previous_image->list->rows*
6588               sizeof(PixelInfo);
6589             if (previous_image == undo_image)
6590               undo_image=NewImageList();
6591             else
6592               previous_image->next->previous=NewImageList();
6593             break;
6594           }
6595           while (previous_image != (Image *) NULL)
6596           {
6597             /*
6598               Delete any excess memory from undo cache.
6599             */
6600             cache_image=previous_image;
6601             previous_image=GetPreviousImageInList(previous_image);
6602             cache_image->list=DestroyImage(cache_image->list);
6603             cache_image=DestroyImage(cache_image);
6604           }
6605         }
6606       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6607         break;
6608       /*
6609         Save image before transformations are applied.
6610       */
6611       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6612       if (cache_image == (Image *) NULL)
6613         break;
6614       XSetCursorState(display,windows,MagickTrue);
6615       XCheckRefreshWindows(display,windows);
6616       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6617       XSetCursorState(display,windows,MagickFalse);
6618       if (cache_image->list == (Image *) NULL)
6619         {
6620           cache_image=DestroyImage(cache_image);
6621           break;
6622         }
6623       cache_image->columns=(size_t) windows->image.ximage->width;
6624       cache_image->rows=(size_t) windows->image.ximage->height;
6625       cache_image->geometry=windows->image.crop_geometry;
6626       if (windows->image.crop_geometry != (char *) NULL)
6627         {
6628           cache_image->geometry=AcquireString((char *) NULL);
6629           (void) CopyMagickString(cache_image->geometry,
6630             windows->image.crop_geometry,MaxTextExtent);
6631         }
6632       if (undo_image == (Image *) NULL)
6633         {
6634           undo_image=cache_image;
6635           break;
6636         }
6637       undo_image->next=cache_image;
6638       undo_image->next->previous=undo_image;
6639       undo_image=undo_image->next;
6640       break;
6641     }
6642     default:
6643       break;
6644   }
6645   if (command == RedoCommand)
6646     {
6647       /*
6648         Redo the last image transformation.
6649       */
6650       if (redo_image == (Image *) NULL)
6651         {
6652           (void) XBell(display,0);
6653           return;
6654         }
6655       windows->image.window_changes.width=(int) redo_image->columns;
6656       windows->image.window_changes.height=(int) redo_image->rows;
6657       if (windows->image.crop_geometry != (char *) NULL)
6658         windows->image.crop_geometry=(char *)
6659           RelinquishMagickMemory(windows->image.crop_geometry);
6660       windows->image.crop_geometry=redo_image->geometry;
6661       *image=DestroyImage(*image);
6662       *image=redo_image;
6663       redo_image=NewImageList();
6664       if (IfMagickTrue(windows->image.orphan) )
6665         return;
6666       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6667       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6668       return;
6669     }
6670   if (command != InfoCommand)
6671     return;
6672   /*
6673     Display image info.
6674   */
6675   XSetCursorState(display,windows,MagickTrue);
6676   XCheckRefreshWindows(display,windows);
6677   XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6678   XSetCursorState(display,windows,MagickFalse);
6679 }
6680 \f
6681 /*
6682 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6683 %                                                                             %
6684 %                                                                             %
6685 %                                                                             %
6686 +   X I m a g e W i n d o w C o m m a n d                                     %
6687 %                                                                             %
6688 %                                                                             %
6689 %                                                                             %
6690 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6691 %
6692 %  XImageWindowCommand() makes a transform to the image or Image window as
6693 %  specified by a user menu button or keyboard command.
6694 %
6695 %  The format of the XImageWindowCommand method is:
6696 %
6697 %      CommandType XImageWindowCommand(Display *display,
6698 %        XResourceInfo *resource_info,XWindows *windows,
6699 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6700 %        ExceptionInfo *exception)
6701 %
6702 %  A description of each parameter follows:
6703 %
6704 %    o nexus:  Method XImageWindowCommand returns an image when the
6705 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6706 %      image is returned.
6707 %
6708 %    o display: Specifies a connection to an X server; returned from
6709 %      XOpenDisplay.
6710 %
6711 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6712 %
6713 %    o windows: Specifies a pointer to a XWindows structure.
6714 %
6715 %    o state: key mask.
6716 %
6717 %    o key_symbol: Specifies a command to perform.
6718 %
6719 %    o image: the image;  XImageWIndowCommand may transform the image and
6720 %      return a new image pointer.
6721 %
6722 %    o exception: return any errors or warnings in this structure.
6723 %
6724 */
6725 static CommandType XImageWindowCommand(Display *display,
6726   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6727   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6728 {
6729   static char
6730     delta[MaxTextExtent] = "";
6731
6732   static const char
6733     Digits[] = "01234567890";
6734
6735   static KeySym
6736     last_symbol = XK_0;
6737
6738   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6739     {
6740       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6741         {
6742           *delta='\0';
6743           resource_info->quantum=1;
6744         }
6745       last_symbol=key_symbol;
6746       delta[strlen(delta)+1]='\0';
6747       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6748       resource_info->quantum=StringToLong(delta);
6749       return(NullCommand);
6750     }
6751   last_symbol=key_symbol;
6752   if (resource_info->immutable)
6753     {
6754       /*
6755         Virtual image window has a restricted command set.
6756       */
6757       switch (key_symbol)
6758       {
6759         case XK_question:
6760           return(InfoCommand);
6761         case XK_p:
6762         case XK_Print:
6763           return(PrintCommand);
6764         case XK_space:
6765           return(NextCommand);
6766         case XK_q:
6767         case XK_Escape:
6768           return(QuitCommand);
6769         default:
6770           break;
6771       }
6772       return(NullCommand);
6773     }
6774   switch ((int) key_symbol)
6775   {
6776     case XK_o:
6777     {
6778       if ((state & ControlMask) == 0)
6779         break;
6780       return(OpenCommand);
6781     }
6782     case XK_space:
6783       return(NextCommand);
6784     case XK_BackSpace:
6785       return(FormerCommand);
6786     case XK_s:
6787     {
6788       if ((state & Mod1Mask) != 0)
6789         return(SwirlCommand);
6790       if ((state & ControlMask) == 0)
6791         return(ShearCommand);
6792       return(SaveCommand);
6793     }
6794     case XK_p:
6795     case XK_Print:
6796     {
6797       if ((state & Mod1Mask) != 0)
6798         return(OilPaintCommand);
6799       if ((state & Mod4Mask) != 0)
6800         return(ColorCommand);
6801       if ((state & ControlMask) == 0)
6802         return(NullCommand);
6803       return(PrintCommand);
6804     }
6805     case XK_d:
6806     {
6807       if ((state & Mod4Mask) != 0)
6808         return(DrawCommand);
6809       if ((state & ControlMask) == 0)
6810         return(NullCommand);
6811       return(DeleteCommand);
6812     }
6813     case XK_Select:
6814     {
6815       if ((state & ControlMask) == 0)
6816         return(NullCommand);
6817       return(SelectCommand);
6818     }
6819     case XK_n:
6820     {
6821       if ((state & ControlMask) == 0)
6822         return(NullCommand);
6823       return(NewCommand);
6824     }
6825     case XK_q:
6826     case XK_Escape:
6827       return(QuitCommand);
6828     case XK_z:
6829     case XK_Undo:
6830     {
6831       if ((state & ControlMask) == 0)
6832         return(NullCommand);
6833       return(UndoCommand);
6834     }
6835     case XK_r:
6836     case XK_Redo:
6837     {
6838       if ((state & ControlMask) == 0)
6839         return(RollCommand);
6840       return(RedoCommand);
6841     }
6842     case XK_x:
6843     {
6844       if ((state & ControlMask) == 0)
6845         return(NullCommand);
6846       return(CutCommand);
6847     }
6848     case XK_c:
6849     {
6850       if ((state & Mod1Mask) != 0)
6851         return(CharcoalDrawCommand);
6852       if ((state & ControlMask) == 0)
6853         return(CropCommand);
6854       return(CopyCommand);
6855     }
6856     case XK_v:
6857     case XK_Insert:
6858     {
6859       if ((state & Mod4Mask) != 0)
6860         return(CompositeCommand);
6861       if ((state & ControlMask) == 0)
6862         return(FlipCommand);
6863       return(PasteCommand);
6864     }
6865     case XK_less:
6866       return(HalfSizeCommand);
6867     case XK_minus:
6868       return(OriginalSizeCommand);
6869     case XK_greater:
6870       return(DoubleSizeCommand);
6871     case XK_percent:
6872       return(ResizeCommand);
6873     case XK_at:
6874       return(RefreshCommand);
6875     case XK_bracketleft:
6876       return(ChopCommand);
6877     case XK_h:
6878       return(FlopCommand);
6879     case XK_slash:
6880       return(RotateRightCommand);
6881     case XK_backslash:
6882       return(RotateLeftCommand);
6883     case XK_asterisk:
6884       return(RotateCommand);
6885     case XK_t:
6886       return(TrimCommand);
6887     case XK_H:
6888       return(HueCommand);
6889     case XK_S:
6890       return(SaturationCommand);
6891     case XK_L:
6892       return(BrightnessCommand);
6893     case XK_G:
6894       return(GammaCommand);
6895     case XK_C:
6896       return(SpiffCommand);
6897     case XK_Z:
6898       return(DullCommand);
6899     case XK_N:
6900       return(NormalizeCommand);
6901     case XK_equal:
6902       return(EqualizeCommand);
6903     case XK_asciitilde:
6904       return(NegateCommand);
6905     case XK_period:
6906       return(GrayscaleCommand);
6907     case XK_numbersign:
6908       return(QuantizeCommand);
6909     case XK_F2:
6910       return(DespeckleCommand);
6911     case XK_F3:
6912       return(EmbossCommand);
6913     case XK_F4:
6914       return(ReduceNoiseCommand);
6915     case XK_F5:
6916       return(AddNoiseCommand);
6917     case XK_F6:
6918       return(SharpenCommand);
6919     case XK_F7:
6920       return(BlurCommand);
6921     case XK_F8:
6922       return(ThresholdCommand);
6923     case XK_F9:
6924       return(EdgeDetectCommand);
6925     case XK_F10:
6926       return(SpreadCommand);
6927     case XK_F11:
6928       return(ShadeCommand);
6929     case XK_F12:
6930       return(RaiseCommand);
6931     case XK_F13:
6932       return(SegmentCommand);
6933     case XK_i:
6934     {
6935       if ((state & Mod1Mask) == 0)
6936         return(NullCommand);
6937       return(ImplodeCommand);
6938     }
6939     case XK_w:
6940     {
6941       if ((state & Mod1Mask) == 0)
6942         return(NullCommand);
6943       return(WaveCommand);
6944     }
6945     case XK_m:
6946     {
6947       if ((state & Mod4Mask) == 0)
6948         return(NullCommand);
6949       return(MatteCommand);
6950     }
6951     case XK_b:
6952     {
6953       if ((state & Mod4Mask) == 0)
6954         return(NullCommand);
6955       return(AddBorderCommand);
6956     }
6957     case XK_f:
6958     {
6959       if ((state & Mod4Mask) == 0)
6960         return(NullCommand);
6961       return(AddFrameCommand);
6962     }
6963     case XK_exclam:
6964     {
6965       if ((state & Mod4Mask) == 0)
6966         return(NullCommand);
6967       return(CommentCommand);
6968     }
6969     case XK_a:
6970     {
6971       if ((state & Mod1Mask) != 0)
6972         return(ApplyCommand);
6973       if ((state & Mod4Mask) != 0)
6974         return(AnnotateCommand);
6975       if ((state & ControlMask) == 0)
6976         return(NullCommand);
6977       return(RegionofInterestCommand);
6978     }
6979     case XK_question:
6980       return(InfoCommand);
6981     case XK_plus:
6982       return(ZoomCommand);
6983     case XK_P:
6984     {
6985       if ((state & ShiftMask) == 0)
6986         return(NullCommand);
6987       return(ShowPreviewCommand);
6988     }
6989     case XK_Execute:
6990       return(LaunchCommand);
6991     case XK_F1:
6992       return(HelpCommand);
6993     case XK_Find:
6994       return(BrowseDocumentationCommand);
6995     case XK_Menu:
6996     {
6997       (void) XMapRaised(display,windows->command.id);
6998       return(NullCommand);
6999     }
7000     case XK_Next:
7001     case XK_Prior:
7002     case XK_Home:
7003     case XK_KP_Home:
7004     {
7005       XTranslateImage(display,windows,*image,key_symbol);
7006       return(NullCommand);
7007     }
7008     case XK_Up:
7009     case XK_KP_Up:
7010     case XK_Down:
7011     case XK_KP_Down:
7012     case XK_Left:
7013     case XK_KP_Left:
7014     case XK_Right:
7015     case XK_KP_Right:
7016     {
7017       if ((state & Mod1Mask) != 0)
7018         {
7019           RectangleInfo
7020             crop_info;
7021
7022           /*
7023             Trim one pixel from edge of image.
7024           */
7025           crop_info.x=0;
7026           crop_info.y=0;
7027           crop_info.width=(size_t) windows->image.ximage->width;
7028           crop_info.height=(size_t) windows->image.ximage->height;
7029           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7030             {
7031               if (resource_info->quantum >= (int) crop_info.height)
7032                 resource_info->quantum=(int) crop_info.height-1;
7033               crop_info.height-=resource_info->quantum;
7034             }
7035           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7036             {
7037               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7038                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7039               crop_info.y+=resource_info->quantum;
7040               crop_info.height-=resource_info->quantum;
7041             }
7042           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7043             {
7044               if (resource_info->quantum >= (int) crop_info.width)
7045                 resource_info->quantum=(int) crop_info.width-1;
7046               crop_info.width-=resource_info->quantum;
7047             }
7048           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7049             {
7050               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7051                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7052               crop_info.x+=resource_info->quantum;
7053               crop_info.width-=resource_info->quantum;
7054             }
7055           if ((int) (windows->image.x+windows->image.width) >
7056               (int) crop_info.width)
7057             windows->image.x=(int) (crop_info.width-windows->image.width);
7058           if ((int) (windows->image.y+windows->image.height) >
7059               (int) crop_info.height)
7060             windows->image.y=(int) (crop_info.height-windows->image.height);
7061           XSetCropGeometry(display,windows,&crop_info,*image);
7062           windows->image.window_changes.width=(int) crop_info.width;
7063           windows->image.window_changes.height=(int) crop_info.height;
7064           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7065           (void) XConfigureImage(display,resource_info,windows,*image,
7066             exception);
7067           return(NullCommand);
7068         }
7069       XTranslateImage(display,windows,*image,key_symbol);
7070       return(NullCommand);
7071     }
7072     default:
7073       return(NullCommand);
7074   }
7075   return(NullCommand);
7076 }
7077 \f
7078 /*
7079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7080 %                                                                             %
7081 %                                                                             %
7082 %                                                                             %
7083 +   X M a g i c k C o m m a n d                                               %
7084 %                                                                             %
7085 %                                                                             %
7086 %                                                                             %
7087 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7088 %
7089 %  XMagickCommand() makes a transform to the image or Image window as
7090 %  specified by a user menu button or keyboard command.
7091 %
7092 %  The format of the XMagickCommand method is:
7093 %
7094 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7095 %        XWindows *windows,const CommandType command,Image **image,
7096 %        ExceptionInfo *exception)
7097 %
7098 %  A description of each parameter follows:
7099 %
7100 %    o display: Specifies a connection to an X server; returned from
7101 %      XOpenDisplay.
7102 %
7103 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7104 %
7105 %    o windows: Specifies a pointer to a XWindows structure.
7106 %
7107 %    o command: Specifies a command to perform.
7108 %
7109 %    o image: the image;  XMagickCommand may transform the image and return a
7110 %      new image pointer.
7111 %
7112 %    o exception: return any errors or warnings in this structure.
7113 %
7114 */
7115 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7116   XWindows *windows,const CommandType command,Image **image,
7117   ExceptionInfo *exception)
7118 {
7119   char
7120     filename[MaxTextExtent],
7121     geometry[MaxTextExtent],
7122     modulate_factors[MaxTextExtent];
7123
7124   GeometryInfo
7125     geometry_info;
7126
7127   Image
7128     *nexus;
7129
7130   ImageInfo
7131     *image_info;
7132
7133   int
7134     x,
7135     y;
7136
7137   MagickStatusType
7138     flags,
7139     status;
7140
7141   QuantizeInfo
7142     quantize_info;
7143
7144   RectangleInfo
7145     page_geometry;
7146
7147   register int
7148     i;
7149
7150   static char
7151     color[MaxTextExtent] = "gray";
7152
7153   unsigned int
7154     height,
7155     width;
7156
7157   /*
7158     Process user command.
7159   */
7160   XCheckRefreshWindows(display,windows);
7161   XImageCache(display,resource_info,windows,command,image,exception);
7162   nexus=NewImageList();
7163   windows->image.window_changes.width=windows->image.ximage->width;
7164   windows->image.window_changes.height=windows->image.ximage->height;
7165   image_info=CloneImageInfo(resource_info->image_info);
7166   SetGeometryInfo(&geometry_info);
7167   GetQuantizeInfo(&quantize_info);
7168   switch (command)
7169   {
7170     case OpenCommand:
7171     {
7172       /*
7173         Load image.
7174       */
7175       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7176       break;
7177     }
7178     case NextCommand:
7179     {
7180       /*
7181         Display next image.
7182       */
7183       for (i=0; i < resource_info->quantum; i++)
7184         XClientMessage(display,windows->image.id,windows->im_protocols,
7185           windows->im_next_image,CurrentTime);
7186       break;
7187     }
7188     case FormerCommand:
7189     {
7190       /*
7191         Display former image.
7192       */
7193       for (i=0; i < resource_info->quantum; i++)
7194         XClientMessage(display,windows->image.id,windows->im_protocols,
7195           windows->im_former_image,CurrentTime);
7196       break;
7197     }
7198     case SelectCommand:
7199     {
7200       int
7201         status;
7202
7203       /*
7204         Select image.
7205       */
7206       if (*resource_info->home_directory == '\0')
7207         (void) CopyMagickString(resource_info->home_directory,".",
7208           MaxTextExtent);
7209       status=chdir(resource_info->home_directory);
7210       if (status == -1)
7211         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7212           "UnableToOpenFile","%s",resource_info->home_directory);
7213       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7214       break;
7215     }
7216     case SaveCommand:
7217     {
7218       /*
7219         Save image.
7220       */
7221       status=XSaveImage(display,resource_info,windows,*image,exception);
7222       if (IfMagickFalse(status) )
7223         {
7224           char
7225             message[MaxTextExtent];
7226
7227           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7228             exception->reason != (char *) NULL ? exception->reason : "",
7229             exception->description != (char *) NULL ? exception->description :
7230             "");
7231           XNoticeWidget(display,windows,"Unable to save file:",message);
7232           break;
7233         }
7234       break;
7235     }
7236     case PrintCommand:
7237     {
7238       /*
7239         Print image.
7240       */
7241       status=XPrintImage(display,resource_info,windows,*image,exception);
7242       if (IfMagickFalse(status) )
7243         {
7244           char
7245             message[MaxTextExtent];
7246
7247           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7248             exception->reason != (char *) NULL ? exception->reason : "",
7249             exception->description != (char *) NULL ? exception->description :
7250             "");
7251           XNoticeWidget(display,windows,"Unable to print file:",message);
7252           break;
7253         }
7254       break;
7255     }
7256     case DeleteCommand:
7257     {
7258       static char
7259         filename[MaxTextExtent] = "\0";
7260
7261       /*
7262         Delete image file.
7263       */
7264       XFileBrowserWidget(display,windows,"Delete",filename);
7265       if (*filename == '\0')
7266         break;
7267       status=ShredFile(filename);
7268       if (IfMagickTrue(status) )
7269         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7270       break;
7271     }
7272     case NewCommand:
7273     {
7274       int
7275         status;
7276
7277       static char
7278         color[MaxTextExtent] = "gray",
7279         geometry[MaxTextExtent] = "640x480";
7280
7281       static const char
7282         *format = "gradient";
7283
7284       /*
7285         Query user for canvas geometry.
7286       */
7287       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7288         geometry);
7289       if (*geometry == '\0')
7290         break;
7291       if (status == 0)
7292         format="xc";
7293       XColorBrowserWidget(display,windows,"Select",color);
7294       if (*color == '\0')
7295         break;
7296       /*
7297         Create canvas.
7298       */
7299       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7300         "%s:%s",format,color);
7301       (void) CloneString(&image_info->size,geometry);
7302       nexus=ReadImage(image_info,exception);
7303       CatchException(exception);
7304       XClientMessage(display,windows->image.id,windows->im_protocols,
7305         windows->im_next_image,CurrentTime);
7306       break;
7307     }
7308     case VisualDirectoryCommand:
7309     {
7310       /*
7311         Visual Image directory.
7312       */
7313       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7314       break;
7315     }
7316     case QuitCommand:
7317     {
7318       /*
7319         exit program.
7320       */
7321       if (IfMagickFalse(resource_info->confirm_exit) )
7322         XClientMessage(display,windows->image.id,windows->im_protocols,
7323           windows->im_exit,CurrentTime);
7324       else
7325         {
7326           int
7327             status;
7328
7329           /*
7330             Confirm program exit.
7331           */
7332           status=XConfirmWidget(display,windows,"Do you really want to exit",
7333             resource_info->client_name);
7334           if (status > 0)
7335             XClientMessage(display,windows->image.id,windows->im_protocols,
7336               windows->im_exit,CurrentTime);
7337         }
7338       break;
7339     }
7340     case CutCommand:
7341     {
7342       /*
7343         Cut image.
7344       */
7345       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7346       break;
7347     }
7348     case CopyCommand:
7349     {
7350       /*
7351         Copy image.
7352       */
7353       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7354         exception);
7355       break;
7356     }
7357     case PasteCommand:
7358     {
7359       /*
7360         Paste image.
7361       */
7362       status=XPasteImage(display,resource_info,windows,*image,exception);
7363       if (IfMagickFalse(status) )
7364         {
7365           XNoticeWidget(display,windows,"Unable to paste X image",
7366             (*image)->filename);
7367           break;
7368         }
7369       break;
7370     }
7371     case HalfSizeCommand:
7372     {
7373       /*
7374         Half image size.
7375       */
7376       windows->image.window_changes.width=windows->image.ximage->width/2;
7377       windows->image.window_changes.height=windows->image.ximage->height/2;
7378       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7379       break;
7380     }
7381     case OriginalSizeCommand:
7382     {
7383       /*
7384         Original image size.
7385       */
7386       windows->image.window_changes.width=(int) (*image)->columns;
7387       windows->image.window_changes.height=(int) (*image)->rows;
7388       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7389       break;
7390     }
7391     case DoubleSizeCommand:
7392     {
7393       /*
7394         Double the image size.
7395       */
7396       windows->image.window_changes.width=windows->image.ximage->width << 1;
7397       windows->image.window_changes.height=windows->image.ximage->height << 1;
7398       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7399       break;
7400     }
7401     case ResizeCommand:
7402     {
7403       int
7404         status;
7405
7406       size_t
7407         height,
7408         width;
7409
7410       ssize_t
7411         x,
7412         y;
7413
7414       /*
7415         Resize image.
7416       */
7417       width=(size_t) windows->image.ximage->width;
7418       height=(size_t) windows->image.ximage->height;
7419       x=0;
7420       y=0;
7421       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7422         (double) width,(double) height);
7423       status=XDialogWidget(display,windows,"Resize",
7424         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7425       if (*geometry == '\0')
7426         break;
7427       if (status == 0)
7428         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7429       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7430       windows->image.window_changes.width=(int) width;
7431       windows->image.window_changes.height=(int) height;
7432       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7433       break;
7434     }
7435     case ApplyCommand:
7436     {
7437       char
7438         image_geometry[MaxTextExtent];
7439
7440       if ((windows->image.crop_geometry == (char *) NULL) &&
7441           ((int) (*image)->columns == windows->image.ximage->width) &&
7442           ((int) (*image)->rows == windows->image.ximage->height))
7443         break;
7444       /*
7445         Apply size transforms to image.
7446       */
7447       XSetCursorState(display,windows,MagickTrue);
7448       XCheckRefreshWindows(display,windows);
7449       /*
7450         Crop and/or scale displayed image.
7451       */
7452       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7453         windows->image.ximage->width,windows->image.ximage->height);
7454       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7455         exception);
7456       if (windows->image.crop_geometry != (char *) NULL)
7457         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7458           windows->image.crop_geometry);
7459       windows->image.x=0;
7460       windows->image.y=0;
7461       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7462       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7463       break;
7464     }
7465     case RefreshCommand:
7466     {
7467       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7468       break;
7469     }
7470     case RestoreCommand:
7471     {
7472       /*
7473         Restore Image window to its original size.
7474       */
7475       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7476           (windows->image.height == (unsigned int) (*image)->rows) &&
7477           (windows->image.crop_geometry == (char *) NULL))
7478         {
7479           (void) XBell(display,0);
7480           break;
7481         }
7482       windows->image.window_changes.width=(int) (*image)->columns;
7483       windows->image.window_changes.height=(int) (*image)->rows;
7484       if (windows->image.crop_geometry != (char *) NULL)
7485         {
7486           windows->image.crop_geometry=(char *)
7487             RelinquishMagickMemory(windows->image.crop_geometry);
7488           windows->image.crop_geometry=(char *) NULL;
7489           windows->image.x=0;
7490           windows->image.y=0;
7491         }
7492       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7493       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7494       break;
7495     }
7496     case CropCommand:
7497     {
7498       /*
7499         Crop image.
7500       */
7501       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7502         exception);
7503       break;
7504     }
7505     case ChopCommand:
7506     {
7507       /*
7508         Chop image.
7509       */
7510       status=XChopImage(display,resource_info,windows,image,exception);
7511       if (IfMagickFalse(status) )
7512         {
7513           XNoticeWidget(display,windows,"Unable to cut X image",
7514             (*image)->filename);
7515           break;
7516         }
7517       break;
7518     }
7519     case FlopCommand:
7520     {
7521       Image
7522         *flop_image;
7523
7524       /*
7525         Flop image scanlines.
7526       */
7527       XSetCursorState(display,windows,MagickTrue);
7528       XCheckRefreshWindows(display,windows);
7529       flop_image=FlopImage(*image,exception);
7530       if (flop_image != (Image *) NULL)
7531         {
7532           *image=DestroyImage(*image);
7533           *image=flop_image;
7534         }
7535       CatchException(exception);
7536       XSetCursorState(display,windows,MagickFalse);
7537       if (windows->image.crop_geometry != (char *) NULL)
7538         {
7539           /*
7540             Flop crop geometry.
7541           */
7542           width=(unsigned int) (*image)->columns;
7543           height=(unsigned int) (*image)->rows;
7544           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7545             &width,&height);
7546           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7547             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7548         }
7549       if (IfMagickTrue(windows->image.orphan) )
7550         break;
7551       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7552       break;
7553     }
7554     case FlipCommand:
7555     {
7556       Image
7557         *flip_image;
7558
7559       /*
7560         Flip image scanlines.
7561       */
7562       XSetCursorState(display,windows,MagickTrue);
7563       XCheckRefreshWindows(display,windows);
7564       flip_image=FlipImage(*image,exception);
7565       if (flip_image != (Image *) NULL)
7566         {
7567           *image=DestroyImage(*image);
7568           *image=flip_image;
7569         }
7570       CatchException(exception);
7571       XSetCursorState(display,windows,MagickFalse);
7572       if (windows->image.crop_geometry != (char *) NULL)
7573         {
7574           /*
7575             Flip crop geometry.
7576           */
7577           width=(unsigned int) (*image)->columns;
7578           height=(unsigned int) (*image)->rows;
7579           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7580             &width,&height);
7581           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7582             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7583         }
7584       if (IfMagickTrue(windows->image.orphan) )
7585         break;
7586       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7587       break;
7588     }
7589     case RotateRightCommand:
7590     {
7591       /*
7592         Rotate image 90 degrees clockwise.
7593       */
7594       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7595       if (IfMagickFalse(status) )
7596         {
7597           XNoticeWidget(display,windows,"Unable to rotate X image",
7598             (*image)->filename);
7599           break;
7600         }
7601       break;
7602     }
7603     case RotateLeftCommand:
7604     {
7605       /*
7606         Rotate image 90 degrees counter-clockwise.
7607       */
7608       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7609       if (IfMagickFalse(status) )
7610         {
7611           XNoticeWidget(display,windows,"Unable to rotate X image",
7612             (*image)->filename);
7613           break;
7614         }
7615       break;
7616     }
7617     case RotateCommand:
7618     {
7619       /*
7620         Rotate image.
7621       */
7622       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7623       if (IfMagickFalse(status) )
7624         {
7625           XNoticeWidget(display,windows,"Unable to rotate X image",
7626             (*image)->filename);
7627           break;
7628         }
7629       break;
7630     }
7631     case ShearCommand:
7632     {
7633       Image
7634         *shear_image;
7635
7636       static char
7637         geometry[MaxTextExtent] = "45.0x45.0";
7638
7639       /*
7640         Query user for shear color and geometry.
7641       */
7642       XColorBrowserWidget(display,windows,"Select",color);
7643       if (*color == '\0')
7644         break;
7645       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7646         geometry);
7647       if (*geometry == '\0')
7648         break;
7649       /*
7650         Shear image.
7651       */
7652       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7653         exception);
7654       XSetCursorState(display,windows,MagickTrue);
7655       XCheckRefreshWindows(display,windows);
7656       (void) QueryColorCompliance(color,AllCompliance,
7657         &(*image)->background_color,exception);
7658       flags=ParseGeometry(geometry,&geometry_info);
7659       if ((flags & SigmaValue) == 0)
7660         geometry_info.sigma=geometry_info.rho;
7661       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7662         exception);
7663       if (shear_image != (Image *) NULL)
7664         {
7665           *image=DestroyImage(*image);
7666           *image=shear_image;
7667         }
7668       CatchException(exception);
7669       XSetCursorState(display,windows,MagickFalse);
7670       if (IfMagickTrue(windows->image.orphan) )
7671         break;
7672       windows->image.window_changes.width=(int) (*image)->columns;
7673       windows->image.window_changes.height=(int) (*image)->rows;
7674       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7675       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7676       break;
7677     }
7678     case RollCommand:
7679     {
7680       Image
7681         *roll_image;
7682
7683       static char
7684         geometry[MaxTextExtent] = "+2+2";
7685
7686       /*
7687         Query user for the roll geometry.
7688       */
7689       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7690         geometry);
7691       if (*geometry == '\0')
7692         break;
7693       /*
7694         Roll image.
7695       */
7696       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7697         exception);
7698       XSetCursorState(display,windows,MagickTrue);
7699       XCheckRefreshWindows(display,windows);
7700       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7701         exception);
7702       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7703         exception);
7704       if (roll_image != (Image *) NULL)
7705         {
7706           *image=DestroyImage(*image);
7707           *image=roll_image;
7708         }
7709       CatchException(exception);
7710       XSetCursorState(display,windows,MagickFalse);
7711       if (IfMagickTrue(windows->image.orphan) )
7712         break;
7713       windows->image.window_changes.width=(int) (*image)->columns;
7714       windows->image.window_changes.height=(int) (*image)->rows;
7715       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7716       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7717       break;
7718     }
7719     case TrimCommand:
7720     {
7721       static char
7722         fuzz[MaxTextExtent];
7723
7724       /*
7725         Query user for the fuzz factor.
7726       */
7727       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7728         (*image)->fuzz/(QuantumRange+1.0));
7729       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7730       if (*fuzz == '\0')
7731         break;
7732       (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7733       /*
7734         Trim image.
7735       */
7736       status=XTrimImage(display,resource_info,windows,*image,exception);
7737       if (IfMagickFalse(status) )
7738         {
7739           XNoticeWidget(display,windows,"Unable to trim X image",
7740             (*image)->filename);
7741           break;
7742         }
7743       break;
7744     }
7745     case HueCommand:
7746     {
7747       static char
7748         hue_percent[MaxTextExtent] = "110";
7749
7750       /*
7751         Query user for percent hue change.
7752       */
7753       (void) XDialogWidget(display,windows,"Apply",
7754         "Enter percent change in image hue (0-200):",hue_percent);
7755       if (*hue_percent == '\0')
7756         break;
7757       /*
7758         Vary the image hue.
7759       */
7760       XSetCursorState(display,windows,MagickTrue);
7761       XCheckRefreshWindows(display,windows);
7762       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7763       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7764         MaxTextExtent);
7765       (void) ModulateImage(*image,modulate_factors,exception);
7766       XSetCursorState(display,windows,MagickFalse);
7767       if (IfMagickTrue(windows->image.orphan) )
7768         break;
7769       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7770       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7771       break;
7772     }
7773     case SaturationCommand:
7774     {
7775       static char
7776         saturation_percent[MaxTextExtent] = "110";
7777
7778       /*
7779         Query user for percent saturation change.
7780       */
7781       (void) XDialogWidget(display,windows,"Apply",
7782         "Enter percent change in color saturation (0-200):",saturation_percent);
7783       if (*saturation_percent == '\0')
7784         break;
7785       /*
7786         Vary color saturation.
7787       */
7788       XSetCursorState(display,windows,MagickTrue);
7789       XCheckRefreshWindows(display,windows);
7790       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7791       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7792         MaxTextExtent);
7793       (void) ModulateImage(*image,modulate_factors,exception);
7794       XSetCursorState(display,windows,MagickFalse);
7795       if (IfMagickTrue(windows->image.orphan) )
7796         break;
7797       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7798       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7799       break;
7800     }
7801     case BrightnessCommand:
7802     {
7803       static char
7804         brightness_percent[MaxTextExtent] = "110";
7805
7806       /*
7807         Query user for percent brightness change.
7808       */
7809       (void) XDialogWidget(display,windows,"Apply",
7810         "Enter percent change in color brightness (0-200):",brightness_percent);
7811       if (*brightness_percent == '\0')
7812         break;
7813       /*
7814         Vary the color brightness.
7815       */
7816       XSetCursorState(display,windows,MagickTrue);
7817       XCheckRefreshWindows(display,windows);
7818       (void) CopyMagickString(modulate_factors,brightness_percent,
7819         MaxTextExtent);
7820       (void) ModulateImage(*image,modulate_factors,exception);
7821       XSetCursorState(display,windows,MagickFalse);
7822       if (IfMagickTrue(windows->image.orphan) )
7823         break;
7824       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7825       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7826       break;
7827     }
7828     case GammaCommand:
7829     {
7830       static char
7831         factor[MaxTextExtent] = "1.6";
7832
7833       /*
7834         Query user for gamma value.
7835       */
7836       (void) XDialogWidget(display,windows,"Gamma",
7837         "Enter gamma value (e.g. 1.2):",factor);
7838       if (*factor == '\0')
7839         break;
7840       /*
7841         Gamma correct image.
7842       */
7843       XSetCursorState(display,windows,MagickTrue);
7844       XCheckRefreshWindows(display,windows);
7845       (void) GammaImage(*image,atof(factor),exception);
7846       XSetCursorState(display,windows,MagickFalse);
7847       if (IfMagickTrue(windows->image.orphan) )
7848         break;
7849       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7850       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7851       break;
7852     }
7853     case SpiffCommand:
7854     {
7855       /*
7856         Sharpen the image contrast.
7857       */
7858       XSetCursorState(display,windows,MagickTrue);
7859       XCheckRefreshWindows(display,windows);
7860       (void) ContrastImage(*image,MagickTrue,exception);
7861       XSetCursorState(display,windows,MagickFalse);
7862       if (IfMagickTrue(windows->image.orphan) )
7863         break;
7864       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7865       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7866       break;
7867     }
7868     case DullCommand:
7869     {
7870       /*
7871         Dull the image contrast.
7872       */
7873       XSetCursorState(display,windows,MagickTrue);
7874       XCheckRefreshWindows(display,windows);
7875       (void) ContrastImage(*image,MagickFalse,exception);
7876       XSetCursorState(display,windows,MagickFalse);
7877       if (IfMagickTrue(windows->image.orphan) )
7878         break;
7879       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7880       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7881       break;
7882     }
7883     case ContrastStretchCommand:
7884     {
7885       double
7886         black_point,
7887         white_point;
7888
7889       static char
7890         levels[MaxTextExtent] = "1%";
7891
7892       /*
7893         Query user for gamma value.
7894       */
7895       (void) XDialogWidget(display,windows,"Contrast Stretch",
7896         "Enter black and white points:",levels);
7897       if (*levels == '\0')
7898         break;
7899       /*
7900         Contrast stretch image.
7901       */
7902       XSetCursorState(display,windows,MagickTrue);
7903       XCheckRefreshWindows(display,windows);
7904       flags=ParseGeometry(levels,&geometry_info);
7905       black_point=geometry_info.rho;
7906       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7907       if ((flags & PercentValue) != 0)
7908         {
7909           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7910           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7911         }
7912       white_point=(double) (*image)->columns*(*image)->rows-white_point;
7913       (void) ContrastStretchImage(*image,black_point,white_point,
7914         exception);
7915       XSetCursorState(display,windows,MagickFalse);
7916       if (IfMagickTrue(windows->image.orphan) )
7917         break;
7918       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7919       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7920       break;
7921     }
7922     case SigmoidalContrastCommand:
7923     {
7924       GeometryInfo
7925         geometry_info;
7926
7927       MagickStatusType
7928         flags;
7929
7930       static char
7931         levels[MaxTextExtent] = "3x50%";
7932
7933       /*
7934         Query user for gamma value.
7935       */
7936       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7937         "Enter contrast and midpoint:",levels);
7938       if (*levels == '\0')
7939         break;
7940       /*
7941         Contrast stretch image.
7942       */
7943       XSetCursorState(display,windows,MagickTrue);
7944       XCheckRefreshWindows(display,windows);
7945       flags=ParseGeometry(levels,&geometry_info);
7946       if ((flags & SigmaValue) == 0)
7947         geometry_info.sigma=1.0*QuantumRange/2.0;
7948       if ((flags & PercentValue) != 0)
7949         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7950       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7951         geometry_info.sigma,exception);
7952       XSetCursorState(display,windows,MagickFalse);
7953       if (IfMagickTrue(windows->image.orphan) )
7954         break;
7955       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7956       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7957       break;
7958     }
7959     case NormalizeCommand:
7960     {
7961       /*
7962         Perform histogram normalization on the image.
7963       */
7964       XSetCursorState(display,windows,MagickTrue);
7965       XCheckRefreshWindows(display,windows);
7966       (void) NormalizeImage(*image,exception);
7967       XSetCursorState(display,windows,MagickFalse);
7968       if (IfMagickTrue(windows->image.orphan) )
7969         break;
7970       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7971       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7972       break;
7973     }
7974     case EqualizeCommand:
7975     {
7976       /*
7977         Perform histogram equalization on the image.
7978       */
7979       XSetCursorState(display,windows,MagickTrue);
7980       XCheckRefreshWindows(display,windows);
7981       (void) EqualizeImage(*image,exception);
7982       XSetCursorState(display,windows,MagickFalse);
7983       if (IfMagickTrue(windows->image.orphan) )
7984         break;
7985       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7986       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7987       break;
7988     }
7989     case NegateCommand:
7990     {
7991       /*
7992         Negate colors in image.
7993       */
7994       XSetCursorState(display,windows,MagickTrue);
7995       XCheckRefreshWindows(display,windows);
7996       (void) NegateImage(*image,MagickFalse,exception);
7997       XSetCursorState(display,windows,MagickFalse);
7998       if (IfMagickTrue(windows->image.orphan) )
7999         break;
8000       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8001       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8002       break;
8003     }
8004     case GrayscaleCommand:
8005     {
8006       /*
8007         Convert image to grayscale.
8008       */
8009       XSetCursorState(display,windows,MagickTrue);
8010       XCheckRefreshWindows(display,windows);
8011       (void) SetImageType(*image,(*image)->alpha_trait != BlendPixelTrait ?
8012         GrayscaleType : GrayscaleMatteType,exception);
8013       XSetCursorState(display,windows,MagickFalse);
8014       if (IfMagickTrue(windows->image.orphan) )
8015         break;
8016       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8017       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8018       break;
8019     }
8020     case MapCommand:
8021     {
8022       Image
8023         *affinity_image;
8024
8025       static char
8026         filename[MaxTextExtent] = "\0";
8027
8028       /*
8029         Request image file name from user.
8030       */
8031       XFileBrowserWidget(display,windows,"Map",filename);
8032       if (*filename == '\0')
8033         break;
8034       /*
8035         Map image.
8036       */
8037       XSetCursorState(display,windows,MagickTrue);
8038       XCheckRefreshWindows(display,windows);
8039       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8040       affinity_image=ReadImage(image_info,exception);
8041       if (affinity_image != (Image *) NULL)
8042         {
8043           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8044           affinity_image=DestroyImage(affinity_image);
8045         }
8046       CatchException(exception);
8047       XSetCursorState(display,windows,MagickFalse);
8048       if (IfMagickTrue(windows->image.orphan) )
8049         break;
8050       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8051       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8052       break;
8053     }
8054     case QuantizeCommand:
8055     {
8056       int
8057         status;
8058
8059       static char
8060         colors[MaxTextExtent] = "256";
8061
8062       /*
8063         Query user for maximum number of colors.
8064       */
8065       status=XDialogWidget(display,windows,"Quantize",
8066         "Maximum number of colors:",colors);
8067       if (*colors == '\0')
8068         break;
8069       /*
8070         Color reduce the image.
8071       */
8072       XSetCursorState(display,windows,MagickTrue);
8073       XCheckRefreshWindows(display,windows);
8074       quantize_info.number_colors=StringToUnsignedLong(colors);
8075       quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8076         NoDitherMethod;
8077       (void) QuantizeImage(&quantize_info,*image,exception);
8078       XSetCursorState(display,windows,MagickFalse);
8079       if (IfMagickTrue(windows->image.orphan) )
8080         break;
8081       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8082       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8083       break;
8084     }
8085     case DespeckleCommand:
8086     {
8087       Image
8088         *despeckle_image;
8089
8090       /*
8091         Despeckle image.
8092       */
8093       XSetCursorState(display,windows,MagickTrue);
8094       XCheckRefreshWindows(display,windows);
8095       despeckle_image=DespeckleImage(*image,exception);
8096       if (despeckle_image != (Image *) NULL)
8097         {
8098           *image=DestroyImage(*image);
8099           *image=despeckle_image;
8100         }
8101       CatchException(exception);
8102       XSetCursorState(display,windows,MagickFalse);
8103       if (IfMagickTrue(windows->image.orphan) )
8104         break;
8105       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8106       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8107       break;
8108     }
8109     case EmbossCommand:
8110     {
8111       Image
8112         *emboss_image;
8113
8114       static char
8115         radius[MaxTextExtent] = "0.0x1.0";
8116
8117       /*
8118         Query user for emboss radius.
8119       */
8120       (void) XDialogWidget(display,windows,"Emboss",
8121         "Enter the emboss radius and standard deviation:",radius);
8122       if (*radius == '\0')
8123         break;
8124       /*
8125         Reduce noise in the image.
8126       */
8127       XSetCursorState(display,windows,MagickTrue);
8128       XCheckRefreshWindows(display,windows);
8129       flags=ParseGeometry(radius,&geometry_info);
8130       if ((flags & SigmaValue) == 0)
8131         geometry_info.sigma=1.0;
8132       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8133         exception);
8134       if (emboss_image != (Image *) NULL)
8135         {
8136           *image=DestroyImage(*image);
8137           *image=emboss_image;
8138         }
8139       CatchException(exception);
8140       XSetCursorState(display,windows,MagickFalse);
8141       if (IfMagickTrue(windows->image.orphan) )
8142         break;
8143       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8144       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8145       break;
8146     }
8147     case ReduceNoiseCommand:
8148     {
8149       Image
8150         *noise_image;
8151
8152       static char
8153         radius[MaxTextExtent] = "0";
8154
8155       /*
8156         Query user for noise radius.
8157       */
8158       (void) XDialogWidget(display,windows,"Reduce Noise",
8159         "Enter the noise radius:",radius);
8160       if (*radius == '\0')
8161         break;
8162       /*
8163         Reduce noise in the image.
8164       */
8165       XSetCursorState(display,windows,MagickTrue);
8166       XCheckRefreshWindows(display,windows);
8167       flags=ParseGeometry(radius,&geometry_info);
8168       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8169         geometry_info.rho,(size_t) geometry_info.rho,exception);
8170       if (noise_image != (Image *) NULL)
8171         {
8172           *image=DestroyImage(*image);
8173           *image=noise_image;
8174         }
8175       CatchException(exception);
8176       XSetCursorState(display,windows,MagickFalse);
8177       if (IfMagickTrue(windows->image.orphan) )
8178         break;
8179       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8180       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8181       break;
8182     }
8183     case AddNoiseCommand:
8184     {
8185       char
8186         **noises;
8187
8188       Image
8189         *noise_image;
8190
8191       static char
8192         noise_type[MaxTextExtent] = "Gaussian";
8193
8194       /*
8195         Add noise to the image.
8196       */
8197       noises=GetCommandOptions(MagickNoiseOptions);
8198       if (noises == (char **) NULL)
8199         break;
8200       XListBrowserWidget(display,windows,&windows->widget,
8201         (const char **) noises,"Add Noise",
8202         "Select a type of noise to add to your image:",noise_type);
8203       noises=DestroyStringList(noises);
8204       if (*noise_type == '\0')
8205         break;
8206       XSetCursorState(display,windows,MagickTrue);
8207       XCheckRefreshWindows(display,windows);
8208       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8209         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8210       if (noise_image != (Image *) NULL)
8211         {
8212           *image=DestroyImage(*image);
8213           *image=noise_image;
8214         }
8215       CatchException(exception);
8216       XSetCursorState(display,windows,MagickFalse);
8217       if (IfMagickTrue(windows->image.orphan) )
8218         break;
8219       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8220       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8221       break;
8222     }
8223     case SharpenCommand:
8224     {
8225       Image
8226         *sharp_image;
8227
8228       static char
8229         radius[MaxTextExtent] = "0.0x1.0";
8230
8231       /*
8232         Query user for sharpen radius.
8233       */
8234       (void) XDialogWidget(display,windows,"Sharpen",
8235         "Enter the sharpen radius and standard deviation:",radius);
8236       if (*radius == '\0')
8237         break;
8238       /*
8239         Sharpen image scanlines.
8240       */
8241       XSetCursorState(display,windows,MagickTrue);
8242       XCheckRefreshWindows(display,windows);
8243       flags=ParseGeometry(radius,&geometry_info);
8244       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8245         exception);
8246       if (sharp_image != (Image *) NULL)
8247         {
8248           *image=DestroyImage(*image);
8249           *image=sharp_image;
8250         }
8251       CatchException(exception);
8252       XSetCursorState(display,windows,MagickFalse);
8253       if (IfMagickTrue(windows->image.orphan) )
8254         break;
8255       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8256       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8257       break;
8258     }
8259     case BlurCommand:
8260     {
8261       Image
8262         *blur_image;
8263
8264       static char
8265         radius[MaxTextExtent] = "0.0x1.0";
8266
8267       /*
8268         Query user for blur radius.
8269       */
8270       (void) XDialogWidget(display,windows,"Blur",
8271         "Enter the blur radius and standard deviation:",radius);
8272       if (*radius == '\0')
8273         break;
8274       /*
8275         Blur an image.
8276       */
8277       XSetCursorState(display,windows,MagickTrue);
8278       XCheckRefreshWindows(display,windows);
8279       flags=ParseGeometry(radius,&geometry_info);
8280       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8281         exception);
8282       if (blur_image != (Image *) NULL)
8283         {
8284           *image=DestroyImage(*image);
8285           *image=blur_image;
8286         }
8287       CatchException(exception);
8288       XSetCursorState(display,windows,MagickFalse);
8289       if (IfMagickTrue(windows->image.orphan) )
8290         break;
8291       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8292       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8293       break;
8294     }
8295     case ThresholdCommand:
8296     {
8297       double
8298         threshold;
8299
8300       static char
8301         factor[MaxTextExtent] = "128";
8302
8303       /*
8304         Query user for threshold value.
8305       */
8306       (void) XDialogWidget(display,windows,"Threshold",
8307         "Enter threshold value:",factor);
8308       if (*factor == '\0')
8309         break;
8310       /*
8311         Gamma correct image.
8312       */
8313       XSetCursorState(display,windows,MagickTrue);
8314       XCheckRefreshWindows(display,windows);
8315       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8316       (void) BilevelImage(*image,threshold,exception);
8317       XSetCursorState(display,windows,MagickFalse);
8318       if (IfMagickTrue(windows->image.orphan) )
8319         break;
8320       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8321       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8322       break;
8323     }
8324     case EdgeDetectCommand:
8325     {
8326       Image
8327         *edge_image;
8328
8329       static char
8330         radius[MaxTextExtent] = "0";
8331
8332       /*
8333         Query user for edge factor.
8334       */
8335       (void) XDialogWidget(display,windows,"Detect Edges",
8336         "Enter the edge detect radius:",radius);
8337       if (*radius == '\0')
8338         break;
8339       /*
8340         Detect edge in image.
8341       */
8342       XSetCursorState(display,windows,MagickTrue);
8343       XCheckRefreshWindows(display,windows);
8344       flags=ParseGeometry(radius,&geometry_info);
8345       edge_image=EdgeImage(*image,geometry_info.rho,exception);
8346       if (edge_image != (Image *) NULL)
8347         {
8348           *image=DestroyImage(*image);
8349           *image=edge_image;
8350         }
8351       CatchException(exception);
8352       XSetCursorState(display,windows,MagickFalse);
8353       if (IfMagickTrue(windows->image.orphan) )
8354         break;
8355       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8356       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8357       break;
8358     }
8359     case SpreadCommand:
8360     {
8361       Image
8362         *spread_image;
8363
8364       static char
8365         amount[MaxTextExtent] = "2";
8366
8367       /*
8368         Query user for spread amount.
8369       */
8370       (void) XDialogWidget(display,windows,"Spread",
8371         "Enter the displacement amount:",amount);
8372       if (*amount == '\0')
8373         break;
8374       /*
8375         Displace image pixels by a random amount.
8376       */
8377       XSetCursorState(display,windows,MagickTrue);
8378       XCheckRefreshWindows(display,windows);
8379       flags=ParseGeometry(amount,&geometry_info);
8380       spread_image=EdgeImage(*image,geometry_info.rho,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 (IfMagickTrue(windows->image.orphan) )
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,IsMagickTrue(status),
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 (IfMagickTrue(windows->image.orphan) )
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 (IfMagickTrue(windows->image.orphan) )
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,sRGBColorspace,MagickFalse,geometry_info.rho,
8484         geometry_info.sigma,exception);
8485       XSetCursorState(display,windows,MagickFalse);
8486       if (IfMagickTrue(windows->image.orphan) )
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=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
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 (IfMagickTrue(windows->image.orphan) )
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=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8551       (void) SolarizeImage(*image,threshold,exception);
8552       XSetCursorState(display,windows,MagickFalse);
8553       if (IfMagickTrue(windows->image.orphan) )
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 (IfMagickTrue(windows->image.orphan) )
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 (IfMagickTrue(windows->image.orphan) )
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,0.0,(ssize_t)
8659         ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8660         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 (IfMagickTrue(windows->image.orphan) )
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 (IfMagickTrue(windows->image.orphan) )
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 (IfMagickTrue(windows->image.orphan) )
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         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 (IfMagickTrue(windows->image.orphan) )
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 (IfMagickFalse(status) )
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 (IfMagickFalse(status) )
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 (IfMagickFalse(status) )
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 (IfMagickFalse(status) )
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 (IfMagickFalse(status) )
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 (IfMagickTrue(windows->image.orphan) )
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 (IfMagickTrue(windows->image.orphan) )
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 (IfMagickFalse(status) )
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 (IfMagickFalse(status) )
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 (IfMagickTrue(windows->magnify.mapped) )
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 (IfMagickFalse(status) )
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 (IfMagickFalse(status) )
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)->alpha_trait != BlendPixelTrait)
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 (IfMagickFalse(status) )
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 (IfMagickFalse(status) )
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 (IfMagickFalse(status) )
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 (IfMagickFalse(status) )
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 (IfMagickTrue(windows->info.mapped) )
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 (IfMagickTrue(windows->info.mapped) )
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 (IfMagickFalse(status) )
9617     ThrowXWindowException(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 (IfMagickTrue(windows->info.mapped) )
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=StringToDoubleInterval(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=StringToDoubleInterval(fuzz,(double) QuantumRange+
9878               1.0);
9879             break;
9880           }
9881           case MatteEditValueCommand:
9882           {
9883             static char
9884               message[MaxTextExtent];
9885
9886             static const char
9887               *MatteMenu[] =
9888               {
9889                 "Opaque",
9890                 "Transparent",
9891                 "Dialog...",
9892                 (char *) NULL,
9893               };
9894
9895             /*
9896               Select a command from the pop-up menu.
9897             */
9898             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9899               command);
9900             if (entry < 0)
9901               break;
9902             if (entry != 2)
9903               {
9904                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9905                   OpaqueAlpha);
9906                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9907                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9908                     (Quantum) TransparentAlpha);
9909                 break;
9910               }
9911             (void) FormatLocaleString(message,MaxTextExtent,
9912               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9913               QuantumRange);
9914             (void) XDialogWidget(display,windows,"Matte",message,matte);
9915             if (*matte == '\0')
9916               break;
9917             break;
9918           }
9919           case MatteEditUndoCommand:
9920           {
9921             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9922               image,exception);
9923             break;
9924           }
9925           case MatteEditHelpCommand:
9926           {
9927             XTextViewWidget(display,resource_info,windows,MagickFalse,
9928               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9929             break;
9930           }
9931           case MatteEditDismissCommand:
9932           {
9933             /*
9934               Prematurely exit.
9935             */
9936             state|=EscapeState;
9937             state|=ExitState;
9938             break;
9939           }
9940           default:
9941             break;
9942         }
9943         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9944         continue;
9945       }
9946     switch (event.type)
9947     {
9948       case ButtonPress:
9949       {
9950         if (event.xbutton.button != Button1)
9951           break;
9952         if ((event.xbutton.window != windows->image.id) &&
9953             (event.xbutton.window != windows->magnify.id))
9954           break;
9955         /*
9956           Update matte data.
9957         */
9958         x=event.xbutton.x;
9959         y=event.xbutton.y;
9960         (void) XMagickCommand(display,resource_info,windows,
9961           SaveToUndoBufferCommand,image,exception);
9962         state|=UpdateConfigurationState;
9963         break;
9964       }
9965       case ButtonRelease:
9966       {
9967         if (event.xbutton.button != Button1)
9968           break;
9969         if ((event.xbutton.window != windows->image.id) &&
9970             (event.xbutton.window != windows->magnify.id))
9971           break;
9972         /*
9973           Update colormap information.
9974         */
9975         x=event.xbutton.x;
9976         y=event.xbutton.y;
9977         XConfigureImageColormap(display,resource_info,windows,*image,exception);
9978         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9979         XInfoWidget(display,windows,text);
9980         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9981         state&=(~UpdateConfigurationState);
9982         break;
9983       }
9984       case Expose:
9985         break;
9986       case KeyPress:
9987       {
9988         char
9989           command[MaxTextExtent];
9990
9991         KeySym
9992           key_symbol;
9993
9994         if (event.xkey.window == windows->magnify.id)
9995           {
9996             Window
9997               window;
9998
9999             window=windows->magnify.id;
10000             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
10001           }
10002         if (event.xkey.window != windows->image.id)
10003           break;
10004         /*
10005           Respond to a user key press.
10006         */
10007         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10008           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10009         switch ((int) key_symbol)
10010         {
10011           case XK_Escape:
10012           case XK_F20:
10013           {
10014             /*
10015               Prematurely exit.
10016             */
10017             state|=ExitState;
10018             break;
10019           }
10020           case XK_F1:
10021           case XK_Help:
10022           {
10023             XTextViewWidget(display,resource_info,windows,MagickFalse,
10024               "Help Viewer - Matte Edit",ImageMatteEditHelp);
10025             break;
10026           }
10027           default:
10028           {
10029             (void) XBell(display,0);
10030             break;
10031           }
10032         }
10033         break;
10034       }
10035       case MotionNotify:
10036       {
10037         /*
10038           Map and unmap Info widget as cursor crosses its boundaries.
10039         */
10040         x=event.xmotion.x;
10041         y=event.xmotion.y;
10042         if (IfMagickTrue(windows->info.mapped) )
10043           {
10044             if ((x < (int) (windows->info.x+windows->info.width)) &&
10045                 (y < (int) (windows->info.y+windows->info.height)))
10046               (void) XWithdrawWindow(display,windows->info.id,
10047                 windows->info.screen);
10048           }
10049         else
10050           if ((x > (int) (windows->info.x+windows->info.width)) ||
10051               (y > (int) (windows->info.y+windows->info.height)))
10052             (void) XMapWindow(display,windows->info.id);
10053         break;
10054       }
10055       default:
10056         break;
10057     }
10058     if (event.xany.window == windows->magnify.id)
10059       {
10060         x=windows->magnify.x-windows->image.x;
10061         y=windows->magnify.y-windows->image.y;
10062       }
10063     x_offset=x;
10064     y_offset=y;
10065     if ((state & UpdateConfigurationState) != 0)
10066       {
10067         CacheView
10068           *image_view;
10069
10070         int
10071           x,
10072           y;
10073
10074         /*
10075           Matte edit is relative to image configuration.
10076         */
10077         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10078           MagickTrue);
10079         XPutPixel(windows->image.ximage,x_offset,y_offset,
10080           windows->pixel_info->background_color.pixel);
10081         width=(unsigned int) (*image)->columns;
10082         height=(unsigned int) (*image)->rows;
10083         x=0;
10084         y=0;
10085         if (windows->image.crop_geometry != (char *) NULL)
10086           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10087             &height);
10088         x_offset=(int) (width*(windows->image.x+x_offset)/
10089           windows->image.ximage->width+x);
10090         y_offset=(int) (height*(windows->image.y+y_offset)/
10091           windows->image.ximage->height+y);
10092         if ((x_offset < 0) || (y_offset < 0))
10093           continue;
10094         if ((x_offset >= (int) (*image)->columns) ||
10095             (y_offset >= (int) (*image)->rows))
10096           continue;
10097         if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10098           return(MagickFalse);
10099         if ((*image)->alpha_trait != BlendPixelTrait)
10100           (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10101         image_view=AcquireAuthenticCacheView(*image,exception);
10102         switch (method)
10103         {
10104           case PointMethod:
10105           default:
10106           {
10107             /*
10108               Update matte information using point algorithm.
10109             */
10110             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10111               (ssize_t) y_offset,1,1,exception);
10112             if (q == (Quantum *) NULL)
10113               break;
10114             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10115             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10116             break;
10117           }
10118           case ReplaceMethod:
10119           {
10120             PixelInfo
10121               pixel,
10122               target;
10123
10124             /*
10125               Update matte information using replace algorithm.
10126             */
10127             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10128               x_offset,(ssize_t) y_offset,&target,exception);
10129             for (y=0; y < (int) (*image)->rows; y++)
10130             {
10131               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10132                 (*image)->columns,1,exception);
10133               if (q == (Quantum *) NULL)
10134                 break;
10135               for (x=0; x < (int) (*image)->columns; x++)
10136               {
10137                 GetPixelInfoPixel(*image,q,&pixel);
10138                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10139                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10140                 q+=GetPixelChannels(*image);
10141               }
10142               if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10143                 break;
10144             }
10145             break;
10146           }
10147           case FloodfillMethod:
10148           case FillToBorderMethod:
10149           {
10150             ChannelType
10151               channel_mask;
10152
10153             DrawInfo
10154               *draw_info;
10155
10156             PixelInfo
10157               target;
10158
10159             /*
10160               Update matte information using floodfill algorithm.
10161             */
10162             (void) GetOneVirtualPixelInfo(*image,
10163               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10164               y_offset,&target,exception);
10165             if (method == FillToBorderMethod)
10166               {
10167                 target.red=(double) ScaleShortToQuantum(
10168                   border_color.red);
10169                 target.green=(double) ScaleShortToQuantum(
10170                   border_color.green);
10171                 target.blue=(double) ScaleShortToQuantum(
10172                   border_color.blue);
10173               }
10174             draw_info=CloneDrawInfo(resource_info->image_info,
10175               (DrawInfo *) NULL);
10176             draw_info->fill.alpha=(double) ClampToQuantum(
10177               StringToDouble(matte,(char **) NULL));
10178             channel_mask=SetImageChannelMask(*image,AlphaChannel);
10179             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10180               x_offset,(ssize_t) y_offset,
10181               IsMagickFalse(method == FloodfillMethod),exception);
10182             (void) SetPixelChannelMask(*image,channel_mask);
10183             draw_info=DestroyDrawInfo(draw_info);
10184             break;
10185           }
10186           case ResetMethod:
10187           {
10188             /*
10189               Update matte information using reset algorithm.
10190             */
10191             if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10192               return(MagickFalse);
10193             for (y=0; y < (int) (*image)->rows; y++)
10194             {
10195               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10196                 (*image)->columns,1,exception);
10197               if (q == (Quantum *) NULL)
10198                 break;
10199               for (x=0; x < (int) (*image)->columns; x++)
10200               {
10201                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10202                 q+=GetPixelChannels(*image);
10203               }
10204               if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10205                 break;
10206             }
10207             if (StringToLong(matte) == (long) OpaqueAlpha)
10208               (*image)->alpha_trait=UndefinedPixelTrait;
10209             break;
10210           }
10211         }
10212         image_view=DestroyCacheView(image_view);
10213         state&=(~UpdateConfigurationState);
10214       }
10215   } while ((state & ExitState) == 0);
10216   (void) XSelectInput(display,windows->image.id,
10217     windows->image.attributes.event_mask);
10218   XSetCursorState(display,windows,MagickFalse);
10219   (void) XFreeCursor(display,cursor);
10220   return(MagickTrue);
10221 }
10222 \f
10223 /*
10224 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10225 %                                                                             %
10226 %                                                                             %
10227 %                                                                             %
10228 +   X O p e n I m a g e                                                       %
10229 %                                                                             %
10230 %                                                                             %
10231 %                                                                             %
10232 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10233 %
10234 %  XOpenImage() loads an image from a file.
10235 %
10236 %  The format of the XOpenImage method is:
10237 %
10238 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10239 %       XWindows *windows,const unsigned int command)
10240 %
10241 %  A description of each parameter follows:
10242 %
10243 %    o display: Specifies a connection to an X server; returned from
10244 %      XOpenDisplay.
10245 %
10246 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10247 %
10248 %    o windows: Specifies a pointer to a XWindows structure.
10249 %
10250 %    o command: A value other than zero indicates that the file is selected
10251 %      from the command line argument list.
10252 %
10253 */
10254 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10255   XWindows *windows,const MagickBooleanType command)
10256 {
10257   const MagickInfo
10258     *magick_info;
10259
10260   ExceptionInfo
10261     *exception;
10262
10263   Image
10264     *nexus;
10265
10266   ImageInfo
10267     *image_info;
10268
10269   static char
10270     filename[MaxTextExtent] = "\0";
10271
10272   /*
10273     Request file name from user.
10274   */
10275   if (IfMagickFalse(command) )
10276     XFileBrowserWidget(display,windows,"Open",filename);
10277   else
10278     {
10279       char
10280         **filelist,
10281         **files;
10282
10283       int
10284         count,
10285         status;
10286
10287       register int
10288         i,
10289         j;
10290
10291       /*
10292         Select next image from the command line.
10293       */
10294       status=XGetCommand(display,windows->image.id,&files,&count);
10295       if (status == 0)
10296         {
10297           ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10298           return((Image *) NULL);
10299         }
10300       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10301       if (filelist == (char **) NULL)
10302         {
10303           ThrowXWindowException(ResourceLimitError,
10304             "MemoryAllocationFailed","...");
10305           (void) XFreeStringList(files);
10306           return((Image *) NULL);
10307         }
10308       j=0;
10309       for (i=1; i < count; i++)
10310         if (*files[i] != '-')
10311           filelist[j++]=files[i];
10312       filelist[j]=(char *) NULL;
10313       XListBrowserWidget(display,windows,&windows->widget,
10314         (const char **) filelist,"Load","Select Image to Load:",filename);
10315       filelist=(char **) RelinquishMagickMemory(filelist);
10316       (void) XFreeStringList(files);
10317     }
10318   if (*filename == '\0')
10319     return((Image *) NULL);
10320   image_info=CloneImageInfo(resource_info->image_info);
10321   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10322     (void *) NULL);
10323   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10324   exception=AcquireExceptionInfo();
10325   (void) SetImageInfo(image_info,0,exception);
10326   if (LocaleCompare(image_info->magick,"X") == 0)
10327     {
10328       char
10329         seconds[MaxTextExtent];
10330
10331       /*
10332         User may want to delay the X server screen grab.
10333       */
10334       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10335       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10336         seconds);
10337       if (*seconds == '\0')
10338         return((Image *) NULL);
10339       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10340     }
10341   magick_info=GetMagickInfo(image_info->magick,exception);
10342   if ((magick_info != (const MagickInfo *) NULL) &&
10343       IfMagickTrue(magick_info->raw))
10344     {
10345       char
10346         geometry[MaxTextExtent];
10347
10348       /*
10349         Request image size from the user.
10350       */
10351       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10352       if (image_info->size != (char *) NULL)
10353         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10354       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10355         geometry);
10356       (void) CloneString(&image_info->size,geometry);
10357     }
10358   /*
10359     Load the image.
10360   */
10361   XSetCursorState(display,windows,MagickTrue);
10362   XCheckRefreshWindows(display,windows);
10363   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10364   nexus=ReadImage(image_info,exception);
10365   CatchException(exception);
10366   XSetCursorState(display,windows,MagickFalse);
10367   if (nexus != (Image *) NULL)
10368     XClientMessage(display,windows->image.id,windows->im_protocols,
10369       windows->im_next_image,CurrentTime);
10370   else
10371     {
10372       char
10373         *text,
10374         **textlist;
10375
10376       /*
10377         Unknown image format.
10378       */
10379       text=FileToString(filename,~0UL,exception);
10380       if (text == (char *) NULL)
10381         return((Image *) NULL);
10382       textlist=StringToList(text);
10383       if (textlist != (char **) NULL)
10384         {
10385           char
10386             title[MaxTextExtent];
10387
10388           register int
10389             i;
10390
10391           (void) FormatLocaleString(title,MaxTextExtent,
10392             "Unknown format: %s",filename);
10393           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10394             (const char **) textlist);
10395           for (i=0; textlist[i] != (char *) NULL; i++)
10396             textlist[i]=DestroyString(textlist[i]);
10397           textlist=(char **) RelinquishMagickMemory(textlist);
10398         }
10399       text=DestroyString(text);
10400     }
10401   exception=DestroyExceptionInfo(exception);
10402   image_info=DestroyImageInfo(image_info);
10403   return(nexus);
10404 }
10405 \f
10406 /*
10407 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10408 %                                                                             %
10409 %                                                                             %
10410 %                                                                             %
10411 +   X P a n I m a g e                                                         %
10412 %                                                                             %
10413 %                                                                             %
10414 %                                                                             %
10415 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10416 %
10417 %  XPanImage() pans the image until the mouse button is released.
10418 %
10419 %  The format of the XPanImage method is:
10420 %
10421 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10422 %        ExceptionInfo *exception)
10423 %
10424 %  A description of each parameter follows:
10425 %
10426 %    o display: Specifies a connection to an X server;  returned from
10427 %      XOpenDisplay.
10428 %
10429 %    o windows: Specifies a pointer to a XWindows structure.
10430 %
10431 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10432 %      the entire image is refreshed.
10433 %
10434 %    o exception: return any errors or warnings in this structure.
10435 %
10436 */
10437 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10438   ExceptionInfo *exception)
10439 {
10440   char
10441     text[MaxTextExtent];
10442
10443   Cursor
10444     cursor;
10445
10446   double
10447     x_factor,
10448     y_factor;
10449
10450   RectangleInfo
10451     pan_info;
10452
10453   size_t
10454     state;
10455
10456   /*
10457     Define cursor.
10458   */
10459   if ((windows->image.ximage->width > (int) windows->image.width) &&
10460       (windows->image.ximage->height > (int) windows->image.height))
10461     cursor=XCreateFontCursor(display,XC_fleur);
10462   else
10463     if (windows->image.ximage->width > (int) windows->image.width)
10464       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10465     else
10466       if (windows->image.ximage->height > (int) windows->image.height)
10467         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10468       else
10469         cursor=XCreateFontCursor(display,XC_arrow);
10470   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10471   /*
10472     Pan image as pointer moves until the mouse button is released.
10473   */
10474   x_factor=(double) windows->image.ximage->width/windows->pan.width;
10475   y_factor=(double) windows->image.ximage->height/windows->pan.height;
10476   pan_info.width=windows->pan.width*windows->image.width/
10477     windows->image.ximage->width;
10478   pan_info.height=windows->pan.height*windows->image.height/
10479     windows->image.ximage->height;
10480   pan_info.x=0;
10481   pan_info.y=0;
10482   state=UpdateConfigurationState;
10483   do
10484   {
10485     switch (event->type)
10486     {
10487       case ButtonPress:
10488       {
10489         /*
10490           User choose an initial pan location.
10491         */
10492         pan_info.x=(ssize_t) event->xbutton.x;
10493         pan_info.y=(ssize_t) event->xbutton.y;
10494         state|=UpdateConfigurationState;
10495         break;
10496       }
10497       case ButtonRelease:
10498       {
10499         /*
10500           User has finished panning the image.
10501         */
10502         pan_info.x=(ssize_t) event->xbutton.x;
10503         pan_info.y=(ssize_t) event->xbutton.y;
10504         state|=UpdateConfigurationState | ExitState;
10505         break;
10506       }
10507       case MotionNotify:
10508       {
10509         pan_info.x=(ssize_t) event->xmotion.x;
10510         pan_info.y=(ssize_t) event->xmotion.y;
10511         state|=UpdateConfigurationState;
10512       }
10513       default:
10514         break;
10515     }
10516     if ((state & UpdateConfigurationState) != 0)
10517       {
10518         /*
10519           Check boundary conditions.
10520         */
10521         if (pan_info.x < (ssize_t) (pan_info.width/2))
10522           pan_info.x=0;
10523         else
10524           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10525         if (pan_info.x < 0)
10526           pan_info.x=0;
10527         else
10528           if ((int) (pan_info.x+windows->image.width) >
10529               windows->image.ximage->width)
10530             pan_info.x=(ssize_t)
10531               (windows->image.ximage->width-windows->image.width);
10532         if (pan_info.y < (ssize_t) (pan_info.height/2))
10533           pan_info.y=0;
10534         else
10535           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10536         if (pan_info.y < 0)
10537           pan_info.y=0;
10538         else
10539           if ((int) (pan_info.y+windows->image.height) >
10540               windows->image.ximage->height)
10541             pan_info.y=(ssize_t)
10542               (windows->image.ximage->height-windows->image.height);
10543         if ((windows->image.x != (int) pan_info.x) ||
10544             (windows->image.y != (int) pan_info.y))
10545           {
10546             /*
10547               Display image pan offset.
10548             */
10549             windows->image.x=(int) pan_info.x;
10550             windows->image.y=(int) pan_info.y;
10551             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10552               windows->image.width,windows->image.height,windows->image.x,
10553               windows->image.y);
10554             XInfoWidget(display,windows,text);
10555             /*
10556               Refresh Image window.
10557             */
10558             XDrawPanRectangle(display,windows);
10559             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10560           }
10561         state&=(~UpdateConfigurationState);
10562       }
10563     /*
10564       Wait for next event.
10565     */
10566     if ((state & ExitState) == 0)
10567       XScreenEvent(display,windows,event,exception);
10568   } while ((state & ExitState) == 0);
10569   /*
10570     Restore cursor.
10571   */
10572   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10573   (void) XFreeCursor(display,cursor);
10574   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10575 }
10576 \f
10577 /*
10578 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10579 %                                                                             %
10580 %                                                                             %
10581 %                                                                             %
10582 +   X P a s t e I m a g e                                                     %
10583 %                                                                             %
10584 %                                                                             %
10585 %                                                                             %
10586 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10587 %
10588 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10589 %  window image at a location the user chooses with the pointer.
10590 %
10591 %  The format of the XPasteImage method is:
10592 %
10593 %      MagickBooleanType XPasteImage(Display *display,
10594 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10595 %        ExceptionInfo *exception)
10596 %
10597 %  A description of each parameter follows:
10598 %
10599 %    o display: Specifies a connection to an X server;  returned from
10600 %      XOpenDisplay.
10601 %
10602 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10603 %
10604 %    o windows: Specifies a pointer to a XWindows structure.
10605 %
10606 %    o image: the image; returned from ReadImage.
10607 %
10608 %    o exception: return any errors or warnings in this structure.
10609 %
10610 */
10611 static MagickBooleanType XPasteImage(Display *display,
10612   XResourceInfo *resource_info,XWindows *windows,Image *image,
10613   ExceptionInfo *exception)
10614 {
10615   static const char
10616     *PasteMenu[] =
10617     {
10618       "Operator",
10619       "Help",
10620       "Dismiss",
10621       (char *) NULL
10622     };
10623
10624   static const ModeType
10625     PasteCommands[] =
10626     {
10627       PasteOperatorsCommand,
10628       PasteHelpCommand,
10629       PasteDismissCommand
10630     };
10631
10632   static CompositeOperator
10633     compose = CopyCompositeOp;
10634
10635   char
10636     text[MaxTextExtent];
10637
10638   Cursor
10639     cursor;
10640
10641   Image
10642     *paste_image;
10643
10644   int
10645     entry,
10646     id,
10647     x,
10648     y;
10649
10650   double
10651     scale_factor;
10652
10653   RectangleInfo
10654     highlight_info,
10655     paste_info;
10656
10657   unsigned int
10658     height,
10659     width;
10660
10661   size_t
10662     state;
10663
10664   XEvent
10665     event;
10666
10667   /*
10668     Copy image.
10669   */
10670   if (resource_info->copy_image == (Image *) NULL)
10671     return(MagickFalse);
10672   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10673   /*
10674     Map Command widget.
10675   */
10676   (void) CloneString(&windows->command.name,"Paste");
10677   windows->command.data=1;
10678   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10679   (void) XMapRaised(display,windows->command.id);
10680   XClientMessage(display,windows->image.id,windows->im_protocols,
10681     windows->im_update_widget,CurrentTime);
10682   /*
10683     Track pointer until button 1 is pressed.
10684   */
10685   XSetCursorState(display,windows,MagickFalse);
10686   XQueryPosition(display,windows->image.id,&x,&y);
10687   (void) XSelectInput(display,windows->image.id,
10688     windows->image.attributes.event_mask | PointerMotionMask);
10689   paste_info.x=(ssize_t) windows->image.x+x;
10690   paste_info.y=(ssize_t) windows->image.y+y;
10691   paste_info.width=0;
10692   paste_info.height=0;
10693   cursor=XCreateFontCursor(display,XC_ul_angle);
10694   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10695   state=DefaultState;
10696   do
10697   {
10698     if (IfMagickTrue(windows->info.mapped) )
10699       {
10700         /*
10701           Display pointer position.
10702         */
10703         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10704           (long) paste_info.x,(long) paste_info.y);
10705         XInfoWidget(display,windows,text);
10706       }
10707     highlight_info=paste_info;
10708     highlight_info.x=paste_info.x-windows->image.x;
10709     highlight_info.y=paste_info.y-windows->image.y;
10710     XHighlightRectangle(display,windows->image.id,
10711       windows->image.highlight_context,&highlight_info);
10712     /*
10713       Wait for next event.
10714     */
10715     XScreenEvent(display,windows,&event,exception);
10716     XHighlightRectangle(display,windows->image.id,
10717       windows->image.highlight_context,&highlight_info);
10718     if (event.xany.window == windows->command.id)
10719       {
10720         /*
10721           Select a command from the Command widget.
10722         */
10723         id=XCommandWidget(display,windows,PasteMenu,&event);
10724         if (id < 0)
10725           continue;
10726         switch (PasteCommands[id])
10727         {
10728           case PasteOperatorsCommand:
10729           {
10730             char
10731               command[MaxTextExtent],
10732               **operators;
10733
10734             /*
10735               Select a command from the pop-up menu.
10736             */
10737             operators=GetCommandOptions(MagickComposeOptions);
10738             if (operators == (char **) NULL)
10739               break;
10740             entry=XMenuWidget(display,windows,PasteMenu[id],
10741               (const char **) operators,command);
10742             if (entry >= 0)
10743               compose=(CompositeOperator) ParseCommandOption(
10744                 MagickComposeOptions,MagickFalse,operators[entry]);
10745             operators=DestroyStringList(operators);
10746             break;
10747           }
10748           case PasteHelpCommand:
10749           {
10750             XTextViewWidget(display,resource_info,windows,MagickFalse,
10751               "Help Viewer - Image Composite",ImagePasteHelp);
10752             break;
10753           }
10754           case PasteDismissCommand:
10755           {
10756             /*
10757               Prematurely exit.
10758             */
10759             state|=EscapeState;
10760             state|=ExitState;
10761             break;
10762           }
10763           default:
10764             break;
10765         }
10766         continue;
10767       }
10768     switch (event.type)
10769     {
10770       case ButtonPress:
10771       {
10772         if (IfMagickTrue(image->debug) )
10773           (void) LogMagickEvent(X11Event,GetMagickModule(),
10774             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10775             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10776         if (event.xbutton.button != Button1)
10777           break;
10778         if (event.xbutton.window != windows->image.id)
10779           break;
10780         /*
10781           Paste rectangle is relative to image configuration.
10782         */
10783         width=(unsigned int) image->columns;
10784         height=(unsigned int) image->rows;
10785         x=0;
10786         y=0;
10787         if (windows->image.crop_geometry != (char *) NULL)
10788           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10789             &width,&height);
10790         scale_factor=(double) windows->image.ximage->width/width;
10791         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10792         scale_factor=(double) windows->image.ximage->height/height;
10793         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10794         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10795         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10796         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10797         break;
10798       }
10799       case ButtonRelease:
10800       {
10801         if (IfMagickTrue(image->debug) )
10802           (void) LogMagickEvent(X11Event,GetMagickModule(),
10803             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10804             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10805         if (event.xbutton.button != Button1)
10806           break;
10807         if (event.xbutton.window != windows->image.id)
10808           break;
10809         if ((paste_info.width != 0) && (paste_info.height != 0))
10810           {
10811             /*
10812               User has selected the location of the paste image.
10813             */
10814             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10815             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10816             state|=ExitState;
10817           }
10818         break;
10819       }
10820       case Expose:
10821         break;
10822       case KeyPress:
10823       {
10824         char
10825           command[MaxTextExtent];
10826
10827         KeySym
10828           key_symbol;
10829
10830         int
10831           length;
10832
10833         if (event.xkey.window != windows->image.id)
10834           break;
10835         /*
10836           Respond to a user key press.
10837         */
10838         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10839           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10840         *(command+length)='\0';
10841         if (IfMagickTrue(image->debug) )
10842           (void) LogMagickEvent(X11Event,GetMagickModule(),
10843             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10844         switch ((int) key_symbol)
10845         {
10846           case XK_Escape:
10847           case XK_F20:
10848           {
10849             /*
10850               Prematurely exit.
10851             */
10852             paste_image=DestroyImage(paste_image);
10853             state|=EscapeState;
10854             state|=ExitState;
10855             break;
10856           }
10857           case XK_F1:
10858           case XK_Help:
10859           {
10860             (void) XSetFunction(display,windows->image.highlight_context,
10861               GXcopy);
10862             XTextViewWidget(display,resource_info,windows,MagickFalse,
10863               "Help Viewer - Image Composite",ImagePasteHelp);
10864             (void) XSetFunction(display,windows->image.highlight_context,
10865               GXinvert);
10866             break;
10867           }
10868           default:
10869           {
10870             (void) XBell(display,0);
10871             break;
10872           }
10873         }
10874         break;
10875       }
10876       case MotionNotify:
10877       {
10878         /*
10879           Map and unmap Info widget as text cursor crosses its boundaries.
10880         */
10881         x=event.xmotion.x;
10882         y=event.xmotion.y;
10883         if (IfMagickTrue(windows->info.mapped) )
10884           {
10885             if ((x < (int) (windows->info.x+windows->info.width)) &&
10886                 (y < (int) (windows->info.y+windows->info.height)))
10887               (void) XWithdrawWindow(display,windows->info.id,
10888                 windows->info.screen);
10889           }
10890         else
10891           if ((x > (int) (windows->info.x+windows->info.width)) ||
10892               (y > (int) (windows->info.y+windows->info.height)))
10893             (void) XMapWindow(display,windows->info.id);
10894         paste_info.x=(ssize_t) windows->image.x+x;
10895         paste_info.y=(ssize_t) windows->image.y+y;
10896         break;
10897       }
10898       default:
10899       {
10900         if (IfMagickTrue(image->debug) )
10901           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10902             event.type);
10903         break;
10904       }
10905     }
10906   } while ((state & ExitState) == 0);
10907   (void) XSelectInput(display,windows->image.id,
10908     windows->image.attributes.event_mask);
10909   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10910   XSetCursorState(display,windows,MagickFalse);
10911   (void) XFreeCursor(display,cursor);
10912   if ((state & EscapeState) != 0)
10913     return(MagickTrue);
10914   /*
10915     Image pasting is relative to image configuration.
10916   */
10917   XSetCursorState(display,windows,MagickTrue);
10918   XCheckRefreshWindows(display,windows);
10919   width=(unsigned int) image->columns;
10920   height=(unsigned int) image->rows;
10921   x=0;
10922   y=0;
10923   if (windows->image.crop_geometry != (char *) NULL)
10924     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10925   scale_factor=(double) width/windows->image.ximage->width;
10926   paste_info.x+=x;
10927   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10928   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10929   scale_factor=(double) height/windows->image.ximage->height;
10930   paste_info.y+=y;
10931   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10932   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10933   /*
10934     Paste image with X Image window.
10935   */
10936   (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10937     paste_info.y,exception);
10938   paste_image=DestroyImage(paste_image);
10939   XSetCursorState(display,windows,MagickFalse);
10940   /*
10941     Update image colormap.
10942   */
10943   XConfigureImageColormap(display,resource_info,windows,image,exception);
10944   (void) XConfigureImage(display,resource_info,windows,image,exception);
10945   return(MagickTrue);
10946 }
10947 \f
10948 /*
10949 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10950 %                                                                             %
10951 %                                                                             %
10952 %                                                                             %
10953 +   X P r i n t I m a g e                                                     %
10954 %                                                                             %
10955 %                                                                             %
10956 %                                                                             %
10957 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10958 %
10959 %  XPrintImage() prints an image to a Postscript printer.
10960 %
10961 %  The format of the XPrintImage method is:
10962 %
10963 %      MagickBooleanType XPrintImage(Display *display,
10964 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10965 %        ExceptionInfo *exception)
10966 %
10967 %  A description of each parameter follows:
10968 %
10969 %    o display: Specifies a connection to an X server; returned from
10970 %      XOpenDisplay.
10971 %
10972 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10973 %
10974 %    o windows: Specifies a pointer to a XWindows structure.
10975 %
10976 %    o image: the image.
10977 %
10978 %    o exception: return any errors or warnings in this structure.
10979 %
10980 */
10981 static MagickBooleanType XPrintImage(Display *display,
10982   XResourceInfo *resource_info,XWindows *windows,Image *image,
10983   ExceptionInfo *exception)
10984 {
10985   char
10986     filename[MaxTextExtent],
10987     geometry[MaxTextExtent];
10988
10989   Image
10990     *print_image;
10991
10992   ImageInfo
10993     *image_info;
10994
10995   MagickStatusType
10996     status;
10997
10998   /*
10999     Request Postscript page geometry from user.
11000   */
11001   image_info=CloneImageInfo(resource_info->image_info);
11002   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
11003   if (image_info->page != (char *) NULL)
11004     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11005   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11006     "Select Postscript Page Geometry:",geometry);
11007   if (*geometry == '\0')
11008     return(MagickTrue);
11009   image_info->page=GetPageGeometry(geometry);
11010   /*
11011     Apply image transforms.
11012   */
11013   XSetCursorState(display,windows,MagickTrue);
11014   XCheckRefreshWindows(display,windows);
11015   print_image=CloneImage(image,0,0,MagickTrue,exception);
11016   if (print_image == (Image *) NULL)
11017     return(MagickFalse);
11018   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11019     windows->image.ximage->width,windows->image.ximage->height);
11020   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11021     exception);
11022   /*
11023     Print image.
11024   */
11025   (void) AcquireUniqueFilename(filename);
11026   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11027     filename);
11028   status=WriteImage(image_info,print_image,exception);
11029   (void) RelinquishUniqueFileResource(filename);
11030   print_image=DestroyImage(print_image);
11031   image_info=DestroyImageInfo(image_info);
11032   XSetCursorState(display,windows,MagickFalse);
11033   return(IsMagickTrue(status));
11034 }
11035 \f
11036 /*
11037 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11038 %                                                                             %
11039 %                                                                             %
11040 %                                                                             %
11041 +   X R O I I m a g e                                                         %
11042 %                                                                             %
11043 %                                                                             %
11044 %                                                                             %
11045 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11046 %
11047 %  XROIImage() applies an image processing technique to a region of interest.
11048 %
11049 %  The format of the XROIImage method is:
11050 %
11051 %      MagickBooleanType XROIImage(Display *display,
11052 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11053 %        ExceptionInfo *exception)
11054 %
11055 %  A description of each parameter follows:
11056 %
11057 %    o display: Specifies a connection to an X server; returned from
11058 %      XOpenDisplay.
11059 %
11060 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11061 %
11062 %    o windows: Specifies a pointer to a XWindows structure.
11063 %
11064 %    o image: the image; returned from ReadImage.
11065 %
11066 %    o exception: return any errors or warnings in this structure.
11067 %
11068 */
11069 static MagickBooleanType XROIImage(Display *display,
11070   XResourceInfo *resource_info,XWindows *windows,Image **image,
11071   ExceptionInfo *exception)
11072 {
11073 #define ApplyMenus  7
11074
11075   static const char
11076     *ROIMenu[] =
11077     {
11078       "Help",
11079       "Dismiss",
11080       (char *) NULL
11081     },
11082     *ApplyMenu[] =
11083     {
11084       "File",
11085       "Edit",
11086       "Transform",
11087       "Enhance",
11088       "Effects",
11089       "F/X",
11090       "Miscellany",
11091       "Help",
11092       "Dismiss",
11093       (char *) NULL
11094     },
11095     *FileMenu[] =
11096     {
11097       "Save...",
11098       "Print...",
11099       (char *) NULL
11100     },
11101     *EditMenu[] =
11102     {
11103       "Undo",
11104       "Redo",
11105       (char *) NULL
11106     },
11107     *TransformMenu[] =
11108     {
11109       "Flop",
11110       "Flip",
11111       "Rotate Right",
11112       "Rotate Left",
11113       (char *) NULL
11114     },
11115     *EnhanceMenu[] =
11116     {
11117       "Hue...",
11118       "Saturation...",
11119       "Brightness...",
11120       "Gamma...",
11121       "Spiff",
11122       "Dull",
11123       "Contrast Stretch...",
11124       "Sigmoidal Contrast...",
11125       "Normalize",
11126       "Equalize",
11127       "Negate",
11128       "Grayscale",
11129       "Map...",
11130       "Quantize...",
11131       (char *) NULL
11132     },
11133     *EffectsMenu[] =
11134     {
11135       "Despeckle",
11136       "Emboss",
11137       "Reduce Noise",
11138       "Add Noise",
11139       "Sharpen...",
11140       "Blur...",
11141       "Threshold...",
11142       "Edge Detect...",
11143       "Spread...",
11144       "Shade...",
11145       "Raise...",
11146       "Segment...",
11147       (char *) NULL
11148     },
11149     *FXMenu[] =
11150     {
11151       "Solarize...",
11152       "Sepia Tone...",
11153       "Swirl...",
11154       "Implode...",
11155       "Vignette...",
11156       "Wave...",
11157       "Oil Paint...",
11158       "Charcoal Draw...",
11159       (char *) NULL
11160     },
11161     *MiscellanyMenu[] =
11162     {
11163       "Image Info",
11164       "Zoom Image",
11165       "Show Preview...",
11166       "Show Histogram",
11167       "Show Matte",
11168       (char *) NULL
11169     };
11170
11171   static const char
11172     **Menus[ApplyMenus] =
11173     {
11174       FileMenu,
11175       EditMenu,
11176       TransformMenu,
11177       EnhanceMenu,
11178       EffectsMenu,
11179       FXMenu,
11180       MiscellanyMenu
11181     };
11182
11183   static const CommandType
11184     ApplyCommands[] =
11185     {
11186       NullCommand,
11187       NullCommand,
11188       NullCommand,
11189       NullCommand,
11190       NullCommand,
11191       NullCommand,
11192       NullCommand,
11193       HelpCommand,
11194       QuitCommand
11195     },
11196     FileCommands[] =
11197     {
11198       SaveCommand,
11199       PrintCommand
11200     },
11201     EditCommands[] =
11202     {
11203       UndoCommand,
11204       RedoCommand
11205     },
11206     TransformCommands[] =
11207     {
11208       FlopCommand,
11209       FlipCommand,
11210       RotateRightCommand,
11211       RotateLeftCommand
11212     },
11213     EnhanceCommands[] =
11214     {
11215       HueCommand,
11216       SaturationCommand,
11217       BrightnessCommand,
11218       GammaCommand,
11219       SpiffCommand,
11220       DullCommand,
11221       ContrastStretchCommand,
11222       SigmoidalContrastCommand,
11223       NormalizeCommand,
11224       EqualizeCommand,
11225       NegateCommand,
11226       GrayscaleCommand,
11227       MapCommand,
11228       QuantizeCommand
11229     },
11230     EffectsCommands[] =
11231     {
11232       DespeckleCommand,
11233       EmbossCommand,
11234       ReduceNoiseCommand,
11235       AddNoiseCommand,
11236       SharpenCommand,
11237       BlurCommand,
11238       EdgeDetectCommand,
11239       SpreadCommand,
11240       ShadeCommand,
11241       RaiseCommand,
11242       SegmentCommand
11243     },
11244     FXCommands[] =
11245     {
11246       SolarizeCommand,
11247       SepiaToneCommand,
11248       SwirlCommand,
11249       ImplodeCommand,
11250       VignetteCommand,
11251       WaveCommand,
11252       OilPaintCommand,
11253       CharcoalDrawCommand
11254     },
11255     MiscellanyCommands[] =
11256     {
11257       InfoCommand,
11258       ZoomCommand,
11259       ShowPreviewCommand,
11260       ShowHistogramCommand,
11261       ShowMatteCommand
11262     },
11263     ROICommands[] =
11264     {
11265       ROIHelpCommand,
11266       ROIDismissCommand
11267     };
11268
11269   static const CommandType
11270     *Commands[ApplyMenus] =
11271     {
11272       FileCommands,
11273       EditCommands,
11274       TransformCommands,
11275       EnhanceCommands,
11276       EffectsCommands,
11277       FXCommands,
11278       MiscellanyCommands
11279     };
11280
11281   char
11282     command[MaxTextExtent],
11283     text[MaxTextExtent];
11284
11285   CommandType
11286     command_type;
11287
11288   Cursor
11289     cursor;
11290
11291   Image
11292     *roi_image;
11293
11294   int
11295     entry,
11296     id,
11297     x,
11298     y;
11299
11300   double
11301     scale_factor;
11302
11303   MagickProgressMonitor
11304     progress_monitor;
11305
11306   RectangleInfo
11307     crop_info,
11308     highlight_info,
11309     roi_info;
11310
11311   unsigned int
11312     height,
11313     width;
11314
11315   size_t
11316     state;
11317
11318   XEvent
11319     event;
11320
11321   /*
11322     Map Command widget.
11323   */
11324   (void) CloneString(&windows->command.name,"ROI");
11325   windows->command.data=0;
11326   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11327   (void) XMapRaised(display,windows->command.id);
11328   XClientMessage(display,windows->image.id,windows->im_protocols,
11329     windows->im_update_widget,CurrentTime);
11330   /*
11331     Track pointer until button 1 is pressed.
11332   */
11333   XQueryPosition(display,windows->image.id,&x,&y);
11334   (void) XSelectInput(display,windows->image.id,
11335     windows->image.attributes.event_mask | PointerMotionMask);
11336   roi_info.x=(ssize_t) windows->image.x+x;
11337   roi_info.y=(ssize_t) windows->image.y+y;
11338   roi_info.width=0;
11339   roi_info.height=0;
11340   cursor=XCreateFontCursor(display,XC_fleur);
11341   state=DefaultState;
11342   do
11343   {
11344     if (IfMagickTrue(windows->info.mapped) )
11345       {
11346         /*
11347           Display pointer position.
11348         */
11349         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11350           (long) roi_info.x,(long) roi_info.y);
11351         XInfoWidget(display,windows,text);
11352       }
11353     /*
11354       Wait for next event.
11355     */
11356     XScreenEvent(display,windows,&event,exception);
11357     if (event.xany.window == windows->command.id)
11358       {
11359         /*
11360           Select a command from the Command widget.
11361         */
11362         id=XCommandWidget(display,windows,ROIMenu,&event);
11363         if (id < 0)
11364           continue;
11365         switch (ROICommands[id])
11366         {
11367           case ROIHelpCommand:
11368           {
11369             XTextViewWidget(display,resource_info,windows,MagickFalse,
11370               "Help Viewer - Region of Interest",ImageROIHelp);
11371             break;
11372           }
11373           case ROIDismissCommand:
11374           {
11375             /*
11376               Prematurely exit.
11377             */
11378             state|=EscapeState;
11379             state|=ExitState;
11380             break;
11381           }
11382           default:
11383             break;
11384         }
11385         continue;
11386       }
11387     switch (event.type)
11388     {
11389       case ButtonPress:
11390       {
11391         if (event.xbutton.button != Button1)
11392           break;
11393         if (event.xbutton.window != windows->image.id)
11394           break;
11395         /*
11396           Note first corner of region of interest rectangle-- exit loop.
11397         */
11398         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11399         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11400         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11401         state|=ExitState;
11402         break;
11403       }
11404       case ButtonRelease:
11405         break;
11406       case Expose:
11407         break;
11408       case KeyPress:
11409       {
11410         KeySym
11411           key_symbol;
11412
11413         if (event.xkey.window != windows->image.id)
11414           break;
11415         /*
11416           Respond to a user key press.
11417         */
11418         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11419           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11420         switch ((int) key_symbol)
11421         {
11422           case XK_Escape:
11423           case XK_F20:
11424           {
11425             /*
11426               Prematurely exit.
11427             */
11428             state|=EscapeState;
11429             state|=ExitState;
11430             break;
11431           }
11432           case XK_F1:
11433           case XK_Help:
11434           {
11435             XTextViewWidget(display,resource_info,windows,MagickFalse,
11436               "Help Viewer - Region of Interest",ImageROIHelp);
11437             break;
11438           }
11439           default:
11440           {
11441             (void) XBell(display,0);
11442             break;
11443           }
11444         }
11445         break;
11446       }
11447       case MotionNotify:
11448       {
11449         /*
11450           Map and unmap Info widget as text cursor crosses its boundaries.
11451         */
11452         x=event.xmotion.x;
11453         y=event.xmotion.y;
11454         if (IfMagickTrue(windows->info.mapped) )
11455           {
11456             if ((x < (int) (windows->info.x+windows->info.width)) &&
11457                 (y < (int) (windows->info.y+windows->info.height)))
11458               (void) XWithdrawWindow(display,windows->info.id,
11459                 windows->info.screen);
11460           }
11461         else
11462           if ((x > (int) (windows->info.x+windows->info.width)) ||
11463               (y > (int) (windows->info.y+windows->info.height)))
11464             (void) XMapWindow(display,windows->info.id);
11465         roi_info.x=(ssize_t) windows->image.x+x;
11466         roi_info.y=(ssize_t) windows->image.y+y;
11467         break;
11468       }
11469       default:
11470         break;
11471     }
11472   } while ((state & ExitState) == 0);
11473   (void) XSelectInput(display,windows->image.id,
11474     windows->image.attributes.event_mask);
11475   if ((state & EscapeState) != 0)
11476     {
11477       /*
11478         User want to exit without region of interest.
11479       */
11480       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11481       (void) XFreeCursor(display,cursor);
11482       return(MagickTrue);
11483     }
11484   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11485   do
11486   {
11487     /*
11488       Size rectangle as pointer moves until the mouse button is released.
11489     */
11490     x=(int) roi_info.x;
11491     y=(int) roi_info.y;
11492     roi_info.width=0;
11493     roi_info.height=0;
11494     state=DefaultState;
11495     do
11496     {
11497       highlight_info=roi_info;
11498       highlight_info.x=roi_info.x-windows->image.x;
11499       highlight_info.y=roi_info.y-windows->image.y;
11500       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11501         {
11502           /*
11503             Display info and draw region of interest rectangle.
11504           */
11505           if (IfMagickFalse(windows->info.mapped) )
11506             (void) XMapWindow(display,windows->info.id);
11507           (void) FormatLocaleString(text,MaxTextExtent,
11508             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11509             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11510           XInfoWidget(display,windows,text);
11511           XHighlightRectangle(display,windows->image.id,
11512             windows->image.highlight_context,&highlight_info);
11513         }
11514       else
11515         if (IfMagickTrue(windows->info.mapped) )
11516           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11517       /*
11518         Wait for next event.
11519       */
11520       XScreenEvent(display,windows,&event,exception);
11521       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11522         XHighlightRectangle(display,windows->image.id,
11523           windows->image.highlight_context,&highlight_info);
11524       switch (event.type)
11525       {
11526         case ButtonPress:
11527         {
11528           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11529           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11530           break;
11531         }
11532         case ButtonRelease:
11533         {
11534           /*
11535             User has committed to region of interest rectangle.
11536           */
11537           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11538           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11539           XSetCursorState(display,windows,MagickFalse);
11540           state|=ExitState;
11541           if (LocaleCompare(windows->command.name,"Apply") == 0)
11542             break;
11543           (void) CloneString(&windows->command.name,"Apply");
11544           windows->command.data=ApplyMenus;
11545           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11546           break;
11547         }
11548         case Expose:
11549           break;
11550         case MotionNotify:
11551         {
11552           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11553           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11554         }
11555         default:
11556           break;
11557       }
11558       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11559           ((state & ExitState) != 0))
11560         {
11561           /*
11562             Check boundary conditions.
11563           */
11564           if (roi_info.x < 0)
11565             roi_info.x=0;
11566           else
11567             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11568               roi_info.x=(ssize_t) windows->image.ximage->width;
11569           if ((int) roi_info.x < x)
11570             roi_info.width=(unsigned int) (x-roi_info.x);
11571           else
11572             {
11573               roi_info.width=(unsigned int) (roi_info.x-x);
11574               roi_info.x=(ssize_t) x;
11575             }
11576           if (roi_info.y < 0)
11577             roi_info.y=0;
11578           else
11579             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11580               roi_info.y=(ssize_t) windows->image.ximage->height;
11581           if ((int) roi_info.y < y)
11582             roi_info.height=(unsigned int) (y-roi_info.y);
11583           else
11584             {
11585               roi_info.height=(unsigned int) (roi_info.y-y);
11586               roi_info.y=(ssize_t) y;
11587             }
11588         }
11589     } while ((state & ExitState) == 0);
11590     /*
11591       Wait for user to grab a corner of the rectangle or press return.
11592     */
11593     state=DefaultState;
11594     command_type=NullCommand;
11595     crop_info.x=0;
11596     crop_info.y=0;
11597     (void) XMapWindow(display,windows->info.id);
11598     do
11599     {
11600       if (IfMagickTrue(windows->info.mapped) )
11601         {
11602           /*
11603             Display pointer position.
11604           */
11605           (void) FormatLocaleString(text,MaxTextExtent,
11606             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11607             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11608           XInfoWidget(display,windows,text);
11609         }
11610       highlight_info=roi_info;
11611       highlight_info.x=roi_info.x-windows->image.x;
11612       highlight_info.y=roi_info.y-windows->image.y;
11613       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11614         {
11615           state|=EscapeState;
11616           state|=ExitState;
11617           break;
11618         }
11619       if ((state & UpdateRegionState) != 0)
11620         {
11621           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11622           switch (command_type)
11623           {
11624             case UndoCommand:
11625             case RedoCommand:
11626             {
11627               (void) XMagickCommand(display,resource_info,windows,command_type,
11628                 image,exception);
11629               break;
11630             }
11631             default:
11632             {
11633               /*
11634                 Region of interest is relative to image configuration.
11635               */
11636               progress_monitor=SetImageProgressMonitor(*image,
11637                 (MagickProgressMonitor) NULL,(*image)->client_data);
11638               crop_info=roi_info;
11639               width=(unsigned int) (*image)->columns;
11640               height=(unsigned int) (*image)->rows;
11641               x=0;
11642               y=0;
11643               if (windows->image.crop_geometry != (char *) NULL)
11644                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11645                   &width,&height);
11646               scale_factor=(double) width/windows->image.ximage->width;
11647               crop_info.x+=x;
11648               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11649               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11650               scale_factor=(double)
11651                 height/windows->image.ximage->height;
11652               crop_info.y+=y;
11653               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11654               crop_info.height=(unsigned int)
11655                 (scale_factor*crop_info.height+0.5);
11656               roi_image=CropImage(*image,&crop_info,exception);
11657               (void) SetImageProgressMonitor(*image,progress_monitor,
11658                 (*image)->client_data);
11659               if (roi_image == (Image *) NULL)
11660                 continue;
11661               /*
11662                 Apply image processing technique to the region of interest.
11663               */
11664               windows->image.orphan=MagickTrue;
11665               (void) XMagickCommand(display,resource_info,windows,command_type,
11666                 &roi_image,exception);
11667               progress_monitor=SetImageProgressMonitor(*image,
11668                 (MagickProgressMonitor) NULL,(*image)->client_data);
11669               (void) XMagickCommand(display,resource_info,windows,
11670                 SaveToUndoBufferCommand,image,exception);
11671               windows->image.orphan=MagickFalse;
11672               (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11673                 MagickTrue,crop_info.x,crop_info.y,exception);
11674               roi_image=DestroyImage(roi_image);
11675               (void) SetImageProgressMonitor(*image,progress_monitor,
11676                 (*image)->client_data);
11677               break;
11678             }
11679           }
11680           if (command_type != InfoCommand)
11681             {
11682               XConfigureImageColormap(display,resource_info,windows,*image,
11683                 exception);
11684               (void) XConfigureImage(display,resource_info,windows,*image,
11685                 exception);
11686             }
11687           XCheckRefreshWindows(display,windows);
11688           XInfoWidget(display,windows,text);
11689           (void) XSetFunction(display,windows->image.highlight_context,
11690             GXinvert);
11691           state&=(~UpdateRegionState);
11692         }
11693       XHighlightRectangle(display,windows->image.id,
11694         windows->image.highlight_context,&highlight_info);
11695       XScreenEvent(display,windows,&event,exception);
11696       if (event.xany.window == windows->command.id)
11697         {
11698           /*
11699             Select a command from the Command widget.
11700           */
11701           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11702           command_type=NullCommand;
11703           id=XCommandWidget(display,windows,ApplyMenu,&event);
11704           if (id >= 0)
11705             {
11706               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11707               command_type=ApplyCommands[id];
11708               if (id < ApplyMenus)
11709                 {
11710                   /*
11711                     Select a command from a pop-up menu.
11712                   */
11713                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11714                     (const char **) Menus[id],command);
11715                   if (entry >= 0)
11716                     {
11717                       (void) CopyMagickString(command,Menus[id][entry],
11718                         MaxTextExtent);
11719                       command_type=Commands[id][entry];
11720                     }
11721                 }
11722             }
11723           (void) XSetFunction(display,windows->image.highlight_context,
11724             GXinvert);
11725           XHighlightRectangle(display,windows->image.id,
11726             windows->image.highlight_context,&highlight_info);
11727           if (command_type == HelpCommand)
11728             {
11729               (void) XSetFunction(display,windows->image.highlight_context,
11730                 GXcopy);
11731               XTextViewWidget(display,resource_info,windows,MagickFalse,
11732                 "Help Viewer - Region of Interest",ImageROIHelp);
11733               (void) XSetFunction(display,windows->image.highlight_context,
11734                 GXinvert);
11735               continue;
11736             }
11737           if (command_type == QuitCommand)
11738             {
11739               /*
11740                 exit.
11741               */
11742               state|=EscapeState;
11743               state|=ExitState;
11744               continue;
11745             }
11746           if (command_type != NullCommand)
11747             state|=UpdateRegionState;
11748           continue;
11749         }
11750       XHighlightRectangle(display,windows->image.id,
11751         windows->image.highlight_context,&highlight_info);
11752       switch (event.type)
11753       {
11754         case ButtonPress:
11755         {
11756           x=windows->image.x;
11757           y=windows->image.y;
11758           if (event.xbutton.button != Button1)
11759             break;
11760           if (event.xbutton.window != windows->image.id)
11761             break;
11762           x=windows->image.x+event.xbutton.x;
11763           y=windows->image.y+event.xbutton.y;
11764           if ((x < (int) (roi_info.x+RoiDelta)) &&
11765               (x > (int) (roi_info.x-RoiDelta)) &&
11766               (y < (int) (roi_info.y+RoiDelta)) &&
11767               (y > (int) (roi_info.y-RoiDelta)))
11768             {
11769               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11770               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11771               state|=UpdateConfigurationState;
11772               break;
11773             }
11774           if ((x < (int) (roi_info.x+RoiDelta)) &&
11775               (x > (int) (roi_info.x-RoiDelta)) &&
11776               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11777               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11778             {
11779               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11780               state|=UpdateConfigurationState;
11781               break;
11782             }
11783           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11784               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11785               (y < (int) (roi_info.y+RoiDelta)) &&
11786               (y > (int) (roi_info.y-RoiDelta)))
11787             {
11788               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11789               state|=UpdateConfigurationState;
11790               break;
11791             }
11792           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11793               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11794               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11795               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11796             {
11797               state|=UpdateConfigurationState;
11798               break;
11799             }
11800         }
11801         case ButtonRelease:
11802         {
11803           if (event.xbutton.window == windows->pan.id)
11804             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11805                 (highlight_info.y != crop_info.y-windows->image.y))
11806               XHighlightRectangle(display,windows->image.id,
11807                 windows->image.highlight_context,&highlight_info);
11808           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11809             event.xbutton.time);
11810           break;
11811         }
11812         case Expose:
11813         {
11814           if (event.xexpose.window == windows->image.id)
11815             if (event.xexpose.count == 0)
11816               {
11817                 event.xexpose.x=(int) highlight_info.x;
11818                 event.xexpose.y=(int) highlight_info.y;
11819                 event.xexpose.width=(int) highlight_info.width;
11820                 event.xexpose.height=(int) highlight_info.height;
11821                 XRefreshWindow(display,&windows->image,&event);
11822               }
11823           if (event.xexpose.window == windows->info.id)
11824             if (event.xexpose.count == 0)
11825               XInfoWidget(display,windows,text);
11826           break;
11827         }
11828         case KeyPress:
11829         {
11830           KeySym
11831             key_symbol;
11832
11833           if (event.xkey.window != windows->image.id)
11834             break;
11835           /*
11836             Respond to a user key press.
11837           */
11838           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11839             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11840           switch ((int) key_symbol)
11841           {
11842             case XK_Shift_L:
11843             case XK_Shift_R:
11844               break;
11845             case XK_Escape:
11846             case XK_F20:
11847               state|=EscapeState;
11848             case XK_Return:
11849             {
11850               state|=ExitState;
11851               break;
11852             }
11853             case XK_Home:
11854             case XK_KP_Home:
11855             {
11856               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11857               roi_info.y=(ssize_t) (windows->image.height/2L-
11858                 roi_info.height/2L);
11859               break;
11860             }
11861             case XK_Left:
11862             case XK_KP_Left:
11863             {
11864               roi_info.x--;
11865               break;
11866             }
11867             case XK_Up:
11868             case XK_KP_Up:
11869             case XK_Next:
11870             {
11871               roi_info.y--;
11872               break;
11873             }
11874             case XK_Right:
11875             case XK_KP_Right:
11876             {
11877               roi_info.x++;
11878               break;
11879             }
11880             case XK_Prior:
11881             case XK_Down:
11882             case XK_KP_Down:
11883             {
11884               roi_info.y++;
11885               break;
11886             }
11887             case XK_F1:
11888             case XK_Help:
11889             {
11890               (void) XSetFunction(display,windows->image.highlight_context,
11891                 GXcopy);
11892               XTextViewWidget(display,resource_info,windows,MagickFalse,
11893                 "Help Viewer - Region of Interest",ImageROIHelp);
11894               (void) XSetFunction(display,windows->image.highlight_context,
11895                 GXinvert);
11896               break;
11897             }
11898             default:
11899             {
11900               command_type=XImageWindowCommand(display,resource_info,windows,
11901                 event.xkey.state,key_symbol,image,exception);
11902               if (command_type != NullCommand)
11903                 state|=UpdateRegionState;
11904               break;
11905             }
11906           }
11907           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11908             event.xkey.time);
11909           break;
11910         }
11911         case KeyRelease:
11912           break;
11913         case MotionNotify:
11914         {
11915           if (event.xbutton.window != windows->image.id)
11916             break;
11917           /*
11918             Map and unmap Info widget as text cursor crosses its boundaries.
11919           */
11920           x=event.xmotion.x;
11921           y=event.xmotion.y;
11922           if (IfMagickTrue(windows->info.mapped) )
11923             {
11924               if ((x < (int) (windows->info.x+windows->info.width)) &&
11925                   (y < (int) (windows->info.y+windows->info.height)))
11926                 (void) XWithdrawWindow(display,windows->info.id,
11927                   windows->info.screen);
11928             }
11929           else
11930             if ((x > (int) (windows->info.x+windows->info.width)) ||
11931                 (y > (int) (windows->info.y+windows->info.height)))
11932               (void) XMapWindow(display,windows->info.id);
11933           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11934           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11935           break;
11936         }
11937         case SelectionRequest:
11938         {
11939           XSelectionEvent
11940             notify;
11941
11942           XSelectionRequestEvent
11943             *request;
11944
11945           /*
11946             Set primary selection.
11947           */
11948           (void) FormatLocaleString(text,MaxTextExtent,
11949             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11950             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11951           request=(&(event.xselectionrequest));
11952           (void) XChangeProperty(request->display,request->requestor,
11953             request->property,request->target,8,PropModeReplace,
11954             (unsigned char *) text,(int) strlen(text));
11955           notify.type=SelectionNotify;
11956           notify.display=request->display;
11957           notify.requestor=request->requestor;
11958           notify.selection=request->selection;
11959           notify.target=request->target;
11960           notify.time=request->time;
11961           if (request->property == None)
11962             notify.property=request->target;
11963           else
11964             notify.property=request->property;
11965           (void) XSendEvent(request->display,request->requestor,False,0,
11966             (XEvent *) &notify);
11967         }
11968         default:
11969           break;
11970       }
11971       if ((state & UpdateConfigurationState) != 0)
11972         {
11973           (void) XPutBackEvent(display,&event);
11974           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11975           break;
11976         }
11977     } while ((state & ExitState) == 0);
11978   } while ((state & ExitState) == 0);
11979   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11980   XSetCursorState(display,windows,MagickFalse);
11981   if ((state & EscapeState) != 0)
11982     return(MagickTrue);
11983   return(MagickTrue);
11984 }
11985 \f
11986 /*
11987 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11988 %                                                                             %
11989 %                                                                             %
11990 %                                                                             %
11991 +   X R o t a t e I m a g e                                                   %
11992 %                                                                             %
11993 %                                                                             %
11994 %                                                                             %
11995 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11996 %
11997 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11998 %  rotation angle is computed from the slope of a line drawn by the user.
11999 %
12000 %  The format of the XRotateImage method is:
12001 %
12002 %      MagickBooleanType XRotateImage(Display *display,
12003 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
12004 %        Image **image,ExceptionInfo *exception)
12005 %
12006 %  A description of each parameter follows:
12007 %
12008 %    o display: Specifies a connection to an X server; returned from
12009 %      XOpenDisplay.
12010 %
12011 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12012 %
12013 %    o windows: Specifies a pointer to a XWindows structure.
12014 %
12015 %    o degrees: Specifies the number of degrees to rotate the image.
12016 %
12017 %    o image: the image.
12018 %
12019 %    o exception: return any errors or warnings in this structure.
12020 %
12021 */
12022 static MagickBooleanType XRotateImage(Display *display,
12023   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12024   ExceptionInfo *exception)
12025 {
12026   static const char
12027     *RotateMenu[] =
12028     {
12029       "Pixel Color",
12030       "Direction",
12031       "Help",
12032       "Dismiss",
12033       (char *) NULL
12034     };
12035
12036   static ModeType
12037     direction = HorizontalRotateCommand;
12038
12039   static const ModeType
12040     DirectionCommands[] =
12041     {
12042       HorizontalRotateCommand,
12043       VerticalRotateCommand
12044     },
12045     RotateCommands[] =
12046     {
12047       RotateColorCommand,
12048       RotateDirectionCommand,
12049       RotateHelpCommand,
12050       RotateDismissCommand
12051     };
12052
12053   static unsigned int
12054     pen_id = 0;
12055
12056   char
12057     command[MaxTextExtent],
12058     text[MaxTextExtent];
12059
12060   Image
12061     *rotate_image;
12062
12063   int
12064     id,
12065     x,
12066     y;
12067
12068   double
12069     normalized_degrees;
12070
12071   register int
12072     i;
12073
12074   unsigned int
12075     height,
12076     rotations,
12077     width;
12078
12079   if (degrees == 0.0)
12080     {
12081       unsigned int
12082         distance;
12083
12084       size_t
12085         state;
12086
12087       XEvent
12088         event;
12089
12090       XSegment
12091         rotate_info;
12092
12093       /*
12094         Map Command widget.
12095       */
12096       (void) CloneString(&windows->command.name,"Rotate");
12097       windows->command.data=2;
12098       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12099       (void) XMapRaised(display,windows->command.id);
12100       XClientMessage(display,windows->image.id,windows->im_protocols,
12101         windows->im_update_widget,CurrentTime);
12102       /*
12103         Wait for first button press.
12104       */
12105       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12106       XQueryPosition(display,windows->image.id,&x,&y);
12107       rotate_info.x1=x;
12108       rotate_info.y1=y;
12109       rotate_info.x2=x;
12110       rotate_info.y2=y;
12111       state=DefaultState;
12112       do
12113       {
12114         XHighlightLine(display,windows->image.id,
12115           windows->image.highlight_context,&rotate_info);
12116         /*
12117           Wait for next event.
12118         */
12119         XScreenEvent(display,windows,&event,exception);
12120         XHighlightLine(display,windows->image.id,
12121           windows->image.highlight_context,&rotate_info);
12122         if (event.xany.window == windows->command.id)
12123           {
12124             /*
12125               Select a command from the Command widget.
12126             */
12127             id=XCommandWidget(display,windows,RotateMenu,&event);
12128             if (id < 0)
12129               continue;
12130             (void) XSetFunction(display,windows->image.highlight_context,
12131               GXcopy);
12132             switch (RotateCommands[id])
12133             {
12134               case RotateColorCommand:
12135               {
12136                 const char
12137                   *ColorMenu[MaxNumberPens];
12138
12139                 int
12140                   pen_number;
12141
12142                 XColor
12143                   color;
12144
12145                 /*
12146                   Initialize menu selections.
12147                 */
12148                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12149                   ColorMenu[i]=resource_info->pen_colors[i];
12150                 ColorMenu[MaxNumberPens-2]="Browser...";
12151                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12152                 /*
12153                   Select a pen color from the pop-up menu.
12154                 */
12155                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12156                   (const char **) ColorMenu,command);
12157                 if (pen_number < 0)
12158                   break;
12159                 if (pen_number == (MaxNumberPens-2))
12160                   {
12161                     static char
12162                       color_name[MaxTextExtent] = "gray";
12163
12164                     /*
12165                       Select a pen color from a dialog.
12166                     */
12167                     resource_info->pen_colors[pen_number]=color_name;
12168                     XColorBrowserWidget(display,windows,"Select",color_name);
12169                     if (*color_name == '\0')
12170                       break;
12171                   }
12172                 /*
12173                   Set pen color.
12174                 */
12175                 (void) XParseColor(display,windows->map_info->colormap,
12176                   resource_info->pen_colors[pen_number],&color);
12177                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12178                   (unsigned int) MaxColors,&color);
12179                 windows->pixel_info->pen_colors[pen_number]=color;
12180                 pen_id=(unsigned int) pen_number;
12181                 break;
12182               }
12183               case RotateDirectionCommand:
12184               {
12185                 static const char
12186                   *Directions[] =
12187                   {
12188                     "horizontal",
12189                     "vertical",
12190                     (char *) NULL,
12191                   };
12192
12193                 /*
12194                   Select a command from the pop-up menu.
12195                 */
12196                 id=XMenuWidget(display,windows,RotateMenu[id],
12197                   Directions,command);
12198                 if (id >= 0)
12199                   direction=DirectionCommands[id];
12200                 break;
12201               }
12202               case RotateHelpCommand:
12203               {
12204                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12205                   "Help Viewer - Image Rotation",ImageRotateHelp);
12206                 break;
12207               }
12208               case RotateDismissCommand:
12209               {
12210                 /*
12211                   Prematurely exit.
12212                 */
12213                 state|=EscapeState;
12214                 state|=ExitState;
12215                 break;
12216               }
12217               default:
12218                 break;
12219             }
12220             (void) XSetFunction(display,windows->image.highlight_context,
12221               GXinvert);
12222             continue;
12223           }
12224         switch (event.type)
12225         {
12226           case ButtonPress:
12227           {
12228             if (event.xbutton.button != Button1)
12229               break;
12230             if (event.xbutton.window != windows->image.id)
12231               break;
12232             /*
12233               exit loop.
12234             */
12235             (void) XSetFunction(display,windows->image.highlight_context,
12236               GXcopy);
12237             rotate_info.x1=event.xbutton.x;
12238             rotate_info.y1=event.xbutton.y;
12239             state|=ExitState;
12240             break;
12241           }
12242           case ButtonRelease:
12243             break;
12244           case Expose:
12245             break;
12246           case KeyPress:
12247           {
12248             char
12249               command[MaxTextExtent];
12250
12251             KeySym
12252               key_symbol;
12253
12254             if (event.xkey.window != windows->image.id)
12255               break;
12256             /*
12257               Respond to a user key press.
12258             */
12259             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12260               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12261             switch ((int) key_symbol)
12262             {
12263               case XK_Escape:
12264               case XK_F20:
12265               {
12266                 /*
12267                   Prematurely exit.
12268                 */
12269                 state|=EscapeState;
12270                 state|=ExitState;
12271                 break;
12272               }
12273               case XK_F1:
12274               case XK_Help:
12275               {
12276                 (void) XSetFunction(display,windows->image.highlight_context,
12277                   GXcopy);
12278                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12279                   "Help Viewer - Image Rotation",ImageRotateHelp);
12280                 (void) XSetFunction(display,windows->image.highlight_context,
12281                   GXinvert);
12282                 break;
12283               }
12284               default:
12285               {
12286                 (void) XBell(display,0);
12287                 break;
12288               }
12289             }
12290             break;
12291           }
12292           case MotionNotify:
12293           {
12294             rotate_info.x1=event.xmotion.x;
12295             rotate_info.y1=event.xmotion.y;
12296           }
12297         }
12298         rotate_info.x2=rotate_info.x1;
12299         rotate_info.y2=rotate_info.y1;
12300         if (direction == HorizontalRotateCommand)
12301           rotate_info.x2+=32;
12302         else
12303           rotate_info.y2-=32;
12304       } while ((state & ExitState) == 0);
12305       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12306       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12307       if ((state & EscapeState) != 0)
12308         return(MagickTrue);
12309       /*
12310         Draw line as pointer moves until the mouse button is released.
12311       */
12312       distance=0;
12313       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12314       state=DefaultState;
12315       do
12316       {
12317         if (distance > 9)
12318           {
12319             /*
12320               Display info and draw rotation line.
12321             */
12322             if (IfMagickFalse(windows->info.mapped) )
12323               (void) XMapWindow(display,windows->info.id);
12324             (void) FormatLocaleString(text,MaxTextExtent," %g",
12325               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12326             XInfoWidget(display,windows,text);
12327             XHighlightLine(display,windows->image.id,
12328               windows->image.highlight_context,&rotate_info);
12329           }
12330         else
12331           if (IfMagickTrue(windows->info.mapped) )
12332             (void) XWithdrawWindow(display,windows->info.id,
12333               windows->info.screen);
12334         /*
12335           Wait for next event.
12336         */
12337         XScreenEvent(display,windows,&event,exception);
12338         if (distance > 9)
12339           XHighlightLine(display,windows->image.id,
12340             windows->image.highlight_context,&rotate_info);
12341         switch (event.type)
12342         {
12343           case ButtonPress:
12344             break;
12345           case ButtonRelease:
12346           {
12347             /*
12348               User has committed to rotation line.
12349             */
12350             rotate_info.x2=event.xbutton.x;
12351             rotate_info.y2=event.xbutton.y;
12352             state|=ExitState;
12353             break;
12354           }
12355           case Expose:
12356             break;
12357           case MotionNotify:
12358           {
12359             rotate_info.x2=event.xmotion.x;
12360             rotate_info.y2=event.xmotion.y;
12361           }
12362           default:
12363             break;
12364         }
12365         /*
12366           Check boundary conditions.
12367         */
12368         if (rotate_info.x2 < 0)
12369           rotate_info.x2=0;
12370         else
12371           if (rotate_info.x2 > (int) windows->image.width)
12372             rotate_info.x2=(short) windows->image.width;
12373         if (rotate_info.y2 < 0)
12374           rotate_info.y2=0;
12375         else
12376           if (rotate_info.y2 > (int) windows->image.height)
12377             rotate_info.y2=(short) windows->image.height;
12378         /*
12379           Compute rotation angle from the slope of the line.
12380         */
12381         degrees=0.0;
12382         distance=(unsigned int)
12383           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12384           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12385         if (distance > 9)
12386           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12387             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12388       } while ((state & ExitState) == 0);
12389       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12390       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12391       if (distance <= 9)
12392         return(MagickTrue);
12393     }
12394   if (direction == VerticalRotateCommand)
12395     degrees-=90.0;
12396   if (degrees == 0.0)
12397     return(MagickTrue);
12398   /*
12399     Rotate image.
12400   */
12401   normalized_degrees=degrees;
12402   while (normalized_degrees < -45.0)
12403     normalized_degrees+=360.0;
12404   for (rotations=0; normalized_degrees > 45.0; rotations++)
12405     normalized_degrees-=90.0;
12406   if (normalized_degrees != 0.0)
12407     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12408       exception);
12409   XSetCursorState(display,windows,MagickTrue);
12410   XCheckRefreshWindows(display,windows);
12411   (*image)->background_color.red=(double) ScaleShortToQuantum(
12412     windows->pixel_info->pen_colors[pen_id].red);
12413   (*image)->background_color.green=(double) ScaleShortToQuantum(
12414     windows->pixel_info->pen_colors[pen_id].green);
12415   (*image)->background_color.blue=(double) ScaleShortToQuantum(
12416     windows->pixel_info->pen_colors[pen_id].blue);
12417   rotate_image=RotateImage(*image,degrees,exception);
12418   XSetCursorState(display,windows,MagickFalse);
12419   if (rotate_image == (Image *) NULL)
12420     return(MagickFalse);
12421   *image=DestroyImage(*image);
12422   *image=rotate_image;
12423   if (windows->image.crop_geometry != (char *) NULL)
12424     {
12425       /*
12426         Rotate crop geometry.
12427       */
12428       width=(unsigned int) (*image)->columns;
12429       height=(unsigned int) (*image)->rows;
12430       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12431       switch (rotations % 4)
12432       {
12433         default:
12434         case 0:
12435           break;
12436         case 1:
12437         {
12438           /*
12439             Rotate 90 degrees.
12440           */
12441           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12442             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12443             (int) height-y,x);
12444           break;
12445         }
12446         case 2:
12447         {
12448           /*
12449             Rotate 180 degrees.
12450           */
12451           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12452             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12453           break;
12454         }
12455         case 3:
12456         {
12457           /*
12458             Rotate 270 degrees.
12459           */
12460           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12461             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12462           break;
12463         }
12464       }
12465     }
12466   if (IfMagickTrue(windows->image.orphan) )
12467     return(MagickTrue);
12468   if (normalized_degrees != 0.0)
12469     {
12470       /*
12471         Update image colormap.
12472       */
12473       windows->image.window_changes.width=(int) (*image)->columns;
12474       windows->image.window_changes.height=(int) (*image)->rows;
12475       if (windows->image.crop_geometry != (char *) NULL)
12476         {
12477           /*
12478             Obtain dimensions of image from crop geometry.
12479           */
12480           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12481             &width,&height);
12482           windows->image.window_changes.width=(int) width;
12483           windows->image.window_changes.height=(int) height;
12484         }
12485       XConfigureImageColormap(display,resource_info,windows,*image,exception);
12486     }
12487   else
12488     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12489       {
12490         windows->image.window_changes.width=windows->image.ximage->height;
12491         windows->image.window_changes.height=windows->image.ximage->width;
12492       }
12493   /*
12494     Update image configuration.
12495   */
12496   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12497   return(MagickTrue);
12498 }
12499 \f
12500 /*
12501 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12502 %                                                                             %
12503 %                                                                             %
12504 %                                                                             %
12505 +   X S a v e I m a g e                                                       %
12506 %                                                                             %
12507 %                                                                             %
12508 %                                                                             %
12509 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12510 %
12511 %  XSaveImage() saves an image to a file.
12512 %
12513 %  The format of the XSaveImage method is:
12514 %
12515 %      MagickBooleanType XSaveImage(Display *display,
12516 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12517 %        ExceptionInfo *exception)
12518 %
12519 %  A description of each parameter follows:
12520 %
12521 %    o display: Specifies a connection to an X server; returned from
12522 %      XOpenDisplay.
12523 %
12524 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12525 %
12526 %    o windows: Specifies a pointer to a XWindows structure.
12527 %
12528 %    o image: the image.
12529 %
12530 %    o exception: return any errors or warnings in this structure.
12531 %
12532 */
12533 static MagickBooleanType XSaveImage(Display *display,
12534   XResourceInfo *resource_info,XWindows *windows,Image *image,
12535   ExceptionInfo *exception)
12536 {
12537   char
12538     filename[MaxTextExtent],
12539     geometry[MaxTextExtent];
12540
12541   Image
12542     *save_image;
12543
12544   ImageInfo
12545     *image_info;
12546
12547   MagickStatusType
12548     status;
12549
12550   /*
12551     Request file name from user.
12552   */
12553   if (resource_info->write_filename != (char *) NULL)
12554     (void) CopyMagickString(filename,resource_info->write_filename,
12555       MaxTextExtent);
12556   else
12557     {
12558       char
12559         path[MaxTextExtent];
12560
12561       int
12562         status;
12563
12564       GetPathComponent(image->filename,HeadPath,path);
12565       GetPathComponent(image->filename,TailPath,filename);
12566       if (*path != '\0')
12567         {
12568           status=chdir(path);
12569           if (status == -1)
12570             (void) ThrowMagickException(exception,GetMagickModule(),
12571               FileOpenError,"UnableToOpenFile","%s",path);
12572         }
12573     }
12574   XFileBrowserWidget(display,windows,"Save",filename);
12575   if (*filename == '\0')
12576     return(MagickTrue);
12577   if (IfMagickTrue(IsPathAccessible(filename)) )
12578     {
12579       int
12580         status;
12581
12582       /*
12583         File exists-- seek user's permission before overwriting.
12584       */
12585       status=XConfirmWidget(display,windows,"Overwrite",filename);
12586       if (status <= 0)
12587         return(MagickTrue);
12588     }
12589   image_info=CloneImageInfo(resource_info->image_info);
12590   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12591   (void) SetImageInfo(image_info,1,exception);
12592   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12593       (LocaleCompare(image_info->magick,"JPG") == 0))
12594     {
12595       char
12596         quality[MaxTextExtent];
12597
12598       int
12599         status;
12600
12601       /*
12602         Request JPEG quality from user.
12603       */
12604       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12605         image->quality);
12606       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12607         quality);
12608       if (*quality == '\0')
12609         return(MagickTrue);
12610       image->quality=StringToUnsignedLong(quality);
12611       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12612     }
12613   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12614       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12615       (LocaleCompare(image_info->magick,"PS") == 0) ||
12616       (LocaleCompare(image_info->magick,"PS2") == 0))
12617     {
12618       char
12619         geometry[MaxTextExtent];
12620
12621       /*
12622         Request page geometry from user.
12623       */
12624       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12625       if (LocaleCompare(image_info->magick,"PDF") == 0)
12626         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12627       if (image_info->page != (char *) NULL)
12628         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12629       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12630         "Select page geometry:",geometry);
12631       if (*geometry != '\0')
12632         image_info->page=GetPageGeometry(geometry);
12633     }
12634   /*
12635     Apply image transforms.
12636   */
12637   XSetCursorState(display,windows,MagickTrue);
12638   XCheckRefreshWindows(display,windows);
12639   save_image=CloneImage(image,0,0,MagickTrue,exception);
12640   if (save_image == (Image *) NULL)
12641     return(MagickFalse);
12642   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12643     windows->image.ximage->width,windows->image.ximage->height);
12644   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12645     exception);
12646   /*
12647     Write image.
12648   */
12649   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12650   status=WriteImage(image_info,save_image,exception);
12651   if (IfMagickTrue(status) )
12652     image->taint=MagickFalse;
12653   save_image=DestroyImage(save_image);
12654   image_info=DestroyImageInfo(image_info);
12655   XSetCursorState(display,windows,MagickFalse);
12656   return(IsMagickTrue(status));
12657 }
12658 \f
12659 /*
12660 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12661 %                                                                             %
12662 %                                                                             %
12663 %                                                                             %
12664 +   X S c r e e n E v e n t                                                   %
12665 %                                                                             %
12666 %                                                                             %
12667 %                                                                             %
12668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12669 %
12670 %  XScreenEvent() handles global events associated with the Pan and Magnify
12671 %  windows.
12672 %
12673 %  The format of the XScreenEvent function is:
12674 %
12675 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12676 %        ExceptionInfo *exception)
12677 %
12678 %  A description of each parameter follows:
12679 %
12680 %    o display: Specifies a pointer to the Display structure;  returned from
12681 %      XOpenDisplay.
12682 %
12683 %    o windows: Specifies a pointer to a XWindows structure.
12684 %
12685 %    o event: Specifies a pointer to a X11 XEvent structure.
12686 %
12687 %    o exception: return any errors or warnings in this structure.
12688 %
12689 */
12690
12691 #if defined(__cplusplus) || defined(c_plusplus)
12692 extern "C" {
12693 #endif
12694
12695 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12696 {
12697   register XWindows
12698     *windows;
12699
12700   windows=(XWindows *) data;
12701   if ((event->type == ClientMessage) &&
12702       (event->xclient.window == windows->image.id))
12703     return(MagickFalse);
12704   return(MagickTrue);
12705 }
12706
12707 #if defined(__cplusplus) || defined(c_plusplus)
12708 }
12709 #endif
12710
12711 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12712   ExceptionInfo *exception)
12713 {
12714   register int
12715     x,
12716     y;
12717
12718   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12719   if (event->xany.window == windows->command.id)
12720     return;
12721   switch (event->type)
12722   {
12723     case ButtonPress:
12724     case ButtonRelease:
12725     {
12726       if ((event->xbutton.button == Button3) &&
12727           (event->xbutton.state & Mod1Mask))
12728         {
12729           /*
12730             Convert Alt-Button3 to Button2.
12731           */
12732           event->xbutton.button=Button2;
12733           event->xbutton.state&=(~Mod1Mask);
12734         }
12735       if (event->xbutton.window == windows->backdrop.id)
12736         {
12737           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12738             event->xbutton.time);
12739           break;
12740         }
12741       if (event->xbutton.window == windows->pan.id)
12742         {
12743           XPanImage(display,windows,event,exception);
12744           break;
12745         }
12746       if (event->xbutton.window == windows->image.id)
12747         if (event->xbutton.button == Button2)
12748           {
12749             /*
12750               Update magnified image.
12751             */
12752             x=event->xbutton.x;
12753             y=event->xbutton.y;
12754             if (x < 0)
12755               x=0;
12756             else
12757               if (x >= (int) windows->image.width)
12758                 x=(int) (windows->image.width-1);
12759             windows->magnify.x=(int) windows->image.x+x;
12760             if (y < 0)
12761               y=0;
12762             else
12763              if (y >= (int) windows->image.height)
12764                y=(int) (windows->image.height-1);
12765             windows->magnify.y=windows->image.y+y;
12766             if (IfMagickFalse(windows->magnify.mapped) )
12767               (void) XMapRaised(display,windows->magnify.id);
12768             XMakeMagnifyImage(display,windows,exception);
12769             if (event->type == ButtonRelease)
12770               (void) XWithdrawWindow(display,windows->info.id,
12771                 windows->info.screen);
12772             break;
12773           }
12774       break;
12775     }
12776     case ClientMessage:
12777     {
12778       /*
12779         If client window delete message, exit.
12780       */
12781       if (event->xclient.message_type != windows->wm_protocols)
12782         break;
12783       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12784         break;
12785       if (event->xclient.window == windows->magnify.id)
12786         {
12787           (void) XWithdrawWindow(display,windows->magnify.id,
12788             windows->magnify.screen);
12789           break;
12790         }
12791       break;
12792     }
12793     case ConfigureNotify:
12794     {
12795       if (event->xconfigure.window == windows->magnify.id)
12796         {
12797           unsigned int
12798             magnify;
12799
12800           /*
12801             Magnify window has a new configuration.
12802           */
12803           windows->magnify.width=(unsigned int) event->xconfigure.width;
12804           windows->magnify.height=(unsigned int) event->xconfigure.height;
12805           if (IfMagickFalse(windows->magnify.mapped) )
12806             break;
12807           magnify=1;
12808           while ((int) magnify <= event->xconfigure.width)
12809             magnify<<=1;
12810           while ((int) magnify <= event->xconfigure.height)
12811             magnify<<=1;
12812           magnify>>=1;
12813           if (((int) magnify != event->xconfigure.width) ||
12814               ((int) magnify != event->xconfigure.height))
12815             {
12816               XWindowChanges
12817                 window_changes;
12818
12819               window_changes.width=(int) magnify;
12820               window_changes.height=(int) magnify;
12821               (void) XReconfigureWMWindow(display,windows->magnify.id,
12822                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12823                 &window_changes);
12824               break;
12825             }
12826           XMakeMagnifyImage(display,windows,exception);
12827           break;
12828         }
12829       break;
12830     }
12831     case Expose:
12832     {
12833       if (event->xexpose.window == windows->image.id)
12834         {
12835           XRefreshWindow(display,&windows->image,event);
12836           break;
12837         }
12838       if (event->xexpose.window == windows->pan.id)
12839         if (event->xexpose.count == 0)
12840           {
12841             XDrawPanRectangle(display,windows);
12842             break;
12843           }
12844       if (event->xexpose.window == windows->magnify.id)
12845         if (event->xexpose.count == 0)
12846           {
12847             XMakeMagnifyImage(display,windows,exception);
12848             break;
12849           }
12850       break;
12851     }
12852     case KeyPress:
12853     {
12854       char
12855         command[MaxTextExtent];
12856
12857       KeySym
12858         key_symbol;
12859
12860       if (event->xkey.window != windows->magnify.id)
12861         break;
12862       /*
12863         Respond to a user key press.
12864       */
12865       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12866         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12867       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12868         exception);
12869       break;
12870     }
12871     case MapNotify:
12872     {
12873       if (event->xmap.window == windows->magnify.id)
12874         {
12875           windows->magnify.mapped=MagickTrue;
12876           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12877           break;
12878         }
12879       if (event->xmap.window == windows->info.id)
12880         {
12881           windows->info.mapped=MagickTrue;
12882           break;
12883         }
12884       break;
12885     }
12886     case MotionNotify:
12887     {
12888       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12889       if (event->xmotion.window == windows->image.id)
12890         if (IfMagickTrue(windows->magnify.mapped) )
12891           {
12892             /*
12893               Update magnified image.
12894             */
12895             x=event->xmotion.x;
12896             y=event->xmotion.y;
12897             if (x < 0)
12898               x=0;
12899             else
12900               if (x >= (int) windows->image.width)
12901                 x=(int) (windows->image.width-1);
12902             windows->magnify.x=(int) windows->image.x+x;
12903             if (y < 0)
12904               y=0;
12905             else
12906              if (y >= (int) windows->image.height)
12907                y=(int) (windows->image.height-1);
12908             windows->magnify.y=windows->image.y+y;
12909             XMakeMagnifyImage(display,windows,exception);
12910           }
12911       break;
12912     }
12913     case UnmapNotify:
12914     {
12915       if (event->xunmap.window == windows->magnify.id)
12916         {
12917           windows->magnify.mapped=MagickFalse;
12918           break;
12919         }
12920       if (event->xunmap.window == windows->info.id)
12921         {
12922           windows->info.mapped=MagickFalse;
12923           break;
12924         }
12925       break;
12926     }
12927     default:
12928       break;
12929   }
12930 }
12931 \f
12932 /*
12933 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12934 %                                                                             %
12935 %                                                                             %
12936 %                                                                             %
12937 +   X S e t C r o p G e o m e t r y                                           %
12938 %                                                                             %
12939 %                                                                             %
12940 %                                                                             %
12941 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12942 %
12943 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12944 %  and translates it to a cropping geometry relative to the image.
12945 %
12946 %  The format of the XSetCropGeometry method is:
12947 %
12948 %      void XSetCropGeometry(Display *display,XWindows *windows,
12949 %        RectangleInfo *crop_info,Image *image)
12950 %
12951 %  A description of each parameter follows:
12952 %
12953 %    o display: Specifies a connection to an X server; returned from
12954 %      XOpenDisplay.
12955 %
12956 %    o windows: Specifies a pointer to a XWindows structure.
12957 %
12958 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12959 %      Image window to crop.
12960 %
12961 %    o image: the image.
12962 %
12963 */
12964 static void XSetCropGeometry(Display *display,XWindows *windows,
12965   RectangleInfo *crop_info,Image *image)
12966 {
12967   char
12968     text[MaxTextExtent];
12969
12970   int
12971     x,
12972     y;
12973
12974   double
12975     scale_factor;
12976
12977   unsigned int
12978     height,
12979     width;
12980
12981   if (IfMagickTrue(windows->info.mapped) )
12982     {
12983       /*
12984         Display info on cropping rectangle.
12985       */
12986       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12987         (double) crop_info->width,(double) crop_info->height,(double)
12988         crop_info->x,(double) crop_info->y);
12989       XInfoWidget(display,windows,text);
12990     }
12991   /*
12992     Cropping geometry is relative to any previous crop geometry.
12993   */
12994   x=0;
12995   y=0;
12996   width=(unsigned int) image->columns;
12997   height=(unsigned int) image->rows;
12998   if (windows->image.crop_geometry != (char *) NULL)
12999     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13000   else
13001     windows->image.crop_geometry=AcquireString((char *) NULL);
13002   /*
13003     Define the crop geometry string from the cropping rectangle.
13004   */
13005   scale_factor=(double) width/windows->image.ximage->width;
13006   if (crop_info->x > 0)
13007     x+=(int) (scale_factor*crop_info->x+0.5);
13008   width=(unsigned int) (scale_factor*crop_info->width+0.5);
13009   if (width == 0)
13010     width=1;
13011   scale_factor=(double) height/windows->image.ximage->height;
13012   if (crop_info->y > 0)
13013     y+=(int) (scale_factor*crop_info->y+0.5);
13014   height=(unsigned int) (scale_factor*crop_info->height+0.5);
13015   if (height == 0)
13016     height=1;
13017   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13018     "%ux%u%+d%+d",width,height,x,y);
13019 }
13020 \f
13021 /*
13022 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13023 %                                                                             %
13024 %                                                                             %
13025 %                                                                             %
13026 +   X T i l e I m a g e                                                       %
13027 %                                                                             %
13028 %                                                                             %
13029 %                                                                             %
13030 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13031 %
13032 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13033 %  The load or delete command is chosen from a menu.
13034 %
13035 %  The format of the XTileImage method is:
13036 %
13037 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13038 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13039 %
13040 %  A description of each parameter follows:
13041 %
13042 %    o tile_image:  XTileImage reads or deletes the tile image
13043 %      and returns it.  A null image is returned if an error occurs.
13044 %
13045 %    o display: Specifies a connection to an X server;  returned from
13046 %      XOpenDisplay.
13047 %
13048 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13049 %
13050 %    o windows: Specifies a pointer to a XWindows structure.
13051 %
13052 %    o image: the image; returned from ReadImage.
13053 %
13054 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13055 %      the entire image is refreshed.
13056 %
13057 %    o exception: return any errors or warnings in this structure.
13058 %
13059 */
13060 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13061   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13062 {
13063   static const char
13064     *VerbMenu[] =
13065     {
13066       "Load",
13067       "Next",
13068       "Former",
13069       "Delete",
13070       "Update",
13071       (char *) NULL,
13072     };
13073
13074   static const ModeType
13075     TileCommands[] =
13076     {
13077       TileLoadCommand,
13078       TileNextCommand,
13079       TileFormerCommand,
13080       TileDeleteCommand,
13081       TileUpdateCommand
13082     };
13083
13084   char
13085     command[MaxTextExtent],
13086     filename[MaxTextExtent];
13087
13088   Image
13089     *tile_image;
13090
13091   int
13092     id,
13093     status,
13094     tile,
13095     x,
13096     y;
13097
13098   double
13099     scale_factor;
13100
13101   register char
13102     *p,
13103     *q;
13104
13105   register int
13106     i;
13107
13108   unsigned int
13109     height,
13110     width;
13111
13112   /*
13113     Tile image is relative to montage image configuration.
13114   */
13115   x=0;
13116   y=0;
13117   width=(unsigned int) image->columns;
13118   height=(unsigned int) image->rows;
13119   if (windows->image.crop_geometry != (char *) NULL)
13120     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13121   scale_factor=(double) width/windows->image.ximage->width;
13122   event->xbutton.x+=windows->image.x;
13123   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13124   scale_factor=(double) height/windows->image.ximage->height;
13125   event->xbutton.y+=windows->image.y;
13126   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13127   /*
13128     Determine size and location of each tile in the visual image directory.
13129   */
13130   width=(unsigned int) image->columns;
13131   height=(unsigned int) image->rows;
13132   x=0;
13133   y=0;
13134   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13135   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13136     (event->xbutton.x-x)/width;
13137   if (tile < 0)
13138     {
13139       /*
13140         Button press is outside any tile.
13141       */
13142       (void) XBell(display,0);
13143       return((Image *) NULL);
13144     }
13145   /*
13146     Determine file name from the tile directory.
13147   */
13148   p=image->directory;
13149   for (i=tile; (i != 0) && (*p != '\0'); )
13150   {
13151     if (*p == '\n')
13152       i--;
13153     p++;
13154   }
13155   if (*p == '\0')
13156     {
13157       /*
13158         Button press is outside any tile.
13159       */
13160       (void) XBell(display,0);
13161       return((Image *) NULL);
13162     }
13163   /*
13164     Select a command from the pop-up menu.
13165   */
13166   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13167   if (id < 0)
13168     return((Image *) NULL);
13169   q=p;
13170   while ((*q != '\n') && (*q != '\0'))
13171     q++;
13172   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13173   /*
13174     Perform command for the selected tile.
13175   */
13176   XSetCursorState(display,windows,MagickTrue);
13177   XCheckRefreshWindows(display,windows);
13178   tile_image=NewImageList();
13179   switch (TileCommands[id])
13180   {
13181     case TileLoadCommand:
13182     {
13183       /*
13184         Load tile image.
13185       */
13186       XCheckRefreshWindows(display,windows);
13187       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13188         MaxTextExtent);
13189       (void) CopyMagickString(resource_info->image_info->filename,filename,
13190         MaxTextExtent);
13191       tile_image=ReadImage(resource_info->image_info,exception);
13192       CatchException(exception);
13193       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13194       break;
13195     }
13196     case TileNextCommand:
13197     {
13198       /*
13199         Display next image.
13200       */
13201       XClientMessage(display,windows->image.id,windows->im_protocols,
13202         windows->im_next_image,CurrentTime);
13203       break;
13204     }
13205     case TileFormerCommand:
13206     {
13207       /*
13208         Display former image.
13209       */
13210       XClientMessage(display,windows->image.id,windows->im_protocols,
13211         windows->im_former_image,CurrentTime);
13212       break;
13213     }
13214     case TileDeleteCommand:
13215     {
13216       /*
13217         Delete tile image.
13218       */
13219       if (IfMagickFalse(IsPathAccessible(filename)) )
13220         {
13221           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13222           break;
13223         }
13224       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13225       if (status <= 0)
13226         break;
13227       status=ShredFile(filename);
13228       if (IfMagickTrue(status) )
13229         {
13230           XNoticeWidget(display,windows,"Unable to delete image file:",
13231             filename);
13232           break;
13233         }
13234     }
13235     case TileUpdateCommand:
13236     {
13237       int
13238         x_offset,
13239         y_offset;
13240
13241       PixelInfo
13242         pixel;
13243
13244       register int
13245         j;
13246
13247       register Quantum
13248         *s;
13249
13250       /*
13251         Ensure all the images exist.
13252       */
13253       tile=0;
13254       GetPixelInfo(image,&pixel);
13255       for (p=image->directory; *p != '\0'; p++)
13256       {
13257         CacheView
13258           *image_view;
13259
13260         q=p;
13261         while ((*q != '\n') && (*q != '\0'))
13262           q++;
13263         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13264         p=q;
13265         if (IfMagickTrue(IsPathAccessible(filename)) )
13266           {
13267             tile++;
13268             continue;
13269           }
13270         /*
13271           Overwrite tile with background color.
13272         */
13273         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13274         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13275         image_view=AcquireAuthenticCacheView(image,exception);
13276         (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13277         for (i=0; i < (int) height; i++)
13278         {
13279           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13280             y_offset+i,width,1,exception);
13281           if (s == (Quantum *) NULL)
13282             break;
13283           for (j=0; j < (int) width; j++)
13284           {
13285             SetPixelInfoPixel(image,&pixel,s);
13286             s+=GetPixelChannels(image);
13287           }
13288           if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
13289             break;
13290         }
13291         image_view=DestroyCacheView(image_view);
13292         tile++;
13293       }
13294       windows->image.window_changes.width=(int) image->columns;
13295       windows->image.window_changes.height=(int) image->rows;
13296       XConfigureImageColormap(display,resource_info,windows,image,exception);
13297       (void) XConfigureImage(display,resource_info,windows,image,exception);
13298       break;
13299     }
13300     default:
13301       break;
13302   }
13303   XSetCursorState(display,windows,MagickFalse);
13304   return(tile_image);
13305 }
13306 \f
13307 /*
13308 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13309 %                                                                             %
13310 %                                                                             %
13311 %                                                                             %
13312 +   X T r a n s l a t e I m a g e                                             %
13313 %                                                                             %
13314 %                                                                             %
13315 %                                                                             %
13316 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13317 %
13318 %  XTranslateImage() translates the image within an Image window by one pixel
13319 %  as specified by the key symbol.  If the image has a montage string the
13320 %  translation is respect to the width and height contained within the string.
13321 %
13322 %  The format of the XTranslateImage method is:
13323 %
13324 %      void XTranslateImage(Display *display,XWindows *windows,
13325 %        Image *image,const KeySym key_symbol)
13326 %
13327 %  A description of each parameter follows:
13328 %
13329 %    o display: Specifies a connection to an X server; returned from
13330 %      XOpenDisplay.
13331 %
13332 %    o windows: Specifies a pointer to a XWindows structure.
13333 %
13334 %    o image: the image.
13335 %
13336 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13337 %      to trim.
13338 %
13339 */
13340 static void XTranslateImage(Display *display,XWindows *windows,
13341   Image *image,const KeySym key_symbol)
13342 {
13343   char
13344     text[MaxTextExtent];
13345
13346   int
13347     x,
13348     y;
13349
13350   unsigned int
13351     x_offset,
13352     y_offset;
13353
13354   /*
13355     User specified a pan position offset.
13356   */
13357   x_offset=windows->image.width;
13358   y_offset=windows->image.height;
13359   if (image->montage != (char *) NULL)
13360     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13361   switch ((int) key_symbol)
13362   {
13363     case XK_Home:
13364     case XK_KP_Home:
13365     {
13366       windows->image.x=(int) windows->image.width/2;
13367       windows->image.y=(int) windows->image.height/2;
13368       break;
13369     }
13370     case XK_Left:
13371     case XK_KP_Left:
13372     {
13373       windows->image.x-=x_offset;
13374       break;
13375     }
13376     case XK_Next:
13377     case XK_Up:
13378     case XK_KP_Up:
13379     {
13380       windows->image.y-=y_offset;
13381       break;
13382     }
13383     case XK_Right:
13384     case XK_KP_Right:
13385     {
13386       windows->image.x+=x_offset;
13387       break;
13388     }
13389     case XK_Prior:
13390     case XK_Down:
13391     case XK_KP_Down:
13392     {
13393       windows->image.y+=y_offset;
13394       break;
13395     }
13396     default:
13397       return;
13398   }
13399   /*
13400     Check boundary conditions.
13401   */
13402   if (windows->image.x < 0)
13403     windows->image.x=0;
13404   else
13405     if ((int) (windows->image.x+windows->image.width) >
13406         windows->image.ximage->width)
13407       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13408   if (windows->image.y < 0)
13409     windows->image.y=0;
13410   else
13411     if ((int) (windows->image.y+windows->image.height) >
13412         windows->image.ximage->height)
13413       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13414   /*
13415     Refresh Image window.
13416   */
13417   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13418     windows->image.width,windows->image.height,windows->image.x,
13419     windows->image.y);
13420   XInfoWidget(display,windows,text);
13421   XCheckRefreshWindows(display,windows);
13422   XDrawPanRectangle(display,windows);
13423   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13424   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13425 }
13426 \f
13427 /*
13428 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13429 %                                                                             %
13430 %                                                                             %
13431 %                                                                             %
13432 +   X T r i m I m a g e                                                       %
13433 %                                                                             %
13434 %                                                                             %
13435 %                                                                             %
13436 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13437 %
13438 %  XTrimImage() trims the edges from the Image window.
13439 %
13440 %  The format of the XTrimImage method is:
13441 %
13442 %      MagickBooleanType XTrimImage(Display *display,
13443 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13444 %        ExceptionInfo *exception)
13445 %
13446 %  A description of each parameter follows:
13447 %
13448 %    o display: Specifies a connection to an X server; returned from
13449 %      XOpenDisplay.
13450 %
13451 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13452 %
13453 %    o windows: Specifies a pointer to a XWindows structure.
13454 %
13455 %    o image: the image.
13456 %
13457 %    o exception: return any errors or warnings in this structure.
13458 %
13459 */
13460 static MagickBooleanType XTrimImage(Display *display,
13461   XResourceInfo *resource_info,XWindows *windows,Image *image,
13462   ExceptionInfo *exception)
13463 {
13464   RectangleInfo
13465     trim_info;
13466
13467   register int
13468     x,
13469     y;
13470
13471   size_t
13472     background,
13473     pixel;
13474
13475   /*
13476     Trim edges from image.
13477   */
13478   XSetCursorState(display,windows,MagickTrue);
13479   XCheckRefreshWindows(display,windows);
13480   /*
13481     Crop the left edge.
13482   */
13483   background=XGetPixel(windows->image.ximage,0,0);
13484   trim_info.width=(size_t) windows->image.ximage->width;
13485   for (x=0; x < windows->image.ximage->width; x++)
13486   {
13487     for (y=0; y < windows->image.ximage->height; y++)
13488     {
13489       pixel=XGetPixel(windows->image.ximage,x,y);
13490       if (pixel != background)
13491         break;
13492     }
13493     if (y < windows->image.ximage->height)
13494       break;
13495   }
13496   trim_info.x=(ssize_t) x;
13497   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13498     {
13499       XSetCursorState(display,windows,MagickFalse);
13500       return(MagickFalse);
13501     }
13502   /*
13503     Crop the right edge.
13504   */
13505   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13506   for (x=windows->image.ximage->width-1; x != 0; x--)
13507   {
13508     for (y=0; y < windows->image.ximage->height; y++)
13509     {
13510       pixel=XGetPixel(windows->image.ximage,x,y);
13511       if (pixel != background)
13512         break;
13513     }
13514     if (y < windows->image.ximage->height)
13515       break;
13516   }
13517   trim_info.width=(size_t) (x-trim_info.x+1);
13518   /*
13519     Crop the top edge.
13520   */
13521   background=XGetPixel(windows->image.ximage,0,0);
13522   trim_info.height=(size_t) windows->image.ximage->height;
13523   for (y=0; y < windows->image.ximage->height; y++)
13524   {
13525     for (x=0; x < windows->image.ximage->width; x++)
13526     {
13527       pixel=XGetPixel(windows->image.ximage,x,y);
13528       if (pixel != background)
13529         break;
13530     }
13531     if (x < windows->image.ximage->width)
13532       break;
13533   }
13534   trim_info.y=(ssize_t) y;
13535   /*
13536     Crop the bottom edge.
13537   */
13538   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13539   for (y=windows->image.ximage->height-1; y != 0; y--)
13540   {
13541     for (x=0; x < windows->image.ximage->width; x++)
13542     {
13543       pixel=XGetPixel(windows->image.ximage,x,y);
13544       if (pixel != background)
13545         break;
13546     }
13547     if (x < windows->image.ximage->width)
13548       break;
13549   }
13550   trim_info.height=(size_t) y-trim_info.y+1;
13551   if (((unsigned int) trim_info.width != windows->image.width) ||
13552       ((unsigned int) trim_info.height != windows->image.height))
13553     {
13554       /*
13555         Reconfigure Image window as defined by the trimming rectangle.
13556       */
13557       XSetCropGeometry(display,windows,&trim_info,image);
13558       windows->image.window_changes.width=(int) trim_info.width;
13559       windows->image.window_changes.height=(int) trim_info.height;
13560       (void) XConfigureImage(display,resource_info,windows,image,exception);
13561     }
13562   XSetCursorState(display,windows,MagickFalse);
13563   return(MagickTrue);
13564 }
13565 \f
13566 /*
13567 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13568 %                                                                             %
13569 %                                                                             %
13570 %                                                                             %
13571 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13572 %                                                                             %
13573 %                                                                             %
13574 %                                                                             %
13575 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13576 %
13577 %  XVisualDirectoryImage() creates a Visual Image Directory.
13578 %
13579 %  The format of the XVisualDirectoryImage method is:
13580 %
13581 %      Image *XVisualDirectoryImage(Display *display,
13582 %        XResourceInfo *resource_info,XWindows *windows,
13583 %        ExceptionInfo *exception)
13584 %
13585 %  A description of each parameter follows:
13586 %
13587 %    o display: Specifies a connection to an X server; returned from
13588 %      XOpenDisplay.
13589 %
13590 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13591 %
13592 %    o windows: Specifies a pointer to a XWindows structure.
13593 %
13594 %    o exception: return any errors or warnings in this structure.
13595 %
13596 */
13597 static Image *XVisualDirectoryImage(Display *display,
13598   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13599 {
13600 #define TileImageTag  "Scale/Image"
13601 #define XClientName  "montage"
13602
13603   char
13604     **filelist;
13605
13606   Image
13607     *images,
13608     *montage_image,
13609     *next_image,
13610     *thumbnail_image;
13611
13612   ImageInfo
13613     *read_info;
13614
13615   int
13616     number_files;
13617
13618   MagickBooleanType
13619     backdrop;
13620
13621   MagickStatusType
13622     status;
13623
13624   MontageInfo
13625     *montage_info;
13626
13627   RectangleInfo
13628     geometry;
13629
13630   register int
13631     i;
13632
13633   static char
13634     filename[MaxTextExtent] = "\0",
13635     filenames[MaxTextExtent] = "*";
13636
13637   XResourceInfo
13638     background_resources;
13639
13640   /*
13641     Request file name from user.
13642   */
13643   XFileBrowserWidget(display,windows,"Directory",filenames);
13644   if (*filenames == '\0')
13645     return((Image *) NULL);
13646   /*
13647     Expand the filenames.
13648   */
13649   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13650   if (filelist == (char **) NULL)
13651     {
13652       ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13653         filenames);
13654       return((Image *) NULL);
13655     }
13656   number_files=1;
13657   filelist[0]=filenames;
13658   status=ExpandFilenames(&number_files,&filelist);
13659   if (IfMagickFalse(status) || (number_files == 0))
13660     {
13661       if (number_files == 0)
13662         ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13663       else
13664         ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13665           filenames);
13666       return((Image *) NULL);
13667     }
13668   /*
13669     Set image background resources.
13670   */
13671   background_resources=(*resource_info);
13672   background_resources.window_id=AcquireString("");
13673   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13674     "0x%lx",windows->image.id);
13675   background_resources.backdrop=MagickTrue;
13676   /*
13677     Read each image and convert them to a tile.
13678   */
13679   backdrop=IsMagickTrue( (windows->visual_info->klass == TrueColor) ||
13680     (windows->visual_info->klass == DirectColor) );
13681   read_info=CloneImageInfo(resource_info->image_info);
13682   (void) SetImageOption(read_info,"jpeg:size","120x120");
13683   (void) CloneString(&read_info->size,DefaultTileGeometry);
13684   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13685     (void *) NULL);
13686   images=NewImageList();
13687   XSetCursorState(display,windows,MagickTrue);
13688   XCheckRefreshWindows(display,windows);
13689   for (i=0; i < (int) number_files; i++)
13690   {
13691     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13692     filelist[i]=DestroyString(filelist[i]);
13693     *read_info->magick='\0';
13694     next_image=ReadImage(read_info,exception);
13695     CatchException(exception);
13696     if (next_image != (Image *) NULL)
13697       {
13698         (void) DeleteImageProperty(next_image,"label");
13699         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13700           read_info,next_image,DefaultTileLabel,exception),exception);
13701         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13702           exception);
13703         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13704           geometry.height,exception);
13705         if (thumbnail_image != (Image *) NULL)
13706           {
13707             next_image=DestroyImage(next_image);
13708             next_image=thumbnail_image;
13709           }
13710         if (backdrop)
13711           {
13712             (void) XDisplayBackgroundImage(display,&background_resources,
13713               next_image,exception);
13714             XSetCursorState(display,windows,MagickTrue);
13715           }
13716         AppendImageToList(&images,next_image);
13717         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13718           {
13719             MagickBooleanType
13720               proceed;
13721
13722             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13723               (MagickSizeType) number_files);
13724             if (IfMagickFalse(proceed) )
13725               break;
13726           }
13727       }
13728   }
13729   filelist=(char **) RelinquishMagickMemory(filelist);
13730   if (images == (Image *) NULL)
13731     {
13732       read_info=DestroyImageInfo(read_info);
13733       XSetCursorState(display,windows,MagickFalse);
13734       ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13735       return((Image *) NULL);
13736     }
13737   /*
13738     Create the Visual Image Directory.
13739   */
13740   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13741   montage_info->pointsize=10;
13742   if (resource_info->font != (char *) NULL)
13743     (void) CloneString(&montage_info->font,resource_info->font);
13744   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13745   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13746     images),exception);
13747   images=DestroyImageList(images);
13748   montage_info=DestroyMontageInfo(montage_info);
13749   read_info=DestroyImageInfo(read_info);
13750   XSetCursorState(display,windows,MagickFalse);
13751   if (montage_image == (Image *) NULL)
13752     return(montage_image);
13753   XClientMessage(display,windows->image.id,windows->im_protocols,
13754     windows->im_next_image,CurrentTime);
13755   return(montage_image);
13756 }
13757 \f
13758 /*
13759 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13760 %                                                                             %
13761 %                                                                             %
13762 %                                                                             %
13763 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13764 %                                                                             %
13765 %                                                                             %
13766 %                                                                             %
13767 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13768 %
13769 %  XDisplayBackgroundImage() displays an image in the background of a window.
13770 %
13771 %  The format of the XDisplayBackgroundImage method is:
13772 %
13773 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13774 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13775 %
13776 %  A description of each parameter follows:
13777 %
13778 %    o display: Specifies a connection to an X server;  returned from
13779 %      XOpenDisplay.
13780 %
13781 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13782 %
13783 %    o image: the image.
13784 %
13785 %    o exception: return any errors or warnings in this structure.
13786 %
13787 */
13788 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13789   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13790 {
13791   char
13792     geometry[MaxTextExtent],
13793     visual_type[MaxTextExtent];
13794
13795   int
13796     height,
13797     status,
13798     width;
13799
13800   RectangleInfo
13801     geometry_info;
13802
13803   static XPixelInfo
13804     pixel;
13805
13806   static XStandardColormap
13807     *map_info;
13808
13809   static XVisualInfo
13810     *visual_info = (XVisualInfo *) NULL;
13811
13812   static XWindowInfo
13813     window_info;
13814
13815   size_t
13816     delay;
13817
13818   Window
13819     root_window;
13820
13821   XGCValues
13822     context_values;
13823
13824   XResourceInfo
13825     resources;
13826
13827   XWindowAttributes
13828     window_attributes;
13829
13830   /*
13831     Determine target window.
13832   */
13833   assert(image != (Image *) NULL);
13834   assert(image->signature == MagickSignature);
13835   if (IfMagickTrue(image->debug) )
13836     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13837   resources=(*resource_info);
13838   window_info.id=(Window) NULL;
13839   root_window=XRootWindow(display,XDefaultScreen(display));
13840   if (LocaleCompare(resources.window_id,"root") == 0)
13841     window_info.id=root_window;
13842   else
13843     {
13844       if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13845         window_info.id=XWindowByID(display,root_window,
13846           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13847       if (window_info.id == (Window) NULL)
13848         window_info.id=XWindowByName(display,root_window,resources.window_id);
13849     }
13850   if (window_info.id == (Window) NULL)
13851     {
13852       ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13853         resources.window_id);
13854       return(MagickFalse);
13855     }
13856   /*
13857     Determine window visual id.
13858   */
13859   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13860   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13861   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13862   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13863   if (status != 0)
13864     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13865       XVisualIDFromVisual(window_attributes.visual));
13866   if (visual_info == (XVisualInfo *) NULL)
13867     {
13868       /*
13869         Allocate standard colormap.
13870       */
13871       map_info=XAllocStandardColormap();
13872       if (map_info == (XStandardColormap *) NULL)
13873         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13874           image->filename);
13875       map_info->colormap=(Colormap) NULL;
13876       pixel.pixels=(unsigned long *) NULL;
13877       /*
13878         Initialize visual info.
13879       */
13880       resources.map_type=(char *) NULL;
13881       resources.visual_type=visual_type;
13882       visual_info=XBestVisualInfo(display,map_info,&resources);
13883       if (visual_info == (XVisualInfo *) NULL)
13884         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13885           resources.visual_type);
13886       /*
13887         Initialize window info.
13888       */
13889       window_info.ximage=(XImage *) NULL;
13890       window_info.matte_image=(XImage *) NULL;
13891       window_info.pixmap=(Pixmap) NULL;
13892       window_info.matte_pixmap=(Pixmap) NULL;
13893     }
13894   /*
13895     Free previous root colors.
13896   */
13897   if (window_info.id == root_window)
13898     (void) XDestroyWindowColors(display,root_window);
13899   /*
13900     Initialize Standard Colormap.
13901   */
13902   resources.colormap=SharedColormap;
13903   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13904     exception);
13905   /*
13906     Graphic context superclass.
13907   */
13908   context_values.background=pixel.background_color.pixel;
13909   context_values.foreground=pixel.foreground_color.pixel;
13910   pixel.annotate_context=XCreateGC(display,window_info.id,
13911     (size_t) (GCBackground | GCForeground),&context_values);
13912   if (pixel.annotate_context == (GC) NULL)
13913     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13914       image->filename);
13915   /*
13916     Initialize Image window attributes.
13917   */
13918   window_info.name=AcquireString("\0");
13919   window_info.icon_name=AcquireString("\0");
13920   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13921     &resources,&window_info);
13922   /*
13923     Create the X image.
13924   */
13925   window_info.width=(unsigned int) image->columns;
13926   window_info.height=(unsigned int) image->rows;
13927   if ((image->columns != window_info.width) ||
13928       (image->rows != window_info.height))
13929     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13930       image->filename);
13931   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13932     window_attributes.width,window_attributes.height);
13933   geometry_info.width=window_info.width;
13934   geometry_info.height=window_info.height;
13935   geometry_info.x=(ssize_t) window_info.x;
13936   geometry_info.y=(ssize_t) window_info.y;
13937   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13938     &geometry_info.width,&geometry_info.height);
13939   window_info.width=(unsigned int) geometry_info.width;
13940   window_info.height=(unsigned int) geometry_info.height;
13941   window_info.x=(int) geometry_info.x;
13942   window_info.y=(int) geometry_info.y;
13943   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13944     window_info.height,exception);
13945   if (IfMagickFalse(status) )
13946     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13947       image->filename);
13948   window_info.x=0;
13949   window_info.y=0;
13950   if (IfMagickTrue(image->debug) )
13951     {
13952       (void) LogMagickEvent(X11Event,GetMagickModule(),
13953         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13954         (double) image->columns,(double) image->rows);
13955       if (image->colors != 0)
13956         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13957           image->colors);
13958       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13959     }
13960   /*
13961     Adjust image dimensions as specified by backdrop or geometry options.
13962   */
13963   width=(int) window_info.width;
13964   height=(int) window_info.height;
13965   if (IfMagickTrue(resources.backdrop) )
13966     {
13967       /*
13968         Center image on window.
13969       */
13970       window_info.x=(window_attributes.width/2)-
13971         (window_info.ximage->width/2);
13972       window_info.y=(window_attributes.height/2)-
13973         (window_info.ximage->height/2);
13974       width=window_attributes.width;
13975       height=window_attributes.height;
13976     }
13977   if ((resources.image_geometry != (char *) NULL) &&
13978       (*resources.image_geometry != '\0'))
13979     {
13980       char
13981         default_geometry[MaxTextExtent];
13982
13983       int
13984         flags,
13985         gravity;
13986
13987       XSizeHints
13988         *size_hints;
13989
13990       /*
13991         User specified geometry.
13992       */
13993       size_hints=XAllocSizeHints();
13994       if (size_hints == (XSizeHints *) NULL)
13995         ThrowXWindowFatalException(ResourceLimitFatalError,
13996           "MemoryAllocationFailed",image->filename);
13997       size_hints->flags=0L;
13998       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13999         width,height);
14000       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14001         default_geometry,window_info.border_width,size_hints,&window_info.x,
14002         &window_info.y,&width,&height,&gravity);
14003       if (flags & (XValue | YValue))
14004         {
14005           width=window_attributes.width;
14006           height=window_attributes.height;
14007         }
14008       (void) XFree((void *) size_hints);
14009     }
14010   /*
14011     Create the X pixmap.
14012   */
14013   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14014     (unsigned int) height,window_info.depth);
14015   if (window_info.pixmap == (Pixmap) NULL)
14016     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14017       image->filename);
14018   /*
14019     Display pixmap on the window.
14020   */
14021   if (((unsigned int) width > window_info.width) ||
14022       ((unsigned int) height > window_info.height))
14023     (void) XFillRectangle(display,window_info.pixmap,
14024       window_info.annotate_context,0,0,(unsigned int) width,
14025       (unsigned int) height);
14026   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14027     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14028     window_info.width,(unsigned int) window_info.height);
14029   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14030   (void) XClearWindow(display,window_info.id);
14031   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14032   XDelay(display,delay == 0UL ? 10UL : delay);
14033   (void) XSync(display,MagickFalse);
14034   return(IsMagickTrue(window_info.id == root_window));
14035 }
14036 \f
14037 /*
14038 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14039 %                                                                             %
14040 %                                                                             %
14041 %                                                                             %
14042 +   X D i s p l a y I m a g e                                                 %
14043 %                                                                             %
14044 %                                                                             %
14045 %                                                                             %
14046 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14047 %
14048 %  XDisplayImage() displays an image via X11.  A new image is created and
14049 %  returned if the user interactively transforms the displayed image.
14050 %
14051 %  The format of the XDisplayImage method is:
14052 %
14053 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14054 %        char **argv,int argc,Image **image,size_t *state,
14055 %        ExceptionInfo *exception)
14056 %
14057 %  A description of each parameter follows:
14058 %
14059 %    o nexus:  Method XDisplayImage returns an image when the
14060 %      user chooses 'Open Image' from the command menu or picks a tile
14061 %      from the image directory.  Otherwise a null image is returned.
14062 %
14063 %    o display: Specifies a connection to an X server;  returned from
14064 %      XOpenDisplay.
14065 %
14066 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14067 %
14068 %    o argv: Specifies the application's argument list.
14069 %
14070 %    o argc: Specifies the number of arguments.
14071 %
14072 %    o image: Specifies an address to an address of an Image structure;
14073 %
14074 %    o exception: return any errors or warnings in this structure.
14075 %
14076 */
14077 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14078   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14079 {
14080 #define MagnifySize  256  /* must be a power of 2 */
14081 #define MagickMenus  10
14082 #define MagickTitle  "Commands"
14083
14084   static const char
14085     *CommandMenu[] =
14086     {
14087       "File",
14088       "Edit",
14089       "View",
14090       "Transform",
14091       "Enhance",
14092       "Effects",
14093       "F/X",
14094       "Image Edit",
14095       "Miscellany",
14096       "Help",
14097       (char *) NULL
14098     },
14099     *FileMenu[] =
14100     {
14101       "Open...",
14102       "Next",
14103       "Former",
14104       "Select...",
14105       "Save...",
14106       "Print...",
14107       "Delete...",
14108       "New...",
14109       "Visual Directory...",
14110       "Quit",
14111       (char *) NULL
14112     },
14113     *EditMenu[] =
14114     {
14115       "Undo",
14116       "Redo",
14117       "Cut",
14118       "Copy",
14119       "Paste",
14120       (char *) NULL
14121     },
14122     *ViewMenu[] =
14123     {
14124       "Half Size",
14125       "Original Size",
14126       "Double Size",
14127       "Resize...",
14128       "Apply",
14129       "Refresh",
14130       "Restore",
14131       (char *) NULL
14132     },
14133     *TransformMenu[] =
14134     {
14135       "Crop",
14136       "Chop",
14137       "Flop",
14138       "Flip",
14139       "Rotate Right",
14140       "Rotate Left",
14141       "Rotate...",
14142       "Shear...",
14143       "Roll...",
14144       "Trim Edges",
14145       (char *) NULL
14146     },
14147     *EnhanceMenu[] =
14148     {
14149       "Hue...",
14150       "Saturation...",
14151       "Brightness...",
14152       "Gamma...",
14153       "Spiff",
14154       "Dull",
14155       "Contrast Stretch...",
14156       "Sigmoidal Contrast...",
14157       "Normalize",
14158       "Equalize",
14159       "Negate",
14160       "Grayscale",
14161       "Map...",
14162       "Quantize...",
14163       (char *) NULL
14164     },
14165     *EffectsMenu[] =
14166     {
14167       "Despeckle",
14168       "Emboss",
14169       "Reduce Noise",
14170       "Add Noise...",
14171       "Sharpen...",
14172       "Blur...",
14173       "Threshold...",
14174       "Edge Detect...",
14175       "Spread...",
14176       "Shade...",
14177       "Raise...",
14178       "Segment...",
14179       (char *) NULL
14180     },
14181     *FXMenu[] =
14182     {
14183       "Solarize...",
14184       "Sepia Tone...",
14185       "Swirl...",
14186       "Implode...",
14187       "Vignette...",
14188       "Wave...",
14189       "Oil Paint...",
14190       "Charcoal Draw...",
14191       (char *) NULL
14192     },
14193     *ImageEditMenu[] =
14194     {
14195       "Annotate...",
14196       "Draw...",
14197       "Color...",
14198       "Matte...",
14199       "Composite...",
14200       "Add Border...",
14201       "Add Frame...",
14202       "Comment...",
14203       "Launch...",
14204       "Region of Interest...",
14205       (char *) NULL
14206     },
14207     *MiscellanyMenu[] =
14208     {
14209       "Image Info",
14210       "Zoom Image",
14211       "Show Preview...",
14212       "Show Histogram",
14213       "Show Matte",
14214       "Background...",
14215       "Slide Show...",
14216       "Preferences...",
14217       (char *) NULL
14218     },
14219     *HelpMenu[] =
14220     {
14221       "Overview",
14222       "Browse Documentation",
14223       "About Display",
14224       (char *) NULL
14225     },
14226     *ShortCutsMenu[] =
14227     {
14228       "Next",
14229       "Former",
14230       "Open...",
14231       "Save...",
14232       "Print...",
14233       "Undo",
14234       "Restore",
14235       "Image Info",
14236       "Quit",
14237       (char *) NULL
14238     },
14239     *VirtualMenu[] =
14240     {
14241       "Image Info",
14242       "Print",
14243       "Next",
14244       "Quit",
14245       (char *) NULL
14246     };
14247
14248   static const char
14249     **Menus[MagickMenus] =
14250     {
14251       FileMenu,
14252       EditMenu,
14253       ViewMenu,
14254       TransformMenu,
14255       EnhanceMenu,
14256       EffectsMenu,
14257       FXMenu,
14258       ImageEditMenu,
14259       MiscellanyMenu,
14260       HelpMenu
14261     };
14262
14263   static CommandType
14264     CommandMenus[] =
14265     {
14266       NullCommand,
14267       NullCommand,
14268       NullCommand,
14269       NullCommand,
14270       NullCommand,
14271       NullCommand,
14272       NullCommand,
14273       NullCommand,
14274       NullCommand,
14275       NullCommand,
14276     },
14277     FileCommands[] =
14278     {
14279       OpenCommand,
14280       NextCommand,
14281       FormerCommand,
14282       SelectCommand,
14283       SaveCommand,
14284       PrintCommand,
14285       DeleteCommand,
14286       NewCommand,
14287       VisualDirectoryCommand,
14288       QuitCommand
14289     },
14290     EditCommands[] =
14291     {
14292       UndoCommand,
14293       RedoCommand,
14294       CutCommand,
14295       CopyCommand,
14296       PasteCommand
14297     },
14298     ViewCommands[] =
14299     {
14300       HalfSizeCommand,
14301       OriginalSizeCommand,
14302       DoubleSizeCommand,
14303       ResizeCommand,
14304       ApplyCommand,
14305       RefreshCommand,
14306       RestoreCommand
14307     },
14308     TransformCommands[] =
14309     {
14310       CropCommand,
14311       ChopCommand,
14312       FlopCommand,
14313       FlipCommand,
14314       RotateRightCommand,
14315       RotateLeftCommand,
14316       RotateCommand,
14317       ShearCommand,
14318       RollCommand,
14319       TrimCommand
14320     },
14321     EnhanceCommands[] =
14322     {
14323       HueCommand,
14324       SaturationCommand,
14325       BrightnessCommand,
14326       GammaCommand,
14327       SpiffCommand,
14328       DullCommand,
14329       ContrastStretchCommand,
14330       SigmoidalContrastCommand,
14331       NormalizeCommand,
14332       EqualizeCommand,
14333       NegateCommand,
14334       GrayscaleCommand,
14335       MapCommand,
14336       QuantizeCommand
14337     },
14338     EffectsCommands[] =
14339     {
14340       DespeckleCommand,
14341       EmbossCommand,
14342       ReduceNoiseCommand,
14343       AddNoiseCommand,
14344       SharpenCommand,
14345       BlurCommand,
14346       ThresholdCommand,
14347       EdgeDetectCommand,
14348       SpreadCommand,
14349       ShadeCommand,
14350       RaiseCommand,
14351       SegmentCommand
14352     },
14353     FXCommands[] =
14354     {
14355       SolarizeCommand,
14356       SepiaToneCommand,
14357       SwirlCommand,
14358       ImplodeCommand,
14359       VignetteCommand,
14360       WaveCommand,
14361       OilPaintCommand,
14362       CharcoalDrawCommand
14363     },
14364     ImageEditCommands[] =
14365     {
14366       AnnotateCommand,
14367       DrawCommand,
14368       ColorCommand,
14369       MatteCommand,
14370       CompositeCommand,
14371       AddBorderCommand,
14372       AddFrameCommand,
14373       CommentCommand,
14374       LaunchCommand,
14375       RegionofInterestCommand
14376     },
14377     MiscellanyCommands[] =
14378     {
14379       InfoCommand,
14380       ZoomCommand,
14381       ShowPreviewCommand,
14382       ShowHistogramCommand,
14383       ShowMatteCommand,
14384       BackgroundCommand,
14385       SlideShowCommand,
14386       PreferencesCommand
14387     },
14388     HelpCommands[] =
14389     {
14390       HelpCommand,
14391       BrowseDocumentationCommand,
14392       VersionCommand
14393     },
14394     ShortCutsCommands[] =
14395     {
14396       NextCommand,
14397       FormerCommand,
14398       OpenCommand,
14399       SaveCommand,
14400       PrintCommand,
14401       UndoCommand,
14402       RestoreCommand,
14403       InfoCommand,
14404       QuitCommand
14405     },
14406     VirtualCommands[] =
14407     {
14408       InfoCommand,
14409       PrintCommand,
14410       NextCommand,
14411       QuitCommand
14412     };
14413
14414   static CommandType
14415     *Commands[MagickMenus] =
14416     {
14417       FileCommands,
14418       EditCommands,
14419       ViewCommands,
14420       TransformCommands,
14421       EnhanceCommands,
14422       EffectsCommands,
14423       FXCommands,
14424       ImageEditCommands,
14425       MiscellanyCommands,
14426       HelpCommands
14427     };
14428
14429   char
14430     command[MaxTextExtent],
14431     *directory,
14432     geometry[MaxTextExtent],
14433     resource_name[MaxTextExtent];
14434
14435   CommandType
14436     command_type;
14437
14438   Image
14439     *display_image,
14440     *nexus;
14441
14442   int
14443     entry,
14444     id;
14445
14446   KeySym
14447     key_symbol;
14448
14449   MagickStatusType
14450     context_mask,
14451     status;
14452
14453   RectangleInfo
14454     geometry_info;
14455
14456   register int
14457     i;
14458
14459   static char
14460     working_directory[MaxTextExtent];
14461
14462   static XPoint
14463     vid_info;
14464
14465   static XWindowInfo
14466     *magick_windows[MaxXWindows];
14467
14468   static unsigned int
14469     number_windows;
14470
14471   struct stat
14472     attributes;
14473
14474   time_t
14475     timer,
14476     timestamp,
14477     update_time;
14478
14479   unsigned int
14480     height,
14481     width;
14482
14483   size_t
14484     delay;
14485
14486   WarningHandler
14487     warning_handler;
14488
14489   Window
14490     root_window;
14491
14492   XClassHint
14493     *class_hints;
14494
14495   XEvent
14496     event;
14497
14498   XFontStruct
14499     *font_info;
14500
14501   XGCValues
14502     context_values;
14503
14504   XPixelInfo
14505     *icon_pixel,
14506     *pixel;
14507
14508   XResourceInfo
14509     *icon_resources;
14510
14511   XStandardColormap
14512     *icon_map,
14513     *map_info;
14514
14515   XVisualInfo
14516     *icon_visual,
14517     *visual_info;
14518
14519   XWindowChanges
14520     window_changes;
14521
14522   XWindows
14523     *windows;
14524
14525   XWMHints
14526     *manager_hints;
14527
14528   assert(image != (Image **) NULL);
14529   assert((*image)->signature == MagickSignature);
14530   if (IfMagickTrue((*image)->debug) )
14531     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14532   display_image=(*image);
14533   warning_handler=(WarningHandler) NULL;
14534   windows=XSetWindows((XWindows *) ~0);
14535   if (windows != (XWindows *) NULL)
14536     {
14537       int
14538         status;
14539
14540       if (*working_directory == '\0')
14541         (void) CopyMagickString(working_directory,".",MaxTextExtent);
14542       status=chdir(working_directory);
14543       if (status == -1)
14544         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14545           "UnableToOpenFile","%s",working_directory);
14546       warning_handler=resource_info->display_warnings ?
14547         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14548       warning_handler=resource_info->display_warnings ?
14549         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14550     }
14551   else
14552     {
14553       /*
14554         Allocate windows structure.
14555       */
14556       resource_info->colors=display_image->colors;
14557       windows=XSetWindows(XInitializeWindows(display,resource_info));
14558       if (windows == (XWindows *) NULL)
14559         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14560           (*image)->filename);
14561       /*
14562         Initialize window id's.
14563       */
14564       number_windows=0;
14565       magick_windows[number_windows++]=(&windows->icon);
14566       magick_windows[number_windows++]=(&windows->backdrop);
14567       magick_windows[number_windows++]=(&windows->image);
14568       magick_windows[number_windows++]=(&windows->info);
14569       magick_windows[number_windows++]=(&windows->command);
14570       magick_windows[number_windows++]=(&windows->widget);
14571       magick_windows[number_windows++]=(&windows->popup);
14572       magick_windows[number_windows++]=(&windows->magnify);
14573       magick_windows[number_windows++]=(&windows->pan);
14574       for (i=0; i < (int) number_windows; i++)
14575         magick_windows[i]->id=(Window) NULL;
14576       vid_info.x=0;
14577       vid_info.y=0;
14578     }
14579   /*
14580     Initialize font info.
14581   */
14582   if (windows->font_info != (XFontStruct *) NULL)
14583     (void) XFreeFont(display,windows->font_info);
14584   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14585   if (windows->font_info == (XFontStruct *) NULL)
14586     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14587       resource_info->font);
14588   /*
14589     Initialize Standard Colormap.
14590   */
14591   map_info=windows->map_info;
14592   icon_map=windows->icon_map;
14593   visual_info=windows->visual_info;
14594   icon_visual=windows->icon_visual;
14595   pixel=windows->pixel_info;
14596   icon_pixel=windows->icon_pixel;
14597   font_info=windows->font_info;
14598   icon_resources=windows->icon_resources;
14599   class_hints=windows->class_hints;
14600   manager_hints=windows->manager_hints;
14601   root_window=XRootWindow(display,visual_info->screen);
14602   nexus=NewImageList();
14603   if (IfMagickTrue(display_image->debug) )
14604     {
14605       (void) LogMagickEvent(X11Event,GetMagickModule(),
14606         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14607         (double) display_image->scene,(double) display_image->columns,
14608         (double) display_image->rows);
14609       if (display_image->colors != 0)
14610         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14611           display_image->colors);
14612       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14613         display_image->magick);
14614     }
14615   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14616     map_info,pixel,exception);
14617   display_image->taint=MagickFalse;
14618   /*
14619     Initialize graphic context.
14620   */
14621   windows->context.id=(Window) NULL;
14622   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14623     resource_info,&windows->context);
14624   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14625   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14626   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14627   manager_hints->flags=InputHint | StateHint;
14628   manager_hints->input=MagickFalse;
14629   manager_hints->initial_state=WithdrawnState;
14630   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14631     &windows->context);
14632   if (IfMagickTrue(display_image->debug) )
14633     (void) LogMagickEvent(X11Event,GetMagickModule(),
14634       "Window id: 0x%lx (context)",windows->context.id);
14635   context_values.background=pixel->background_color.pixel;
14636   context_values.font=font_info->fid;
14637   context_values.foreground=pixel->foreground_color.pixel;
14638   context_values.graphics_exposures=MagickFalse;
14639   context_mask=(MagickStatusType)
14640     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14641   if (pixel->annotate_context != (GC) NULL)
14642     (void) XFreeGC(display,pixel->annotate_context);
14643   pixel->annotate_context=XCreateGC(display,windows->context.id,
14644     context_mask,&context_values);
14645   if (pixel->annotate_context == (GC) NULL)
14646     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14647       display_image->filename);
14648   context_values.background=pixel->depth_color.pixel;
14649   if (pixel->widget_context != (GC) NULL)
14650     (void) XFreeGC(display,pixel->widget_context);
14651   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14652     &context_values);
14653   if (pixel->widget_context == (GC) NULL)
14654     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14655       display_image->filename);
14656   context_values.background=pixel->foreground_color.pixel;
14657   context_values.foreground=pixel->background_color.pixel;
14658   context_values.plane_mask=context_values.background ^
14659     context_values.foreground;
14660   if (pixel->highlight_context != (GC) NULL)
14661     (void) XFreeGC(display,pixel->highlight_context);
14662   pixel->highlight_context=XCreateGC(display,windows->context.id,
14663     (size_t) (context_mask | GCPlaneMask),&context_values);
14664   if (pixel->highlight_context == (GC) NULL)
14665     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14666       display_image->filename);
14667   (void) XDestroyWindow(display,windows->context.id);
14668   /*
14669     Initialize icon window.
14670   */
14671   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14672     icon_resources,&windows->icon);
14673   windows->icon.geometry=resource_info->icon_geometry;
14674   XBestIconSize(display,&windows->icon,display_image);
14675   windows->icon.attributes.colormap=XDefaultColormap(display,
14676     icon_visual->screen);
14677   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14678   manager_hints->flags=InputHint | StateHint;
14679   manager_hints->input=MagickFalse;
14680   manager_hints->initial_state=IconicState;
14681   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14682     &windows->icon);
14683   if (IfMagickTrue(display_image->debug) )
14684     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14685       windows->icon.id);
14686   /*
14687     Initialize graphic context for icon window.
14688   */
14689   if (icon_pixel->annotate_context != (GC) NULL)
14690     (void) XFreeGC(display,icon_pixel->annotate_context);
14691   context_values.background=icon_pixel->background_color.pixel;
14692   context_values.foreground=icon_pixel->foreground_color.pixel;
14693   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14694     (size_t) (GCBackground | GCForeground),&context_values);
14695   if (icon_pixel->annotate_context == (GC) NULL)
14696     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14697       display_image->filename);
14698   windows->icon.annotate_context=icon_pixel->annotate_context;
14699   /*
14700     Initialize Image window.
14701   */
14702   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14703     &windows->image);
14704   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14705   if (IfMagickFalse(resource_info->use_shared_memory) )
14706     windows->image.shared_memory=MagickFalse;
14707   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14708     {
14709       char
14710         *title;
14711
14712       title=InterpretImageProperties(resource_info->image_info,display_image,
14713         resource_info->title,exception);
14714       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14715       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14716       title=DestroyString(title);
14717     }
14718   else
14719     {
14720       char
14721         filename[MaxTextExtent];
14722
14723       /*
14724         Window name is the base of the filename.
14725       */
14726       GetPathComponent(display_image->magick_filename,TailPath,filename);
14727       if (display_image->scene == 0)
14728         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14729           "%s: %s",MagickPackageName,filename);
14730       else
14731         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14732           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14733           (double) display_image->scene,(double) GetImageListLength(
14734           display_image));
14735       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14736     }
14737   if (resource_info->immutable)
14738     windows->image.immutable=MagickTrue;
14739   windows->image.use_pixmap=resource_info->use_pixmap;
14740   windows->image.geometry=resource_info->image_geometry;
14741   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14742     XDisplayWidth(display,visual_info->screen),
14743     XDisplayHeight(display,visual_info->screen));
14744   geometry_info.width=display_image->columns;
14745   geometry_info.height=display_image->rows;
14746   geometry_info.x=0;
14747   geometry_info.y=0;
14748   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14749     &geometry_info.width,&geometry_info.height);
14750   windows->image.width=(unsigned int) geometry_info.width;
14751   windows->image.height=(unsigned int) geometry_info.height;
14752   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14753     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14754     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14755     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14756   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14757     resource_info,&windows->backdrop);
14758   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14759     {
14760       /*
14761         Initialize backdrop window.
14762       */
14763       windows->backdrop.x=0;
14764       windows->backdrop.y=0;
14765       (void) CloneString(&windows->backdrop.name,"Backdrop");
14766       windows->backdrop.flags=(size_t) (USSize | USPosition);
14767       windows->backdrop.width=(unsigned int)
14768         XDisplayWidth(display,visual_info->screen);
14769       windows->backdrop.height=(unsigned int)
14770         XDisplayHeight(display,visual_info->screen);
14771       windows->backdrop.border_width=0;
14772       windows->backdrop.immutable=MagickTrue;
14773       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14774         ButtonReleaseMask;
14775       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14776         StructureNotifyMask;
14777       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14778       manager_hints->icon_window=windows->icon.id;
14779       manager_hints->input=MagickTrue;
14780       manager_hints->initial_state=resource_info->iconic ? IconicState :
14781         NormalState;
14782       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14783         &windows->backdrop);
14784       if (IfMagickTrue(display_image->debug) )
14785         (void) LogMagickEvent(X11Event,GetMagickModule(),
14786           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14787       (void) XMapWindow(display,windows->backdrop.id);
14788       (void) XClearWindow(display,windows->backdrop.id);
14789       if (windows->image.id != (Window) NULL)
14790         {
14791           (void) XDestroyWindow(display,windows->image.id);
14792           windows->image.id=(Window) NULL;
14793         }
14794       /*
14795         Position image in the center the backdrop.
14796       */
14797       windows->image.flags|=USPosition;
14798       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14799         (windows->image.width/2);
14800       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14801         (windows->image.height/2);
14802     }
14803   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14804   manager_hints->icon_window=windows->icon.id;
14805   manager_hints->input=MagickTrue;
14806   manager_hints->initial_state=resource_info->iconic ? IconicState :
14807     NormalState;
14808   if (windows->group_leader.id != (Window) NULL)
14809     {
14810       /*
14811         Follow the leader.
14812       */
14813       manager_hints->flags|=WindowGroupHint;
14814       manager_hints->window_group=windows->group_leader.id;
14815       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14816       if (IfMagickTrue(display_image->debug) )
14817         (void) LogMagickEvent(X11Event,GetMagickModule(),
14818           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14819     }
14820   XMakeWindow(display,
14821     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14822     argv,argc,class_hints,manager_hints,&windows->image);
14823   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14824     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14825   if (windows->group_leader.id != (Window) NULL)
14826     (void) XSetTransientForHint(display,windows->image.id,
14827       windows->group_leader.id);
14828   if (IfMagickTrue(display_image->debug) )
14829     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14830       windows->image.id);
14831   /*
14832     Initialize Info widget.
14833   */
14834   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14835     &windows->info);
14836   (void) CloneString(&windows->info.name,"Info");
14837   (void) CloneString(&windows->info.icon_name,"Info");
14838   windows->info.border_width=1;
14839   windows->info.x=2;
14840   windows->info.y=2;
14841   windows->info.flags|=PPosition;
14842   windows->info.attributes.win_gravity=UnmapGravity;
14843   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14844     StructureNotifyMask;
14845   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14846   manager_hints->input=MagickFalse;
14847   manager_hints->initial_state=NormalState;
14848   manager_hints->window_group=windows->image.id;
14849   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14850     &windows->info);
14851   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14852     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14853   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14854     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14855   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14856   if (IfMagickTrue(windows->image.mapped) )
14857     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14858   if (IfMagickTrue(display_image->debug) )
14859     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14860       windows->info.id);
14861   /*
14862     Initialize Command widget.
14863   */
14864   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14865     resource_info,&windows->command);
14866   windows->command.data=MagickMenus;
14867   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14868   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14869     resource_info->client_name);
14870   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14871     resource_name,"geometry",(char *) NULL);
14872   (void) CloneString(&windows->command.name,MagickTitle);
14873   windows->command.border_width=0;
14874   windows->command.flags|=PPosition;
14875   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14876     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14877     OwnerGrabButtonMask | StructureNotifyMask;
14878   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14879   manager_hints->input=MagickTrue;
14880   manager_hints->initial_state=NormalState;
14881   manager_hints->window_group=windows->image.id;
14882   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14883     &windows->command);
14884   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14885     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14886     HighlightHeight);
14887   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14888     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14889   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14890   if (IfMagickTrue(windows->command.mapped) )
14891     (void) XMapRaised(display,windows->command.id);
14892   if (IfMagickTrue(display_image->debug) )
14893     (void) LogMagickEvent(X11Event,GetMagickModule(),
14894       "Window id: 0x%lx (command)",windows->command.id);
14895   /*
14896     Initialize Widget window.
14897   */
14898   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14899     resource_info,&windows->widget);
14900   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14901     resource_info->client_name);
14902   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14903     resource_name,"geometry",(char *) NULL);
14904   windows->widget.border_width=0;
14905   windows->widget.flags|=PPosition;
14906   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14907     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14908     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14909     StructureNotifyMask;
14910   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14911   manager_hints->input=MagickTrue;
14912   manager_hints->initial_state=NormalState;
14913   manager_hints->window_group=windows->image.id;
14914   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14915     &windows->widget);
14916   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14917     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14918   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14919     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14920   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14921   if (IfMagickTrue(display_image->debug) )
14922     (void) LogMagickEvent(X11Event,GetMagickModule(),
14923       "Window id: 0x%lx (widget)",windows->widget.id);
14924   /*
14925     Initialize popup window.
14926   */
14927   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14928     resource_info,&windows->popup);
14929   windows->popup.border_width=0;
14930   windows->popup.flags|=PPosition;
14931   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14932     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14933     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14934   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14935   manager_hints->input=MagickTrue;
14936   manager_hints->initial_state=NormalState;
14937   manager_hints->window_group=windows->image.id;
14938   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14939     &windows->popup);
14940   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14941     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14942   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14943     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14944   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14945   if (IfMagickTrue(display_image->debug) )
14946     (void) LogMagickEvent(X11Event,GetMagickModule(),
14947       "Window id: 0x%lx (pop up)",windows->popup.id);
14948   /*
14949     Initialize Magnify window and cursor.
14950   */
14951   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14952     resource_info,&windows->magnify);
14953   if (IfMagickFalse(resource_info->use_shared_memory) )
14954     windows->magnify.shared_memory=MagickFalse;
14955   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14956     resource_info->client_name);
14957   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14958     resource_name,"geometry",(char *) NULL);
14959   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14960     resource_info->magnify);
14961   if (windows->magnify.cursor != (Cursor) NULL)
14962     (void) XFreeCursor(display,windows->magnify.cursor);
14963   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14964     map_info->colormap,resource_info->background_color,
14965     resource_info->foreground_color);
14966   if (windows->magnify.cursor == (Cursor) NULL)
14967     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14968       display_image->filename);
14969   windows->magnify.width=MagnifySize;
14970   windows->magnify.height=MagnifySize;
14971   windows->magnify.flags|=PPosition;
14972   windows->magnify.min_width=MagnifySize;
14973   windows->magnify.min_height=MagnifySize;
14974   windows->magnify.width_inc=MagnifySize;
14975   windows->magnify.height_inc=MagnifySize;
14976   windows->magnify.data=resource_info->magnify;
14977   windows->magnify.attributes.cursor=windows->magnify.cursor;
14978   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14979     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14980     StructureNotifyMask;
14981   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14982   manager_hints->input=MagickTrue;
14983   manager_hints->initial_state=NormalState;
14984   manager_hints->window_group=windows->image.id;
14985   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14986     &windows->magnify);
14987   if (IfMagickTrue(display_image->debug) )
14988     (void) LogMagickEvent(X11Event,GetMagickModule(),
14989       "Window id: 0x%lx (magnify)",windows->magnify.id);
14990   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14991   /*
14992     Initialize panning window.
14993   */
14994   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14995     resource_info,&windows->pan);
14996   (void) CloneString(&windows->pan.name,"Pan Icon");
14997   windows->pan.width=windows->icon.width;
14998   windows->pan.height=windows->icon.height;
14999   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
15000     resource_info->client_name);
15001   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15002     resource_name,"geometry",(char *) NULL);
15003   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15004     &windows->pan.width,&windows->pan.height);
15005   windows->pan.flags|=PPosition;
15006   windows->pan.immutable=MagickTrue;
15007   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15008     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15009     StructureNotifyMask;
15010   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15011   manager_hints->input=MagickFalse;
15012   manager_hints->initial_state=NormalState;
15013   manager_hints->window_group=windows->image.id;
15014   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15015     &windows->pan);
15016   if (IfMagickTrue(display_image->debug) )
15017     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15018       windows->pan.id);
15019   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15020   if (IfMagickTrue(windows->info.mapped) )
15021     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15022   if (IfMagickFalse(windows->image.mapped) ||
15023       (windows->backdrop.id != (Window) NULL))
15024     (void) XMapWindow(display,windows->image.id);
15025   /*
15026     Set our progress monitor and warning handlers.
15027   */
15028   if (warning_handler == (WarningHandler) NULL)
15029     {
15030       warning_handler=resource_info->display_warnings ?
15031         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15032       warning_handler=resource_info->display_warnings ?
15033         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15034     }
15035   /*
15036     Initialize Image and Magnify X images.
15037   */
15038   windows->image.x=0;
15039   windows->image.y=0;
15040   windows->magnify.shape=MagickFalse;
15041   width=(unsigned int) display_image->columns;
15042   height=(unsigned int) display_image->rows;
15043   if ((display_image->columns != width) || (display_image->rows != height))
15044     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15045       display_image->filename);
15046   status=XMakeImage(display,resource_info,&windows->image,display_image,
15047     width,height,exception);
15048   if (IfMagickFalse(status) )
15049     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15050       display_image->filename);
15051   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15052     windows->magnify.width,windows->magnify.height,exception);
15053   if (IfMagickFalse(status))
15054     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15055       display_image->filename);
15056   if (IfMagickTrue(windows->magnify.mapped) )
15057     (void) XMapRaised(display,windows->magnify.id);
15058   if (IfMagickTrue(windows->pan.mapped) )
15059     (void) XMapRaised(display,windows->pan.id);
15060   windows->image.window_changes.width=(int) display_image->columns;
15061   windows->image.window_changes.height=(int) display_image->rows;
15062   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15063   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15064   (void) XSync(display,MagickFalse);
15065   /*
15066     Respond to events.
15067   */
15068   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15069   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15070   update_time=0;
15071   if (IfMagickTrue(resource_info->update) )
15072     {
15073       MagickBooleanType
15074         status;
15075
15076       /*
15077         Determine when file data was last modified.
15078       */
15079       status=GetPathAttributes(display_image->filename,&attributes);
15080       if (IfMagickTrue(status) )
15081         update_time=attributes.st_mtime;
15082     }
15083   *state&=(~FormerImageState);
15084   *state&=(~MontageImageState);
15085   *state&=(~NextImageState);
15086   do
15087   {
15088     /*
15089       Handle a window event.
15090     */
15091     if (IfMagickTrue(windows->image.mapped) )
15092       if ((display_image->delay != 0) || (resource_info->update != 0))
15093         {
15094           if (timer < time((time_t *) NULL))
15095             {
15096               if (IfMagickFalse(resource_info->update) )
15097                 *state|=NextImageState | ExitState;
15098               else
15099                 {
15100                   MagickBooleanType
15101                     status;
15102
15103                   /*
15104                     Determine if image file was modified.
15105                   */
15106                   status=GetPathAttributes(display_image->filename,&attributes);
15107                   if (IfMagickTrue(status) )
15108                     if (update_time != attributes.st_mtime)
15109                       {
15110                         /*
15111                           Redisplay image.
15112                         */
15113                         (void) FormatLocaleString(
15114                           resource_info->image_info->filename,MaxTextExtent,
15115                           "%s:%s",display_image->magick,
15116                           display_image->filename);
15117                         nexus=ReadImage(resource_info->image_info,exception);
15118                         if (nexus != (Image *) NULL)
15119                           {
15120                             nexus=DestroyImage(nexus);
15121                             *state|=NextImageState | ExitState;
15122                           }
15123                       }
15124                   delay=display_image->delay/MagickMax(
15125                     display_image->ticks_per_second,1L);
15126                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15127                 }
15128             }
15129           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15130             {
15131               /*
15132                 Do not block if delay > 0.
15133               */
15134               XDelay(display,SuspendTime << 2);
15135               continue;
15136             }
15137         }
15138     timestamp=time((time_t *) NULL);
15139     (void) XNextEvent(display,&event);
15140     if (IfMagickFalse(windows->image.stasis) )
15141       windows->image.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15142     if (IfMagickFalse(windows->magnify.stasis) )
15143       windows->magnify.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15144     if (event.xany.window == windows->command.id)
15145       {
15146         /*
15147           Select a command from the Command widget.
15148         */
15149         id=XCommandWidget(display,windows,CommandMenu,&event);
15150         if (id < 0)
15151           continue;
15152         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15153         command_type=CommandMenus[id];
15154         if (id < MagickMenus)
15155           {
15156             /*
15157               Select a command from a pop-up menu.
15158             */
15159             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15160               command);
15161             if (entry < 0)
15162               continue;
15163             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15164             command_type=Commands[id][entry];
15165           }
15166         if (command_type != NullCommand)
15167           nexus=XMagickCommand(display,resource_info,windows,command_type,
15168             &display_image,exception);
15169         continue;
15170       }
15171     switch (event.type)
15172     {
15173       case ButtonPress:
15174       {
15175         if (IfMagickTrue(display_image->debug) )
15176           (void) LogMagickEvent(X11Event,GetMagickModule(),
15177             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15178             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15179         if ((event.xbutton.button == Button3) &&
15180             (event.xbutton.state & Mod1Mask))
15181           {
15182             /*
15183               Convert Alt-Button3 to Button2.
15184             */
15185             event.xbutton.button=Button2;
15186             event.xbutton.state&=(~Mod1Mask);
15187           }
15188         if (event.xbutton.window == windows->backdrop.id)
15189           {
15190             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15191               event.xbutton.time);
15192             break;
15193           }
15194         if (event.xbutton.window == windows->image.id)
15195           {
15196             switch (event.xbutton.button)
15197             {
15198               case Button1:
15199               {
15200                 if (resource_info->immutable)
15201                   {
15202                     /*
15203                       Select a command from the Virtual menu.
15204                     */
15205                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15206                       command);
15207                     if (entry >= 0)
15208                       nexus=XMagickCommand(display,resource_info,windows,
15209                         VirtualCommands[entry],&display_image,exception);
15210                     break;
15211                   }
15212                 /*
15213                   Map/unmap Command widget.
15214                 */
15215                 if (IfMagickTrue(windows->command.mapped) )
15216                   (void) XWithdrawWindow(display,windows->command.id,
15217                     windows->command.screen);
15218                 else
15219                   {
15220                     (void) XCommandWidget(display,windows,CommandMenu,
15221                       (XEvent *) NULL);
15222                     (void) XMapRaised(display,windows->command.id);
15223                   }
15224                 break;
15225               }
15226               case Button2:
15227               {
15228                 /*
15229                   User pressed the image magnify button.
15230                 */
15231                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15232                   &display_image,exception);
15233                 XMagnifyImage(display,windows,&event,exception);
15234                 break;
15235               }
15236               case Button3:
15237               {
15238                 if (resource_info->immutable)
15239                   {
15240                     /*
15241                       Select a command from the Virtual menu.
15242                     */
15243                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15244                       command);
15245                     if (entry >= 0)
15246                       nexus=XMagickCommand(display,resource_info,windows,
15247                         VirtualCommands[entry],&display_image,exception);
15248                     break;
15249                   }
15250                 if (display_image->montage != (char *) NULL)
15251                   {
15252                     /*
15253                       Open or delete a tile from a visual image directory.
15254                     */
15255                     nexus=XTileImage(display,resource_info,windows,
15256                       display_image,&event,exception);
15257                     if (nexus != (Image *) NULL)
15258                       *state|=MontageImageState | NextImageState | ExitState;
15259                     vid_info.x=(short int) windows->image.x;
15260                     vid_info.y=(short int) windows->image.y;
15261                     break;
15262                   }
15263                 /*
15264                   Select a command from the Short Cuts menu.
15265                 */
15266                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15267                   command);
15268                 if (entry >= 0)
15269                   nexus=XMagickCommand(display,resource_info,windows,
15270                     ShortCutsCommands[entry],&display_image,exception);
15271                 break;
15272               }
15273               case Button4:
15274               {
15275                 /*
15276                   Wheel up.
15277                 */
15278                 XTranslateImage(display,windows,*image,XK_Up);
15279                 break;
15280               }
15281               case Button5:
15282               {
15283                 /*
15284                   Wheel down.
15285                 */
15286                 XTranslateImage(display,windows,*image,XK_Down);
15287                 break;
15288               }
15289               default:
15290                 break;
15291             }
15292             break;
15293           }
15294         if (event.xbutton.window == windows->magnify.id)
15295           {
15296             int
15297               factor;
15298
15299             static const char
15300               *MagnifyMenu[] =
15301               {
15302                 "2",
15303                 "4",
15304                 "5",
15305                 "6",
15306                 "7",
15307                 "8",
15308                 "9",
15309                 "3",
15310                 (char *) NULL,
15311               };
15312
15313             static KeySym
15314               MagnifyCommands[] =
15315               {
15316                 XK_2,
15317                 XK_4,
15318                 XK_5,
15319                 XK_6,
15320                 XK_7,
15321                 XK_8,
15322                 XK_9,
15323                 XK_3
15324               };
15325
15326             /*
15327               Select a magnify factor from the pop-up menu.
15328             */
15329             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15330             if (factor >= 0)
15331               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15332                 exception);
15333             break;
15334           }
15335         if (event.xbutton.window == windows->pan.id)
15336           {
15337             switch (event.xbutton.button)
15338             {
15339               case Button4:
15340               {
15341                 /*
15342                   Wheel up.
15343                 */
15344                 XTranslateImage(display,windows,*image,XK_Up);
15345                 break;
15346               }
15347               case Button5:
15348               {
15349                 /*
15350                   Wheel down.
15351                 */
15352                 XTranslateImage(display,windows,*image,XK_Down);
15353                 break;
15354               }
15355               default:
15356               {
15357                 XPanImage(display,windows,&event,exception);
15358                 break;
15359               }
15360             }
15361             break;
15362           }
15363         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15364           1L);
15365         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15366         break;
15367       }
15368       case ButtonRelease:
15369       {
15370         if (IfMagickTrue(display_image->debug) )
15371           (void) LogMagickEvent(X11Event,GetMagickModule(),
15372             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15373             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15374         break;
15375       }
15376       case ClientMessage:
15377       {
15378         if (IfMagickTrue(display_image->debug) )
15379           (void) LogMagickEvent(X11Event,GetMagickModule(),
15380             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15381             event.xclient.message_type,event.xclient.format,(unsigned long)
15382             event.xclient.data.l[0]);
15383         if (event.xclient.message_type == windows->im_protocols)
15384           {
15385             if (*event.xclient.data.l == (long) windows->im_update_widget)
15386               {
15387                 (void) CloneString(&windows->command.name,MagickTitle);
15388                 windows->command.data=MagickMenus;
15389                 (void) XCommandWidget(display,windows,CommandMenu,
15390                   (XEvent *) NULL);
15391                 break;
15392               }
15393             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15394               {
15395                 /*
15396                   Update graphic context and window colormap.
15397                 */
15398                 for (i=0; i < (int) number_windows; i++)
15399                 {
15400                   if (magick_windows[i]->id == windows->icon.id)
15401                     continue;
15402                   context_values.background=pixel->background_color.pixel;
15403                   context_values.foreground=pixel->foreground_color.pixel;
15404                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15405                     context_mask,&context_values);
15406                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15407                     context_mask,&context_values);
15408                   context_values.background=pixel->foreground_color.pixel;
15409                   context_values.foreground=pixel->background_color.pixel;
15410                   context_values.plane_mask=context_values.background ^
15411                     context_values.foreground;
15412                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15413                     (size_t) (context_mask | GCPlaneMask),
15414                     &context_values);
15415                   magick_windows[i]->attributes.background_pixel=
15416                     pixel->background_color.pixel;
15417                   magick_windows[i]->attributes.border_pixel=
15418                     pixel->border_color.pixel;
15419                   magick_windows[i]->attributes.colormap=map_info->colormap;
15420                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15421                     (unsigned long) magick_windows[i]->mask,
15422                     &magick_windows[i]->attributes);
15423                 }
15424                 if (IfMagickTrue(windows->pan.mapped) )
15425                   {
15426                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15427                       windows->pan.pixmap);
15428                     (void) XClearWindow(display,windows->pan.id);
15429                     XDrawPanRectangle(display,windows);
15430                   }
15431                 if (windows->backdrop.id != (Window) NULL)
15432                   (void) XInstallColormap(display,map_info->colormap);
15433                 break;
15434               }
15435             if (*event.xclient.data.l == (long) windows->im_former_image)
15436               {
15437                 *state|=FormerImageState | ExitState;
15438                 break;
15439               }
15440             if (*event.xclient.data.l == (long) windows->im_next_image)
15441               {
15442                 *state|=NextImageState | ExitState;
15443                 break;
15444               }
15445             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15446               {
15447                 *state|=RetainColorsState;
15448                 break;
15449               }
15450             if (*event.xclient.data.l == (long) windows->im_exit)
15451               {
15452                 *state|=ExitState;
15453                 break;
15454               }
15455             break;
15456           }
15457         if (event.xclient.message_type == windows->dnd_protocols)
15458           {
15459             Atom
15460               selection,
15461               type;
15462
15463             int
15464               format,
15465               status;
15466
15467             unsigned char
15468               *data;
15469
15470             unsigned long
15471               after,
15472               length;
15473
15474             /*
15475               Display image named by the Drag-and-Drop selection.
15476             */
15477             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15478               break;
15479             selection=XInternAtom(display,"DndSelection",MagickFalse);
15480             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15481               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15482               &length,&after,&data);
15483             if ((status != Success) || (length == 0))
15484               break;
15485             if (*event.xclient.data.l == 2)
15486               {
15487                 /*
15488                   Offix DND.
15489                 */
15490                 (void) CopyMagickString(resource_info->image_info->filename,
15491                   (char *) data,MaxTextExtent);
15492               }
15493             else
15494               {
15495                 /*
15496                   XDND.
15497                 */
15498                 if (strncmp((char *) data, "file:", 5) != 0)
15499                   {
15500                     (void) XFree((void *) data);
15501                     break;
15502                   }
15503                 (void) CopyMagickString(resource_info->image_info->filename,
15504                   ((char *) data)+5,MaxTextExtent);
15505               }
15506             nexus=ReadImage(resource_info->image_info,exception);
15507             CatchException(exception);
15508             if (nexus != (Image *) NULL)
15509               *state|=NextImageState | ExitState;
15510             (void) XFree((void *) data);
15511             break;
15512           }
15513         /*
15514           If client window delete message, exit.
15515         */
15516         if (event.xclient.message_type != windows->wm_protocols)
15517           break;
15518         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15519           break;
15520         (void) XWithdrawWindow(display,event.xclient.window,
15521           visual_info->screen);
15522         if (event.xclient.window == windows->image.id)
15523           {
15524             *state|=ExitState;
15525             break;
15526           }
15527         if (event.xclient.window == windows->pan.id)
15528           {
15529             /*
15530               Restore original image size when pan window is deleted.
15531             */
15532             windows->image.window_changes.width=windows->image.ximage->width;
15533             windows->image.window_changes.height=windows->image.ximage->height;
15534             (void) XConfigureImage(display,resource_info,windows,
15535               display_image,exception);
15536           }
15537         break;
15538       }
15539       case ConfigureNotify:
15540       {
15541         if (IfMagickTrue(display_image->debug) )
15542           (void) LogMagickEvent(X11Event,GetMagickModule(),
15543             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15544             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15545             event.xconfigure.y,event.xconfigure.send_event);
15546         if (event.xconfigure.window == windows->image.id)
15547           {
15548             /*
15549               Image window has a new configuration.
15550             */
15551             if (event.xconfigure.send_event != 0)
15552               {
15553                 XWindowChanges
15554                   window_changes;
15555
15556                 /*
15557                   Position the transient windows relative of the Image window.
15558                 */
15559                 if (windows->command.geometry == (char *) NULL)
15560                   if (IfMagickFalse(windows->command.mapped) )
15561                     {
15562                       windows->command.x=event.xconfigure.x-
15563                         windows->command.width-25;
15564                       windows->command.y=event.xconfigure.y;
15565                       XConstrainWindowPosition(display,&windows->command);
15566                       window_changes.x=windows->command.x;
15567                       window_changes.y=windows->command.y;
15568                       (void) XReconfigureWMWindow(display,windows->command.id,
15569                         windows->command.screen,(unsigned int) (CWX | CWY),
15570                         &window_changes);
15571                     }
15572                 if (windows->widget.geometry == (char *) NULL)
15573                   if (IfMagickFalse(windows->widget.mapped) )
15574                     {
15575                       windows->widget.x=event.xconfigure.x+
15576                         event.xconfigure.width/10;
15577                       windows->widget.y=event.xconfigure.y+
15578                         event.xconfigure.height/10;
15579                       XConstrainWindowPosition(display,&windows->widget);
15580                       window_changes.x=windows->widget.x;
15581                       window_changes.y=windows->widget.y;
15582                       (void) XReconfigureWMWindow(display,windows->widget.id,
15583                         windows->widget.screen,(unsigned int) (CWX | CWY),
15584                         &window_changes);
15585                     }
15586                 if (windows->magnify.geometry == (char *) NULL)
15587                   if (IfMagickFalse(windows->magnify.mapped) )
15588                     {
15589                       windows->magnify.x=event.xconfigure.x+
15590                         event.xconfigure.width+25;
15591                       windows->magnify.y=event.xconfigure.y;
15592                       XConstrainWindowPosition(display,&windows->magnify);
15593                       window_changes.x=windows->magnify.x;
15594                       window_changes.y=windows->magnify.y;
15595                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15596                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15597                         &window_changes);
15598                     }
15599                 if (windows->pan.geometry == (char *) NULL)
15600                   if (IfMagickFalse(windows->pan.mapped) )
15601                     {
15602                       windows->pan.x=event.xconfigure.x+
15603                         event.xconfigure.width+25;
15604                       windows->pan.y=event.xconfigure.y+
15605                         windows->magnify.height+50;
15606                       XConstrainWindowPosition(display,&windows->pan);
15607                       window_changes.x=windows->pan.x;
15608                       window_changes.y=windows->pan.y;
15609                       (void) XReconfigureWMWindow(display,windows->pan.id,
15610                         windows->pan.screen,(unsigned int) (CWX | CWY),
15611                         &window_changes);
15612                     }
15613               }
15614             if ((event.xconfigure.width == (int) windows->image.width) &&
15615                 (event.xconfigure.height == (int) windows->image.height))
15616               break;
15617             windows->image.width=(unsigned int) event.xconfigure.width;
15618             windows->image.height=(unsigned int) event.xconfigure.height;
15619             windows->image.x=0;
15620             windows->image.y=0;
15621             if (display_image->montage != (char *) NULL)
15622               {
15623                 windows->image.x=vid_info.x;
15624                 windows->image.y=vid_info.y;
15625               }
15626             if (IfMagickTrue(windows->image.mapped) &&
15627                 IfMagickTrue(windows->image.stasis) )
15628               {
15629                 /*
15630                   Update image window configuration.
15631                 */
15632                 windows->image.window_changes.width=event.xconfigure.width;
15633                 windows->image.window_changes.height=event.xconfigure.height;
15634                 (void) XConfigureImage(display,resource_info,windows,
15635                   display_image,exception);
15636               }
15637             /*
15638               Update pan window configuration.
15639             */
15640             if ((event.xconfigure.width < windows->image.ximage->width) ||
15641                 (event.xconfigure.height < windows->image.ximage->height))
15642               {
15643                 (void) XMapRaised(display,windows->pan.id);
15644                 XDrawPanRectangle(display,windows);
15645               }
15646             else
15647               if (IfMagickTrue(windows->pan.mapped) )
15648                 (void) XWithdrawWindow(display,windows->pan.id,
15649                   windows->pan.screen);
15650             break;
15651           }
15652         if (event.xconfigure.window == windows->magnify.id)
15653           {
15654             unsigned int
15655               magnify;
15656
15657             /*
15658               Magnify window has a new configuration.
15659             */
15660             windows->magnify.width=(unsigned int) event.xconfigure.width;
15661             windows->magnify.height=(unsigned int) event.xconfigure.height;
15662             if (IfMagickFalse(windows->magnify.mapped) )
15663               break;
15664             magnify=1;
15665             while ((int) magnify <= event.xconfigure.width)
15666               magnify<<=1;
15667             while ((int) magnify <= event.xconfigure.height)
15668               magnify<<=1;
15669             magnify>>=1;
15670             if (((int) magnify != event.xconfigure.width) ||
15671                 ((int) magnify != event.xconfigure.height))
15672               {
15673                 window_changes.width=(int) magnify;
15674                 window_changes.height=(int) magnify;
15675                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15676                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15677                   &window_changes);
15678                 break;
15679               }
15680             if (IfMagickTrue(windows->magnify.mapped) &&
15681                 IfMagickTrue(windows->magnify.stasis) )
15682               {
15683                 status=XMakeImage(display,resource_info,&windows->magnify,
15684                   display_image,windows->magnify.width,windows->magnify.height,
15685                   exception);
15686                 XMakeMagnifyImage(display,windows,exception);
15687               }
15688             break;
15689           }
15690         if (IfMagickTrue(windows->magnify.mapped) &&
15691             (event.xconfigure.window == windows->pan.id))
15692           {
15693             /*
15694               Pan icon window has a new configuration.
15695             */
15696             if (event.xconfigure.send_event != 0)
15697               {
15698                 windows->pan.x=event.xconfigure.x;
15699                 windows->pan.y=event.xconfigure.y;
15700               }
15701             windows->pan.width=(unsigned int) event.xconfigure.width;
15702             windows->pan.height=(unsigned int) event.xconfigure.height;
15703             break;
15704           }
15705         if (event.xconfigure.window == windows->icon.id)
15706           {
15707             /*
15708               Icon window has a new configuration.
15709             */
15710             windows->icon.width=(unsigned int) event.xconfigure.width;
15711             windows->icon.height=(unsigned int) event.xconfigure.height;
15712             break;
15713           }
15714         break;
15715       }
15716       case DestroyNotify:
15717       {
15718         /*
15719           Group leader has exited.
15720         */
15721         if (IfMagickTrue(display_image->debug) )
15722           (void) LogMagickEvent(X11Event,GetMagickModule(),
15723             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15724         if (event.xdestroywindow.window == windows->group_leader.id)
15725           {
15726             *state|=ExitState;
15727             break;
15728           }
15729         break;
15730       }
15731       case EnterNotify:
15732       {
15733         /*
15734           Selectively install colormap.
15735         */
15736         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15737           if (event.xcrossing.mode != NotifyUngrab)
15738             XInstallColormap(display,map_info->colormap);
15739         break;
15740       }
15741       case Expose:
15742       {
15743         if (IfMagickTrue(display_image->debug) )
15744           (void) LogMagickEvent(X11Event,GetMagickModule(),
15745             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15746             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15747             event.xexpose.y);
15748         /*
15749           Refresh windows that are now exposed.
15750         */
15751         if ((event.xexpose.window == windows->image.id) &&
15752             IfMagickTrue(windows->image.mapped) )
15753           {
15754             XRefreshWindow(display,&windows->image,&event);
15755             delay=display_image->delay/MagickMax(
15756               display_image->ticks_per_second,1L);
15757             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15758             break;
15759           }
15760         if ((event.xexpose.window == windows->magnify.id) &&
15761             IfMagickTrue(windows->magnify.mapped))
15762           {
15763             XMakeMagnifyImage(display,windows,exception);
15764             break;
15765           }
15766         if (event.xexpose.window == windows->pan.id)
15767           {
15768             XDrawPanRectangle(display,windows);
15769             break;
15770           }
15771         if (event.xexpose.window == windows->icon.id)
15772           {
15773             XRefreshWindow(display,&windows->icon,&event);
15774             break;
15775           }
15776         break;
15777       }
15778       case KeyPress:
15779       {
15780         int
15781           length;
15782
15783         /*
15784           Respond to a user key press.
15785         */
15786         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15787           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15788         *(command+length)='\0';
15789         if (IfMagickTrue(display_image->debug) )
15790           (void) LogMagickEvent(X11Event,GetMagickModule(),
15791             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15792             key_symbol,command);
15793         if (event.xkey.window == windows->image.id)
15794           {
15795             command_type=XImageWindowCommand(display,resource_info,windows,
15796               event.xkey.state,key_symbol,&display_image,exception);
15797             if (command_type != NullCommand)
15798               nexus=XMagickCommand(display,resource_info,windows,command_type,
15799                 &display_image,exception);
15800           }
15801         if (event.xkey.window == windows->magnify.id)
15802           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15803             exception);
15804         if (event.xkey.window == windows->pan.id)
15805           {
15806             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15807               (void) XWithdrawWindow(display,windows->pan.id,
15808                 windows->pan.screen);
15809             else
15810               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15811                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15812                   "Help Viewer - Image Pan",ImagePanHelp);
15813               else
15814                 XTranslateImage(display,windows,*image,key_symbol);
15815           }
15816         delay=display_image->delay/MagickMax(
15817           display_image->ticks_per_second,1L);
15818         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15819         break;
15820       }
15821       case KeyRelease:
15822       {
15823         /*
15824           Respond to a user key release.
15825         */
15826         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15827           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15828         if (IfMagickTrue(display_image->debug) )
15829           (void) LogMagickEvent(X11Event,GetMagickModule(),
15830             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15831         break;
15832       }
15833       case LeaveNotify:
15834       {
15835         /*
15836           Selectively uninstall colormap.
15837         */
15838         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15839           if (event.xcrossing.mode != NotifyUngrab)
15840             XUninstallColormap(display,map_info->colormap);
15841         break;
15842       }
15843       case MapNotify:
15844       {
15845         if (IfMagickTrue(display_image->debug) )
15846           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15847             event.xmap.window);
15848         if (event.xmap.window == windows->backdrop.id)
15849           {
15850             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15851               CurrentTime);
15852             windows->backdrop.mapped=MagickTrue;
15853             break;
15854           }
15855         if (event.xmap.window == windows->image.id)
15856           {
15857             if (windows->backdrop.id != (Window) NULL)
15858               (void) XInstallColormap(display,map_info->colormap);
15859             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15860               {
15861                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15862                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15863               }
15864             if (((int) windows->image.width < windows->image.ximage->width) ||
15865                 ((int) windows->image.height < windows->image.ximage->height))
15866               (void) XMapRaised(display,windows->pan.id);
15867             windows->image.mapped=MagickTrue;
15868             break;
15869           }
15870         if (event.xmap.window == windows->magnify.id)
15871           {
15872             XMakeMagnifyImage(display,windows,exception);
15873             windows->magnify.mapped=MagickTrue;
15874             (void) XWithdrawWindow(display,windows->info.id,
15875               windows->info.screen);
15876             break;
15877           }
15878         if (event.xmap.window == windows->pan.id)
15879           {
15880             XMakePanImage(display,resource_info,windows,display_image,
15881               exception);
15882             windows->pan.mapped=MagickTrue;
15883             break;
15884           }
15885         if (event.xmap.window == windows->info.id)
15886           {
15887             windows->info.mapped=MagickTrue;
15888             break;
15889           }
15890         if (event.xmap.window == windows->icon.id)
15891           {
15892             MagickBooleanType
15893               taint;
15894
15895             /*
15896               Create an icon image.
15897             */
15898             taint=display_image->taint;
15899             XMakeStandardColormap(display,icon_visual,icon_resources,
15900               display_image,icon_map,icon_pixel,exception);
15901             (void) XMakeImage(display,icon_resources,&windows->icon,
15902               display_image,windows->icon.width,windows->icon.height,
15903               exception);
15904             display_image->taint=taint;
15905             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15906               windows->icon.pixmap);
15907             (void) XClearWindow(display,windows->icon.id);
15908             (void) XWithdrawWindow(display,windows->info.id,
15909               windows->info.screen);
15910             windows->icon.mapped=MagickTrue;
15911             break;
15912           }
15913         if (event.xmap.window == windows->command.id)
15914           {
15915             windows->command.mapped=MagickTrue;
15916             break;
15917           }
15918         if (event.xmap.window == windows->popup.id)
15919           {
15920             windows->popup.mapped=MagickTrue;
15921             break;
15922           }
15923         if (event.xmap.window == windows->widget.id)
15924           {
15925             windows->widget.mapped=MagickTrue;
15926             break;
15927           }
15928         break;
15929       }
15930       case MappingNotify:
15931       {
15932         (void) XRefreshKeyboardMapping(&event.xmapping);
15933         break;
15934       }
15935       case NoExpose:
15936         break;
15937       case PropertyNotify:
15938       {
15939         Atom
15940           type;
15941
15942         int
15943           format,
15944           status;
15945
15946         unsigned char
15947           *data;
15948
15949         unsigned long
15950           after,
15951           length;
15952
15953         if (IfMagickTrue(display_image->debug) )
15954           (void) LogMagickEvent(X11Event,GetMagickModule(),
15955             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15956             event.xproperty.atom,event.xproperty.state);
15957         if (event.xproperty.atom != windows->im_remote_command)
15958           break;
15959         /*
15960           Display image named by the remote command protocol.
15961         */
15962         status=XGetWindowProperty(display,event.xproperty.window,
15963           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15964           AnyPropertyType,&type,&format,&length,&after,&data);
15965         if ((status != Success) || (length == 0))
15966           break;
15967         if (LocaleCompare((char *) data,"-quit") == 0)
15968           {
15969             XClientMessage(display,windows->image.id,windows->im_protocols,
15970               windows->im_exit,CurrentTime);
15971             (void) XFree((void *) data);
15972             break;
15973           }
15974         (void) CopyMagickString(resource_info->image_info->filename,
15975           (char *) data,MaxTextExtent);
15976         (void) XFree((void *) data);
15977         nexus=ReadImage(resource_info->image_info,exception);
15978         CatchException(exception);
15979         if (nexus != (Image *) NULL)
15980           *state|=NextImageState | ExitState;
15981         break;
15982       }
15983       case ReparentNotify:
15984       {
15985         if (IfMagickTrue(display_image->debug) )
15986           (void) LogMagickEvent(X11Event,GetMagickModule(),
15987             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15988             event.xreparent.window);
15989         break;
15990       }
15991       case UnmapNotify:
15992       {
15993         if (IfMagickTrue(display_image->debug) )
15994           (void) LogMagickEvent(X11Event,GetMagickModule(),
15995             "Unmap Notify: 0x%lx",event.xunmap.window);
15996         if (event.xunmap.window == windows->backdrop.id)
15997           {
15998             windows->backdrop.mapped=MagickFalse;
15999             break;
16000           }
16001         if (event.xunmap.window == windows->image.id)
16002           {
16003             windows->image.mapped=MagickFalse;
16004             break;
16005           }
16006         if (event.xunmap.window == windows->magnify.id)
16007           {
16008             windows->magnify.mapped=MagickFalse;
16009             break;
16010           }
16011         if (event.xunmap.window == windows->pan.id)
16012           {
16013             windows->pan.mapped=MagickFalse;
16014             break;
16015           }
16016         if (event.xunmap.window == windows->info.id)
16017           {
16018             windows->info.mapped=MagickFalse;
16019             break;
16020           }
16021         if (event.xunmap.window == windows->icon.id)
16022           {
16023             if (map_info->colormap == icon_map->colormap)
16024               XConfigureImageColormap(display,resource_info,windows,
16025                 display_image,exception);
16026             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16027               icon_pixel);
16028             windows->icon.mapped=MagickFalse;
16029             break;
16030           }
16031         if (event.xunmap.window == windows->command.id)
16032           {
16033             windows->command.mapped=MagickFalse;
16034             break;
16035           }
16036         if (event.xunmap.window == windows->popup.id)
16037           {
16038             if (windows->backdrop.id != (Window) NULL)
16039               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16040                 CurrentTime);
16041             windows->popup.mapped=MagickFalse;
16042             break;
16043           }
16044         if (event.xunmap.window == windows->widget.id)
16045           {
16046             if (windows->backdrop.id != (Window) NULL)
16047               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16048                 CurrentTime);
16049             windows->widget.mapped=MagickFalse;
16050             break;
16051           }
16052         break;
16053       }
16054       default:
16055       {
16056         if (IfMagickTrue(display_image->debug) )
16057           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16058             event.type);
16059         break;
16060       }
16061     }
16062   } while (!(*state & ExitState));
16063   if ((*state & ExitState) == 0)
16064     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16065       &display_image,exception);
16066   else
16067     if (IfMagickTrue(resource_info->confirm_edit) )
16068       {
16069         /*
16070           Query user if image has changed.
16071         */
16072         if (IfMagickFalse(resource_info->immutable) &&
16073             IfMagickTrue(display_image->taint))
16074           {
16075             int
16076               status;
16077
16078             status=XConfirmWidget(display,windows,"Your image changed.",
16079               "Do you want to save it");
16080             if (status == 0)
16081               *state&=(~ExitState);
16082             else
16083               if (status > 0)
16084                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16085                   &display_image,exception);
16086           }
16087       }
16088   if ((windows->visual_info->klass == GrayScale) ||
16089       (windows->visual_info->klass == PseudoColor) ||
16090       (windows->visual_info->klass == DirectColor))
16091     {
16092       /*
16093         Withdraw pan and Magnify window.
16094       */
16095       if (IfMagickTrue(windows->info.mapped) )
16096         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16097       if (IfMagickTrue(windows->magnify.mapped) )
16098         (void) XWithdrawWindow(display,windows->magnify.id,
16099           windows->magnify.screen);
16100       if (IfMagickTrue(windows->command.mapped) )
16101         (void) XWithdrawWindow(display,windows->command.id,
16102           windows->command.screen);
16103     }
16104   if (IfMagickTrue(windows->pan.mapped) )
16105     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16106   if (IfMagickFalse(resource_info->backdrop) )
16107     if (windows->backdrop.mapped)
16108       {
16109         (void) XWithdrawWindow(display,windows->backdrop.id,
16110           windows->backdrop.screen);
16111         (void) XDestroyWindow(display,windows->backdrop.id);
16112         windows->backdrop.id=(Window) NULL;
16113         (void) XWithdrawWindow(display,windows->image.id,
16114           windows->image.screen);
16115         (void) XDestroyWindow(display,windows->image.id);
16116         windows->image.id=(Window) NULL;
16117       }
16118   XSetCursorState(display,windows,MagickTrue);
16119   XCheckRefreshWindows(display,windows);
16120   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16121     *state&=(~ExitState);
16122   if (*state & ExitState)
16123     {
16124       /*
16125         Free Standard Colormap.
16126       */
16127       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16128       if (resource_info->map_type == (char *) NULL)
16129         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16130       /*
16131         Free X resources.
16132       */
16133       if (resource_info->copy_image != (Image *) NULL)
16134         {
16135           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16136           resource_info->copy_image=NewImageList();
16137         }
16138       DestroyXResources();
16139     }
16140   (void) XSync(display,MagickFalse);
16141   /*
16142     Restore our progress monitor and warning handlers.
16143   */
16144   (void) SetErrorHandler(warning_handler);
16145   (void) SetWarningHandler(warning_handler);
16146   /*
16147     Change to home directory.
16148   */
16149   directory=getcwd(working_directory,MaxTextExtent);
16150   (void) directory;
16151   {
16152     int
16153       status;
16154
16155     if (*resource_info->home_directory == '\0')
16156       (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16157     status=chdir(resource_info->home_directory);
16158     if (status == -1)
16159       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16160         "UnableToOpenFile","%s",resource_info->home_directory);
16161   }
16162   *image=display_image;
16163   return(nexus);
16164 }
16165 #else
16166 \f
16167 /*
16168 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16169 %                                                                             %
16170 %                                                                             %
16171 %                                                                             %
16172 +   D i s p l a y I m a g e s                                                 %
16173 %                                                                             %
16174 %                                                                             %
16175 %                                                                             %
16176 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16177 %
16178 %  DisplayImages() displays an image sequence to any X window screen.  It
16179 %  returns a value other than 0 if successful.  Check the exception member
16180 %  of image to determine the reason for any failure.
16181 %
16182 %  The format of the DisplayImages method is:
16183 %
16184 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16185 %        Image *images,ExceptionInfo *exception)
16186 %
16187 %  A description of each parameter follows:
16188 %
16189 %    o image_info: the image info.
16190 %
16191 %    o image: the image.
16192 %
16193 %    o exception: return any errors or warnings in this structure.
16194 %
16195 */
16196 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16197   Image *image,ExceptionInfo *exception)
16198 {
16199   assert(image_info != (const ImageInfo *) NULL);
16200   assert(image_info->signature == MagickSignature);
16201   assert(image != (Image *) NULL);
16202   assert(image->signature == MagickSignature);
16203   (void) image_info;
16204   if (IfMagickTrue(image->debug) )
16205     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16206   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16207     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16208   return(MagickFalse);
16209 }
16210 \f
16211 /*
16212 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16213 %                                                                             %
16214 %                                                                             %
16215 %                                                                             %
16216 +   R e m o t e D i s p l a y C o m m a n d                                   %
16217 %                                                                             %
16218 %                                                                             %
16219 %                                                                             %
16220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16221 %
16222 %  RemoteDisplayCommand() encourages a remote display program to display the
16223 %  specified image filename.
16224 %
16225 %  The format of the RemoteDisplayCommand method is:
16226 %
16227 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16228 %        const char *window,const char *filename,ExceptionInfo *exception)
16229 %
16230 %  A description of each parameter follows:
16231 %
16232 %    o image_info: the image info.
16233 %
16234 %    o window: Specifies the name or id of an X window.
16235 %
16236 %    o filename: the name of the image filename to display.
16237 %
16238 %    o exception: return any errors or warnings in this structure.
16239 %
16240 */
16241 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16242   const char *window,const char *filename,ExceptionInfo *exception)
16243 {
16244   assert(image_info != (const ImageInfo *) NULL);
16245   assert(image_info->signature == MagickSignature);
16246   assert(filename != (char *) NULL);
16247   (void) window;
16248   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16249   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16250     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16251   return(MagickFalse);
16252 }
16253 #endif