]> granicus.if.org Git - imagemagick/blob - MagickCore/display.c
(no commit message)
[imagemagick] / MagickCore / display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7 %               D   D    I    SS     P   P  L      A   A   Y Y                %
8 %               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9 %               D   D    I       SS  P      L      A   A    Y                 %
10 %               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11 %                                                                             %
12 %                                                                             %
13 %        MagickCore Methods to Interactively Display and Edit an Image        %
14 %                                                                             %
15 %                             Software Design                                 %
16 %                                  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/option.h"
76 #include "MagickCore/paint.h"
77 #include "MagickCore/pixel.h"
78 #include "MagickCore/pixel-accessor.h"
79 #include "MagickCore/PreRvIcccm.h"
80 #include "MagickCore/property.h"
81 #include "MagickCore/quantum.h"
82 #include "MagickCore/quantum-private.h"
83 #include "MagickCore/resize.h"
84 #include "MagickCore/resource_.h"
85 #include "MagickCore/shear.h"
86 #include "MagickCore/segment.h"
87 #include "MagickCore/statistic.h"
88 #include "MagickCore/string_.h"
89 #include "MagickCore/string-private.h"
90 #include "MagickCore/transform.h"
91 #include "MagickCore/threshold.h"
92 #include "MagickCore/utility.h"
93 #include "MagickCore/utility-private.h"
94 #include "MagickCore/version.h"
95 #include "MagickCore/widget.h"
96 #include "MagickCore/widget-private.h"
97 #include "MagickCore/xwindow.h"
98 #include "MagickCore/xwindow-private.h"
99 \f
100 #if defined(MAGICKCORE_X11_DELEGATE)
101 /*
102   Define declarations.
103 */
104 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
105 \f
106 /*
107   Constant declarations.
108 */
109 static const unsigned char
110   HighlightBitmap[8] =
111   {
112     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
113   },
114   OpaqueBitmap[8] =
115   {
116     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
117   },
118   ShadowBitmap[8] =
119   {
120     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
121   };
122
123 static const char
124   *PageSizes[] =
125   {
126     "Letter",
127     "Tabloid",
128     "Ledger",
129     "Legal",
130     "Statement",
131     "Executive",
132     "A3",
133     "A4",
134     "A5",
135     "B4",
136     "B5",
137     "Folio",
138     "Quarto",
139     "10x14",
140     (char *) NULL
141   };
142 \f
143 /*
144   Help widget declarations.
145 */
146 static const char
147   *ImageAnnotateHelp[] =
148   {
149     "In annotate mode, the Command widget has these options:",
150     "",
151     "    Font Name",
152     "      fixed",
153     "      variable",
154     "      5x8",
155     "      6x10",
156     "      7x13bold",
157     "      8x13bold",
158     "      9x15bold",
159     "      10x20",
160     "      12x24",
161     "      Browser...",
162     "    Font Color",
163     "      black",
164     "      blue",
165     "      cyan",
166     "      green",
167     "      gray",
168     "      red",
169     "      magenta",
170     "      yellow",
171     "      white",
172     "      transparent",
173     "      Browser...",
174     "    Font Color",
175     "      black",
176     "      blue",
177     "      cyan",
178     "      green",
179     "      gray",
180     "      red",
181     "      magenta",
182     "      yellow",
183     "      white",
184     "      transparent",
185     "      Browser...",
186     "    Rotate Text",
187     "      -90",
188     "      -45",
189     "      -30",
190     "      0",
191     "      30",
192     "      45",
193     "      90",
194     "      180",
195     "      Dialog...",
196     "    Help",
197     "    Dismiss",
198     "",
199     "Choose a font name from the Font Name sub-menu.  Additional",
200     "font names can be specified with the font browser.  You can",
201     "change the menu names by setting the X resources font1",
202     "through font9.",
203     "",
204     "Choose a font color from the Font Color sub-menu.",
205     "Additional font colors can be specified with the color",
206     "browser.  You can change the menu colors by setting the X",
207     "resources pen1 through pen9.",
208     "",
209     "If you select the color browser and press Grab, you can",
210     "choose the font color by moving the pointer to the desired",
211     "color on the screen and press any button.",
212     "",
213     "If you choose to rotate the text, choose Rotate Text from the",
214     "menu and select an angle.  Typically you will only want to",
215     "rotate one line of text at a time.  Depending on the angle you",
216     "choose, subsequent lines may end up overwriting each other.",
217     "",
218     "Choosing a font and its color is optional.  The default font",
219     "is fixed and the default color is black.  However, you must",
220     "choose a location to begin entering text and press button 1.",
221     "An underscore character will appear at the location of the",
222     "pointer.  The cursor changes to a pencil to indicate you are",
223     "in text mode.  To exit immediately, press Dismiss.",
224     "",
225     "In text mode, any key presses will display the character at",
226     "the location of the underscore and advance the underscore",
227     "cursor.  Enter your text and once completed press Apply to",
228     "finish your image annotation.  To correct errors press BACK",
229     "SPACE.  To delete an entire line of text, press DELETE.  Any",
230     "text that exceeds the boundaries of the image window is",
231     "automagically continued onto the next line.",
232     "",
233     "The actual color you request for the font is saved in the",
234     "image.  However, the color that appears in your image window",
235     "may be different.  For example, on a monochrome screen the",
236     "text will appear black or white even if you choose the color",
237     "red as the font color.  However, the image saved to a file",
238     "with -write is written with red lettering.  To assure the",
239     "correct color text in the final image, any PseudoClass image",
240     "is promoted to DirectClass (see miff(5)).  To force a",
241     "PseudoClass image to remain PseudoClass, use -colors.",
242     (char *) NULL,
243   },
244   *ImageChopHelp[] =
245   {
246     "In chop mode, the Command widget has these options:",
247     "",
248     "    Direction",
249     "      horizontal",
250     "      vertical",
251     "    Help",
252     "    Dismiss",
253     "",
254     "If the you choose the horizontal direction (this the",
255     "default), the area of the image between the two horizontal",
256     "endpoints of the chop line is removed.  Otherwise, the area",
257     "of the image between the two vertical endpoints of the chop",
258     "line is removed.",
259     "",
260     "Select a location within the image window to begin your chop,",
261     "press and hold any button.  Next, move the pointer to",
262     "another location in the image.  As you move a line will",
263     "connect the initial location and the pointer.  When you",
264     "release the button, the area within the image to chop is",
265     "determined by which direction you choose from the Command",
266     "widget.",
267     "",
268     "To cancel the image chopping, move the pointer back to the",
269     "starting point of the line and release the button.",
270     (char *) NULL,
271   },
272   *ImageColorEditHelp[] =
273   {
274     "In color edit mode, the Command widget has these options:",
275     "",
276     "    Method",
277     "      point",
278     "      replace",
279     "      floodfill",
280     "      filltoborder",
281     "      reset",
282     "    Pixel Color",
283     "      black",
284     "      blue",
285     "      cyan",
286     "      green",
287     "      gray",
288     "      red",
289     "      magenta",
290     "      yellow",
291     "      white",
292     "      Browser...",
293     "    Border Color",
294     "      black",
295     "      blue",
296     "      cyan",
297     "      green",
298     "      gray",
299     "      red",
300     "      magenta",
301     "      yellow",
302     "      white",
303     "      Browser...",
304     "    Fuzz",
305     "      0%",
306     "      2%",
307     "      5%",
308     "      10%",
309     "      15%",
310     "      Dialog...",
311     "    Undo",
312     "    Help",
313     "    Dismiss",
314     "",
315     "Choose a color editing method from the Method sub-menu",
316     "of the Command widget.  The point method recolors any pixel",
317     "selected with the pointer until the button is released.  The",
318     "replace method recolors any pixel that matches the color of",
319     "the pixel you select with a button press.  Floodfill recolors",
320     "any pixel that matches the color of the pixel you select with",
321     "a button press and is a neighbor.  Whereas filltoborder recolors",
322     "any neighbor pixel that is not the border color.  Finally reset",
323     "changes the entire image to the designated color.",
324     "",
325     "Next, choose a pixel color from the Pixel Color sub-menu.",
326     "Additional pixel colors can be specified with the color",
327     "browser.  You can change the menu colors by setting the X",
328     "resources pen1 through pen9.",
329     "",
330     "Now press button 1 to select a pixel within the image window",
331     "to change its color.  Additional pixels may be recolored as",
332     "prescribed by the method you choose.",
333     "",
334     "If the Magnify widget is mapped, it can be helpful in positioning",
335     "your pointer within the image (refer to button 2).",
336     "",
337     "The actual color you request for the pixels is saved in the",
338     "image.  However, the color that appears in your image window",
339     "may be different.  For example, on a monochrome screen the",
340     "pixel will appear black or white even if you choose the",
341     "color red as the pixel color.  However, the image saved to a",
342     "file with -write is written with red pixels.  To assure the",
343     "correct color text in the final image, any PseudoClass image",
344     "is promoted to DirectClass (see miff(5)).  To force a",
345     "PseudoClass image to remain PseudoClass, use -colors.",
346     (char *) NULL,
347   },
348   *ImageCompositeHelp[] =
349   {
350     "First a widget window is displayed requesting you to enter an",
351     "image name. Press Composite, Grab or type a file name.",
352     "Press Cancel if you choose not to create a composite image.",
353     "When you choose Grab, move the pointer to the desired window",
354     "and press any button.",
355     "",
356     "If the Composite image does not have any matte information,",
357     "you are informed and the file browser is displayed again.",
358     "Enter the name of a mask image.  The image is typically",
359     "grayscale and the same size as the composite image.  If the",
360     "image is not grayscale, it is converted to grayscale and the",
361     "resulting intensities are used as matte information.",
362     "",
363     "A small window appears showing the location of the cursor in",
364     "the image window. You are now in composite mode.  To exit",
365     "immediately, press Dismiss.  In composite mode, the Command",
366     "widget has these options:",
367     "",
368     "    Operators",
369     "      Over",
370     "      In",
371     "      Out",
372     "      Atop",
373     "      Xor",
374     "      Plus",
375     "      Minus",
376     "      Add",
377     "      Subtract",
378     "      Difference",
379     "      Multiply",
380     "      Bumpmap",
381     "      Copy",
382     "      CopyRed",
383     "      CopyGreen",
384     "      CopyBlue",
385     "      CopyOpacity",
386     "      Clear",
387     "    Dissolve",
388     "    Displace",
389     "    Help",
390     "    Dismiss",
391     "",
392     "Choose a composite operation from the Operators sub-menu of",
393     "the Command widget.  How each operator behaves is described",
394     "below.  Image window is the image currently displayed on",
395     "your X server and image is the image obtained with the File",
396     "Browser widget.",
397     "",
398     "Over     The result is the union of the two image shapes,",
399     "         with image obscuring image window in the region of",
400     "         overlap.",
401     "",
402     "In       The result is simply image cut by the shape of",
403     "         image window.  None of the image data of image",
404     "         window is in the result.",
405     "",
406     "Out      The resulting image is image with the shape of",
407     "         image window cut out.",
408     "",
409     "Atop     The result is the same shape as image image window,",
410     "         with image obscuring image window where the image",
411     "         shapes overlap.  Note this differs from over",
412     "         because the portion of image outside image window's",
413     "         shape does not appear in the result.",
414     "",
415     "Xor      The result is the image data from both image and",
416     "         image window that is outside the overlap region.",
417     "         The overlap region is blank.",
418     "",
419     "Plus     The result is just the sum of the image data.",
420     "         Output values are cropped to QuantumRange (no overflow).",
421     "",
422     "Minus    The result of image - image window, with underflow",
423     "         cropped to zero.",
424     "",
425     "Add      The result of image + image window, with overflow",
426     "         wrapping around (mod 256).",
427     "",
428     "Subtract The result of image - image window, with underflow",
429     "         wrapping around (mod 256).  The add and subtract",
430     "         operators can be used to perform reversible",
431     "         transformations.",
432     "",
433     "Difference",
434     "         The result of abs(image - image window).  This",
435     "         useful for comparing two very similar images.",
436     "",
437     "Multiply",
438     "         The result of image * image window.  This",
439     "         useful for the creation of drop-shadows.",
440     "",
441     "Bumpmap  The result of surface normals from image * image",
442     "         window.",
443     "",
444     "Copy     The resulting image is image window replaced with",
445     "         image.  Here the matte information is ignored.",
446     "",
447     "CopyRed  The red layer of the image window is replace with",
448     "         the red layer of the image.  The other layers are",
449     "         untouched.",
450     "",
451     "CopyGreen",
452     "         The green layer of the image window is replace with",
453     "         the green layer of the image.  The other layers are",
454     "         untouched.",
455     "",
456     "CopyBlue The blue layer of the image window is replace with",
457     "         the blue layer of the image.  The other layers are",
458     "         untouched.",
459     "",
460     "CopyOpacity",
461     "         The matte layer of the image window is replace with",
462     "         the matte layer of the image.  The other layers are",
463     "         untouched.",
464     "",
465     "The image compositor requires a matte, or alpha channel in",
466     "the image for some operations.  This extra channel usually",
467     "defines a mask which represents a sort of a cookie-cutter",
468     "for the image.  This the case when matte is opaque (full",
469     "coverage) for pixels inside the shape, zero outside, and",
470     "between 0 and QuantumRange on the boundary.  If image does not",
471     "have a matte channel, it is initialized with 0 for any pixel",
472     "matching in color to pixel location (0,0), otherwise QuantumRange.",
473     "",
474     "If you choose Dissolve, the composite operator becomes Over.  The",
475     "image matte channel percent transparency is initialized to factor.",
476     "The image window is initialized to (100-factor). Where factor is the",
477     "value you specify in the Dialog widget.",
478     "",
479     "Displace shifts the image pixels as defined by a displacement",
480     "map.  With this option, image is used as a displacement map.",
481     "Black, within the displacement map, is a maximum positive",
482     "displacement.  White is a maximum negative displacement and",
483     "middle gray is neutral.  The displacement is scaled to determine",
484     "the pixel shift.  By default, the displacement applies in both the",
485     "horizontal and vertical directions.  However, if you specify a mask,",
486     "image is the horizontal X displacement and mask the vertical Y",
487     "displacement.",
488     "",
489     "Note that matte information for image window is not retained",
490     "for colormapped X server visuals (e.g. StaticColor,",
491     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
492     "behavior may require a TrueColor or DirectColor visual or a",
493     "Standard Colormap.",
494     "",
495     "Choosing a composite operator is optional.  The default",
496     "operator is replace.  However, you must choose a location to",
497     "composite your image and press button 1.  Press and hold the",
498     "button before releasing and an outline of the image will",
499     "appear to help you identify your location.",
500     "",
501     "The actual colors of the composite image is saved.  However,",
502     "the color that appears in image window may be different.",
503     "For example, on a monochrome screen image window will appear",
504     "black or white even though your composited image may have",
505     "many colors.  If the image is saved to a file it is written",
506     "with the correct colors.  To assure the correct colors are",
507     "saved in the final image, any PseudoClass image is promoted",
508     "to DirectClass (see miff(5)).  To force a PseudoClass image",
509     "to remain PseudoClass, use -colors.",
510     (char *) NULL,
511   },
512   *ImageCutHelp[] =
513   {
514     "In cut mode, the Command widget has these options:",
515     "",
516     "    Help",
517     "    Dismiss",
518     "",
519     "To define a cut region, press button 1 and drag.  The",
520     "cut region is defined by a highlighted rectangle that",
521     "expands or contracts as it follows the pointer.  Once you",
522     "are satisfied with the cut region, release the button.",
523     "You are now in rectify mode.  In rectify mode, the Command",
524     "widget has these options:",
525     "",
526     "    Cut",
527     "    Help",
528     "    Dismiss",
529     "",
530     "You can make adjustments by moving the pointer to one of the",
531     "cut rectangle corners, pressing a button, and dragging.",
532     "Finally, press Cut to commit your copy region.  To",
533     "exit without cutting the image, press Dismiss.",
534     (char *) NULL,
535   },
536   *ImageCopyHelp[] =
537   {
538     "In copy mode, the Command widget has these options:",
539     "",
540     "    Help",
541     "    Dismiss",
542     "",
543     "To define a copy region, press button 1 and drag.  The",
544     "copy region is defined by a highlighted rectangle that",
545     "expands or contracts as it follows the pointer.  Once you",
546     "are satisfied with the copy region, release the button.",
547     "You are now in rectify mode.  In rectify mode, the Command",
548     "widget has these options:",
549     "",
550     "    Copy",
551     "    Help",
552     "    Dismiss",
553     "",
554     "You can make adjustments by moving the pointer to one of the",
555     "copy rectangle corners, pressing a button, and dragging.",
556     "Finally, press Copy to commit your copy region.  To",
557     "exit without copying the image, press Dismiss.",
558     (char *) NULL,
559   },
560   *ImageCropHelp[] =
561   {
562     "In crop mode, the Command widget has these options:",
563     "",
564     "    Help",
565     "    Dismiss",
566     "",
567     "To define a cropping region, press button 1 and drag.  The",
568     "cropping region is defined by a highlighted rectangle that",
569     "expands or contracts as it follows the pointer.  Once you",
570     "are satisfied with the cropping region, release the button.",
571     "You are now in rectify mode.  In rectify mode, the Command",
572     "widget has these options:",
573     "",
574     "    Crop",
575     "    Help",
576     "    Dismiss",
577     "",
578     "You can make adjustments by moving the pointer to one of the",
579     "cropping rectangle corners, pressing a button, and dragging.",
580     "Finally, press Crop to commit your cropping region.  To",
581     "exit without cropping the image, press Dismiss.",
582     (char *) NULL,
583   },
584   *ImageDrawHelp[] =
585   {
586     "The cursor changes to a crosshair to indicate you are in",
587     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
588     "the Command widget has these options:",
589     "",
590     "    Element",
591     "      point",
592     "      line",
593     "      rectangle",
594     "      fill rectangle",
595     "      circle",
596     "      fill circle",
597     "      ellipse",
598     "      fill ellipse",
599     "      polygon",
600     "      fill polygon",
601     "    Color",
602     "      black",
603     "      blue",
604     "      cyan",
605     "      green",
606     "      gray",
607     "      red",
608     "      magenta",
609     "      yellow",
610     "      white",
611     "      transparent",
612     "      Browser...",
613     "    Stipple",
614     "      Brick",
615     "      Diagonal",
616     "      Scales",
617     "      Vertical",
618     "      Wavy",
619     "      Translucent",
620     "      Opaque",
621     "      Open...",
622     "    Width",
623     "      1",
624     "      2",
625     "      4",
626     "      8",
627     "      16",
628     "      Dialog...",
629     "    Undo",
630     "    Help",
631     "    Dismiss",
632     "",
633     "Choose a drawing primitive from the Element sub-menu.",
634     "",
635     "Choose a color from the Color sub-menu.  Additional",
636     "colors can be specified with the color browser.",
637     "",
638     "If you choose the color browser and press Grab, you can",
639     "select the color by moving the pointer to the desired",
640     "color on the screen and press any button.  The transparent",
641     "color updates the image matte channel and is useful for",
642     "image compositing.",
643     "",
644     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
645     "Additional stipples can be specified with the file browser.",
646     "Stipples obtained from the file browser must be on disk in the",
647     "X11 bitmap format.",
648     "",
649     "Choose a width, if appropriate, from the Width sub-menu.  To",
650     "choose a specific width select the Dialog widget.",
651     "",
652     "Choose a point in the Image window and press button 1 and",
653     "hold.  Next, move the pointer to another location in the",
654     "image.  As you move, a line connects the initial location and",
655     "the pointer.  When you release the button, the image is",
656     "updated with the primitive you just drew.  For polygons, the",
657     "image is updated when you press and release the button without",
658     "moving the pointer.",
659     "",
660     "To cancel image drawing, move the pointer back to the",
661     "starting point of the line and release the button.",
662     (char *) NULL,
663   },
664   *DisplayHelp[] =
665   {
666     "BUTTONS",
667     "  The effects of each button press is described below.  Three",
668     "  buttons are required.  If you have a two button mouse,",
669     "  button 1 and 3 are returned.  Press ALT and button 3 to",
670     "  simulate button 2.",
671     "",
672     "  1    Press this button to map or unmap the Command widget.",
673     "",
674     "  2    Press and drag to define a region of the image to",
675     "       magnify.",
676     "",
677     "  3    Press and drag to choose from a select set of commands.",
678     "       This button behaves differently if the image being",
679     "       displayed is a visual image directory.  Here, choose a",
680     "       particular tile of the directory and press this button and",
681     "       drag to select a command from a pop-up menu.  Choose from",
682     "       these menu items:",
683     "",
684     "           Open",
685     "           Next",
686     "           Former",
687     "           Delete",
688     "           Update",
689     "",
690     "       If you choose Open, the image represented by the tile is",
691     "       displayed.  To return to the visual image directory, choose",
692     "       Next from the Command widget.  Next and Former moves to the",
693     "       next or former image respectively.  Choose Delete to delete",
694     "       a particular image tile.  Finally, choose Update to",
695     "       synchronize all the image tiles with their respective",
696     "       images.",
697     "",
698     "COMMAND WIDGET",
699     "  The Command widget lists a number of sub-menus and commands.",
700     "  They are",
701     "",
702     "      File",
703     "        Open...",
704     "        Next",
705     "        Former",
706     "        Select...",
707     "        Save...",
708     "        Print...",
709     "        Delete...",
710     "        New...",
711     "        Visual Directory...",
712     "        Quit",
713     "      Edit",
714     "        Undo",
715     "        Redo",
716     "        Cut",
717     "        Copy",
718     "        Paste",
719     "      View",
720     "        Half Size",
721     "        Original Size",
722     "        Double Size",
723     "        Resize...",
724     "        Apply",
725     "        Refresh",
726     "        Restore",
727     "      Transform",
728     "        Crop",
729     "        Chop",
730     "        Flop",
731     "        Flip",
732     "        Rotate Right",
733     "        Rotate Left",
734     "        Rotate...",
735     "        Shear...",
736     "        Roll...",
737     "        Trim Edges",
738     "      Enhance",
739     "        Brightness...",
740     "        Saturation...",
741     "        Hue...",
742     "        Gamma...",
743     "        Sharpen...",
744     "        Dull",
745     "        Contrast Stretch...",
746     "        Sigmoidal Contrast...",
747     "        Normalize",
748     "        Equalize",
749     "        Negate",
750     "        Grayscale",
751     "        Map...",
752     "        Quantize...",
753     "      Effects",
754     "        Despeckle",
755     "        Emboss",
756     "        Reduce Noise",
757     "        Add Noise",
758     "        Sharpen...",
759     "        Blur...",
760     "        Threshold...",
761     "        Edge Detect...",
762     "        Spread...",
763     "        Shade...",
764     "        Painting...",
765     "        Segment...",
766     "      F/X",
767     "        Solarize...",
768     "        Sepia Tone...",
769     "        Swirl...",
770     "        Implode...",
771     "        Vignette...",
772     "        Wave...",
773     "        Oil Painting...",
774     "        Charcoal Drawing...",
775     "      Image Edit",
776     "        Annotate...",
777     "        Draw...",
778     "        Color...",
779     "        Matte...",
780     "        Composite...",
781     "        Add Border...",
782     "        Add Frame...",
783     "        Comment...",
784     "        Launch...",
785     "        Region of Interest...",
786     "      Miscellany",
787     "        Image Info",
788     "        Zoom Image",
789     "        Show Preview...",
790     "        Show Histogram",
791     "        Show Matte",
792     "        Background...",
793     "        Slide Show",
794     "        Preferences...",
795     "      Help",
796     "        Overview",
797     "        Browse Documentation",
798     "        About Display",
799     "",
800     "  Menu items with a indented triangle have a sub-menu.  They",
801     "  are represented above as the indented items.  To access a",
802     "  sub-menu item, move the pointer to the appropriate menu and",
803     "  press a button and drag.  When you find the desired sub-menu",
804     "  item, release the button and the command is executed.  Move",
805     "  the pointer away from the sub-menu if you decide not to",
806     "  execute a particular command.",
807     "",
808     "KEYBOARD ACCELERATORS",
809     "  Accelerators are one or two key presses that effect a",
810     "  particular command.  The keyboard accelerators that",
811     "  display(1) understands is:",
812     "",
813     "  Ctl+O     Press to open an image from a file.",
814     "",
815     "  space     Press to display the next image.",
816     "",
817     "            If the image is a multi-paged document such as a Postscript",
818     "            document, you can skip ahead several pages by preceding",
819     "            this command with a number.  For example to display the",
820     "            third page beyond the current page, press 3<space>.",
821     "",
822     "  backspace Press to display the former image.",
823     "",
824     "            If the image is a multi-paged document such as a Postscript",
825     "            document, you can skip behind several pages by preceding",
826     "            this command with a number.  For example to display the",
827     "            third page preceding the current page, press 3<backspace>.",
828     "",
829     "  Ctl+S     Press to write the image to a file.",
830     "",
831     "  Ctl+P     Press to print the image to a Postscript printer.",
832     "",
833     "  Ctl+D     Press to delete an image file.",
834     "",
835     "  Ctl+N     Press to create a blank canvas.",
836     "",
837     "  Ctl+Q     Press to discard all images and exit program.",
838     "",
839     "  Ctl+Z     Press to undo last image transformation.",
840     "",
841     "  Ctl+R     Press to redo last image transformation.",
842     "",
843     "  Ctl+X     Press to cut a region of the image.",
844     "",
845     "  Ctl+C     Press to copy a region of the image.",
846     "",
847     "  Ctl+V     Press to paste a region to the image.",
848     "",
849     "  <         Press to half the image size.",
850     "",
851     "  -         Press to return to the original image size.",
852     "",
853     "  >         Press to double the image size.",
854     "",
855     "  %         Press to resize the image to a width and height you",
856     "            specify.",
857     "",
858     "Cmd-A       Press to make any image transformations permanent."
859     "",
860     "            By default, any image size transformations are applied",
861     "            to the original image to create the image displayed on",
862     "            the X server.  However, the transformations are not",
863     "            permanent (i.e. the original image does not change",
864     "            size only the X image does).  For example, if you",
865     "            press > the X image will appear to double in size,",
866     "            but the original image will in fact remain the same size.",
867     "            To force the original image to double in size, press >",
868     "            followed by Cmd-A.",
869     "",
870     "  @         Press to refresh the image window.",
871     "",
872     "  C         Press to cut out a rectangular region of the image.",
873     "",
874     "  [         Press to chop the image.",
875     "",
876     "  H         Press to flop image in the horizontal direction.",
877     "",
878     "  V         Press to flip image in the vertical direction.",
879     "",
880     "  /         Press to rotate the image 90 degrees clockwise.",
881     "",
882     " \\         Press to rotate the image 90 degrees counter-clockwise.",
883     "",
884     "  *         Press to rotate the image the number of degrees you",
885     "            specify.",
886     "",
887     "  S         Press to shear the image the number of degrees you",
888     "            specify.",
889     "",
890     "  R         Press to roll the image.",
891     "",
892     "  T         Press to trim the image edges.",
893     "",
894     "  Shft-H    Press to vary the image hue.",
895     "",
896     "  Shft-S    Press to vary the color saturation.",
897     "",
898     "  Shft-L    Press to vary the color brightness.",
899     "",
900     "  Shft-G    Press to gamma correct the image.",
901     "",
902     "  Shft-C    Press to sharpen the image contrast.",
903     "",
904     "  Shft-Z    Press to dull the image contrast.",
905     "",
906     "  =         Press to perform histogram equalization on the image.",
907     "",
908     "  Shft-N    Press to perform histogram normalization on the image.",
909     "",
910     "  Shft-~    Press to negate the colors of the image.",
911     "",
912     "  .         Press to convert the image colors to gray.",
913     "",
914     "  Shft-#    Press to set the maximum number of unique colors in the",
915     "            image.",
916     "",
917     "  F2        Press to reduce the speckles in an image.",
918     "",
919     "  F3        Press to eliminate peak noise from an image.",
920     "",
921     "  F4        Press to add noise to an image.",
922     "",
923     "  F5        Press to sharpen an image.",
924     "",
925     "  F6        Press to delete an image file.",
926     "",
927     "  F7        Press to threshold the image.",
928     "",
929     "  F8        Press to detect edges within an image.",
930     "",
931     "  F9        Press to emboss an image.",
932     "",
933     "  F10       Press to displace pixels by a random amount.",
934     "",
935     "  F11       Press to negate all pixels above the threshold level.",
936     "",
937     "  F12       Press to shade the image using a distant light source.",
938     "",
939     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
940     "",
941     "  F14       Press to segment the image by color.",
942     "",
943     "  Meta-S    Press to swirl image pixels about the center.",
944     "",
945     "  Meta-I    Press to implode image pixels about the center.",
946     "",
947     "  Meta-W    Press to alter an image along a sine wave.",
948     "",
949     "  Meta-P    Press to simulate an oil painting.",
950     "",
951     "  Meta-C    Press to simulate a charcoal drawing.",
952     "",
953     "  Alt-A     Press to annotate the image with text.",
954     "",
955     "  Alt-D     Press to draw on an image.",
956     "",
957     "  Alt-P     Press to edit an image pixel color.",
958     "",
959     "  Alt-M     Press to edit the image matte information.",
960     "",
961     "  Alt-V     Press to composite the image with another.",
962     "",
963     "  Alt-B     Press to add a border to the image.",
964     "",
965     "  Alt-F     Press to add an ornamental border to the image.",
966     "",
967     "  Alt-Shft-!",
968     "            Press to add an image comment.",
969     "",
970     "  Ctl-A     Press to apply image processing techniques to a region",
971     "            of interest.",
972     "",
973     "  Shft-?    Press to display information about the image.",
974     "",
975     "  Shft-+    Press to map the zoom image window.",
976     "",
977     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
978     "",
979     "  F1        Press to display helpful information about display(1).",
980     "",
981     "  Find      Press to browse documentation about ImageMagick.",
982     "",
983     "  1-9       Press to change the level of magnification.",
984     "",
985     "  Use the arrow keys to move the image one pixel up, down,",
986     "  left, or right within the magnify window.  Be sure to first",
987     "  map the magnify window by pressing button 2.",
988     "",
989     "  Press ALT and one of the arrow keys to trim off one pixel",
990     "  from any side of the image.",
991     (char *) NULL,
992   },
993   *ImageMatteEditHelp[] =
994   {
995     "Matte information within an image is useful for some",
996     "operations such as image compositing (See IMAGE",
997     "COMPOSITING).  This extra channel usually defines a mask",
998     "which represents a sort of a cookie-cutter for the image.",
999     "This the case when matte is opaque (full coverage) for",
1000     "pixels inside the shape, zero outside, and between 0 and",
1001     "QuantumRange on the boundary.",
1002     "",
1003     "A small window appears showing the location of the cursor in",
1004     "the image window. You are now in matte edit mode.  To exit",
1005     "immediately, press Dismiss.  In matte edit mode, the Command",
1006     "widget has these options:",
1007     "",
1008     "    Method",
1009     "      point",
1010     "      replace",
1011     "      floodfill",
1012     "      filltoborder",
1013     "      reset",
1014     "    Border Color",
1015     "      black",
1016     "      blue",
1017     "      cyan",
1018     "      green",
1019     "      gray",
1020     "      red",
1021     "      magenta",
1022     "      yellow",
1023     "      white",
1024     "      Browser...",
1025     "    Fuzz",
1026     "      0%",
1027     "      2%",
1028     "      5%",
1029     "      10%",
1030     "      15%",
1031     "      Dialog...",
1032     "    Matte",
1033     "      Opaque",
1034     "      Transparent",
1035     "      Dialog...",
1036     "    Undo",
1037     "    Help",
1038     "    Dismiss",
1039     "",
1040     "Choose a matte editing method from the Method sub-menu of",
1041     "the Command widget.  The point method changes the matte value",
1042     "of any pixel selected with the pointer until the button is",
1043     "is released.  The replace method changes the matte value of",
1044     "any pixel that matches the color of the pixel you select with",
1045     "a button press.  Floodfill changes the matte value of any pixel",
1046     "that matches the color of the pixel you select with a button",
1047     "press and is a neighbor.  Whereas filltoborder changes the matte",
1048     "value any neighbor pixel that is not the border color.  Finally",
1049     "reset changes the entire image to the designated matte value.",
1050     "",
1051     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1052     "select the Dialog entry.  Here a dialog appears requesting a matte",
1053     "value.  The value you select is assigned as the opacity value of the",
1054     "selected pixel or pixels.",
1055     "",
1056     "Now, press any button to select a pixel within the image",
1057     "window to change its matte value.",
1058     "",
1059     "If the Magnify widget is mapped, it can be helpful in positioning",
1060     "your pointer within the image (refer to button 2).",
1061     "",
1062     "Matte information is only valid in a DirectClass image.",
1063     "Therefore, any PseudoClass image is promoted to DirectClass",
1064     "(see miff(5)).  Note that matte information for PseudoClass",
1065     "is not retained for colormapped X server visuals (e.g.",
1066     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1067     "immediately save your image to a file (refer to Write).",
1068     "Correct matte editing behavior may require a TrueColor or",
1069     "DirectColor visual or a Standard Colormap.",
1070     (char *) NULL,
1071   },
1072   *ImagePanHelp[] =
1073   {
1074     "When an image exceeds the width or height of the X server",
1075     "screen, display maps a small panning icon.  The rectangle",
1076     "within the panning icon shows the area that is currently",
1077     "displayed in the image window.  To pan about the image,",
1078     "press any button and drag the pointer within the panning",
1079     "icon.  The pan rectangle moves with the pointer and the",
1080     "image window is updated to reflect the location of the",
1081     "rectangle within the panning icon.  When you have selected",
1082     "the area of the image you wish to view, release the button.",
1083     "",
1084     "Use the arrow keys to pan the image one pixel up, down,",
1085     "left, or right within the image window.",
1086     "",
1087     "The panning icon is withdrawn if the image becomes smaller",
1088     "than the dimensions of the X server screen.",
1089     (char *) NULL,
1090   },
1091   *ImagePasteHelp[] =
1092   {
1093     "A small window appears showing the location of the cursor in",
1094     "the image window. You are now in paste mode.  To exit",
1095     "immediately, press Dismiss.  In paste mode, the Command",
1096     "widget has these options:",
1097     "",
1098     "    Operators",
1099     "      over",
1100     "      in",
1101     "      out",
1102     "      atop",
1103     "      xor",
1104     "      plus",
1105     "      minus",
1106     "      add",
1107     "      subtract",
1108     "      difference",
1109     "      replace",
1110     "    Help",
1111     "    Dismiss",
1112     "",
1113     "Choose a composite operation from the Operators sub-menu of",
1114     "the Command widget.  How each operator behaves is described",
1115     "below.  Image window is the image currently displayed on",
1116     "your X server and image is the image obtained with the File",
1117     "Browser widget.",
1118     "",
1119     "Over     The result is the union of the two image shapes,",
1120     "         with image obscuring image window in the region of",
1121     "         overlap.",
1122     "",
1123     "In       The result is simply image cut by the shape of",
1124     "         image window.  None of the image data of image",
1125     "         window is in the result.",
1126     "",
1127     "Out      The resulting image is image with the shape of",
1128     "         image window cut out.",
1129     "",
1130     "Atop     The result is the same shape as image image window,",
1131     "         with image obscuring image window where the image",
1132     "         shapes overlap.  Note this differs from over",
1133     "         because the portion of image outside image window's",
1134     "         shape does not appear in the result.",
1135     "",
1136     "Xor      The result is the image data from both image and",
1137     "         image window that is outside the overlap region.",
1138     "         The overlap region is blank.",
1139     "",
1140     "Plus     The result is just the sum of the image data.",
1141     "         Output values are cropped to QuantumRange (no overflow).",
1142     "         This operation is independent of the matte",
1143     "         channels.",
1144     "",
1145     "Minus    The result of image - image window, with underflow",
1146     "         cropped to zero.",
1147     "",
1148     "Add      The result of image + image window, with overflow",
1149     "         wrapping around (mod 256).",
1150     "",
1151     "Subtract The result of image - image window, with underflow",
1152     "         wrapping around (mod 256).  The add and subtract",
1153     "         operators can be used to perform reversible",
1154     "         transformations.",
1155     "",
1156     "Difference",
1157     "         The result of abs(image - image window).  This",
1158     "         useful for comparing two very similar images.",
1159     "",
1160     "Copy     The resulting image is image window replaced with",
1161     "         image.  Here the matte information is ignored.",
1162     "",
1163     "CopyRed  The red layer of the image window is replace with",
1164     "         the red layer of the image.  The other layers are",
1165     "         untouched.",
1166     "",
1167     "CopyGreen",
1168     "         The green layer of the image window is replace with",
1169     "         the green layer of the image.  The other layers are",
1170     "         untouched.",
1171     "",
1172     "CopyBlue The blue layer of the image window is replace with",
1173     "         the blue layer of the image.  The other layers are",
1174     "         untouched.",
1175     "",
1176     "CopyOpacity",
1177     "         The matte layer of the image window is replace with",
1178     "         the matte layer of the image.  The other layers are",
1179     "         untouched.",
1180     "",
1181     "The image compositor requires a matte, or alpha channel in",
1182     "the image for some operations.  This extra channel usually",
1183     "defines a mask which represents a sort of a cookie-cutter",
1184     "for the image.  This the case when matte is opaque (full",
1185     "coverage) for pixels inside the shape, zero outside, and",
1186     "between 0 and QuantumRange on the boundary.  If image does not",
1187     "have a matte channel, it is initialized with 0 for any pixel",
1188     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1189     "",
1190     "Note that matte information for image window is not retained",
1191     "for colormapped X server visuals (e.g. StaticColor,",
1192     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1193     "behavior may require a TrueColor or DirectColor visual or a",
1194     "Standard Colormap.",
1195     "",
1196     "Choosing a composite operator is optional.  The default",
1197     "operator is replace.  However, you must choose a location to",
1198     "paste your image and press button 1.  Press and hold the",
1199     "button before releasing and an outline of the image will",
1200     "appear to help you identify your location.",
1201     "",
1202     "The actual colors of the pasted image is saved.  However,",
1203     "the color that appears in image window may be different.",
1204     "For example, on a monochrome screen image window will appear",
1205     "black or white even though your pasted image may have",
1206     "many colors.  If the image is saved to a file it is written",
1207     "with the correct colors.  To assure the correct colors are",
1208     "saved in the final image, any PseudoClass image is promoted",
1209     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1210     "to remain PseudoClass, use -colors.",
1211     (char *) NULL,
1212   },
1213   *ImageROIHelp[] =
1214   {
1215     "In region of interest mode, the Command widget has these",
1216     "options:",
1217     "",
1218     "    Help",
1219     "    Dismiss",
1220     "",
1221     "To define a region of interest, press button 1 and drag.",
1222     "The region of interest is defined by a highlighted rectangle",
1223     "that expands or contracts as it follows the pointer.  Once",
1224     "you are satisfied with the region of interest, release the",
1225     "button.  You are now in apply mode.  In apply mode the",
1226     "Command widget has these options:",
1227     "",
1228     "      File",
1229     "        Save...",
1230     "        Print...",
1231     "      Edit",
1232     "        Undo",
1233     "        Redo",
1234     "      Transform",
1235     "        Flop",
1236     "        Flip",
1237     "        Rotate Right",
1238     "        Rotate Left",
1239     "      Enhance",
1240     "        Hue...",
1241     "        Saturation...",
1242     "        Brightness...",
1243     "        Gamma...",
1244     "        Spiff",
1245     "        Dull",
1246     "        Contrast Stretch",
1247     "        Sigmoidal Contrast...",
1248     "        Normalize",
1249     "        Equalize",
1250     "        Negate",
1251     "        Grayscale",
1252     "        Map...",
1253     "        Quantize...",
1254     "      Effects",
1255     "        Despeckle",
1256     "        Emboss",
1257     "        Reduce Noise",
1258     "        Sharpen...",
1259     "        Blur...",
1260     "        Threshold...",
1261     "        Edge Detect...",
1262     "        Spread...",
1263     "        Shade...",
1264     "        Raise...",
1265     "        Segment...",
1266     "      F/X",
1267     "        Solarize...",
1268     "        Sepia Tone...",
1269     "        Swirl...",
1270     "        Implode...",
1271     "        Vignette...",
1272     "        Wave...",
1273     "        Oil Painting...",
1274     "        Charcoal Drawing...",
1275     "      Miscellany",
1276     "        Image Info",
1277     "        Zoom Image",
1278     "        Show Preview...",
1279     "        Show Histogram",
1280     "        Show Matte",
1281     "      Help",
1282     "      Dismiss",
1283     "",
1284     "You can make adjustments to the region of interest by moving",
1285     "the pointer to one of the rectangle corners, pressing a",
1286     "button, and dragging.  Finally, choose an image processing",
1287     "technique from the Command widget.  You can choose more than",
1288     "one image processing technique to apply to an area.",
1289     "Alternatively, you can move the region of interest before",
1290     "applying another image processing technique.  To exit, press",
1291     "Dismiss.",
1292     (char *) NULL,
1293   },
1294   *ImageRotateHelp[] =
1295   {
1296     "In rotate mode, the Command widget has these options:",
1297     "",
1298     "    Pixel Color",
1299     "      black",
1300     "      blue",
1301     "      cyan",
1302     "      green",
1303     "      gray",
1304     "      red",
1305     "      magenta",
1306     "      yellow",
1307     "      white",
1308     "      Browser...",
1309     "    Direction",
1310     "      horizontal",
1311     "      vertical",
1312     "    Help",
1313     "    Dismiss",
1314     "",
1315     "Choose a background color from the Pixel Color sub-menu.",
1316     "Additional background colors can be specified with the color",
1317     "browser.  You can change the menu colors by setting the X",
1318     "resources pen1 through pen9.",
1319     "",
1320     "If you choose the color browser and press Grab, you can",
1321     "select the background color by moving the pointer to the",
1322     "desired color on the screen and press any button.",
1323     "",
1324     "Choose a point in the image window and press this button and",
1325     "hold.  Next, move the pointer to another location in the",
1326     "image.  As you move a line connects the initial location and",
1327     "the pointer.  When you release the button, the degree of",
1328     "image rotation is determined by the slope of the line you",
1329     "just drew.  The slope is relative to the direction you",
1330     "choose from the Direction sub-menu of the Command widget.",
1331     "",
1332     "To cancel the image rotation, move the pointer back to the",
1333     "starting point of the line and release the button.",
1334     (char *) NULL,
1335   };
1336 \f
1337 /*
1338   Enumeration declarations.
1339 */
1340 typedef enum
1341 {
1342   CopyMode,
1343   CropMode,
1344   CutMode
1345 } ClipboardMode;
1346
1347 typedef enum
1348 {
1349   OpenCommand,
1350   NextCommand,
1351   FormerCommand,
1352   SelectCommand,
1353   SaveCommand,
1354   PrintCommand,
1355   DeleteCommand,
1356   NewCommand,
1357   VisualDirectoryCommand,
1358   QuitCommand,
1359   UndoCommand,
1360   RedoCommand,
1361   CutCommand,
1362   CopyCommand,
1363   PasteCommand,
1364   HalfSizeCommand,
1365   OriginalSizeCommand,
1366   DoubleSizeCommand,
1367   ResizeCommand,
1368   ApplyCommand,
1369   RefreshCommand,
1370   RestoreCommand,
1371   CropCommand,
1372   ChopCommand,
1373   FlopCommand,
1374   FlipCommand,
1375   RotateRightCommand,
1376   RotateLeftCommand,
1377   RotateCommand,
1378   ShearCommand,
1379   RollCommand,
1380   TrimCommand,
1381   HueCommand,
1382   SaturationCommand,
1383   BrightnessCommand,
1384   GammaCommand,
1385   SpiffCommand,
1386   DullCommand,
1387   ContrastStretchCommand,
1388   SigmoidalContrastCommand,
1389   NormalizeCommand,
1390   EqualizeCommand,
1391   NegateCommand,
1392   GrayscaleCommand,
1393   MapCommand,
1394   QuantizeCommand,
1395   DespeckleCommand,
1396   EmbossCommand,
1397   ReduceNoiseCommand,
1398   AddNoiseCommand,
1399   SharpenCommand,
1400   BlurCommand,
1401   ThresholdCommand,
1402   EdgeDetectCommand,
1403   SpreadCommand,
1404   ShadeCommand,
1405   RaiseCommand,
1406   SegmentCommand,
1407   SolarizeCommand,
1408   SepiaToneCommand,
1409   SwirlCommand,
1410   ImplodeCommand,
1411   VignetteCommand,
1412   WaveCommand,
1413   OilPaintCommand,
1414   CharcoalDrawCommand,
1415   AnnotateCommand,
1416   DrawCommand,
1417   ColorCommand,
1418   MatteCommand,
1419   CompositeCommand,
1420   AddBorderCommand,
1421   AddFrameCommand,
1422   CommentCommand,
1423   LaunchCommand,
1424   RegionofInterestCommand,
1425   ROIHelpCommand,
1426   ROIDismissCommand,
1427   InfoCommand,
1428   ZoomCommand,
1429   ShowPreviewCommand,
1430   ShowHistogramCommand,
1431   ShowMatteCommand,
1432   BackgroundCommand,
1433   SlideShowCommand,
1434   PreferencesCommand,
1435   HelpCommand,
1436   BrowseDocumentationCommand,
1437   VersionCommand,
1438   SaveToUndoBufferCommand,
1439   FreeBuffersCommand,
1440   NullCommand
1441 } CommandType;
1442
1443 typedef enum
1444 {
1445   AnnotateNameCommand,
1446   AnnotateFontColorCommand,
1447   AnnotateBackgroundColorCommand,
1448   AnnotateRotateCommand,
1449   AnnotateHelpCommand,
1450   AnnotateDismissCommand,
1451   TextHelpCommand,
1452   TextApplyCommand,
1453   ChopDirectionCommand,
1454   ChopHelpCommand,
1455   ChopDismissCommand,
1456   HorizontalChopCommand,
1457   VerticalChopCommand,
1458   ColorEditMethodCommand,
1459   ColorEditColorCommand,
1460   ColorEditBorderCommand,
1461   ColorEditFuzzCommand,
1462   ColorEditUndoCommand,
1463   ColorEditHelpCommand,
1464   ColorEditDismissCommand,
1465   CompositeOperatorsCommand,
1466   CompositeDissolveCommand,
1467   CompositeDisplaceCommand,
1468   CompositeHelpCommand,
1469   CompositeDismissCommand,
1470   CropHelpCommand,
1471   CropDismissCommand,
1472   RectifyCopyCommand,
1473   RectifyHelpCommand,
1474   RectifyDismissCommand,
1475   DrawElementCommand,
1476   DrawColorCommand,
1477   DrawStippleCommand,
1478   DrawWidthCommand,
1479   DrawUndoCommand,
1480   DrawHelpCommand,
1481   DrawDismissCommand,
1482   MatteEditMethod,
1483   MatteEditBorderCommand,
1484   MatteEditFuzzCommand,
1485   MatteEditValueCommand,
1486   MatteEditUndoCommand,
1487   MatteEditHelpCommand,
1488   MatteEditDismissCommand,
1489   PasteOperatorsCommand,
1490   PasteHelpCommand,
1491   PasteDismissCommand,
1492   RotateColorCommand,
1493   RotateDirectionCommand,
1494   RotateCropCommand,
1495   RotateSharpenCommand,
1496   RotateHelpCommand,
1497   RotateDismissCommand,
1498   HorizontalRotateCommand,
1499   VerticalRotateCommand,
1500   TileLoadCommand,
1501   TileNextCommand,
1502   TileFormerCommand,
1503   TileDeleteCommand,
1504   TileUpdateCommand
1505 } ModeType;
1506 \f
1507 /*
1508   Stipples.
1509 */
1510 #define BricksWidth  20
1511 #define BricksHeight  20
1512 #define DiagonalWidth  16
1513 #define DiagonalHeight  16
1514 #define HighlightWidth  8
1515 #define HighlightHeight  8
1516 #define OpaqueWidth  8
1517 #define OpaqueHeight  8
1518 #define ScalesWidth  16
1519 #define ScalesHeight  16
1520 #define ShadowWidth  8
1521 #define ShadowHeight  8
1522 #define VerticalWidth  16
1523 #define VerticalHeight  16
1524 #define WavyWidth  16
1525 #define WavyHeight  16
1526 \f
1527 /*
1528   Constant declaration.
1529 */
1530 static const int
1531   RoiDelta = 8;
1532
1533 static const unsigned char
1534   BricksBitmap[] =
1535   {
1536     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1537     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1538     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1539     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1540     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1541   },
1542   DiagonalBitmap[] =
1543   {
1544     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1545     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1546     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1547   },
1548   ScalesBitmap[] =
1549   {
1550     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1551     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1552     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1553   },
1554   VerticalBitmap[] =
1555   {
1556     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1557     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1558     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1559   },
1560   WavyBitmap[] =
1561   {
1562     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1563     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1564     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1565   };
1566 \f
1567 /*
1568   Function prototypes.
1569 */
1570 static CommandType
1571   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1572     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1573
1574 static Image
1575   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1576     Image **,ExceptionInfo *),
1577   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1578   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1579     ExceptionInfo *),
1580   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1581     ExceptionInfo *);
1582
1583 static MagickBooleanType
1584   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1585     ExceptionInfo *),
1586   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1587     ExceptionInfo *),
1588   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1589     ExceptionInfo *),
1590   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1591     ExceptionInfo *),
1592   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1593     ExceptionInfo *),
1594   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1595     ExceptionInfo *),
1596   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1597   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1598     ExceptionInfo *),
1599   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1600     ExceptionInfo *),
1601   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1603   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1604     ExceptionInfo *),
1605   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1606   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1607   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1608
1609 static void
1610   XDrawPanRectangle(Display *,XWindows *),
1611   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1612     ExceptionInfo *),
1613   XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1614   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1615   XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1616   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1617     const KeySym,ExceptionInfo *),
1618   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1619   XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1620   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1621 \f
1622 /*
1623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1624 %                                                                             %
1625 %                                                                             %
1626 %                                                                             %
1627 %   D i s p l a y I m a g e s                                                 %
1628 %                                                                             %
1629 %                                                                             %
1630 %                                                                             %
1631 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1632 %
1633 %  DisplayImages() displays an image sequence to any X window screen.  It
1634 %  returns a value other than 0 if successful.  Check the exception member
1635 %  of image to determine the reason for any failure.
1636 %
1637 %  The format of the DisplayImages method is:
1638 %
1639 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1640 %        Image *images,ExceptionInfo *exception)
1641 %
1642 %  A description of each parameter follows:
1643 %
1644 %    o image_info: the image info.
1645 %
1646 %    o image: the image.
1647 %
1648 %    o exception: return any errors or warnings in this structure.
1649 %
1650 */
1651 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1652   Image *images,ExceptionInfo *exception)
1653 {
1654   char
1655     *argv[1];
1656
1657   Display
1658     *display;
1659
1660   Image
1661     *image;
1662
1663   register ssize_t
1664     i;
1665
1666   size_t
1667     state;
1668
1669   XrmDatabase
1670     resource_database;
1671
1672   XResourceInfo
1673     resource_info;
1674
1675   assert(image_info != (const ImageInfo *) NULL);
1676   assert(image_info->signature == MagickSignature);
1677   assert(images != (Image *) NULL);
1678   assert(images->signature == MagickSignature);
1679   if (IfMagickTrue(images->debug) )
1680     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1681   display=XOpenDisplay(image_info->server_name);
1682   if (display == (Display *) NULL)
1683     {
1684       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1685         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1686       return(MagickFalse);
1687     }
1688   if (exception->severity != UndefinedException)
1689     CatchException(exception);
1690   (void) XSetErrorHandler(XError);
1691   resource_database=XGetResourceDatabase(display,GetClientName());
1692   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1693   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1694   if (image_info->page != (char *) NULL)
1695     resource_info.image_geometry=AcquireString(image_info->page);
1696   resource_info.immutable=MagickTrue;
1697   argv[0]=AcquireString(GetClientName());
1698   state=DefaultState;
1699   for (i=0; (state & ExitState) == 0; i++)
1700   {
1701     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1702       break;
1703     image=GetImageFromList(images,i % GetImageListLength(images));
1704     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1705   }
1706   (void) SetErrorHandler((ErrorHandler) NULL);
1707   (void) SetWarningHandler((WarningHandler) NULL);
1708   argv[0]=DestroyString(argv[0]);
1709   (void) XCloseDisplay(display);
1710   XDestroyResourceInfo(&resource_info);
1711   if (exception->severity != UndefinedException)
1712     return(MagickFalse);
1713   return(MagickTrue);
1714 }
1715 \f
1716 /*
1717 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1718 %                                                                             %
1719 %                                                                             %
1720 %                                                                             %
1721 %   R e m o t e D i s p l a y C o m m a n d                                   %
1722 %                                                                             %
1723 %                                                                             %
1724 %                                                                             %
1725 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1726 %
1727 %  RemoteDisplayCommand() encourages a remote display program to display the
1728 %  specified image filename.
1729 %
1730 %  The format of the RemoteDisplayCommand method is:
1731 %
1732 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1733 %        const char *window,const char *filename,ExceptionInfo *exception)
1734 %
1735 %  A description of each parameter follows:
1736 %
1737 %    o image_info: the image info.
1738 %
1739 %    o window: Specifies the name or id of an X window.
1740 %
1741 %    o filename: the name of the image filename to display.
1742 %
1743 %    o exception: return any errors or warnings in this structure.
1744 %
1745 */
1746 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1747   const char *window,const char *filename,ExceptionInfo *exception)
1748 {
1749   Display
1750     *display;
1751
1752   MagickStatusType
1753     status;
1754
1755   assert(image_info != (const ImageInfo *) NULL);
1756   assert(image_info->signature == MagickSignature);
1757   assert(filename != (char *) NULL);
1758   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1759   display=XOpenDisplay(image_info->server_name);
1760   if (display == (Display *) NULL)
1761     {
1762       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1763         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1764       return(MagickFalse);
1765     }
1766   (void) XSetErrorHandler(XError);
1767   status=XRemoteCommand(display,window,filename);
1768   (void) XCloseDisplay(display);
1769   return(IsMagickTrue(status));
1770 }
1771 \f
1772 /*
1773 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1774 %                                                                             %
1775 %                                                                             %
1776 %                                                                             %
1777 +   X A n n o t a t e E d i t I m a g e                                       %
1778 %                                                                             %
1779 %                                                                             %
1780 %                                                                             %
1781 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1782 %
1783 %  XAnnotateEditImage() annotates the image with text.
1784 %
1785 %  The format of the XAnnotateEditImage method is:
1786 %
1787 %      MagickBooleanType XAnnotateEditImage(Display *display,
1788 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1789 %        ExceptionInfo *exception)
1790 %
1791 %  A description of each parameter follows:
1792 %
1793 %    o display: Specifies a connection to an X server;  returned from
1794 %      XOpenDisplay.
1795 %
1796 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1797 %
1798 %    o windows: Specifies a pointer to a XWindows structure.
1799 %
1800 %    o image: the image; returned from ReadImage.
1801 %
1802 */
1803
1804 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1805 {
1806   if (x > y)
1807     return(x);
1808   return(y);
1809 }
1810
1811 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1812 {
1813   if (x < y)
1814     return(x);
1815   return(y);
1816 }
1817
1818 static MagickBooleanType XAnnotateEditImage(Display *display,
1819   XResourceInfo *resource_info,XWindows *windows,Image *image,
1820   ExceptionInfo *exception)
1821 {
1822   static const char
1823     *AnnotateMenu[] =
1824     {
1825       "Font Name",
1826       "Font Color",
1827       "Box Color",
1828       "Rotate Text",
1829       "Help",
1830       "Dismiss",
1831       (char *) NULL
1832     },
1833     *TextMenu[] =
1834     {
1835       "Help",
1836       "Apply",
1837       (char *) NULL
1838     };
1839
1840   static const ModeType
1841     AnnotateCommands[] =
1842     {
1843       AnnotateNameCommand,
1844       AnnotateFontColorCommand,
1845       AnnotateBackgroundColorCommand,
1846       AnnotateRotateCommand,
1847       AnnotateHelpCommand,
1848       AnnotateDismissCommand
1849     },
1850     TextCommands[] =
1851     {
1852       TextHelpCommand,
1853       TextApplyCommand
1854     };
1855
1856   static MagickBooleanType
1857     transparent_box = MagickTrue,
1858     transparent_pen = MagickFalse;
1859
1860   static double
1861     degrees = 0.0;
1862
1863   static unsigned int
1864     box_id = MaxNumberPens-2,
1865     font_id = 0,
1866     pen_id = 0;
1867
1868   char
1869     command[MaxTextExtent],
1870     text[MaxTextExtent];
1871
1872   const char
1873     *ColorMenu[MaxNumberPens+1];
1874
1875   Cursor
1876     cursor;
1877
1878   GC
1879     annotate_context;
1880
1881   int
1882     id,
1883     pen_number,
1884     status,
1885     x,
1886     y;
1887
1888   KeySym
1889     key_symbol;
1890
1891   register char
1892     *p;
1893
1894   register ssize_t
1895     i;
1896
1897   unsigned int
1898     height,
1899     width;
1900
1901   size_t
1902     state;
1903
1904   XAnnotateInfo
1905     *annotate_info,
1906     *previous_info;
1907
1908   XColor
1909     color;
1910
1911   XFontStruct
1912     *font_info;
1913
1914   XEvent
1915     event,
1916     text_event;
1917
1918   /*
1919     Map Command widget.
1920   */
1921   (void) CloneString(&windows->command.name,"Annotate");
1922   windows->command.data=4;
1923   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1924   (void) XMapRaised(display,windows->command.id);
1925   XClientMessage(display,windows->image.id,windows->im_protocols,
1926     windows->im_update_widget,CurrentTime);
1927   /*
1928     Track pointer until button 1 is pressed.
1929   */
1930   XQueryPosition(display,windows->image.id,&x,&y);
1931   (void) XSelectInput(display,windows->image.id,
1932     windows->image.attributes.event_mask | PointerMotionMask);
1933   cursor=XCreateFontCursor(display,XC_left_side);
1934   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1935   state=DefaultState;
1936   do
1937   {
1938     if (IfMagickTrue(windows->info.mapped) )
1939       {
1940         /*
1941           Display pointer position.
1942         */
1943         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1944           x+windows->image.x,y+windows->image.y);
1945         XInfoWidget(display,windows,text);
1946       }
1947     /*
1948       Wait for next event.
1949     */
1950     XScreenEvent(display,windows,&event,exception);
1951     if (event.xany.window == windows->command.id)
1952       {
1953         /*
1954           Select a command from the Command widget.
1955         */
1956         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1957         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1958         if (id < 0)
1959           continue;
1960         switch (AnnotateCommands[id])
1961         {
1962           case AnnotateNameCommand:
1963           {
1964             const char
1965               *FontMenu[MaxNumberFonts];
1966
1967             int
1968               font_number;
1969
1970             /*
1971               Initialize menu selections.
1972             */
1973             for (i=0; i < MaxNumberFonts; i++)
1974               FontMenu[i]=resource_info->font_name[i];
1975             FontMenu[MaxNumberFonts-2]="Browser...";
1976             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1977             /*
1978               Select a font name from the pop-up menu.
1979             */
1980             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1981               (const char **) FontMenu,command);
1982             if (font_number < 0)
1983               break;
1984             if (font_number == (MaxNumberFonts-2))
1985               {
1986                 static char
1987                   font_name[MaxTextExtent] = "fixed";
1988
1989                 /*
1990                   Select a font name from a browser.
1991                 */
1992                 resource_info->font_name[font_number]=font_name;
1993                 XFontBrowserWidget(display,windows,"Select",font_name);
1994                 if (*font_name == '\0')
1995                   break;
1996               }
1997             /*
1998               Initialize font info.
1999             */
2000             font_info=XLoadQueryFont(display,resource_info->font_name[
2001               font_number]);
2002             if (font_info == (XFontStruct *) NULL)
2003               {
2004                 XNoticeWidget(display,windows,"Unable to load font:",
2005                   resource_info->font_name[font_number]);
2006                 break;
2007               }
2008             font_id=(unsigned int) font_number;
2009             (void) XFreeFont(display,font_info);
2010             break;
2011           }
2012           case AnnotateFontColorCommand:
2013           {
2014             /*
2015               Initialize menu selections.
2016             */
2017             for (i=0; i < (int) (MaxNumberPens-2); i++)
2018               ColorMenu[i]=resource_info->pen_colors[i];
2019             ColorMenu[MaxNumberPens-2]="transparent";
2020             ColorMenu[MaxNumberPens-1]="Browser...";
2021             ColorMenu[MaxNumberPens]=(const char *) NULL;
2022             /*
2023               Select a pen color from the pop-up menu.
2024             */
2025             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2026               (const char **) ColorMenu,command);
2027             if (pen_number < 0)
2028               break;
2029             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2030               MagickFalse;
2031             if (IfMagickTrue(transparent_pen) )
2032               break;
2033             if (pen_number == (MaxNumberPens-1))
2034               {
2035                 static char
2036                   color_name[MaxTextExtent] = "gray";
2037
2038                 /*
2039                   Select a pen color from a dialog.
2040                 */
2041                 resource_info->pen_colors[pen_number]=color_name;
2042                 XColorBrowserWidget(display,windows,"Select",color_name);
2043                 if (*color_name == '\0')
2044                   break;
2045               }
2046             /*
2047               Set pen color.
2048             */
2049             (void) XParseColor(display,windows->map_info->colormap,
2050               resource_info->pen_colors[pen_number],&color);
2051             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2052               (unsigned int) MaxColors,&color);
2053             windows->pixel_info->pen_colors[pen_number]=color;
2054             pen_id=(unsigned int) pen_number;
2055             break;
2056           }
2057           case AnnotateBackgroundColorCommand:
2058           {
2059             /*
2060               Initialize menu selections.
2061             */
2062             for (i=0; i < (int) (MaxNumberPens-2); i++)
2063               ColorMenu[i]=resource_info->pen_colors[i];
2064             ColorMenu[MaxNumberPens-2]="transparent";
2065             ColorMenu[MaxNumberPens-1]="Browser...";
2066             ColorMenu[MaxNumberPens]=(const char *) NULL;
2067             /*
2068               Select a pen color from the pop-up menu.
2069             */
2070             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2071               (const char **) ColorMenu,command);
2072             if (pen_number < 0)
2073               break;
2074             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2075               MagickFalse;
2076             if (IfMagickTrue(transparent_box) )
2077               break;
2078             if (pen_number == (MaxNumberPens-1))
2079               {
2080                 static char
2081                   color_name[MaxTextExtent] = "gray";
2082
2083                 /*
2084                   Select a pen color from a dialog.
2085                 */
2086                 resource_info->pen_colors[pen_number]=color_name;
2087                 XColorBrowserWidget(display,windows,"Select",color_name);
2088                 if (*color_name == '\0')
2089                   break;
2090               }
2091             /*
2092               Set pen color.
2093             */
2094             (void) XParseColor(display,windows->map_info->colormap,
2095               resource_info->pen_colors[pen_number],&color);
2096             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2097               (unsigned int) MaxColors,&color);
2098             windows->pixel_info->pen_colors[pen_number]=color;
2099             box_id=(unsigned int) pen_number;
2100             break;
2101           }
2102           case AnnotateRotateCommand:
2103           {
2104             int
2105               entry;
2106
2107             static char
2108               angle[MaxTextExtent] = "30.0";
2109
2110             static const char
2111               *RotateMenu[] =
2112               {
2113                 "-90",
2114                 "-45",
2115                 "-30",
2116                 "0",
2117                 "30",
2118                 "45",
2119                 "90",
2120                 "180",
2121                 "Dialog...",
2122                 (char *) NULL,
2123               };
2124
2125             /*
2126               Select a command from the pop-up menu.
2127             */
2128             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2129               command);
2130             if (entry < 0)
2131               break;
2132             if (entry != 8)
2133               {
2134                 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2135                 break;
2136               }
2137             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2138               angle);
2139             if (*angle == '\0')
2140               break;
2141             degrees=StringToDouble(angle,(char **) NULL);
2142             break;
2143           }
2144           case AnnotateHelpCommand:
2145           {
2146             XTextViewWidget(display,resource_info,windows,MagickFalse,
2147               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2148             break;
2149           }
2150           case AnnotateDismissCommand:
2151           {
2152             /*
2153               Prematurely exit.
2154             */
2155             state|=EscapeState;
2156             state|=ExitState;
2157             break;
2158           }
2159           default:
2160             break;
2161         }
2162         continue;
2163       }
2164     switch (event.type)
2165     {
2166       case ButtonPress:
2167       {
2168         if (event.xbutton.button != Button1)
2169           break;
2170         if (event.xbutton.window != windows->image.id)
2171           break;
2172         /*
2173           Change to text entering mode.
2174         */
2175         x=event.xbutton.x;
2176         y=event.xbutton.y;
2177         state|=ExitState;
2178         break;
2179       }
2180       case ButtonRelease:
2181         break;
2182       case Expose:
2183         break;
2184       case KeyPress:
2185       {
2186         if (event.xkey.window != windows->image.id)
2187           break;
2188         /*
2189           Respond to a user key press.
2190         */
2191         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2192           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2193         switch ((int) key_symbol)
2194         {
2195           case XK_Escape:
2196           case XK_F20:
2197           {
2198             /*
2199               Prematurely exit.
2200             */
2201             state|=EscapeState;
2202             state|=ExitState;
2203             break;
2204           }
2205           case XK_F1:
2206           case XK_Help:
2207           {
2208             XTextViewWidget(display,resource_info,windows,MagickFalse,
2209               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2210             break;
2211           }
2212           default:
2213           {
2214             (void) XBell(display,0);
2215             break;
2216           }
2217         }
2218         break;
2219       }
2220       case MotionNotify:
2221       {
2222         /*
2223           Map and unmap Info widget as cursor crosses its boundaries.
2224         */
2225         x=event.xmotion.x;
2226         y=event.xmotion.y;
2227         if (IfMagickTrue(windows->info.mapped) )
2228           {
2229             if ((x < (int) (windows->info.x+windows->info.width)) &&
2230                 (y < (int) (windows->info.y+windows->info.height)))
2231               (void) XWithdrawWindow(display,windows->info.id,
2232                 windows->info.screen);
2233           }
2234         else
2235           if ((x > (int) (windows->info.x+windows->info.width)) ||
2236               (y > (int) (windows->info.y+windows->info.height)))
2237             (void) XMapWindow(display,windows->info.id);
2238         break;
2239       }
2240       default:
2241         break;
2242     }
2243   } while ((state & ExitState) == 0);
2244   (void) XSelectInput(display,windows->image.id,
2245     windows->image.attributes.event_mask);
2246   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2247   if ((state & EscapeState) != 0)
2248     return(MagickTrue);
2249   /*
2250     Set font info and check boundary conditions.
2251   */
2252   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2253   if (font_info == (XFontStruct *) NULL)
2254     {
2255       XNoticeWidget(display,windows,"Unable to load font:",
2256         resource_info->font_name[font_id]);
2257       font_info=windows->font_info;
2258     }
2259   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2260     x=(int) windows->image.width-font_info->max_bounds.width;
2261   if (y < (int) (font_info->ascent+font_info->descent))
2262     y=(int) font_info->ascent+font_info->descent;
2263   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2264       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2265     return(MagickFalse);
2266   /*
2267     Initialize annotate structure.
2268   */
2269   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2270   if (annotate_info == (XAnnotateInfo *) NULL)
2271     return(MagickFalse);
2272   XGetAnnotateInfo(annotate_info);
2273   annotate_info->x=x;
2274   annotate_info->y=y;
2275   if (IfMagickFalse(transparent_box) && IfMagickFalse(transparent_pen))
2276     annotate_info->stencil=OpaqueStencil;
2277   else
2278     if (IfMagickFalse(transparent_box) )
2279       annotate_info->stencil=BackgroundStencil;
2280     else
2281       annotate_info->stencil=ForegroundStencil;
2282   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2283   annotate_info->degrees=degrees;
2284   annotate_info->font_info=font_info;
2285   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2286     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2287     sizeof(*annotate_info->text));
2288   if (annotate_info->text == (char *) NULL)
2289     return(MagickFalse);
2290   /*
2291     Create cursor and set graphic context.
2292   */
2293   cursor=XCreateFontCursor(display,XC_pencil);
2294   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2295   annotate_context=windows->image.annotate_context;
2296   (void) XSetFont(display,annotate_context,font_info->fid);
2297   (void) XSetBackground(display,annotate_context,
2298     windows->pixel_info->pen_colors[box_id].pixel);
2299   (void) XSetForeground(display,annotate_context,
2300     windows->pixel_info->pen_colors[pen_id].pixel);
2301   /*
2302     Begin annotating the image with text.
2303   */
2304   (void) CloneString(&windows->command.name,"Text");
2305   windows->command.data=0;
2306   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2307   state=DefaultState;
2308   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2309   text_event.xexpose.width=(int) font_info->max_bounds.width;
2310   text_event.xexpose.height=font_info->max_bounds.ascent+
2311     font_info->max_bounds.descent;
2312   p=annotate_info->text;
2313   do
2314   {
2315     /*
2316       Display text cursor.
2317     */
2318     *p='\0';
2319     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2320     /*
2321       Wait for next event.
2322     */
2323     XScreenEvent(display,windows,&event,exception);
2324     if (event.xany.window == windows->command.id)
2325       {
2326         /*
2327           Select a command from the Command widget.
2328         */
2329         (void) XSetBackground(display,annotate_context,
2330           windows->pixel_info->background_color.pixel);
2331         (void) XSetForeground(display,annotate_context,
2332           windows->pixel_info->foreground_color.pixel);
2333         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2334         (void) XSetBackground(display,annotate_context,
2335           windows->pixel_info->pen_colors[box_id].pixel);
2336         (void) XSetForeground(display,annotate_context,
2337           windows->pixel_info->pen_colors[pen_id].pixel);
2338         if (id < 0)
2339           continue;
2340         switch (TextCommands[id])
2341         {
2342           case TextHelpCommand:
2343           {
2344             XTextViewWidget(display,resource_info,windows,MagickFalse,
2345               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2346             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2347             break;
2348           }
2349           case TextApplyCommand:
2350           {
2351             /*
2352               Finished annotating.
2353             */
2354             annotate_info->width=(unsigned int) XTextWidth(font_info,
2355               annotate_info->text,(int) strlen(annotate_info->text));
2356             XRefreshWindow(display,&windows->image,&text_event);
2357             state|=ExitState;
2358             break;
2359           }
2360           default:
2361             break;
2362         }
2363         continue;
2364       }
2365     /*
2366       Erase text cursor.
2367     */
2368     text_event.xexpose.x=x;
2369     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2370     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2371       (unsigned int) text_event.xexpose.width,(unsigned int)
2372       text_event.xexpose.height,MagickFalse);
2373     XRefreshWindow(display,&windows->image,&text_event);
2374     switch (event.type)
2375     {
2376       case ButtonPress:
2377       {
2378         if (event.xbutton.window != windows->image.id)
2379           break;
2380         if (event.xbutton.button == Button2)
2381           {
2382             /*
2383               Request primary selection.
2384             */
2385             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2386               windows->image.id,CurrentTime);
2387             break;
2388           }
2389         break;
2390       }
2391       case Expose:
2392       {
2393         if (event.xexpose.count == 0)
2394           {
2395             XAnnotateInfo
2396               *text_info;
2397
2398             /*
2399               Refresh Image window.
2400             */
2401             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2402             text_info=annotate_info;
2403             while (text_info != (XAnnotateInfo *) NULL)
2404             {
2405               if (annotate_info->stencil == ForegroundStencil)
2406                 (void) XDrawString(display,windows->image.id,annotate_context,
2407                   text_info->x,text_info->y,text_info->text,
2408                   (int) strlen(text_info->text));
2409               else
2410                 (void) XDrawImageString(display,windows->image.id,
2411                   annotate_context,text_info->x,text_info->y,text_info->text,
2412                   (int) strlen(text_info->text));
2413               text_info=text_info->previous;
2414             }
2415             (void) XDrawString(display,windows->image.id,annotate_context,
2416               x,y,"_",1);
2417           }
2418         break;
2419       }
2420       case KeyPress:
2421       {
2422         int
2423           length;
2424
2425         if (event.xkey.window != windows->image.id)
2426           break;
2427         /*
2428           Respond to a user key press.
2429         */
2430         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2431           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2432         *(command+length)='\0';
2433         if (((event.xkey.state & ControlMask) != 0) ||
2434             ((event.xkey.state & Mod1Mask) != 0))
2435           state|=ModifierState;
2436         if ((state & ModifierState) != 0)
2437           switch ((int) key_symbol)
2438           {
2439             case XK_u:
2440             case XK_U:
2441             {
2442               key_symbol=DeleteCommand;
2443               break;
2444             }
2445             default:
2446               break;
2447           }
2448         switch ((int) key_symbol)
2449         {
2450           case XK_BackSpace:
2451           {
2452             /*
2453               Erase one character.
2454             */
2455             if (p == annotate_info->text)
2456               {
2457                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2458                   break;
2459                 else
2460                   {
2461                     /*
2462                       Go to end of the previous line of text.
2463                     */
2464                     annotate_info=annotate_info->previous;
2465                     p=annotate_info->text;
2466                     x=annotate_info->x+annotate_info->width;
2467                     y=annotate_info->y;
2468                     if (annotate_info->width != 0)
2469                       p+=strlen(annotate_info->text);
2470                     break;
2471                   }
2472               }
2473             p--;
2474             x-=XTextWidth(font_info,p,1);
2475             text_event.xexpose.x=x;
2476             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2477             XRefreshWindow(display,&windows->image,&text_event);
2478             break;
2479           }
2480           case XK_bracketleft:
2481           {
2482             key_symbol=XK_Escape;
2483             break;
2484           }
2485           case DeleteCommand:
2486           {
2487             /*
2488               Erase the entire line of text.
2489             */
2490             while (p != annotate_info->text)
2491             {
2492               p--;
2493               x-=XTextWidth(font_info,p,1);
2494               text_event.xexpose.x=x;
2495               XRefreshWindow(display,&windows->image,&text_event);
2496             }
2497             break;
2498           }
2499           case XK_Escape:
2500           case XK_F20:
2501           {
2502             /*
2503               Finished annotating.
2504             */
2505             annotate_info->width=(unsigned int) XTextWidth(font_info,
2506               annotate_info->text,(int) strlen(annotate_info->text));
2507             XRefreshWindow(display,&windows->image,&text_event);
2508             state|=ExitState;
2509             break;
2510           }
2511           default:
2512           {
2513             /*
2514               Draw a single character on the Image window.
2515             */
2516             if ((state & ModifierState) != 0)
2517               break;
2518             if (*command == '\0')
2519               break;
2520             *p=(*command);
2521             if (annotate_info->stencil == ForegroundStencil)
2522               (void) XDrawString(display,windows->image.id,annotate_context,
2523                 x,y,p,1);
2524             else
2525               (void) XDrawImageString(display,windows->image.id,
2526                 annotate_context,x,y,p,1);
2527             x+=XTextWidth(font_info,p,1);
2528             p++;
2529             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2530               break;
2531           }
2532           case XK_Return:
2533           case XK_KP_Enter:
2534           {
2535             /*
2536               Advance to the next line of text.
2537             */
2538             *p='\0';
2539             annotate_info->width=(unsigned int) XTextWidth(font_info,
2540               annotate_info->text,(int) strlen(annotate_info->text));
2541             if (annotate_info->next != (XAnnotateInfo *) NULL)
2542               {
2543                 /*
2544                   Line of text already exists.
2545                 */
2546                 annotate_info=annotate_info->next;
2547                 x=annotate_info->x;
2548                 y=annotate_info->y;
2549                 p=annotate_info->text;
2550                 break;
2551               }
2552             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2553               sizeof(*annotate_info->next));
2554             if (annotate_info->next == (XAnnotateInfo *) NULL)
2555               return(MagickFalse);
2556             *annotate_info->next=(*annotate_info);
2557             annotate_info->next->previous=annotate_info;
2558             annotate_info=annotate_info->next;
2559             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2560               windows->image.width/MagickMax((ssize_t)
2561               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2562             if (annotate_info->text == (char *) NULL)
2563               return(MagickFalse);
2564             annotate_info->y+=annotate_info->height;
2565             if (annotate_info->y > (int) windows->image.height)
2566               annotate_info->y=(int) annotate_info->height;
2567             annotate_info->next=(XAnnotateInfo *) NULL;
2568             x=annotate_info->x;
2569             y=annotate_info->y;
2570             p=annotate_info->text;
2571             break;
2572           }
2573         }
2574         break;
2575       }
2576       case KeyRelease:
2577       {
2578         /*
2579           Respond to a user key release.
2580         */
2581         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2582           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2583         state&=(~ModifierState);
2584         break;
2585       }
2586       case SelectionNotify:
2587       {
2588         Atom
2589           type;
2590
2591         int
2592           format;
2593
2594         unsigned char
2595           *data;
2596
2597         unsigned long
2598           after,
2599           length;
2600
2601         /*
2602           Obtain response from primary selection.
2603         */
2604         if (event.xselection.property == (Atom) None)
2605           break;
2606         status=XGetWindowProperty(display,event.xselection.requestor,
2607           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2608           &type,&format,&length,&after,&data);
2609         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2610             (length == 0))
2611           break;
2612         /*
2613           Annotate Image window with primary selection.
2614         */
2615         for (i=0; i < (ssize_t) length; i++)
2616         {
2617           if ((char) data[i] != '\n')
2618             {
2619               /*
2620                 Draw a single character on the Image window.
2621               */
2622               *p=(char) data[i];
2623               (void) XDrawString(display,windows->image.id,annotate_context,
2624                 x,y,p,1);
2625               x+=XTextWidth(font_info,p,1);
2626               p++;
2627               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2628                 continue;
2629             }
2630           /*
2631             Advance to the next line of text.
2632           */
2633           *p='\0';
2634           annotate_info->width=(unsigned int) XTextWidth(font_info,
2635             annotate_info->text,(int) strlen(annotate_info->text));
2636           if (annotate_info->next != (XAnnotateInfo *) NULL)
2637             {
2638               /*
2639                 Line of text already exists.
2640               */
2641               annotate_info=annotate_info->next;
2642               x=annotate_info->x;
2643               y=annotate_info->y;
2644               p=annotate_info->text;
2645               continue;
2646             }
2647           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2648             sizeof(*annotate_info->next));
2649           if (annotate_info->next == (XAnnotateInfo *) NULL)
2650             return(MagickFalse);
2651           *annotate_info->next=(*annotate_info);
2652           annotate_info->next->previous=annotate_info;
2653           annotate_info=annotate_info->next;
2654           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2655             windows->image.width/MagickMax((ssize_t)
2656             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2657           if (annotate_info->text == (char *) NULL)
2658             return(MagickFalse);
2659           annotate_info->y+=annotate_info->height;
2660           if (annotate_info->y > (int) windows->image.height)
2661             annotate_info->y=(int) annotate_info->height;
2662           annotate_info->next=(XAnnotateInfo *) NULL;
2663           x=annotate_info->x;
2664           y=annotate_info->y;
2665           p=annotate_info->text;
2666         }
2667         (void) XFree((void *) data);
2668         break;
2669       }
2670       default:
2671         break;
2672     }
2673   } while ((state & ExitState) == 0);
2674   (void) XFreeCursor(display,cursor);
2675   /*
2676     Annotation is relative to image configuration.
2677   */
2678   width=(unsigned int) image->columns;
2679   height=(unsigned int) image->rows;
2680   x=0;
2681   y=0;
2682   if (windows->image.crop_geometry != (char *) NULL)
2683     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2684   /*
2685     Initialize annotated image.
2686   */
2687   XSetCursorState(display,windows,MagickTrue);
2688   XCheckRefreshWindows(display,windows);
2689   while (annotate_info != (XAnnotateInfo *) NULL)
2690   {
2691     if (annotate_info->width == 0)
2692       {
2693         /*
2694           No text on this line--  go to the next line of text.
2695         */
2696         previous_info=annotate_info->previous;
2697         annotate_info->text=(char *)
2698           RelinquishMagickMemory(annotate_info->text);
2699         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2700         annotate_info=previous_info;
2701         continue;
2702       }
2703     /*
2704       Determine pixel index for box and pen color.
2705     */
2706     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2707     if (windows->pixel_info->colors != 0)
2708       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2709         if (windows->pixel_info->pixels[i] ==
2710             windows->pixel_info->pen_colors[box_id].pixel)
2711           {
2712             windows->pixel_info->box_index=(unsigned short) i;
2713             break;
2714           }
2715     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2716     if (windows->pixel_info->colors != 0)
2717       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2718         if (windows->pixel_info->pixels[i] ==
2719             windows->pixel_info->pen_colors[pen_id].pixel)
2720           {
2721             windows->pixel_info->pen_index=(unsigned short) i;
2722             break;
2723           }
2724     /*
2725       Define the annotate geometry string.
2726     */
2727     annotate_info->x=(int)
2728       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2729     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2730       windows->image.y)/windows->image.ximage->height;
2731     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2732       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2733       height*annotate_info->height/windows->image.ximage->height,
2734       annotate_info->x+x,annotate_info->y+y);
2735     /*
2736       Annotate image with text.
2737     */
2738     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2739       exception);
2740     if (status == 0)
2741       return(MagickFalse);
2742     /*
2743       Free up memory.
2744     */
2745     previous_info=annotate_info->previous;
2746     annotate_info->text=DestroyString(annotate_info->text);
2747     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2748     annotate_info=previous_info;
2749   }
2750   (void) XSetForeground(display,annotate_context,
2751     windows->pixel_info->foreground_color.pixel);
2752   (void) XSetBackground(display,annotate_context,
2753     windows->pixel_info->background_color.pixel);
2754   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2755   XSetCursorState(display,windows,MagickFalse);
2756   (void) XFreeFont(display,font_info);
2757   /*
2758     Update image configuration.
2759   */
2760   XConfigureImageColormap(display,resource_info,windows,image,exception);
2761   (void) XConfigureImage(display,resource_info,windows,image,exception);
2762   return(MagickTrue);
2763 }
2764 \f
2765 /*
2766 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2767 %                                                                             %
2768 %                                                                             %
2769 %                                                                             %
2770 +   X B a c k g r o u n d I m a g e                                           %
2771 %                                                                             %
2772 %                                                                             %
2773 %                                                                             %
2774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2775 %
2776 %  XBackgroundImage() displays the image in the background of a window.
2777 %
2778 %  The format of the XBackgroundImage method is:
2779 %
2780 %      MagickBooleanType XBackgroundImage(Display *display,
2781 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2782 %        ExceptionInfo *exception)
2783 %
2784 %  A description of each parameter follows:
2785 %
2786 %    o display: Specifies a connection to an X server; returned from
2787 %      XOpenDisplay.
2788 %
2789 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2790 %
2791 %    o windows: Specifies a pointer to a XWindows structure.
2792 %
2793 %    o image: the image.
2794 %
2795 %    o exception: return any errors or warnings in this structure.
2796 %
2797 */
2798 static MagickBooleanType XBackgroundImage(Display *display,
2799   XResourceInfo *resource_info,XWindows *windows,Image **image,
2800   ExceptionInfo *exception)
2801 {
2802 #define BackgroundImageTag  "Background/Image"
2803
2804   int
2805     status;
2806
2807   static char
2808     window_id[MaxTextExtent] = "root";
2809
2810   XResourceInfo
2811     background_resources;
2812
2813   /*
2814     Put image in background.
2815   */
2816   status=XDialogWidget(display,windows,"Background",
2817     "Enter window id (id 0x00 selects window with pointer):",window_id);
2818   if (*window_id == '\0')
2819     return(MagickFalse);
2820   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2821     exception);
2822   XInfoWidget(display,windows,BackgroundImageTag);
2823   XSetCursorState(display,windows,MagickTrue);
2824   XCheckRefreshWindows(display,windows);
2825   background_resources=(*resource_info);
2826   background_resources.window_id=window_id;
2827   background_resources.backdrop=IsMagickTrue(status);
2828   status=XDisplayBackgroundImage(display,&background_resources,*image,
2829     exception);
2830   if (IfMagickTrue(status))
2831     XClientMessage(display,windows->image.id,windows->im_protocols,
2832       windows->im_retain_colors,CurrentTime);
2833   XSetCursorState(display,windows,MagickFalse);
2834   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2835     exception);
2836   return(MagickTrue);
2837 }
2838 \f
2839 /*
2840 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2841 %                                                                             %
2842 %                                                                             %
2843 %                                                                             %
2844 +   X C h o p I m a g e                                                       %
2845 %                                                                             %
2846 %                                                                             %
2847 %                                                                             %
2848 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2849 %
2850 %  XChopImage() chops the X image.
2851 %
2852 %  The format of the XChopImage method is:
2853 %
2854 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2855 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2856 %
2857 %  A description of each parameter follows:
2858 %
2859 %    o display: Specifies a connection to an X server; returned from
2860 %      XOpenDisplay.
2861 %
2862 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2863 %
2864 %    o windows: Specifies a pointer to a XWindows structure.
2865 %
2866 %    o image: the image.
2867 %
2868 %    o exception: return any errors or warnings in this structure.
2869 %
2870 */
2871 static MagickBooleanType XChopImage(Display *display,
2872   XResourceInfo *resource_info,XWindows *windows,Image **image,
2873   ExceptionInfo *exception)
2874 {
2875   static const char
2876     *ChopMenu[] =
2877     {
2878       "Direction",
2879       "Help",
2880       "Dismiss",
2881       (char *) NULL
2882     };
2883
2884   static ModeType
2885     direction = HorizontalChopCommand;
2886
2887   static const ModeType
2888     ChopCommands[] =
2889     {
2890       ChopDirectionCommand,
2891       ChopHelpCommand,
2892       ChopDismissCommand
2893     },
2894     DirectionCommands[] =
2895     {
2896       HorizontalChopCommand,
2897       VerticalChopCommand
2898     };
2899
2900   char
2901     text[MaxTextExtent];
2902
2903   Image
2904     *chop_image;
2905
2906   int
2907     id,
2908     x,
2909     y;
2910
2911   double
2912     scale_factor;
2913
2914   RectangleInfo
2915     chop_info;
2916
2917   unsigned int
2918     distance,
2919     height,
2920     width;
2921
2922   size_t
2923     state;
2924
2925   XEvent
2926     event;
2927
2928   XSegment
2929     segment_info;
2930
2931   /*
2932     Map Command widget.
2933   */
2934   (void) CloneString(&windows->command.name,"Chop");
2935   windows->command.data=1;
2936   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2937   (void) XMapRaised(display,windows->command.id);
2938   XClientMessage(display,windows->image.id,windows->im_protocols,
2939     windows->im_update_widget,CurrentTime);
2940   /*
2941     Track pointer until button 1 is pressed.
2942   */
2943   XQueryPosition(display,windows->image.id,&x,&y);
2944   (void) XSelectInput(display,windows->image.id,
2945     windows->image.attributes.event_mask | PointerMotionMask);
2946   state=DefaultState;
2947   do
2948   {
2949     if (IfMagickTrue(windows->info.mapped) )
2950       {
2951         /*
2952           Display pointer position.
2953         */
2954         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2955           x+windows->image.x,y+windows->image.y);
2956         XInfoWidget(display,windows,text);
2957       }
2958     /*
2959       Wait for next event.
2960     */
2961     XScreenEvent(display,windows,&event,exception);
2962     if (event.xany.window == windows->command.id)
2963       {
2964         /*
2965           Select a command from the Command widget.
2966         */
2967         id=XCommandWidget(display,windows,ChopMenu,&event);
2968         if (id < 0)
2969           continue;
2970         switch (ChopCommands[id])
2971         {
2972           case ChopDirectionCommand:
2973           {
2974             char
2975               command[MaxTextExtent];
2976
2977             static const char
2978               *Directions[] =
2979               {
2980                 "horizontal",
2981                 "vertical",
2982                 (char *) NULL,
2983               };
2984
2985             /*
2986               Select a command from the pop-up menu.
2987             */
2988             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2989             if (id >= 0)
2990               direction=DirectionCommands[id];
2991             break;
2992           }
2993           case ChopHelpCommand:
2994           {
2995             XTextViewWidget(display,resource_info,windows,MagickFalse,
2996               "Help Viewer - Image Chop",ImageChopHelp);
2997             break;
2998           }
2999           case ChopDismissCommand:
3000           {
3001             /*
3002               Prematurely exit.
3003             */
3004             state|=EscapeState;
3005             state|=ExitState;
3006             break;
3007           }
3008           default:
3009             break;
3010         }
3011         continue;
3012       }
3013     switch (event.type)
3014     {
3015       case ButtonPress:
3016       {
3017         if (event.xbutton.button != Button1)
3018           break;
3019         if (event.xbutton.window != windows->image.id)
3020           break;
3021         /*
3022           User has committed to start point of chopping line.
3023         */
3024         segment_info.x1=(short int) event.xbutton.x;
3025         segment_info.x2=(short int) event.xbutton.x;
3026         segment_info.y1=(short int) event.xbutton.y;
3027         segment_info.y2=(short int) event.xbutton.y;
3028         state|=ExitState;
3029         break;
3030       }
3031       case ButtonRelease:
3032         break;
3033       case Expose:
3034         break;
3035       case KeyPress:
3036       {
3037         char
3038           command[MaxTextExtent];
3039
3040         KeySym
3041           key_symbol;
3042
3043         if (event.xkey.window != windows->image.id)
3044           break;
3045         /*
3046           Respond to a user key press.
3047         */
3048         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3049           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3050         switch ((int) key_symbol)
3051         {
3052           case XK_Escape:
3053           case XK_F20:
3054           {
3055             /*
3056               Prematurely exit.
3057             */
3058             state|=EscapeState;
3059             state|=ExitState;
3060             break;
3061           }
3062           case XK_F1:
3063           case XK_Help:
3064           {
3065             (void) XSetFunction(display,windows->image.highlight_context,
3066               GXcopy);
3067             XTextViewWidget(display,resource_info,windows,MagickFalse,
3068               "Help Viewer - Image Chop",ImageChopHelp);
3069             (void) XSetFunction(display,windows->image.highlight_context,
3070               GXinvert);
3071             break;
3072           }
3073           default:
3074           {
3075             (void) XBell(display,0);
3076             break;
3077           }
3078         }
3079         break;
3080       }
3081       case MotionNotify:
3082       {
3083         /*
3084           Map and unmap Info widget as text cursor crosses its boundaries.
3085         */
3086         x=event.xmotion.x;
3087         y=event.xmotion.y;
3088         if (IfMagickTrue(windows->info.mapped) )
3089           {
3090             if ((x < (int) (windows->info.x+windows->info.width)) &&
3091                 (y < (int) (windows->info.y+windows->info.height)))
3092               (void) XWithdrawWindow(display,windows->info.id,
3093                 windows->info.screen);
3094           }
3095         else
3096           if ((x > (int) (windows->info.x+windows->info.width)) ||
3097               (y > (int) (windows->info.y+windows->info.height)))
3098             (void) XMapWindow(display,windows->info.id);
3099       }
3100     }
3101   } while ((state & ExitState) == 0);
3102   (void) XSelectInput(display,windows->image.id,
3103     windows->image.attributes.event_mask);
3104   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3105   if ((state & EscapeState) != 0)
3106     return(MagickTrue);
3107   /*
3108     Draw line as pointer moves until the mouse button is released.
3109   */
3110   chop_info.width=0;
3111   chop_info.height=0;
3112   chop_info.x=0;
3113   chop_info.y=0;
3114   distance=0;
3115   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3116   state=DefaultState;
3117   do
3118   {
3119     if (distance > 9)
3120       {
3121         /*
3122           Display info and draw chopping line.
3123         */
3124         if (IfMagickFalse(windows->info.mapped) )
3125           (void) XMapWindow(display,windows->info.id);
3126         (void) FormatLocaleString(text,MaxTextExtent,
3127           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3128           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3129         XInfoWidget(display,windows,text);
3130         XHighlightLine(display,windows->image.id,
3131           windows->image.highlight_context,&segment_info);
3132       }
3133     else
3134       if (IfMagickTrue(windows->info.mapped) )
3135         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3136     /*
3137       Wait for next event.
3138     */
3139     XScreenEvent(display,windows,&event,exception);
3140     if (distance > 9)
3141       XHighlightLine(display,windows->image.id,
3142         windows->image.highlight_context,&segment_info);
3143     switch (event.type)
3144     {
3145       case ButtonPress:
3146       {
3147         segment_info.x2=(short int) event.xmotion.x;
3148         segment_info.y2=(short int) event.xmotion.y;
3149         break;
3150       }
3151       case ButtonRelease:
3152       {
3153         /*
3154           User has committed to chopping line.
3155         */
3156         segment_info.x2=(short int) event.xbutton.x;
3157         segment_info.y2=(short int) event.xbutton.y;
3158         state|=ExitState;
3159         break;
3160       }
3161       case Expose:
3162         break;
3163       case MotionNotify:
3164       {
3165         segment_info.x2=(short int) event.xmotion.x;
3166         segment_info.y2=(short int) event.xmotion.y;
3167       }
3168       default:
3169         break;
3170     }
3171     /*
3172       Check boundary conditions.
3173     */
3174     if (segment_info.x2 < 0)
3175       segment_info.x2=0;
3176     else
3177       if (segment_info.x2 > windows->image.ximage->width)
3178         segment_info.x2=windows->image.ximage->width;
3179     if (segment_info.y2 < 0)
3180       segment_info.y2=0;
3181     else
3182       if (segment_info.y2 > windows->image.ximage->height)
3183         segment_info.y2=windows->image.ximage->height;
3184     distance=(unsigned int)
3185       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3186        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3187     /*
3188       Compute chopping geometry.
3189     */
3190     if (direction == HorizontalChopCommand)
3191       {
3192         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3193         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3194         chop_info.height=0;
3195         chop_info.y=0;
3196         if (segment_info.x1 > (int) segment_info.x2)
3197           {
3198             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3199             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3200           }
3201       }
3202     else
3203       {
3204         chop_info.width=0;
3205         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3206         chop_info.x=0;
3207         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3208         if (segment_info.y1 > segment_info.y2)
3209           {
3210             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3211             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3212           }
3213       }
3214   } while ((state & ExitState) == 0);
3215   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3216   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3217   if (distance <= 9)
3218     return(MagickTrue);
3219   /*
3220     Image chopping is relative to image configuration.
3221   */
3222   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3223     exception);
3224   XSetCursorState(display,windows,MagickTrue);
3225   XCheckRefreshWindows(display,windows);
3226   windows->image.window_changes.width=windows->image.ximage->width-
3227     (unsigned int) chop_info.width;
3228   windows->image.window_changes.height=windows->image.ximage->height-
3229     (unsigned int) chop_info.height;
3230   width=(unsigned int) (*image)->columns;
3231   height=(unsigned int) (*image)->rows;
3232   x=0;
3233   y=0;
3234   if (windows->image.crop_geometry != (char *) NULL)
3235     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3236   scale_factor=(double) width/windows->image.ximage->width;
3237   chop_info.x+=x;
3238   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3239   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3240   scale_factor=(double) height/windows->image.ximage->height;
3241   chop_info.y+=y;
3242   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3243   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3244   /*
3245     Chop image.
3246   */
3247   chop_image=ChopImage(*image,&chop_info,exception);
3248   XSetCursorState(display,windows,MagickFalse);
3249   if (chop_image == (Image *) NULL)
3250     return(MagickFalse);
3251   *image=DestroyImage(*image);
3252   *image=chop_image;
3253   /*
3254     Update image configuration.
3255   */
3256   XConfigureImageColormap(display,resource_info,windows,*image,exception);
3257   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3258   return(MagickTrue);
3259 }
3260 \f
3261 /*
3262 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3263 %                                                                             %
3264 %                                                                             %
3265 %                                                                             %
3266 +   X C o l o r E d i t I m a g e                                             %
3267 %                                                                             %
3268 %                                                                             %
3269 %                                                                             %
3270 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3271 %
3272 %  XColorEditImage() allows the user to interactively change the color of one
3273 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3274 %
3275 %  The format of the XColorEditImage method is:
3276 %
3277 %      MagickBooleanType XColorEditImage(Display *display,
3278 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3279 %          ExceptionInfo *exception)
3280 %
3281 %  A description of each parameter follows:
3282 %
3283 %    o display: Specifies a connection to an X server;  returned from
3284 %      XOpenDisplay.
3285 %
3286 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3287 %
3288 %    o windows: Specifies a pointer to a XWindows structure.
3289 %
3290 %    o image: the image; returned from ReadImage.
3291 %
3292 %    o exception: return any errors or warnings in this structure.
3293 %
3294 */
3295 static MagickBooleanType XColorEditImage(Display *display,
3296   XResourceInfo *resource_info,XWindows *windows,Image **image,
3297   ExceptionInfo *exception)
3298 {
3299   static const char
3300     *ColorEditMenu[] =
3301     {
3302       "Method",
3303       "Pixel Color",
3304       "Border Color",
3305       "Fuzz",
3306       "Undo",
3307       "Help",
3308       "Dismiss",
3309       (char *) NULL
3310     };
3311
3312   static const ModeType
3313     ColorEditCommands[] =
3314     {
3315       ColorEditMethodCommand,
3316       ColorEditColorCommand,
3317       ColorEditBorderCommand,
3318       ColorEditFuzzCommand,
3319       ColorEditUndoCommand,
3320       ColorEditHelpCommand,
3321       ColorEditDismissCommand
3322     };
3323
3324   static PaintMethod
3325     method = PointMethod;
3326
3327   static unsigned int
3328     pen_id = 0;
3329
3330   static XColor
3331     border_color = { 0, 0, 0, 0, 0, 0 };
3332
3333   char
3334     command[MaxTextExtent],
3335     text[MaxTextExtent];
3336
3337   Cursor
3338     cursor;
3339
3340   int
3341     entry,
3342     id,
3343     x,
3344     x_offset,
3345     y,
3346     y_offset;
3347
3348   register Quantum
3349     *q;
3350
3351   register ssize_t
3352     i;
3353
3354   unsigned int
3355     height,
3356     width;
3357
3358   size_t
3359     state;
3360
3361   XColor
3362     color;
3363
3364   XEvent
3365     event;
3366
3367   /*
3368     Map Command widget.
3369   */
3370   (void) CloneString(&windows->command.name,"Color Edit");
3371   windows->command.data=4;
3372   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3373   (void) XMapRaised(display,windows->command.id);
3374   XClientMessage(display,windows->image.id,windows->im_protocols,
3375     windows->im_update_widget,CurrentTime);
3376   /*
3377     Make cursor.
3378   */
3379   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3380     resource_info->background_color,resource_info->foreground_color);
3381   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3382   /*
3383     Track pointer until button 1 is pressed.
3384   */
3385   XQueryPosition(display,windows->image.id,&x,&y);
3386   (void) XSelectInput(display,windows->image.id,
3387     windows->image.attributes.event_mask | PointerMotionMask);
3388   state=DefaultState;
3389   do
3390   {
3391     if (IfMagickTrue(windows->info.mapped) )
3392       {
3393         /*
3394           Display pointer position.
3395         */
3396         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3397           x+windows->image.x,y+windows->image.y);
3398         XInfoWidget(display,windows,text);
3399       }
3400     /*
3401       Wait for next event.
3402     */
3403     XScreenEvent(display,windows,&event,exception);
3404     if (event.xany.window == windows->command.id)
3405       {
3406         /*
3407           Select a command from the Command widget.
3408         */
3409         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3410         if (id < 0)
3411           {
3412             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3413             continue;
3414           }
3415         switch (ColorEditCommands[id])
3416         {
3417           case ColorEditMethodCommand:
3418           {
3419             char
3420               **methods;
3421
3422             /*
3423               Select a method from the pop-up menu.
3424             */
3425             methods=(char **) GetCommandOptions(MagickMethodOptions);
3426             if (methods == (char **) NULL)
3427               break;
3428             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3429               (const char **) methods,command);
3430             if (entry >= 0)
3431               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3432                 MagickFalse,methods[entry]);
3433             methods=DestroyStringList(methods);
3434             break;
3435           }
3436           case ColorEditColorCommand:
3437           {
3438             const char
3439               *ColorMenu[MaxNumberPens];
3440
3441             int
3442               pen_number;
3443
3444             /*
3445               Initialize menu selections.
3446             */
3447             for (i=0; i < (int) (MaxNumberPens-2); i++)
3448               ColorMenu[i]=resource_info->pen_colors[i];
3449             ColorMenu[MaxNumberPens-2]="Browser...";
3450             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3451             /*
3452               Select a pen color from the pop-up menu.
3453             */
3454             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3455               (const char **) ColorMenu,command);
3456             if (pen_number < 0)
3457               break;
3458             if (pen_number == (MaxNumberPens-2))
3459               {
3460                 static char
3461                   color_name[MaxTextExtent] = "gray";
3462
3463                 /*
3464                   Select a pen color from a dialog.
3465                 */
3466                 resource_info->pen_colors[pen_number]=color_name;
3467                 XColorBrowserWidget(display,windows,"Select",color_name);
3468                 if (*color_name == '\0')
3469                   break;
3470               }
3471             /*
3472               Set pen color.
3473             */
3474             (void) XParseColor(display,windows->map_info->colormap,
3475               resource_info->pen_colors[pen_number],&color);
3476             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3477               (unsigned int) MaxColors,&color);
3478             windows->pixel_info->pen_colors[pen_number]=color;
3479             pen_id=(unsigned int) pen_number;
3480             break;
3481           }
3482           case ColorEditBorderCommand:
3483           {
3484             const char
3485               *ColorMenu[MaxNumberPens];
3486
3487             int
3488               pen_number;
3489
3490             /*
3491               Initialize menu selections.
3492             */
3493             for (i=0; i < (int) (MaxNumberPens-2); i++)
3494               ColorMenu[i]=resource_info->pen_colors[i];
3495             ColorMenu[MaxNumberPens-2]="Browser...";
3496             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3497             /*
3498               Select a pen color from the pop-up menu.
3499             */
3500             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3501               (const char **) ColorMenu,command);
3502             if (pen_number < 0)
3503               break;
3504             if (pen_number == (MaxNumberPens-2))
3505               {
3506                 static char
3507                   color_name[MaxTextExtent] = "gray";
3508
3509                 /*
3510                   Select a pen color from a dialog.
3511                 */
3512                 resource_info->pen_colors[pen_number]=color_name;
3513                 XColorBrowserWidget(display,windows,"Select",color_name);
3514                 if (*color_name == '\0')
3515                   break;
3516               }
3517             /*
3518               Set border color.
3519             */
3520             (void) XParseColor(display,windows->map_info->colormap,
3521               resource_info->pen_colors[pen_number],&border_color);
3522             break;
3523           }
3524           case ColorEditFuzzCommand:
3525           {
3526             static char
3527               fuzz[MaxTextExtent];
3528
3529             static const char
3530               *FuzzMenu[] =
3531               {
3532                 "0%",
3533                 "2%",
3534                 "5%",
3535                 "10%",
3536                 "15%",
3537                 "Dialog...",
3538                 (char *) NULL,
3539               };
3540
3541             /*
3542               Select a command from the pop-up menu.
3543             */
3544             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3545               command);
3546             if (entry < 0)
3547               break;
3548             if (entry != 5)
3549               {
3550                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3551                   QuantumRange+1.0);
3552                 break;
3553               }
3554             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3555             (void) XDialogWidget(display,windows,"Ok",
3556               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3557             if (*fuzz == '\0')
3558               break;
3559             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3560             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3561               1.0);
3562             break;
3563           }
3564           case ColorEditUndoCommand:
3565           {
3566             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3567               image,exception);
3568             break;
3569           }
3570           case ColorEditHelpCommand:
3571           default:
3572           {
3573             XTextViewWidget(display,resource_info,windows,MagickFalse,
3574               "Help Viewer - Image Annotation",ImageColorEditHelp);
3575             break;
3576           }
3577           case ColorEditDismissCommand:
3578           {
3579             /*
3580               Prematurely exit.
3581             */
3582             state|=EscapeState;
3583             state|=ExitState;
3584             break;
3585           }
3586         }
3587         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3588         continue;
3589       }
3590     switch (event.type)
3591     {
3592       case ButtonPress:
3593       {
3594         if (event.xbutton.button != Button1)
3595           break;
3596         if ((event.xbutton.window != windows->image.id) &&
3597             (event.xbutton.window != windows->magnify.id))
3598           break;
3599         /*
3600           exit loop.
3601         */
3602         x=event.xbutton.x;
3603         y=event.xbutton.y;
3604         (void) XMagickCommand(display,resource_info,windows,
3605           SaveToUndoBufferCommand,image,exception);
3606         state|=UpdateConfigurationState;
3607         break;
3608       }
3609       case ButtonRelease:
3610       {
3611         if (event.xbutton.button != Button1)
3612           break;
3613         if ((event.xbutton.window != windows->image.id) &&
3614             (event.xbutton.window != windows->magnify.id))
3615           break;
3616         /*
3617           Update colormap information.
3618         */
3619         x=event.xbutton.x;
3620         y=event.xbutton.y;
3621         XConfigureImageColormap(display,resource_info,windows,*image,exception);
3622         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3623         XInfoWidget(display,windows,text);
3624         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3625         state&=(~UpdateConfigurationState);
3626         break;
3627       }
3628       case Expose:
3629         break;
3630       case KeyPress:
3631       {
3632         KeySym
3633           key_symbol;
3634
3635         if (event.xkey.window == windows->magnify.id)
3636           {
3637             Window
3638               window;
3639
3640             window=windows->magnify.id;
3641             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3642           }
3643         if (event.xkey.window != windows->image.id)
3644           break;
3645         /*
3646           Respond to a user key press.
3647         */
3648         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3649           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3650         switch ((int) key_symbol)
3651         {
3652           case XK_Escape:
3653           case XK_F20:
3654           {
3655             /*
3656               Prematurely exit.
3657             */
3658             state|=ExitState;
3659             break;
3660           }
3661           case XK_F1:
3662           case XK_Help:
3663           {
3664             XTextViewWidget(display,resource_info,windows,MagickFalse,
3665               "Help Viewer - Image Annotation",ImageColorEditHelp);
3666             break;
3667           }
3668           default:
3669           {
3670             (void) XBell(display,0);
3671             break;
3672           }
3673         }
3674         break;
3675       }
3676       case MotionNotify:
3677       {
3678         /*
3679           Map and unmap Info widget as cursor crosses its boundaries.
3680         */
3681         x=event.xmotion.x;
3682         y=event.xmotion.y;
3683         if (IfMagickTrue(windows->info.mapped) )
3684           {
3685             if ((x < (int) (windows->info.x+windows->info.width)) &&
3686                 (y < (int) (windows->info.y+windows->info.height)))
3687               (void) XWithdrawWindow(display,windows->info.id,
3688                 windows->info.screen);
3689           }
3690         else
3691           if ((x > (int) (windows->info.x+windows->info.width)) ||
3692               (y > (int) (windows->info.y+windows->info.height)))
3693             (void) XMapWindow(display,windows->info.id);
3694         break;
3695       }
3696       default:
3697         break;
3698     }
3699     if (event.xany.window == windows->magnify.id)
3700       {
3701         x=windows->magnify.x-windows->image.x;
3702         y=windows->magnify.y-windows->image.y;
3703       }
3704     x_offset=x;
3705     y_offset=y;
3706     if ((state & UpdateConfigurationState) != 0)
3707       {
3708         CacheView
3709           *image_view;
3710
3711         int
3712           x,
3713           y;
3714
3715         /*
3716           Pixel edit is relative to image configuration.
3717         */
3718         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3719           MagickTrue);
3720         color=windows->pixel_info->pen_colors[pen_id];
3721         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3722         width=(unsigned int) (*image)->columns;
3723         height=(unsigned int) (*image)->rows;
3724         x=0;
3725         y=0;
3726         if (windows->image.crop_geometry != (char *) NULL)
3727           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3728             &width,&height);
3729         x_offset=(int)
3730           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3731         y_offset=(int)
3732           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3733         if ((x_offset < 0) || (y_offset < 0))
3734           continue;
3735         if ((x_offset >= (int) (*image)->columns) ||
3736             (y_offset >= (int) (*image)->rows))
3737           continue;
3738         image_view=AcquireAuthenticCacheView(*image,exception);
3739         switch (method)
3740         {
3741           case PointMethod:
3742           default:
3743           {
3744             /*
3745               Update color information using point algorithm.
3746             */
3747             if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3748               return(MagickFalse);
3749             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3750               (ssize_t) y_offset,1,1,exception);
3751             if (q == (Quantum *) NULL)
3752               break;
3753             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3754             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3755             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3756             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3757             break;
3758           }
3759           case ReplaceMethod:
3760           {
3761             PixelInfo
3762               pixel,
3763               target;
3764
3765             /*
3766               Update color information using replace algorithm.
3767             */
3768             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3769               x_offset,(ssize_t) y_offset,&target,exception);
3770             if ((*image)->storage_class == DirectClass)
3771               {
3772                 for (y=0; y < (int) (*image)->rows; y++)
3773                 {
3774                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3775                     (*image)->columns,1,exception);
3776                   if (q == (Quantum *) NULL)
3777                     break;
3778                   for (x=0; x < (int) (*image)->columns; x++)
3779                   {
3780                     GetPixelInfoPixel(*image,q,&pixel);
3781                     if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3782                       {
3783                         SetPixelRed(*image,ScaleShortToQuantum(
3784                           color.red),q);
3785                         SetPixelGreen(*image,ScaleShortToQuantum(
3786                           color.green),q);
3787                         SetPixelBlue(*image,ScaleShortToQuantum(
3788                           color.blue),q);
3789                       }
3790                     q+=GetPixelChannels(*image);
3791                   }
3792                   if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3793                     break;
3794                 }
3795               }
3796             else
3797               {
3798                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3799                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3800                     {
3801                       (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3802                         color.red);
3803                       (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3804                         color.green);
3805                       (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3806                         color.blue);
3807                     }
3808                 (void) SyncImage(*image,exception);
3809               }
3810             break;
3811           }
3812           case FloodfillMethod:
3813           case FillToBorderMethod:
3814           {
3815             DrawInfo
3816               *draw_info;
3817
3818             PixelInfo
3819               target;
3820
3821             /*
3822               Update color information using floodfill algorithm.
3823             */
3824             (void) GetOneVirtualPixelInfo(*image,
3825               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3826               y_offset,&target,exception);
3827             if (method == FillToBorderMethod)
3828               {
3829                 target.red=(double)
3830                   ScaleShortToQuantum(border_color.red);
3831                 target.green=(double)
3832                   ScaleShortToQuantum(border_color.green);
3833                 target.blue=(double)
3834                   ScaleShortToQuantum(border_color.blue);
3835               }
3836             draw_info=CloneDrawInfo(resource_info->image_info,
3837               (DrawInfo *) NULL);
3838             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3839               AllCompliance,&draw_info->fill,exception);
3840             (void) FloodfillPaintImage(*image,draw_info,&target,
3841               (ssize_t)x_offset,(ssize_t)y_offset,
3842               IsMagickFalse(method == FloodfillMethod),exception);
3843             draw_info=DestroyDrawInfo(draw_info);
3844             break;
3845           }
3846           case ResetMethod:
3847           {
3848             /*
3849               Update color information using reset algorithm.
3850             */
3851             if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
3852               return(MagickFalse);
3853             for (y=0; y < (int) (*image)->rows; y++)
3854             {
3855               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3856                 (*image)->columns,1,exception);
3857               if (q == (Quantum *) NULL)
3858                 break;
3859               for (x=0; x < (int) (*image)->columns; x++)
3860               {
3861                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3862                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3863                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3864                 q+=GetPixelChannels(*image);
3865               }
3866               if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
3867                 break;
3868             }
3869             break;
3870           }
3871         }
3872         image_view=DestroyCacheView(image_view);
3873         state&=(~UpdateConfigurationState);
3874       }
3875   } while ((state & ExitState) == 0);
3876   (void) XSelectInput(display,windows->image.id,
3877     windows->image.attributes.event_mask);
3878   XSetCursorState(display,windows,MagickFalse);
3879   (void) XFreeCursor(display,cursor);
3880   return(MagickTrue);
3881 }
3882 \f
3883 /*
3884 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3885 %                                                                             %
3886 %                                                                             %
3887 %                                                                             %
3888 +   X C o m p o s i t e I m a g e                                             %
3889 %                                                                             %
3890 %                                                                             %
3891 %                                                                             %
3892 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3893 %
3894 %  XCompositeImage() requests an image name from the user, reads the image and
3895 %  composites it with the X window image at a location the user chooses with
3896 %  the pointer.
3897 %
3898 %  The format of the XCompositeImage method is:
3899 %
3900 %      MagickBooleanType XCompositeImage(Display *display,
3901 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3902 %        ExceptionInfo *exception)
3903 %
3904 %  A description of each parameter follows:
3905 %
3906 %    o display: Specifies a connection to an X server;  returned from
3907 %      XOpenDisplay.
3908 %
3909 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3910 %
3911 %    o windows: Specifies a pointer to a XWindows structure.
3912 %
3913 %    o image: the image; returned from ReadImage.
3914 %
3915 %    o exception: return any errors or warnings in this structure.
3916 %
3917 */
3918 static MagickBooleanType XCompositeImage(Display *display,
3919   XResourceInfo *resource_info,XWindows *windows,Image *image,
3920   ExceptionInfo *exception)
3921 {
3922   static char
3923     displacement_geometry[MaxTextExtent] = "30x30",
3924     filename[MaxTextExtent] = "\0";
3925
3926   static const char
3927     *CompositeMenu[] =
3928     {
3929       "Operators",
3930       "Dissolve",
3931       "Displace",
3932       "Help",
3933       "Dismiss",
3934       (char *) NULL
3935     };
3936
3937   static CompositeOperator
3938     compose = CopyCompositeOp;
3939
3940   static const ModeType
3941     CompositeCommands[] =
3942     {
3943       CompositeOperatorsCommand,
3944       CompositeDissolveCommand,
3945       CompositeDisplaceCommand,
3946       CompositeHelpCommand,
3947       CompositeDismissCommand
3948     };
3949
3950   char
3951     text[MaxTextExtent];
3952
3953   Cursor
3954     cursor;
3955
3956   Image
3957     *composite_image;
3958
3959   int
3960     entry,
3961     id,
3962     x,
3963     y;
3964
3965   double
3966     blend,
3967     scale_factor;
3968
3969   RectangleInfo
3970     highlight_info,
3971     composite_info;
3972
3973   unsigned int
3974     height,
3975     width;
3976
3977   size_t
3978     state;
3979
3980   XEvent
3981     event;
3982
3983   /*
3984     Request image file name from user.
3985   */
3986   XFileBrowserWidget(display,windows,"Composite",filename);
3987   if (*filename == '\0')
3988     return(MagickTrue);
3989   /*
3990     Read image.
3991   */
3992   XSetCursorState(display,windows,MagickTrue);
3993   XCheckRefreshWindows(display,windows);
3994   (void) CopyMagickString(resource_info->image_info->filename,filename,
3995     MaxTextExtent);
3996   composite_image=ReadImage(resource_info->image_info,exception);
3997   CatchException(exception);
3998   XSetCursorState(display,windows,MagickFalse);
3999   if (composite_image == (Image *) NULL)
4000     return(MagickFalse);
4001   /*
4002     Map Command widget.
4003   */
4004   (void) CloneString(&windows->command.name,"Composite");
4005   windows->command.data=1;
4006   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4007   (void) XMapRaised(display,windows->command.id);
4008   XClientMessage(display,windows->image.id,windows->im_protocols,
4009     windows->im_update_widget,CurrentTime);
4010   /*
4011     Track pointer until button 1 is pressed.
4012   */
4013   XQueryPosition(display,windows->image.id,&x,&y);
4014   (void) XSelectInput(display,windows->image.id,
4015     windows->image.attributes.event_mask | PointerMotionMask);
4016   composite_info.x=(ssize_t) windows->image.x+x;
4017   composite_info.y=(ssize_t) windows->image.y+y;
4018   composite_info.width=0;
4019   composite_info.height=0;
4020   cursor=XCreateFontCursor(display,XC_ul_angle);
4021   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4022   blend=0.0;
4023   state=DefaultState;
4024   do
4025   {
4026     if (IfMagickTrue(windows->info.mapped) )
4027       {
4028         /*
4029           Display pointer position.
4030         */
4031         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4032           (long) composite_info.x,(long) composite_info.y);
4033         XInfoWidget(display,windows,text);
4034       }
4035     highlight_info=composite_info;
4036     highlight_info.x=composite_info.x-windows->image.x;
4037     highlight_info.y=composite_info.y-windows->image.y;
4038     XHighlightRectangle(display,windows->image.id,
4039       windows->image.highlight_context,&highlight_info);
4040     /*
4041       Wait for next event.
4042     */
4043     XScreenEvent(display,windows,&event,exception);
4044     XHighlightRectangle(display,windows->image.id,
4045       windows->image.highlight_context,&highlight_info);
4046     if (event.xany.window == windows->command.id)
4047       {
4048         /*
4049           Select a command from the Command widget.
4050         */
4051         id=XCommandWidget(display,windows,CompositeMenu,&event);
4052         if (id < 0)
4053           continue;
4054         switch (CompositeCommands[id])
4055         {
4056           case CompositeOperatorsCommand:
4057           {
4058             char
4059               command[MaxTextExtent],
4060               **operators;
4061
4062             /*
4063               Select a command from the pop-up menu.
4064             */
4065             operators=GetCommandOptions(MagickComposeOptions);
4066             if (operators == (char **) NULL)
4067               break;
4068             entry=XMenuWidget(display,windows,CompositeMenu[id],
4069               (const char **) operators,command);
4070             if (entry >= 0)
4071               compose=(CompositeOperator) ParseCommandOption(
4072                 MagickComposeOptions,MagickFalse,operators[entry]);
4073             operators=DestroyStringList(operators);
4074             break;
4075           }
4076           case CompositeDissolveCommand:
4077           {
4078             static char
4079               factor[MaxTextExtent] = "20.0";
4080
4081             /*
4082               Dissolve the two images a given percent.
4083             */
4084             (void) XSetFunction(display,windows->image.highlight_context,
4085               GXcopy);
4086             (void) XDialogWidget(display,windows,"Dissolve",
4087               "Enter the blend factor (0.0 - 99.9%):",factor);
4088             (void) XSetFunction(display,windows->image.highlight_context,
4089               GXinvert);
4090             if (*factor == '\0')
4091               break;
4092             blend=StringToDouble(factor,(char **) NULL);
4093             compose=DissolveCompositeOp;
4094             break;
4095           }
4096           case CompositeDisplaceCommand:
4097           {
4098             /*
4099               Get horizontal and vertical scale displacement geometry.
4100             */
4101             (void) XSetFunction(display,windows->image.highlight_context,
4102               GXcopy);
4103             (void) XDialogWidget(display,windows,"Displace",
4104               "Enter the horizontal and vertical scale:",displacement_geometry);
4105             (void) XSetFunction(display,windows->image.highlight_context,
4106               GXinvert);
4107             if (*displacement_geometry == '\0')
4108               break;
4109             compose=DisplaceCompositeOp;
4110             break;
4111           }
4112           case CompositeHelpCommand:
4113           {
4114             (void) XSetFunction(display,windows->image.highlight_context,
4115               GXcopy);
4116             XTextViewWidget(display,resource_info,windows,MagickFalse,
4117               "Help Viewer - Image Composite",ImageCompositeHelp);
4118             (void) XSetFunction(display,windows->image.highlight_context,
4119               GXinvert);
4120             break;
4121           }
4122           case CompositeDismissCommand:
4123           {
4124             /*
4125               Prematurely exit.
4126             */
4127             state|=EscapeState;
4128             state|=ExitState;
4129             break;
4130           }
4131           default:
4132             break;
4133         }
4134         continue;
4135       }
4136     switch (event.type)
4137     {
4138       case ButtonPress:
4139       {
4140         if (IfMagickTrue(image->debug) )
4141           (void) LogMagickEvent(X11Event,GetMagickModule(),
4142             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4143             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4144         if (event.xbutton.button != Button1)
4145           break;
4146         if (event.xbutton.window != windows->image.id)
4147           break;
4148         /*
4149           Change cursor.
4150         */
4151         composite_info.width=composite_image->columns;
4152         composite_info.height=composite_image->rows;
4153         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4154         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4155         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4156         break;
4157       }
4158       case ButtonRelease:
4159       {
4160         if (IfMagickTrue(image->debug) )
4161           (void) LogMagickEvent(X11Event,GetMagickModule(),
4162             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4163             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4164         if (event.xbutton.button != Button1)
4165           break;
4166         if (event.xbutton.window != windows->image.id)
4167           break;
4168         if ((composite_info.width != 0) && (composite_info.height != 0))
4169           {
4170             /*
4171               User has selected the location of the composite image.
4172             */
4173             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4174             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4175             state|=ExitState;
4176           }
4177         break;
4178       }
4179       case Expose:
4180         break;
4181       case KeyPress:
4182       {
4183         char
4184           command[MaxTextExtent];
4185
4186         KeySym
4187           key_symbol;
4188
4189         int
4190           length;
4191
4192         if (event.xkey.window != windows->image.id)
4193           break;
4194         /*
4195           Respond to a user key press.
4196         */
4197         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4198           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4199         *(command+length)='\0';
4200         if (IfMagickTrue(image->debug) )
4201           (void) LogMagickEvent(X11Event,GetMagickModule(),
4202             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4203         switch ((int) key_symbol)
4204         {
4205           case XK_Escape:
4206           case XK_F20:
4207           {
4208             /*
4209               Prematurely exit.
4210             */
4211             composite_image=DestroyImage(composite_image);
4212             state|=EscapeState;
4213             state|=ExitState;
4214             break;
4215           }
4216           case XK_F1:
4217           case XK_Help:
4218           {
4219             (void) XSetFunction(display,windows->image.highlight_context,
4220               GXcopy);
4221             XTextViewWidget(display,resource_info,windows,MagickFalse,
4222               "Help Viewer - Image Composite",ImageCompositeHelp);
4223             (void) XSetFunction(display,windows->image.highlight_context,
4224               GXinvert);
4225             break;
4226           }
4227           default:
4228           {
4229             (void) XBell(display,0);
4230             break;
4231           }
4232         }
4233         break;
4234       }
4235       case MotionNotify:
4236       {
4237         /*
4238           Map and unmap Info widget as text cursor crosses its boundaries.
4239         */
4240         x=event.xmotion.x;
4241         y=event.xmotion.y;
4242         if (IfMagickTrue(windows->info.mapped) )
4243           {
4244             if ((x < (int) (windows->info.x+windows->info.width)) &&
4245                 (y < (int) (windows->info.y+windows->info.height)))
4246               (void) XWithdrawWindow(display,windows->info.id,
4247                 windows->info.screen);
4248           }
4249         else
4250           if ((x > (int) (windows->info.x+windows->info.width)) ||
4251               (y > (int) (windows->info.y+windows->info.height)))
4252             (void) XMapWindow(display,windows->info.id);
4253         composite_info.x=(ssize_t) windows->image.x+x;
4254         composite_info.y=(ssize_t) windows->image.y+y;
4255         break;
4256       }
4257       default:
4258       {
4259         if (IfMagickTrue(image->debug) )
4260           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4261             event.type);
4262         break;
4263       }
4264     }
4265   } while ((state & ExitState) == 0);
4266   (void) XSelectInput(display,windows->image.id,
4267     windows->image.attributes.event_mask);
4268   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4269   XSetCursorState(display,windows,MagickFalse);
4270   (void) XFreeCursor(display,cursor);
4271   if ((state & EscapeState) != 0)
4272     return(MagickTrue);
4273   /*
4274     Image compositing is relative to image configuration.
4275   */
4276   XSetCursorState(display,windows,MagickTrue);
4277   XCheckRefreshWindows(display,windows);
4278   width=(unsigned int) image->columns;
4279   height=(unsigned int) image->rows;
4280   x=0;
4281   y=0;
4282   if (windows->image.crop_geometry != (char *) NULL)
4283     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4284   scale_factor=(double) width/windows->image.ximage->width;
4285   composite_info.x+=x;
4286   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4287   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4288   scale_factor=(double) height/windows->image.ximage->height;
4289   composite_info.y+=y;
4290   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4291   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4292   if ((composite_info.width != composite_image->columns) ||
4293       (composite_info.height != composite_image->rows))
4294     {
4295       Image
4296         *resize_image;
4297
4298       /*
4299         Scale composite image.
4300       */
4301       resize_image=ResizeImage(composite_image,composite_info.width,
4302         composite_info.height,composite_image->filter,exception);
4303       composite_image=DestroyImage(composite_image);
4304       if (resize_image == (Image *) NULL)
4305         {
4306           XSetCursorState(display,windows,MagickFalse);
4307           return(MagickFalse);
4308         }
4309       composite_image=resize_image;
4310     }
4311   if (compose == DisplaceCompositeOp)
4312     (void) SetImageArtifact(composite_image,"compose:args",
4313       displacement_geometry);
4314   if (blend != 0.0)
4315     {
4316       CacheView
4317         *image_view;
4318
4319       int
4320         y;
4321
4322       Quantum
4323         opacity;
4324
4325       register int
4326         x;
4327
4328       register Quantum
4329         *q;
4330
4331       /*
4332         Create mattes for blending.
4333       */
4334       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4335       opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4336         ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4337       if (IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
4338         return(MagickFalse);
4339       image->alpha_trait=BlendPixelTrait;
4340       image_view=AcquireAuthenticCacheView(image,exception);
4341       for (y=0; y < (int) image->rows; y++)
4342       {
4343         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4344           exception);
4345         if (q == (Quantum *) NULL)
4346           break;
4347         for (x=0; x < (int) image->columns; x++)
4348         {
4349           SetPixelAlpha(image,opacity,q);
4350           q+=GetPixelChannels(image);
4351         }
4352         if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
4353           break;
4354       }
4355       image_view=DestroyCacheView(image_view);
4356     }
4357   /*
4358     Composite image with X Image window.
4359   */
4360   (void) CompositeImage(image,composite_image,compose,MagickTrue,
4361     composite_info.x,composite_info.y,exception);
4362   composite_image=DestroyImage(composite_image);
4363   XSetCursorState(display,windows,MagickFalse);
4364   /*
4365     Update image configuration.
4366   */
4367   XConfigureImageColormap(display,resource_info,windows,image,exception);
4368   (void) XConfigureImage(display,resource_info,windows,image,exception);
4369   return(MagickTrue);
4370 }
4371 \f
4372 /*
4373 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4374 %                                                                             %
4375 %                                                                             %
4376 %                                                                             %
4377 +   X C o n f i g u r e I m a g e                                             %
4378 %                                                                             %
4379 %                                                                             %
4380 %                                                                             %
4381 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4382 %
4383 %  XConfigureImage() creates a new X image.  It also notifies the window
4384 %  manager of the new image size and configures the transient widows.
4385 %
4386 %  The format of the XConfigureImage method is:
4387 %
4388 %      MagickBooleanType XConfigureImage(Display *display,
4389 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4390 %        ExceptionInfo *exception)
4391 %
4392 %  A description of each parameter follows:
4393 %
4394 %    o display: Specifies a connection to an X server; returned from
4395 %      XOpenDisplay.
4396 %
4397 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4398 %
4399 %    o windows: Specifies a pointer to a XWindows structure.
4400 %
4401 %    o image: the image.
4402 %
4403 %    o exception: return any errors or warnings in this structure.
4404 %
4405 %    o exception: return any errors or warnings in this structure.
4406 %
4407 */
4408 static MagickBooleanType XConfigureImage(Display *display,
4409   XResourceInfo *resource_info,XWindows *windows,Image *image,
4410   ExceptionInfo *exception)
4411 {
4412   char
4413     geometry[MaxTextExtent];
4414
4415   MagickStatusType
4416     status;
4417
4418   size_t
4419     mask,
4420     height,
4421     width;
4422
4423   ssize_t
4424     x,
4425     y;
4426
4427   XSizeHints
4428     *size_hints;
4429
4430   XWindowChanges
4431     window_changes;
4432
4433   /*
4434     Dismiss if window dimensions are zero.
4435   */
4436   width=(unsigned int) windows->image.window_changes.width;
4437   height=(unsigned int) windows->image.window_changes.height;
4438   if (IfMagickTrue(image->debug) )
4439     (void) LogMagickEvent(X11Event,GetMagickModule(),
4440       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4441       windows->image.ximage->height,(double) width,(double) height);
4442   if ((width*height) == 0)
4443     return(MagickTrue);
4444   x=0;
4445   y=0;
4446   /*
4447     Resize image to fit Image window dimensions.
4448   */
4449   XSetCursorState(display,windows,MagickTrue);
4450   (void) XFlush(display);
4451   if (((int) width != windows->image.ximage->width) ||
4452       ((int) height != windows->image.ximage->height))
4453     image->taint=MagickTrue;
4454   windows->magnify.x=(int)
4455     width*windows->magnify.x/windows->image.ximage->width;
4456   windows->magnify.y=(int)
4457     height*windows->magnify.y/windows->image.ximage->height;
4458   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4459   windows->image.y=(int)
4460     (height*windows->image.y/windows->image.ximage->height);
4461   status=XMakeImage(display,resource_info,&windows->image,image,
4462     (unsigned int) width,(unsigned int) height,exception);
4463   if (IfMagickFalse(status) )
4464     XNoticeWidget(display,windows,"Unable to configure X image:",
4465       windows->image.name);
4466   /*
4467     Notify window manager of the new configuration.
4468   */
4469   if (resource_info->image_geometry != (char *) NULL)
4470     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4471       resource_info->image_geometry);
4472   else
4473     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4474       XDisplayWidth(display,windows->image.screen),
4475       XDisplayHeight(display,windows->image.screen));
4476   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4477   window_changes.width=(int) width;
4478   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4479     window_changes.width=XDisplayWidth(display,windows->image.screen);
4480   window_changes.height=(int) height;
4481   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4482     window_changes.height=XDisplayHeight(display,windows->image.screen);
4483   mask=(size_t) (CWWidth | CWHeight);
4484   if (resource_info->backdrop)
4485     {
4486       mask|=CWX | CWY;
4487       window_changes.x=(int)
4488         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4489       window_changes.y=(int)
4490         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4491     }
4492   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4493     (unsigned int) mask,&window_changes);
4494   (void) XClearWindow(display,windows->image.id);
4495   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4496   /*
4497     Update Magnify window configuration.
4498   */
4499   if (IfMagickTrue(windows->magnify.mapped) )
4500     XMakeMagnifyImage(display,windows,exception);
4501   windows->pan.crop_geometry=windows->image.crop_geometry;
4502   XBestIconSize(display,&windows->pan,image);
4503   while (((windows->pan.width << 1) < MaxIconSize) &&
4504          ((windows->pan.height << 1) < MaxIconSize))
4505   {
4506     windows->pan.width<<=1;
4507     windows->pan.height<<=1;
4508   }
4509   if (windows->pan.geometry != (char *) NULL)
4510     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4511       &windows->pan.width,&windows->pan.height);
4512   window_changes.width=(int) windows->pan.width;
4513   window_changes.height=(int) windows->pan.height;
4514   size_hints=XAllocSizeHints();
4515   if (size_hints != (XSizeHints *) NULL)
4516     {
4517       /*
4518         Set new size hints.
4519       */
4520       size_hints->flags=PSize | PMinSize | PMaxSize;
4521       size_hints->width=window_changes.width;
4522       size_hints->height=window_changes.height;
4523       size_hints->min_width=size_hints->width;
4524       size_hints->min_height=size_hints->height;
4525       size_hints->max_width=size_hints->width;
4526       size_hints->max_height=size_hints->height;
4527       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4528       (void) XFree((void *) size_hints);
4529     }
4530   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4531     (unsigned int) (CWWidth | CWHeight),&window_changes);
4532   /*
4533     Update icon window configuration.
4534   */
4535   windows->icon.crop_geometry=windows->image.crop_geometry;
4536   XBestIconSize(display,&windows->icon,image);
4537   window_changes.width=(int) windows->icon.width;
4538   window_changes.height=(int) windows->icon.height;
4539   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4540     (unsigned int) (CWWidth | CWHeight),&window_changes);
4541   XSetCursorState(display,windows,MagickFalse);
4542   return(IsMagickTrue(status));
4543 }
4544 \f
4545 /*
4546 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4547 %                                                                             %
4548 %                                                                             %
4549 %                                                                             %
4550 +   X C r o p I m a g e                                                       %
4551 %                                                                             %
4552 %                                                                             %
4553 %                                                                             %
4554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4555 %
4556 %  XCropImage() allows the user to select a region of the image and crop, copy,
4557 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4558 %  the image with XPasteImage.
4559 %
4560 %  The format of the XCropImage method is:
4561 %
4562 %      MagickBooleanType XCropImage(Display *display,
4563 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4564 %        const ClipboardMode mode,ExceptionInfo *exception)
4565 %
4566 %  A description of each parameter follows:
4567 %
4568 %    o display: Specifies a connection to an X server; returned from
4569 %      XOpenDisplay.
4570 %
4571 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4572 %
4573 %    o windows: Specifies a pointer to a XWindows structure.
4574 %
4575 %    o image: the image; returned from ReadImage.
4576 %
4577 %    o mode: This unsigned value specified whether the image should be
4578 %      cropped, copied, or cut.
4579 %
4580 %    o exception: return any errors or warnings in this structure.
4581 %
4582 */
4583 static MagickBooleanType XCropImage(Display *display,
4584   XResourceInfo *resource_info,XWindows *windows,Image *image,
4585   const ClipboardMode mode,ExceptionInfo *exception)
4586 {
4587   static const char
4588     *CropModeMenu[] =
4589     {
4590       "Help",
4591       "Dismiss",
4592       (char *) NULL
4593     },
4594     *RectifyModeMenu[] =
4595     {
4596       "Crop",
4597       "Help",
4598       "Dismiss",
4599       (char *) NULL
4600     };
4601
4602   static const ModeType
4603     CropCommands[] =
4604     {
4605       CropHelpCommand,
4606       CropDismissCommand
4607     },
4608     RectifyCommands[] =
4609     {
4610       RectifyCopyCommand,
4611       RectifyHelpCommand,
4612       RectifyDismissCommand
4613     };
4614
4615   CacheView
4616     *image_view;
4617
4618   char
4619     command[MaxTextExtent],
4620     text[MaxTextExtent];
4621
4622   Cursor
4623     cursor;
4624
4625   int
4626     id,
4627     x,
4628     y;
4629
4630   KeySym
4631     key_symbol;
4632
4633   Image
4634     *crop_image;
4635
4636   double
4637     scale_factor;
4638
4639   RectangleInfo
4640     crop_info,
4641     highlight_info;
4642
4643   register Quantum
4644     *q;
4645
4646   unsigned int
4647     height,
4648     width;
4649
4650   size_t
4651     state;
4652
4653   XEvent
4654     event;
4655
4656   /*
4657     Map Command widget.
4658   */
4659   switch (mode)
4660   {
4661     case CopyMode:
4662     {
4663       (void) CloneString(&windows->command.name,"Copy");
4664       break;
4665     }
4666     case CropMode:
4667     {
4668       (void) CloneString(&windows->command.name,"Crop");
4669       break;
4670     }
4671     case CutMode:
4672     {
4673       (void) CloneString(&windows->command.name,"Cut");
4674       break;
4675     }
4676   }
4677   RectifyModeMenu[0]=windows->command.name;
4678   windows->command.data=0;
4679   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4680   (void) XMapRaised(display,windows->command.id);
4681   XClientMessage(display,windows->image.id,windows->im_protocols,
4682     windows->im_update_widget,CurrentTime);
4683   /*
4684     Track pointer until button 1 is pressed.
4685   */
4686   XQueryPosition(display,windows->image.id,&x,&y);
4687   (void) XSelectInput(display,windows->image.id,
4688     windows->image.attributes.event_mask | PointerMotionMask);
4689   crop_info.x=(ssize_t) windows->image.x+x;
4690   crop_info.y=(ssize_t) windows->image.y+y;
4691   crop_info.width=0;
4692   crop_info.height=0;
4693   cursor=XCreateFontCursor(display,XC_fleur);
4694   state=DefaultState;
4695   do
4696   {
4697     if (IfMagickTrue(windows->info.mapped) )
4698       {
4699         /*
4700           Display pointer position.
4701         */
4702         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4703           (long) crop_info.x,(long) crop_info.y);
4704         XInfoWidget(display,windows,text);
4705       }
4706     /*
4707       Wait for next event.
4708     */
4709     XScreenEvent(display,windows,&event,exception);
4710     if (event.xany.window == windows->command.id)
4711       {
4712         /*
4713           Select a command from the Command widget.
4714         */
4715         id=XCommandWidget(display,windows,CropModeMenu,&event);
4716         if (id < 0)
4717           continue;
4718         switch (CropCommands[id])
4719         {
4720           case CropHelpCommand:
4721           {
4722             switch (mode)
4723             {
4724               case CopyMode:
4725               {
4726                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4727                   "Help Viewer - Image Copy",ImageCopyHelp);
4728                 break;
4729               }
4730               case CropMode:
4731               {
4732                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4733                   "Help Viewer - Image Crop",ImageCropHelp);
4734                 break;
4735               }
4736               case CutMode:
4737               {
4738                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4739                   "Help Viewer - Image Cut",ImageCutHelp);
4740                 break;
4741               }
4742             }
4743             break;
4744           }
4745           case CropDismissCommand:
4746           {
4747             /*
4748               Prematurely exit.
4749             */
4750             state|=EscapeState;
4751             state|=ExitState;
4752             break;
4753           }
4754           default:
4755             break;
4756         }
4757         continue;
4758       }
4759     switch (event.type)
4760     {
4761       case ButtonPress:
4762       {
4763         if (event.xbutton.button != Button1)
4764           break;
4765         if (event.xbutton.window != windows->image.id)
4766           break;
4767         /*
4768           Note first corner of cropping rectangle-- exit loop.
4769         */
4770         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4771         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4772         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4773         state|=ExitState;
4774         break;
4775       }
4776       case ButtonRelease:
4777         break;
4778       case Expose:
4779         break;
4780       case KeyPress:
4781       {
4782         if (event.xkey.window != windows->image.id)
4783           break;
4784         /*
4785           Respond to a user key press.
4786         */
4787         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4788           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4789         switch ((int) key_symbol)
4790         {
4791           case XK_Escape:
4792           case XK_F20:
4793           {
4794             /*
4795               Prematurely exit.
4796             */
4797             state|=EscapeState;
4798             state|=ExitState;
4799             break;
4800           }
4801           case XK_F1:
4802           case XK_Help:
4803           {
4804             switch (mode)
4805             {
4806               case CopyMode:
4807               {
4808                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4809                   "Help Viewer - Image Copy",ImageCopyHelp);
4810                 break;
4811               }
4812               case CropMode:
4813               {
4814                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4815                   "Help Viewer - Image Crop",ImageCropHelp);
4816                 break;
4817               }
4818               case CutMode:
4819               {
4820                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4821                   "Help Viewer - Image Cut",ImageCutHelp);
4822                 break;
4823               }
4824             }
4825             break;
4826           }
4827           default:
4828           {
4829             (void) XBell(display,0);
4830             break;
4831           }
4832         }
4833         break;
4834       }
4835       case MotionNotify:
4836       {
4837         if (event.xmotion.window != windows->image.id)
4838           break;
4839         /*
4840           Map and unmap Info widget as text cursor crosses its boundaries.
4841         */
4842         x=event.xmotion.x;
4843         y=event.xmotion.y;
4844         if (IfMagickTrue(windows->info.mapped) )
4845           {
4846             if ((x < (int) (windows->info.x+windows->info.width)) &&
4847                 (y < (int) (windows->info.y+windows->info.height)))
4848               (void) XWithdrawWindow(display,windows->info.id,
4849                 windows->info.screen);
4850           }
4851         else
4852           if ((x > (int) (windows->info.x+windows->info.width)) ||
4853               (y > (int) (windows->info.y+windows->info.height)))
4854             (void) XMapWindow(display,windows->info.id);
4855         crop_info.x=(ssize_t) windows->image.x+x;
4856         crop_info.y=(ssize_t) windows->image.y+y;
4857         break;
4858       }
4859       default:
4860         break;
4861     }
4862   } while ((state & ExitState) == 0);
4863   (void) XSelectInput(display,windows->image.id,
4864     windows->image.attributes.event_mask);
4865   if ((state & EscapeState) != 0)
4866     {
4867       /*
4868         User want to exit without cropping.
4869       */
4870       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4871       (void) XFreeCursor(display,cursor);
4872       return(MagickTrue);
4873     }
4874   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4875   do
4876   {
4877     /*
4878       Size rectangle as pointer moves until the mouse button is released.
4879     */
4880     x=(int) crop_info.x;
4881     y=(int) crop_info.y;
4882     crop_info.width=0;
4883     crop_info.height=0;
4884     state=DefaultState;
4885     do
4886     {
4887       highlight_info=crop_info;
4888       highlight_info.x=crop_info.x-windows->image.x;
4889       highlight_info.y=crop_info.y-windows->image.y;
4890       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4891         {
4892           /*
4893             Display info and draw cropping rectangle.
4894           */
4895           if (IfMagickFalse(windows->info.mapped) )
4896             (void) XMapWindow(display,windows->info.id);
4897           (void) FormatLocaleString(text,MaxTextExtent,
4898             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4899             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4900           XInfoWidget(display,windows,text);
4901           XHighlightRectangle(display,windows->image.id,
4902             windows->image.highlight_context,&highlight_info);
4903         }
4904       else
4905         if (IfMagickTrue(windows->info.mapped) )
4906           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4907       /*
4908         Wait for next event.
4909       */
4910       XScreenEvent(display,windows,&event,exception);
4911       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4912         XHighlightRectangle(display,windows->image.id,
4913           windows->image.highlight_context,&highlight_info);
4914       switch (event.type)
4915       {
4916         case ButtonPress:
4917         {
4918           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4919           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4920           break;
4921         }
4922         case ButtonRelease:
4923         {
4924           /*
4925             User has committed to cropping rectangle.
4926           */
4927           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4928           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4929           XSetCursorState(display,windows,MagickFalse);
4930           state|=ExitState;
4931           windows->command.data=0;
4932           (void) XCommandWidget(display,windows,RectifyModeMenu,
4933             (XEvent *) NULL);
4934           break;
4935         }
4936         case Expose:
4937           break;
4938         case MotionNotify:
4939         {
4940           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4941           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4942         }
4943         default:
4944           break;
4945       }
4946       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4947           ((state & ExitState) != 0))
4948         {
4949           /*
4950             Check boundary conditions.
4951           */
4952           if (crop_info.x < 0)
4953             crop_info.x=0;
4954           else
4955             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4956               crop_info.x=(ssize_t) windows->image.ximage->width;
4957           if ((int) crop_info.x < x)
4958             crop_info.width=(unsigned int) (x-crop_info.x);
4959           else
4960             {
4961               crop_info.width=(unsigned int) (crop_info.x-x);
4962               crop_info.x=(ssize_t) x;
4963             }
4964           if (crop_info.y < 0)
4965             crop_info.y=0;
4966           else
4967             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4968               crop_info.y=(ssize_t) windows->image.ximage->height;
4969           if ((int) crop_info.y < y)
4970             crop_info.height=(unsigned int) (y-crop_info.y);
4971           else
4972             {
4973               crop_info.height=(unsigned int) (crop_info.y-y);
4974               crop_info.y=(ssize_t) y;
4975             }
4976         }
4977     } while ((state & ExitState) == 0);
4978     /*
4979       Wait for user to grab a corner of the rectangle or press return.
4980     */
4981     state=DefaultState;
4982     (void) XMapWindow(display,windows->info.id);
4983     do
4984     {
4985       if (IfMagickTrue(windows->info.mapped) )
4986         {
4987           /*
4988             Display pointer position.
4989           */
4990           (void) FormatLocaleString(text,MaxTextExtent,
4991             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4992             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4993           XInfoWidget(display,windows,text);
4994         }
4995       highlight_info=crop_info;
4996       highlight_info.x=crop_info.x-windows->image.x;
4997       highlight_info.y=crop_info.y-windows->image.y;
4998       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4999         {
5000           state|=EscapeState;
5001           state|=ExitState;
5002           break;
5003         }
5004       XHighlightRectangle(display,windows->image.id,
5005         windows->image.highlight_context,&highlight_info);
5006       XScreenEvent(display,windows,&event,exception);
5007       if (event.xany.window == windows->command.id)
5008         {
5009           /*
5010             Select a command from the Command widget.
5011           */
5012           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5013           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5014           (void) XSetFunction(display,windows->image.highlight_context,
5015             GXinvert);
5016           XHighlightRectangle(display,windows->image.id,
5017             windows->image.highlight_context,&highlight_info);
5018           if (id >= 0)
5019             switch (RectifyCommands[id])
5020             {
5021               case RectifyCopyCommand:
5022               {
5023                 state|=ExitState;
5024                 break;
5025               }
5026               case RectifyHelpCommand:
5027               {
5028                 (void) XSetFunction(display,windows->image.highlight_context,
5029                   GXcopy);
5030                 switch (mode)
5031                 {
5032                   case CopyMode:
5033                   {
5034                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5035                       "Help Viewer - Image Copy",ImageCopyHelp);
5036                     break;
5037                   }
5038                   case CropMode:
5039                   {
5040                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5041                       "Help Viewer - Image Crop",ImageCropHelp);
5042                     break;
5043                   }
5044                   case CutMode:
5045                   {
5046                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5047                       "Help Viewer - Image Cut",ImageCutHelp);
5048                     break;
5049                   }
5050                 }
5051                 (void) XSetFunction(display,windows->image.highlight_context,
5052                   GXinvert);
5053                 break;
5054               }
5055               case RectifyDismissCommand:
5056               {
5057                 /*
5058                   Prematurely exit.
5059                 */
5060                 state|=EscapeState;
5061                 state|=ExitState;
5062                 break;
5063               }
5064               default:
5065                 break;
5066             }
5067           continue;
5068         }
5069       XHighlightRectangle(display,windows->image.id,
5070         windows->image.highlight_context,&highlight_info);
5071       switch (event.type)
5072       {
5073         case ButtonPress:
5074         {
5075           if (event.xbutton.button != Button1)
5076             break;
5077           if (event.xbutton.window != windows->image.id)
5078             break;
5079           x=windows->image.x+event.xbutton.x;
5080           y=windows->image.y+event.xbutton.y;
5081           if ((x < (int) (crop_info.x+RoiDelta)) &&
5082               (x > (int) (crop_info.x-RoiDelta)) &&
5083               (y < (int) (crop_info.y+RoiDelta)) &&
5084               (y > (int) (crop_info.y-RoiDelta)))
5085             {
5086               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5087               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5088               state|=UpdateConfigurationState;
5089               break;
5090             }
5091           if ((x < (int) (crop_info.x+RoiDelta)) &&
5092               (x > (int) (crop_info.x-RoiDelta)) &&
5093               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5094               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5095             {
5096               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5097               state|=UpdateConfigurationState;
5098               break;
5099             }
5100           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5101               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5102               (y < (int) (crop_info.y+RoiDelta)) &&
5103               (y > (int) (crop_info.y-RoiDelta)))
5104             {
5105               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5106               state|=UpdateConfigurationState;
5107               break;
5108             }
5109           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5110               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5111               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5112               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5113             {
5114               state|=UpdateConfigurationState;
5115               break;
5116             }
5117         }
5118         case ButtonRelease:
5119         {
5120           if (event.xbutton.window == windows->pan.id)
5121             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5122                 (highlight_info.y != crop_info.y-windows->image.y))
5123               XHighlightRectangle(display,windows->image.id,
5124                 windows->image.highlight_context,&highlight_info);
5125           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5126             event.xbutton.time);
5127           break;
5128         }
5129         case Expose:
5130         {
5131           if (event.xexpose.window == windows->image.id)
5132             if (event.xexpose.count == 0)
5133               {
5134                 event.xexpose.x=(int) highlight_info.x;
5135                 event.xexpose.y=(int) highlight_info.y;
5136                 event.xexpose.width=(int) highlight_info.width;
5137                 event.xexpose.height=(int) highlight_info.height;
5138                 XRefreshWindow(display,&windows->image,&event);
5139               }
5140           if (event.xexpose.window == windows->info.id)
5141             if (event.xexpose.count == 0)
5142               XInfoWidget(display,windows,text);
5143           break;
5144         }
5145         case KeyPress:
5146         {
5147           if (event.xkey.window != windows->image.id)
5148             break;
5149           /*
5150             Respond to a user key press.
5151           */
5152           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5153             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5154           switch ((int) key_symbol)
5155           {
5156             case XK_Escape:
5157             case XK_F20:
5158               state|=EscapeState;
5159             case XK_Return:
5160             {
5161               state|=ExitState;
5162               break;
5163             }
5164             case XK_Home:
5165             case XK_KP_Home:
5166             {
5167               crop_info.x=(ssize_t) (windows->image.width/2L-
5168                 crop_info.width/2L);
5169               crop_info.y=(ssize_t) (windows->image.height/2L-
5170                 crop_info.height/2L);
5171               break;
5172             }
5173             case XK_Left:
5174             case XK_KP_Left:
5175             {
5176               crop_info.x--;
5177               break;
5178             }
5179             case XK_Up:
5180             case XK_KP_Up:
5181             case XK_Next:
5182             {
5183               crop_info.y--;
5184               break;
5185             }
5186             case XK_Right:
5187             case XK_KP_Right:
5188             {
5189               crop_info.x++;
5190               break;
5191             }
5192             case XK_Prior:
5193             case XK_Down:
5194             case XK_KP_Down:
5195             {
5196               crop_info.y++;
5197               break;
5198             }
5199             case XK_F1:
5200             case XK_Help:
5201             {
5202               (void) XSetFunction(display,windows->image.highlight_context,
5203                 GXcopy);
5204               switch (mode)
5205               {
5206                 case CopyMode:
5207                 {
5208                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5209                     "Help Viewer - Image Copy",ImageCopyHelp);
5210                   break;
5211                 }
5212                 case CropMode:
5213                 {
5214                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5215                     "Help Viewer - Image Cropg",ImageCropHelp);
5216                   break;
5217                 }
5218                 case CutMode:
5219                 {
5220                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5221                     "Help Viewer - Image Cutg",ImageCutHelp);
5222                   break;
5223                 }
5224               }
5225               (void) XSetFunction(display,windows->image.highlight_context,
5226                 GXinvert);
5227               break;
5228             }
5229             default:
5230             {
5231               (void) XBell(display,0);
5232               break;
5233             }
5234           }
5235           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5236             event.xkey.time);
5237           break;
5238         }
5239         case KeyRelease:
5240           break;
5241         case MotionNotify:
5242         {
5243           if (event.xmotion.window != windows->image.id)
5244             break;
5245           /*
5246             Map and unmap Info widget as text cursor crosses its boundaries.
5247           */
5248           x=event.xmotion.x;
5249           y=event.xmotion.y;
5250           if (IfMagickTrue(windows->info.mapped) )
5251             {
5252               if ((x < (int) (windows->info.x+windows->info.width)) &&
5253                   (y < (int) (windows->info.y+windows->info.height)))
5254                 (void) XWithdrawWindow(display,windows->info.id,
5255                   windows->info.screen);
5256             }
5257           else
5258             if ((x > (int) (windows->info.x+windows->info.width)) ||
5259                 (y > (int) (windows->info.y+windows->info.height)))
5260               (void) XMapWindow(display,windows->info.id);
5261           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5262           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5263           break;
5264         }
5265         case SelectionRequest:
5266         {
5267           XSelectionEvent
5268             notify;
5269
5270           XSelectionRequestEvent
5271             *request;
5272
5273           /*
5274             Set primary selection.
5275           */
5276           (void) FormatLocaleString(text,MaxTextExtent,
5277             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5278             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5279           request=(&(event.xselectionrequest));
5280           (void) XChangeProperty(request->display,request->requestor,
5281             request->property,request->target,8,PropModeReplace,
5282             (unsigned char *) text,(int) strlen(text));
5283           notify.type=SelectionNotify;
5284           notify.display=request->display;
5285           notify.requestor=request->requestor;
5286           notify.selection=request->selection;
5287           notify.target=request->target;
5288           notify.time=request->time;
5289           if (request->property == None)
5290             notify.property=request->target;
5291           else
5292             notify.property=request->property;
5293           (void) XSendEvent(request->display,request->requestor,False,0,
5294             (XEvent *) &notify);
5295         }
5296         default:
5297           break;
5298       }
5299       if ((state & UpdateConfigurationState) != 0)
5300         {
5301           (void) XPutBackEvent(display,&event);
5302           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5303           break;
5304         }
5305     } while ((state & ExitState) == 0);
5306   } while ((state & ExitState) == 0);
5307   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5308   XSetCursorState(display,windows,MagickFalse);
5309   if ((state & EscapeState) != 0)
5310     return(MagickTrue);
5311   if (mode == CropMode)
5312     if (((int) crop_info.width != windows->image.ximage->width) ||
5313         ((int) crop_info.height != windows->image.ximage->height))
5314       {
5315         /*
5316           Reconfigure Image window as defined by cropping rectangle.
5317         */
5318         XSetCropGeometry(display,windows,&crop_info,image);
5319         windows->image.window_changes.width=(int) crop_info.width;
5320         windows->image.window_changes.height=(int) crop_info.height;
5321         (void) XConfigureImage(display,resource_info,windows,image,exception);
5322         return(MagickTrue);
5323       }
5324   /*
5325     Copy image before applying image transforms.
5326   */
5327   XSetCursorState(display,windows,MagickTrue);
5328   XCheckRefreshWindows(display,windows);
5329   width=(unsigned int) image->columns;
5330   height=(unsigned int) image->rows;
5331   x=0;
5332   y=0;
5333   if (windows->image.crop_geometry != (char *) NULL)
5334     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5335   scale_factor=(double) width/windows->image.ximage->width;
5336   crop_info.x+=x;
5337   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5338   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5339   scale_factor=(double) height/windows->image.ximage->height;
5340   crop_info.y+=y;
5341   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5342   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5343   crop_image=CropImage(image,&crop_info,exception);
5344   XSetCursorState(display,windows,MagickFalse);
5345   if (crop_image == (Image *) NULL)
5346     return(MagickFalse);
5347   if (resource_info->copy_image != (Image *) NULL)
5348     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5349   resource_info->copy_image=crop_image;
5350   if (mode == CopyMode)
5351     {
5352       (void) XConfigureImage(display,resource_info,windows,image,exception);
5353       return(MagickTrue);
5354     }
5355   /*
5356     Cut image.
5357   */
5358   if (IfMagickFalse(SetImageStorageClass(image,DirectClass,exception)) )
5359     return(MagickFalse);
5360   image->alpha_trait=BlendPixelTrait;
5361   image_view=AcquireAuthenticCacheView(image,exception);
5362   for (y=0; y < (int) crop_info.height; y++)
5363   {
5364     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5365       crop_info.width,1,exception);
5366     if (q == (Quantum *) NULL)
5367       break;
5368     for (x=0; x < (int) crop_info.width; x++)
5369     {
5370       SetPixelAlpha(image,TransparentAlpha,q);
5371       q+=GetPixelChannels(image);
5372     }
5373     if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
5374       break;
5375   }
5376   image_view=DestroyCacheView(image_view);
5377   /*
5378     Update image configuration.
5379   */
5380   XConfigureImageColormap(display,resource_info,windows,image,exception);
5381   (void) XConfigureImage(display,resource_info,windows,image,exception);
5382   return(MagickTrue);
5383 }
5384 \f
5385 /*
5386 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5387 %                                                                             %
5388 %                                                                             %
5389 %                                                                             %
5390 +   X D r a w I m a g e                                                       %
5391 %                                                                             %
5392 %                                                                             %
5393 %                                                                             %
5394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5395 %
5396 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5397 %  the image.
5398 %
5399 %  The format of the XDrawEditImage method is:
5400 %
5401 %      MagickBooleanType XDrawEditImage(Display *display,
5402 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5403 %        ExceptionInfo *exception)
5404 %
5405 %  A description of each parameter follows:
5406 %
5407 %    o display: Specifies a connection to an X server; returned from
5408 %      XOpenDisplay.
5409 %
5410 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5411 %
5412 %    o windows: Specifies a pointer to a XWindows structure.
5413 %
5414 %    o image: the image.
5415 %
5416 %    o exception: return any errors or warnings in this structure.
5417 %
5418 */
5419 static MagickBooleanType XDrawEditImage(Display *display,
5420   XResourceInfo *resource_info,XWindows *windows,Image **image,
5421   ExceptionInfo *exception)
5422 {
5423   static const char
5424     *DrawMenu[] =
5425     {
5426       "Element",
5427       "Color",
5428       "Stipple",
5429       "Width",
5430       "Undo",
5431       "Help",
5432       "Dismiss",
5433       (char *) NULL
5434     };
5435
5436   static ElementType
5437     element = PointElement;
5438
5439   static const ModeType
5440     DrawCommands[] =
5441     {
5442       DrawElementCommand,
5443       DrawColorCommand,
5444       DrawStippleCommand,
5445       DrawWidthCommand,
5446       DrawUndoCommand,
5447       DrawHelpCommand,
5448       DrawDismissCommand
5449     };
5450
5451   static Pixmap
5452     stipple = (Pixmap) NULL;
5453
5454   static unsigned int
5455     pen_id = 0,
5456     line_width = 1;
5457
5458   char
5459     command[MaxTextExtent],
5460     text[MaxTextExtent];
5461
5462   Cursor
5463     cursor;
5464
5465   int
5466     entry,
5467     id,
5468     number_coordinates,
5469     x,
5470     y;
5471
5472   double
5473     degrees;
5474
5475   MagickStatusType
5476     status;
5477
5478   RectangleInfo
5479     rectangle_info;
5480
5481   register int
5482     i;
5483
5484   unsigned int
5485     distance,
5486     height,
5487     max_coordinates,
5488     width;
5489
5490   size_t
5491     state;
5492
5493   Window
5494     root_window;
5495
5496   XDrawInfo
5497     draw_info;
5498
5499   XEvent
5500     event;
5501
5502   XPoint
5503     *coordinate_info;
5504
5505   XSegment
5506     line_info;
5507
5508   /*
5509     Allocate polygon info.
5510   */
5511   max_coordinates=2048;
5512   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5513     sizeof(*coordinate_info));
5514   if (coordinate_info == (XPoint *) NULL)
5515     {
5516       (void) ThrowMagickException(exception,GetMagickModule(),
5517         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5518       return(MagickFalse);
5519     }
5520   /*
5521     Map Command widget.
5522   */
5523   (void) CloneString(&windows->command.name,"Draw");
5524   windows->command.data=4;
5525   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5526   (void) XMapRaised(display,windows->command.id);
5527   XClientMessage(display,windows->image.id,windows->im_protocols,
5528     windows->im_update_widget,CurrentTime);
5529   /*
5530     Wait for first button press.
5531   */
5532   root_window=XRootWindow(display,XDefaultScreen(display));
5533   draw_info.stencil=OpaqueStencil;
5534   status=MagickTrue;
5535   cursor=XCreateFontCursor(display,XC_tcross);
5536   for ( ; ; )
5537   {
5538     XQueryPosition(display,windows->image.id,&x,&y);
5539     (void) XSelectInput(display,windows->image.id,
5540       windows->image.attributes.event_mask | PointerMotionMask);
5541     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5542     state=DefaultState;
5543     do
5544     {
5545       if (IfMagickTrue(windows->info.mapped) )
5546         {
5547           /*
5548             Display pointer position.
5549           */
5550           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5551             x+windows->image.x,y+windows->image.y);
5552           XInfoWidget(display,windows,text);
5553         }
5554       /*
5555         Wait for next event.
5556       */
5557       XScreenEvent(display,windows,&event,exception);
5558       if (event.xany.window == windows->command.id)
5559         {
5560           /*
5561             Select a command from the Command widget.
5562           */
5563           id=XCommandWidget(display,windows,DrawMenu,&event);
5564           if (id < 0)
5565             continue;
5566           switch (DrawCommands[id])
5567           {
5568             case DrawElementCommand:
5569             {
5570               static const char
5571                 *Elements[] =
5572                 {
5573                   "point",
5574                   "line",
5575                   "rectangle",
5576                   "fill rectangle",
5577                   "circle",
5578                   "fill circle",
5579                   "ellipse",
5580                   "fill ellipse",
5581                   "polygon",
5582                   "fill polygon",
5583                   (char *) NULL,
5584                 };
5585
5586               /*
5587                 Select a command from the pop-up menu.
5588               */
5589               element=(ElementType) (XMenuWidget(display,windows,
5590                 DrawMenu[id],Elements,command)+1);
5591               break;
5592             }
5593             case DrawColorCommand:
5594             {
5595               const char
5596                 *ColorMenu[MaxNumberPens+1];
5597
5598               int
5599                 pen_number;
5600
5601               MagickBooleanType
5602                 transparent;
5603
5604               XColor
5605                 color;
5606
5607               /*
5608                 Initialize menu selections.
5609               */
5610               for (i=0; i < (int) (MaxNumberPens-2); i++)
5611                 ColorMenu[i]=resource_info->pen_colors[i];
5612               ColorMenu[MaxNumberPens-2]="transparent";
5613               ColorMenu[MaxNumberPens-1]="Browser...";
5614               ColorMenu[MaxNumberPens]=(char *) NULL;
5615               /*
5616                 Select a pen color from the pop-up menu.
5617               */
5618               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5619                 (const char **) ColorMenu,command);
5620               if (pen_number < 0)
5621                 break;
5622               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5623                 MagickFalse;
5624               if (IfMagickTrue(transparent) )
5625                 {
5626                   draw_info.stencil=TransparentStencil;
5627                   break;
5628                 }
5629               if (pen_number == (MaxNumberPens-1))
5630                 {
5631                   static char
5632                     color_name[MaxTextExtent] = "gray";
5633
5634                   /*
5635                     Select a pen color from a dialog.
5636                   */
5637                   resource_info->pen_colors[pen_number]=color_name;
5638                   XColorBrowserWidget(display,windows,"Select",color_name);
5639                   if (*color_name == '\0')
5640                     break;
5641                 }
5642               /*
5643                 Set pen color.
5644               */
5645               (void) XParseColor(display,windows->map_info->colormap,
5646                 resource_info->pen_colors[pen_number],&color);
5647               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5648                 (unsigned int) MaxColors,&color);
5649               windows->pixel_info->pen_colors[pen_number]=color;
5650               pen_id=(unsigned int) pen_number;
5651               draw_info.stencil=OpaqueStencil;
5652               break;
5653             }
5654             case DrawStippleCommand:
5655             {
5656               Image
5657                 *stipple_image;
5658
5659               ImageInfo
5660                 *image_info;
5661
5662               int
5663                 status;
5664
5665               static char
5666                 filename[MaxTextExtent] = "\0";
5667
5668               static const char
5669                 *StipplesMenu[] =
5670                 {
5671                   "Brick",
5672                   "Diagonal",
5673                   "Scales",
5674                   "Vertical",
5675                   "Wavy",
5676                   "Translucent",
5677                   "Opaque",
5678                   (char *) NULL,
5679                   (char *) NULL,
5680                 };
5681
5682               /*
5683                 Select a command from the pop-up menu.
5684               */
5685               StipplesMenu[7]="Open...";
5686               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5687                 command);
5688               if (entry < 0)
5689                 break;
5690               if (stipple != (Pixmap) NULL)
5691                 (void) XFreePixmap(display,stipple);
5692               stipple=(Pixmap) NULL;
5693               if (entry != 7)
5694                 {
5695                   switch (entry)
5696                   {
5697                     case 0:
5698                     {
5699                       stipple=XCreateBitmapFromData(display,root_window,
5700                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5701                       break;
5702                     }
5703                     case 1:
5704                     {
5705                       stipple=XCreateBitmapFromData(display,root_window,
5706                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5707                       break;
5708                     }
5709                     case 2:
5710                     {
5711                       stipple=XCreateBitmapFromData(display,root_window,
5712                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5713                       break;
5714                     }
5715                     case 3:
5716                     {
5717                       stipple=XCreateBitmapFromData(display,root_window,
5718                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5719                       break;
5720                     }
5721                     case 4:
5722                     {
5723                       stipple=XCreateBitmapFromData(display,root_window,
5724                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5725                       break;
5726                     }
5727                     case 5:
5728                     {
5729                       stipple=XCreateBitmapFromData(display,root_window,
5730                         (char *) HighlightBitmap,HighlightWidth,
5731                         HighlightHeight);
5732                       break;
5733                     }
5734                     case 6:
5735                     default:
5736                     {
5737                       stipple=XCreateBitmapFromData(display,root_window,
5738                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5739                       break;
5740                     }
5741                   }
5742                   break;
5743                 }
5744               XFileBrowserWidget(display,windows,"Stipple",filename);
5745               if (*filename == '\0')
5746                 break;
5747               /*
5748                 Read image.
5749               */
5750               XSetCursorState(display,windows,MagickTrue);
5751               XCheckRefreshWindows(display,windows);
5752               image_info=AcquireImageInfo();
5753               (void) CopyMagickString(image_info->filename,filename,
5754                 MaxTextExtent);
5755               stipple_image=ReadImage(image_info,exception);
5756               CatchException(exception);
5757               XSetCursorState(display,windows,MagickFalse);
5758               if (stipple_image == (Image *) NULL)
5759                 break;
5760               (void) AcquireUniqueFileResource(filename);
5761               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5762                 "xbm:%s",filename);
5763               (void) WriteImage(image_info,stipple_image,exception);
5764               stipple_image=DestroyImage(stipple_image);
5765               image_info=DestroyImageInfo(image_info);
5766               status=XReadBitmapFile(display,root_window,filename,&width,
5767                 &height,&stipple,&x,&y);
5768               (void) RelinquishUniqueFileResource(filename);
5769               if ((status != BitmapSuccess) != 0)
5770                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5771                   filename);
5772               break;
5773             }
5774             case DrawWidthCommand:
5775             {
5776               static char
5777                 width[MaxTextExtent] = "0";
5778
5779               static const char
5780                 *WidthsMenu[] =
5781                 {
5782                   "1",
5783                   "2",
5784                   "4",
5785                   "8",
5786                   "16",
5787                   "Dialog...",
5788                   (char *) NULL,
5789                 };
5790
5791               /*
5792                 Select a command from the pop-up menu.
5793               */
5794               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5795                 command);
5796               if (entry < 0)
5797                 break;
5798               if (entry != 5)
5799                 {
5800                   line_width=(unsigned int) StringToUnsignedLong(
5801                     WidthsMenu[entry]);
5802                   break;
5803                 }
5804               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5805                 width);
5806               if (*width == '\0')
5807                 break;
5808               line_width=(unsigned int) StringToUnsignedLong(width);
5809               break;
5810             }
5811             case DrawUndoCommand:
5812             {
5813               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5814                 image,exception);
5815               break;
5816             }
5817             case DrawHelpCommand:
5818             {
5819               XTextViewWidget(display,resource_info,windows,MagickFalse,
5820                 "Help Viewer - Image Rotation",ImageDrawHelp);
5821               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5822               break;
5823             }
5824             case DrawDismissCommand:
5825             {
5826               /*
5827                 Prematurely exit.
5828               */
5829               state|=EscapeState;
5830               state|=ExitState;
5831               break;
5832             }
5833             default:
5834               break;
5835           }
5836           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5837           continue;
5838         }
5839       switch (event.type)
5840       {
5841         case ButtonPress:
5842         {
5843           if (event.xbutton.button != Button1)
5844             break;
5845           if (event.xbutton.window != windows->image.id)
5846             break;
5847           /*
5848             exit loop.
5849           */
5850           x=event.xbutton.x;
5851           y=event.xbutton.y;
5852           state|=ExitState;
5853           break;
5854         }
5855         case ButtonRelease:
5856           break;
5857         case Expose:
5858           break;
5859         case KeyPress:
5860         {
5861           KeySym
5862             key_symbol;
5863
5864           if (event.xkey.window != windows->image.id)
5865             break;
5866           /*
5867             Respond to a user key press.
5868           */
5869           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5870             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5871           switch ((int) key_symbol)
5872           {
5873             case XK_Escape:
5874             case XK_F20:
5875             {
5876               /*
5877                 Prematurely exit.
5878               */
5879               state|=EscapeState;
5880               state|=ExitState;
5881               break;
5882             }
5883             case XK_F1:
5884             case XK_Help:
5885             {
5886               XTextViewWidget(display,resource_info,windows,MagickFalse,
5887                 "Help Viewer - Image Rotation",ImageDrawHelp);
5888               break;
5889             }
5890             default:
5891             {
5892               (void) XBell(display,0);
5893               break;
5894             }
5895           }
5896           break;
5897         }
5898         case MotionNotify:
5899         {
5900           /*
5901             Map and unmap Info widget as text cursor crosses its boundaries.
5902           */
5903           x=event.xmotion.x;
5904           y=event.xmotion.y;
5905           if (IfMagickTrue(windows->info.mapped) )
5906             {
5907               if ((x < (int) (windows->info.x+windows->info.width)) &&
5908                   (y < (int) (windows->info.y+windows->info.height)))
5909                 (void) XWithdrawWindow(display,windows->info.id,
5910                   windows->info.screen);
5911             }
5912           else
5913             if ((x > (int) (windows->info.x+windows->info.width)) ||
5914                 (y > (int) (windows->info.y+windows->info.height)))
5915               (void) XMapWindow(display,windows->info.id);
5916           break;
5917         }
5918       }
5919     } while ((state & ExitState) == 0);
5920     (void) XSelectInput(display,windows->image.id,
5921       windows->image.attributes.event_mask);
5922     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5923     if ((state & EscapeState) != 0)
5924       break;
5925     /*
5926       Draw element as pointer moves until the button is released.
5927     */
5928     distance=0;
5929     degrees=0.0;
5930     line_info.x1=x;
5931     line_info.y1=y;
5932     line_info.x2=x;
5933     line_info.y2=y;
5934     rectangle_info.x=(ssize_t) x;
5935     rectangle_info.y=(ssize_t) y;
5936     rectangle_info.width=0;
5937     rectangle_info.height=0;
5938     number_coordinates=1;
5939     coordinate_info->x=x;
5940     coordinate_info->y=y;
5941     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5942     state=DefaultState;
5943     do
5944     {
5945       switch (element)
5946       {
5947         case PointElement:
5948         default:
5949         {
5950           if (number_coordinates > 1)
5951             {
5952               (void) XDrawLines(display,windows->image.id,
5953                 windows->image.highlight_context,coordinate_info,
5954                 number_coordinates,CoordModeOrigin);
5955               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5956                 coordinate_info[number_coordinates-1].x,
5957                 coordinate_info[number_coordinates-1].y);
5958               XInfoWidget(display,windows,text);
5959             }
5960           break;
5961         }
5962         case LineElement:
5963         {
5964           if (distance > 9)
5965             {
5966               /*
5967                 Display angle of the line.
5968               */
5969               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5970                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5971               (void) FormatLocaleString(text,MaxTextExtent," %g",
5972                 (double) degrees);
5973               XInfoWidget(display,windows,text);
5974               XHighlightLine(display,windows->image.id,
5975                 windows->image.highlight_context,&line_info);
5976             }
5977           else
5978             if (IfMagickTrue(windows->info.mapped) )
5979               (void) XWithdrawWindow(display,windows->info.id,
5980                 windows->info.screen);
5981           break;
5982         }
5983         case RectangleElement:
5984         case FillRectangleElement:
5985         {
5986           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5987             {
5988               /*
5989                 Display info and draw drawing rectangle.
5990               */
5991               (void) FormatLocaleString(text,MaxTextExtent,
5992                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5993                 (double) rectangle_info.height,(double) rectangle_info.x,
5994                 (double) rectangle_info.y);
5995               XInfoWidget(display,windows,text);
5996               XHighlightRectangle(display,windows->image.id,
5997                 windows->image.highlight_context,&rectangle_info);
5998             }
5999           else
6000             if (IfMagickTrue(windows->info.mapped) )
6001               (void) XWithdrawWindow(display,windows->info.id,
6002                 windows->info.screen);
6003           break;
6004         }
6005         case CircleElement:
6006         case FillCircleElement:
6007         case EllipseElement:
6008         case FillEllipseElement:
6009         {
6010           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6011             {
6012               /*
6013                 Display info and draw drawing rectangle.
6014               */
6015               (void) FormatLocaleString(text,MaxTextExtent,
6016                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6017                 (double) rectangle_info.height,(double) rectangle_info.x,
6018                 (double) rectangle_info.y);
6019               XInfoWidget(display,windows,text);
6020               XHighlightEllipse(display,windows->image.id,
6021                 windows->image.highlight_context,&rectangle_info);
6022             }
6023           else
6024             if (IfMagickTrue(windows->info.mapped) )
6025               (void) XWithdrawWindow(display,windows->info.id,
6026                 windows->info.screen);
6027           break;
6028         }
6029         case PolygonElement:
6030         case FillPolygonElement:
6031         {
6032           if (number_coordinates > 1)
6033             (void) XDrawLines(display,windows->image.id,
6034               windows->image.highlight_context,coordinate_info,
6035               number_coordinates,CoordModeOrigin);
6036           if (distance > 9)
6037             {
6038               /*
6039                 Display angle of the line.
6040               */
6041               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6042                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6043               (void) FormatLocaleString(text,MaxTextExtent," %g",
6044                 (double) degrees);
6045               XInfoWidget(display,windows,text);
6046               XHighlightLine(display,windows->image.id,
6047                 windows->image.highlight_context,&line_info);
6048             }
6049           else
6050             if (IfMagickTrue(windows->info.mapped) )
6051               (void) XWithdrawWindow(display,windows->info.id,
6052                 windows->info.screen);
6053           break;
6054         }
6055       }
6056       /*
6057         Wait for next event.
6058       */
6059       XScreenEvent(display,windows,&event,exception);
6060       switch (element)
6061       {
6062         case PointElement:
6063         default:
6064         {
6065           if (number_coordinates > 1)
6066             (void) XDrawLines(display,windows->image.id,
6067               windows->image.highlight_context,coordinate_info,
6068               number_coordinates,CoordModeOrigin);
6069           break;
6070         }
6071         case LineElement:
6072         {
6073           if (distance > 9)
6074             XHighlightLine(display,windows->image.id,
6075               windows->image.highlight_context,&line_info);
6076           break;
6077         }
6078         case RectangleElement:
6079         case FillRectangleElement:
6080         {
6081           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6082             XHighlightRectangle(display,windows->image.id,
6083               windows->image.highlight_context,&rectangle_info);
6084           break;
6085         }
6086         case CircleElement:
6087         case FillCircleElement:
6088         case EllipseElement:
6089         case FillEllipseElement:
6090         {
6091           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6092             XHighlightEllipse(display,windows->image.id,
6093               windows->image.highlight_context,&rectangle_info);
6094           break;
6095         }
6096         case PolygonElement:
6097         case FillPolygonElement:
6098         {
6099           if (number_coordinates > 1)
6100             (void) XDrawLines(display,windows->image.id,
6101               windows->image.highlight_context,coordinate_info,
6102               number_coordinates,CoordModeOrigin);
6103           if (distance > 9)
6104             XHighlightLine(display,windows->image.id,
6105               windows->image.highlight_context,&line_info);
6106           break;
6107         }
6108       }
6109       switch (event.type)
6110       {
6111         case ButtonPress:
6112           break;
6113         case ButtonRelease:
6114         {
6115           /*
6116             User has committed to element.
6117           */
6118           line_info.x2=event.xbutton.x;
6119           line_info.y2=event.xbutton.y;
6120           rectangle_info.x=(ssize_t) event.xbutton.x;
6121           rectangle_info.y=(ssize_t) event.xbutton.y;
6122           coordinate_info[number_coordinates].x=event.xbutton.x;
6123           coordinate_info[number_coordinates].y=event.xbutton.y;
6124           if (((element != PolygonElement) &&
6125                (element != FillPolygonElement)) || (distance <= 9))
6126             {
6127               state|=ExitState;
6128               break;
6129             }
6130           number_coordinates++;
6131           if (number_coordinates < (int) max_coordinates)
6132             {
6133               line_info.x1=event.xbutton.x;
6134               line_info.y1=event.xbutton.y;
6135               break;
6136             }
6137           max_coordinates<<=1;
6138           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6139             max_coordinates,sizeof(*coordinate_info));
6140           if (coordinate_info == (XPoint *) NULL)
6141             (void) ThrowMagickException(exception,GetMagickModule(),
6142               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6143           break;
6144         }
6145         case Expose:
6146           break;
6147         case MotionNotify:
6148         {
6149           if (event.xmotion.window != windows->image.id)
6150             break;
6151           if (element != PointElement)
6152             {
6153               line_info.x2=event.xmotion.x;
6154               line_info.y2=event.xmotion.y;
6155               rectangle_info.x=(ssize_t) event.xmotion.x;
6156               rectangle_info.y=(ssize_t) event.xmotion.y;
6157               break;
6158             }
6159           coordinate_info[number_coordinates].x=event.xbutton.x;
6160           coordinate_info[number_coordinates].y=event.xbutton.y;
6161           number_coordinates++;
6162           if (number_coordinates < (int) max_coordinates)
6163             break;
6164           max_coordinates<<=1;
6165           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6166             max_coordinates,sizeof(*coordinate_info));
6167           if (coordinate_info == (XPoint *) NULL)
6168             (void) ThrowMagickException(exception,GetMagickModule(),
6169               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6170           break;
6171         }
6172         default:
6173           break;
6174       }
6175       /*
6176         Check boundary conditions.
6177       */
6178       if (line_info.x2 < 0)
6179         line_info.x2=0;
6180       else
6181         if (line_info.x2 > (int) windows->image.width)
6182           line_info.x2=(short) windows->image.width;
6183       if (line_info.y2 < 0)
6184         line_info.y2=0;
6185       else
6186         if (line_info.y2 > (int) windows->image.height)
6187           line_info.y2=(short) windows->image.height;
6188       distance=(unsigned int)
6189         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6190          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6191       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6192           ((state & ExitState) != 0))
6193         {
6194           if (rectangle_info.x < 0)
6195             rectangle_info.x=0;
6196           else
6197             if (rectangle_info.x > (ssize_t) windows->image.width)
6198               rectangle_info.x=(ssize_t) windows->image.width;
6199           if ((int) rectangle_info.x < x)
6200             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6201           else
6202             {
6203               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6204               rectangle_info.x=(ssize_t) x;
6205             }
6206           if (rectangle_info.y < 0)
6207             rectangle_info.y=0;
6208           else
6209             if (rectangle_info.y > (ssize_t) windows->image.height)
6210               rectangle_info.y=(ssize_t) windows->image.height;
6211           if ((int) rectangle_info.y < y)
6212             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6213           else
6214             {
6215               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6216               rectangle_info.y=(ssize_t) y;
6217             }
6218         }
6219     } while ((state & ExitState) == 0);
6220     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6221     if ((element == PointElement) || (element == PolygonElement) ||
6222         (element == FillPolygonElement))
6223       {
6224         /*
6225           Determine polygon bounding box.
6226         */
6227         rectangle_info.x=(ssize_t) coordinate_info->x;
6228         rectangle_info.y=(ssize_t) coordinate_info->y;
6229         x=coordinate_info->x;
6230         y=coordinate_info->y;
6231         for (i=1; i < number_coordinates; i++)
6232         {
6233           if (coordinate_info[i].x > x)
6234             x=coordinate_info[i].x;
6235           if (coordinate_info[i].y > y)
6236             y=coordinate_info[i].y;
6237           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6238             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6239           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6240             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6241         }
6242         rectangle_info.width=(size_t) (x-rectangle_info.x);
6243         rectangle_info.height=(size_t) (y-rectangle_info.y);
6244         for (i=0; i < number_coordinates; i++)
6245         {
6246           coordinate_info[i].x-=rectangle_info.x;
6247           coordinate_info[i].y-=rectangle_info.y;
6248         }
6249       }
6250     else
6251       if (distance <= 9)
6252         continue;
6253       else
6254         if ((element == RectangleElement) ||
6255             (element == CircleElement) || (element == EllipseElement))
6256           {
6257             rectangle_info.width--;
6258             rectangle_info.height--;
6259           }
6260     /*
6261       Drawing is relative to image configuration.
6262     */
6263     draw_info.x=(int) rectangle_info.x;
6264     draw_info.y=(int) rectangle_info.y;
6265     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6266       image,exception);
6267     width=(unsigned int) (*image)->columns;
6268     height=(unsigned int) (*image)->rows;
6269     x=0;
6270     y=0;
6271     if (windows->image.crop_geometry != (char *) NULL)
6272       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6273     draw_info.x+=windows->image.x-(line_width/2);
6274     if (draw_info.x < 0)
6275       draw_info.x=0;
6276     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6277     draw_info.y+=windows->image.y-(line_width/2);
6278     if (draw_info.y < 0)
6279       draw_info.y=0;
6280     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6281     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6282     if (draw_info.width > (unsigned int) (*image)->columns)
6283       draw_info.width=(unsigned int) (*image)->columns;
6284     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6285     if (draw_info.height > (unsigned int) (*image)->rows)
6286       draw_info.height=(unsigned int) (*image)->rows;
6287     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6288       width*draw_info.width/windows->image.ximage->width,
6289       height*draw_info.height/windows->image.ximage->height,
6290       draw_info.x+x,draw_info.y+y);
6291     /*
6292       Initialize drawing attributes.
6293     */
6294     draw_info.degrees=0.0;
6295     draw_info.element=element;
6296     draw_info.stipple=stipple;
6297     draw_info.line_width=line_width;
6298     draw_info.line_info=line_info;
6299     if (line_info.x1 > (int) (line_width/2))
6300       draw_info.line_info.x1=(short) line_width/2;
6301     if (line_info.y1 > (int) (line_width/2))
6302       draw_info.line_info.y1=(short) line_width/2;
6303     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6304     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6305     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6306       {
6307         draw_info.line_info.x2=(-draw_info.line_info.x2);
6308         draw_info.line_info.y2=(-draw_info.line_info.y2);
6309       }
6310     if (draw_info.line_info.x2 < 0)
6311       {
6312         draw_info.line_info.x2=(-draw_info.line_info.x2);
6313         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6314       }
6315     if (draw_info.line_info.y2 < 0)
6316       {
6317         draw_info.line_info.y2=(-draw_info.line_info.y2);
6318         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6319       }
6320     draw_info.rectangle_info=rectangle_info;
6321     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6322       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6323     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6324       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6325     draw_info.number_coordinates=(unsigned int) number_coordinates;
6326     draw_info.coordinate_info=coordinate_info;
6327     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6328     /*
6329       Draw element on image.
6330     */
6331     XSetCursorState(display,windows,MagickTrue);
6332     XCheckRefreshWindows(display,windows);
6333     status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6334     XSetCursorState(display,windows,MagickFalse);
6335     /*
6336       Update image colormap and return to image drawing.
6337     */
6338     XConfigureImageColormap(display,resource_info,windows,*image,exception);
6339     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6340   }
6341   XSetCursorState(display,windows,MagickFalse);
6342   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6343   return(IsMagickTrue(status));
6344 }
6345 \f
6346 /*
6347 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6348 %                                                                             %
6349 %                                                                             %
6350 %                                                                             %
6351 +   X D r a w P a n R e c t a n g l e                                         %
6352 %                                                                             %
6353 %                                                                             %
6354 %                                                                             %
6355 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6356 %
6357 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6358 %  displays a zoom image and the rectangle shows which portion of the image is
6359 %  displayed in the Image window.
6360 %
6361 %  The format of the XDrawPanRectangle method is:
6362 %
6363 %      XDrawPanRectangle(Display *display,XWindows *windows)
6364 %
6365 %  A description of each parameter follows:
6366 %
6367 %    o display: Specifies a connection to an X server;  returned from
6368 %      XOpenDisplay.
6369 %
6370 %    o windows: Specifies a pointer to a XWindows structure.
6371 %
6372 */
6373 static void XDrawPanRectangle(Display *display,XWindows *windows)
6374 {
6375   double
6376     scale_factor;
6377
6378   RectangleInfo
6379     highlight_info;
6380
6381   /*
6382     Determine dimensions of the panning rectangle.
6383   */
6384   scale_factor=(double) windows->pan.width/windows->image.ximage->width;
6385   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6386   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6387   scale_factor=(double)
6388     windows->pan.height/windows->image.ximage->height;
6389   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6390   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6391   /*
6392     Display the panning rectangle.
6393   */
6394   (void) XClearWindow(display,windows->pan.id);
6395   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6396     &highlight_info);
6397 }
6398 \f
6399 /*
6400 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6401 %                                                                             %
6402 %                                                                             %
6403 %                                                                             %
6404 +   X I m a g e C a c h e                                                     %
6405 %                                                                             %
6406 %                                                                             %
6407 %                                                                             %
6408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6409 %
6410 %  XImageCache() handles the creation, manipulation, and destruction of the
6411 %  image cache (undo and redo buffers).
6412 %
6413 %  The format of the XImageCache method is:
6414 %
6415 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6416 %        XWindows *windows,const CommandType command,Image **image,
6417 %        ExceptionInfo *exception)
6418 %
6419 %  A description of each parameter follows:
6420 %
6421 %    o display: Specifies a connection to an X server; returned from
6422 %      XOpenDisplay.
6423 %
6424 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6425 %
6426 %    o windows: Specifies a pointer to a XWindows structure.
6427 %
6428 %    o command: Specifies a command to perform.
6429 %
6430 %    o image: the image;  XImageCache may transform the image and return a new
6431 %      image pointer.
6432 %
6433 %    o exception: return any errors or warnings in this structure.
6434 %
6435 */
6436 static void XImageCache(Display *display,XResourceInfo *resource_info,
6437   XWindows *windows,const CommandType command,Image **image,
6438   ExceptionInfo *exception)
6439 {
6440   Image
6441     *cache_image;
6442
6443   static Image
6444     *redo_image = (Image *) NULL,
6445     *undo_image = (Image *) NULL;
6446
6447   switch (command)
6448   {
6449     case FreeBuffersCommand:
6450     {
6451       /*
6452         Free memory from the undo and redo cache.
6453       */
6454       while (undo_image != (Image *) NULL)
6455       {
6456         cache_image=undo_image;
6457         undo_image=GetPreviousImageInList(undo_image);
6458         cache_image->list=DestroyImage(cache_image->list);
6459         cache_image=DestroyImage(cache_image);
6460       }
6461       undo_image=NewImageList();
6462       if (redo_image != (Image *) NULL)
6463         redo_image=DestroyImage(redo_image);
6464       redo_image=NewImageList();
6465       return;
6466     }
6467     case UndoCommand:
6468     {
6469       char
6470         image_geometry[MaxTextExtent];
6471
6472       /*
6473         Undo the last image transformation.
6474       */
6475       if (undo_image == (Image *) NULL)
6476         {
6477           (void) XBell(display,0);
6478           return;
6479         }
6480       cache_image=undo_image;
6481       undo_image=GetPreviousImageInList(undo_image);
6482       windows->image.window_changes.width=(int) cache_image->columns;
6483       windows->image.window_changes.height=(int) cache_image->rows;
6484       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6485         windows->image.ximage->width,windows->image.ximage->height);
6486       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6487         exception);
6488       if (windows->image.crop_geometry != (char *) NULL)
6489         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6490           windows->image.crop_geometry);
6491       windows->image.crop_geometry=cache_image->geometry;
6492       if (redo_image != (Image *) NULL)
6493         redo_image=DestroyImage(redo_image);
6494       redo_image=(*image);
6495       *image=cache_image->list;
6496       cache_image=DestroyImage(cache_image);
6497       if (IfMagickTrue(windows->image.orphan) )
6498         return;
6499       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6500       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6501       return;
6502     }
6503     case CutCommand:
6504     case PasteCommand:
6505     case ApplyCommand:
6506     case HalfSizeCommand:
6507     case OriginalSizeCommand:
6508     case DoubleSizeCommand:
6509     case ResizeCommand:
6510     case TrimCommand:
6511     case CropCommand:
6512     case ChopCommand:
6513     case FlipCommand:
6514     case FlopCommand:
6515     case RotateRightCommand:
6516     case RotateLeftCommand:
6517     case RotateCommand:
6518     case ShearCommand:
6519     case RollCommand:
6520     case NegateCommand:
6521     case ContrastStretchCommand:
6522     case SigmoidalContrastCommand:
6523     case NormalizeCommand:
6524     case EqualizeCommand:
6525     case HueCommand:
6526     case SaturationCommand:
6527     case BrightnessCommand:
6528     case GammaCommand:
6529     case SpiffCommand:
6530     case DullCommand:
6531     case GrayscaleCommand:
6532     case MapCommand:
6533     case QuantizeCommand:
6534     case DespeckleCommand:
6535     case EmbossCommand:
6536     case ReduceNoiseCommand:
6537     case AddNoiseCommand:
6538     case SharpenCommand:
6539     case BlurCommand:
6540     case ThresholdCommand:
6541     case EdgeDetectCommand:
6542     case SpreadCommand:
6543     case ShadeCommand:
6544     case RaiseCommand:
6545     case SegmentCommand:
6546     case SolarizeCommand:
6547     case SepiaToneCommand:
6548     case SwirlCommand:
6549     case ImplodeCommand:
6550     case VignetteCommand:
6551     case WaveCommand:
6552     case OilPaintCommand:
6553     case CharcoalDrawCommand:
6554     case AnnotateCommand:
6555     case AddBorderCommand:
6556     case AddFrameCommand:
6557     case CompositeCommand:
6558     case CommentCommand:
6559     case LaunchCommand:
6560     case RegionofInterestCommand:
6561     case SaveToUndoBufferCommand:
6562     case RedoCommand:
6563     {
6564       Image
6565         *previous_image;
6566
6567       ssize_t
6568         bytes;
6569
6570       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6571       if (undo_image != (Image *) NULL)
6572         {
6573           /*
6574             Ensure the undo cache has enough memory available.
6575           */
6576           previous_image=undo_image;
6577           while (previous_image != (Image *) NULL)
6578           {
6579             bytes+=previous_image->list->columns*previous_image->list->rows*
6580               sizeof(PixelInfo);
6581             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6582               {
6583                 previous_image=GetPreviousImageInList(previous_image);
6584                 continue;
6585               }
6586             bytes-=previous_image->list->columns*previous_image->list->rows*
6587               sizeof(PixelInfo);
6588             if (previous_image == undo_image)
6589               undo_image=NewImageList();
6590             else
6591               previous_image->next->previous=NewImageList();
6592             break;
6593           }
6594           while (previous_image != (Image *) NULL)
6595           {
6596             /*
6597               Delete any excess memory from undo cache.
6598             */
6599             cache_image=previous_image;
6600             previous_image=GetPreviousImageInList(previous_image);
6601             cache_image->list=DestroyImage(cache_image->list);
6602             cache_image=DestroyImage(cache_image);
6603           }
6604         }
6605       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6606         break;
6607       /*
6608         Save image before transformations are applied.
6609       */
6610       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6611       if (cache_image == (Image *) NULL)
6612         break;
6613       XSetCursorState(display,windows,MagickTrue);
6614       XCheckRefreshWindows(display,windows);
6615       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6616       XSetCursorState(display,windows,MagickFalse);
6617       if (cache_image->list == (Image *) NULL)
6618         {
6619           cache_image=DestroyImage(cache_image);
6620           break;
6621         }
6622       cache_image->columns=(size_t) windows->image.ximage->width;
6623       cache_image->rows=(size_t) windows->image.ximage->height;
6624       cache_image->geometry=windows->image.crop_geometry;
6625       if (windows->image.crop_geometry != (char *) NULL)
6626         {
6627           cache_image->geometry=AcquireString((char *) NULL);
6628           (void) CopyMagickString(cache_image->geometry,
6629             windows->image.crop_geometry,MaxTextExtent);
6630         }
6631       if (undo_image == (Image *) NULL)
6632         {
6633           undo_image=cache_image;
6634           break;
6635         }
6636       undo_image->next=cache_image;
6637       undo_image->next->previous=undo_image;
6638       undo_image=undo_image->next;
6639       break;
6640     }
6641     default:
6642       break;
6643   }
6644   if (command == RedoCommand)
6645     {
6646       /*
6647         Redo the last image transformation.
6648       */
6649       if (redo_image == (Image *) NULL)
6650         {
6651           (void) XBell(display,0);
6652           return;
6653         }
6654       windows->image.window_changes.width=(int) redo_image->columns;
6655       windows->image.window_changes.height=(int) redo_image->rows;
6656       if (windows->image.crop_geometry != (char *) NULL)
6657         windows->image.crop_geometry=(char *)
6658           RelinquishMagickMemory(windows->image.crop_geometry);
6659       windows->image.crop_geometry=redo_image->geometry;
6660       *image=DestroyImage(*image);
6661       *image=redo_image;
6662       redo_image=NewImageList();
6663       if (IfMagickTrue(windows->image.orphan) )
6664         return;
6665       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6666       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6667       return;
6668     }
6669   if (command != InfoCommand)
6670     return;
6671   /*
6672     Display image info.
6673   */
6674   XSetCursorState(display,windows,MagickTrue);
6675   XCheckRefreshWindows(display,windows);
6676   XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6677   XSetCursorState(display,windows,MagickFalse);
6678 }
6679 \f
6680 /*
6681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6682 %                                                                             %
6683 %                                                                             %
6684 %                                                                             %
6685 +   X I m a g e W i n d o w C o m m a n d                                     %
6686 %                                                                             %
6687 %                                                                             %
6688 %                                                                             %
6689 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6690 %
6691 %  XImageWindowCommand() makes a transform to the image or Image window as
6692 %  specified by a user menu button or keyboard command.
6693 %
6694 %  The format of the XImageWindowCommand method is:
6695 %
6696 %      CommandType XImageWindowCommand(Display *display,
6697 %        XResourceInfo *resource_info,XWindows *windows,
6698 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6699 %        ExceptionInfo *exception)
6700 %
6701 %  A description of each parameter follows:
6702 %
6703 %    o nexus:  Method XImageWindowCommand returns an image when the
6704 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6705 %      image is returned.
6706 %
6707 %    o display: Specifies a connection to an X server; returned from
6708 %      XOpenDisplay.
6709 %
6710 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6711 %
6712 %    o windows: Specifies a pointer to a XWindows structure.
6713 %
6714 %    o state: key mask.
6715 %
6716 %    o key_symbol: Specifies a command to perform.
6717 %
6718 %    o image: the image;  XImageWIndowCommand may transform the image and
6719 %      return a new image pointer.
6720 %
6721 %    o exception: return any errors or warnings in this structure.
6722 %
6723 */
6724 static CommandType XImageWindowCommand(Display *display,
6725   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6726   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6727 {
6728   static char
6729     delta[MaxTextExtent] = "";
6730
6731   static const char
6732     Digits[] = "01234567890";
6733
6734   static KeySym
6735     last_symbol = XK_0;
6736
6737   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6738     {
6739       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6740         {
6741           *delta='\0';
6742           resource_info->quantum=1;
6743         }
6744       last_symbol=key_symbol;
6745       delta[strlen(delta)+1]='\0';
6746       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6747       resource_info->quantum=StringToLong(delta);
6748       return(NullCommand);
6749     }
6750   last_symbol=key_symbol;
6751   if (resource_info->immutable)
6752     {
6753       /*
6754         Virtual image window has a restricted command set.
6755       */
6756       switch (key_symbol)
6757       {
6758         case XK_question:
6759           return(InfoCommand);
6760         case XK_p:
6761         case XK_Print:
6762           return(PrintCommand);
6763         case XK_space:
6764           return(NextCommand);
6765         case XK_q:
6766         case XK_Escape:
6767           return(QuitCommand);
6768         default:
6769           break;
6770       }
6771       return(NullCommand);
6772     }
6773   switch ((int) key_symbol)
6774   {
6775     case XK_o:
6776     {
6777       if ((state & ControlMask) == 0)
6778         break;
6779       return(OpenCommand);
6780     }
6781     case XK_space:
6782       return(NextCommand);
6783     case XK_BackSpace:
6784       return(FormerCommand);
6785     case XK_s:
6786     {
6787       if ((state & Mod1Mask) != 0)
6788         return(SwirlCommand);
6789       if ((state & ControlMask) == 0)
6790         return(ShearCommand);
6791       return(SaveCommand);
6792     }
6793     case XK_p:
6794     case XK_Print:
6795     {
6796       if ((state & Mod1Mask) != 0)
6797         return(OilPaintCommand);
6798       if ((state & Mod4Mask) != 0)
6799         return(ColorCommand);
6800       if ((state & ControlMask) == 0)
6801         return(NullCommand);
6802       return(PrintCommand);
6803     }
6804     case XK_d:
6805     {
6806       if ((state & Mod4Mask) != 0)
6807         return(DrawCommand);
6808       if ((state & ControlMask) == 0)
6809         return(NullCommand);
6810       return(DeleteCommand);
6811     }
6812     case XK_Select:
6813     {
6814       if ((state & ControlMask) == 0)
6815         return(NullCommand);
6816       return(SelectCommand);
6817     }
6818     case XK_n:
6819     {
6820       if ((state & ControlMask) == 0)
6821         return(NullCommand);
6822       return(NewCommand);
6823     }
6824     case XK_q:
6825     case XK_Escape:
6826       return(QuitCommand);
6827     case XK_z:
6828     case XK_Undo:
6829     {
6830       if ((state & ControlMask) == 0)
6831         return(NullCommand);
6832       return(UndoCommand);
6833     }
6834     case XK_r:
6835     case XK_Redo:
6836     {
6837       if ((state & ControlMask) == 0)
6838         return(RollCommand);
6839       return(RedoCommand);
6840     }
6841     case XK_x:
6842     {
6843       if ((state & ControlMask) == 0)
6844         return(NullCommand);
6845       return(CutCommand);
6846     }
6847     case XK_c:
6848     {
6849       if ((state & Mod1Mask) != 0)
6850         return(CharcoalDrawCommand);
6851       if ((state & ControlMask) == 0)
6852         return(CropCommand);
6853       return(CopyCommand);
6854     }
6855     case XK_v:
6856     case XK_Insert:
6857     {
6858       if ((state & Mod4Mask) != 0)
6859         return(CompositeCommand);
6860       if ((state & ControlMask) == 0)
6861         return(FlipCommand);
6862       return(PasteCommand);
6863     }
6864     case XK_less:
6865       return(HalfSizeCommand);
6866     case XK_minus:
6867       return(OriginalSizeCommand);
6868     case XK_greater:
6869       return(DoubleSizeCommand);
6870     case XK_percent:
6871       return(ResizeCommand);
6872     case XK_at:
6873       return(RefreshCommand);
6874     case XK_bracketleft:
6875       return(ChopCommand);
6876     case XK_h:
6877       return(FlopCommand);
6878     case XK_slash:
6879       return(RotateRightCommand);
6880     case XK_backslash:
6881       return(RotateLeftCommand);
6882     case XK_asterisk:
6883       return(RotateCommand);
6884     case XK_t:
6885       return(TrimCommand);
6886     case XK_H:
6887       return(HueCommand);
6888     case XK_S:
6889       return(SaturationCommand);
6890     case XK_L:
6891       return(BrightnessCommand);
6892     case XK_G:
6893       return(GammaCommand);
6894     case XK_C:
6895       return(SpiffCommand);
6896     case XK_Z:
6897       return(DullCommand);
6898     case XK_N:
6899       return(NormalizeCommand);
6900     case XK_equal:
6901       return(EqualizeCommand);
6902     case XK_asciitilde:
6903       return(NegateCommand);
6904     case XK_period:
6905       return(GrayscaleCommand);
6906     case XK_numbersign:
6907       return(QuantizeCommand);
6908     case XK_F2:
6909       return(DespeckleCommand);
6910     case XK_F3:
6911       return(EmbossCommand);
6912     case XK_F4:
6913       return(ReduceNoiseCommand);
6914     case XK_F5:
6915       return(AddNoiseCommand);
6916     case XK_F6:
6917       return(SharpenCommand);
6918     case XK_F7:
6919       return(BlurCommand);
6920     case XK_F8:
6921       return(ThresholdCommand);
6922     case XK_F9:
6923       return(EdgeDetectCommand);
6924     case XK_F10:
6925       return(SpreadCommand);
6926     case XK_F11:
6927       return(ShadeCommand);
6928     case XK_F12:
6929       return(RaiseCommand);
6930     case XK_F13:
6931       return(SegmentCommand);
6932     case XK_i:
6933     {
6934       if ((state & Mod1Mask) == 0)
6935         return(NullCommand);
6936       return(ImplodeCommand);
6937     }
6938     case XK_w:
6939     {
6940       if ((state & Mod1Mask) == 0)
6941         return(NullCommand);
6942       return(WaveCommand);
6943     }
6944     case XK_m:
6945     {
6946       if ((state & Mod4Mask) == 0)
6947         return(NullCommand);
6948       return(MatteCommand);
6949     }
6950     case XK_b:
6951     {
6952       if ((state & Mod4Mask) == 0)
6953         return(NullCommand);
6954       return(AddBorderCommand);
6955     }
6956     case XK_f:
6957     {
6958       if ((state & Mod4Mask) == 0)
6959         return(NullCommand);
6960       return(AddFrameCommand);
6961     }
6962     case XK_exclam:
6963     {
6964       if ((state & Mod4Mask) == 0)
6965         return(NullCommand);
6966       return(CommentCommand);
6967     }
6968     case XK_a:
6969     {
6970       if ((state & Mod1Mask) != 0)
6971         return(ApplyCommand);
6972       if ((state & Mod4Mask) != 0)
6973         return(AnnotateCommand);
6974       if ((state & ControlMask) == 0)
6975         return(NullCommand);
6976       return(RegionofInterestCommand);
6977     }
6978     case XK_question:
6979       return(InfoCommand);
6980     case XK_plus:
6981       return(ZoomCommand);
6982     case XK_P:
6983     {
6984       if ((state & ShiftMask) == 0)
6985         return(NullCommand);
6986       return(ShowPreviewCommand);
6987     }
6988     case XK_Execute:
6989       return(LaunchCommand);
6990     case XK_F1:
6991       return(HelpCommand);
6992     case XK_Find:
6993       return(BrowseDocumentationCommand);
6994     case XK_Menu:
6995     {
6996       (void) XMapRaised(display,windows->command.id);
6997       return(NullCommand);
6998     }
6999     case XK_Next:
7000     case XK_Prior:
7001     case XK_Home:
7002     case XK_KP_Home:
7003     {
7004       XTranslateImage(display,windows,*image,key_symbol);
7005       return(NullCommand);
7006     }
7007     case XK_Up:
7008     case XK_KP_Up:
7009     case XK_Down:
7010     case XK_KP_Down:
7011     case XK_Left:
7012     case XK_KP_Left:
7013     case XK_Right:
7014     case XK_KP_Right:
7015     {
7016       if ((state & Mod1Mask) != 0)
7017         {
7018           RectangleInfo
7019             crop_info;
7020
7021           /*
7022             Trim one pixel from edge of image.
7023           */
7024           crop_info.x=0;
7025           crop_info.y=0;
7026           crop_info.width=(size_t) windows->image.ximage->width;
7027           crop_info.height=(size_t) windows->image.ximage->height;
7028           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7029             {
7030               if (resource_info->quantum >= (int) crop_info.height)
7031                 resource_info->quantum=(int) crop_info.height-1;
7032               crop_info.height-=resource_info->quantum;
7033             }
7034           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7035             {
7036               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7037                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7038               crop_info.y+=resource_info->quantum;
7039               crop_info.height-=resource_info->quantum;
7040             }
7041           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7042             {
7043               if (resource_info->quantum >= (int) crop_info.width)
7044                 resource_info->quantum=(int) crop_info.width-1;
7045               crop_info.width-=resource_info->quantum;
7046             }
7047           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7048             {
7049               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7050                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7051               crop_info.x+=resource_info->quantum;
7052               crop_info.width-=resource_info->quantum;
7053             }
7054           if ((int) (windows->image.x+windows->image.width) >
7055               (int) crop_info.width)
7056             windows->image.x=(int) (crop_info.width-windows->image.width);
7057           if ((int) (windows->image.y+windows->image.height) >
7058               (int) crop_info.height)
7059             windows->image.y=(int) (crop_info.height-windows->image.height);
7060           XSetCropGeometry(display,windows,&crop_info,*image);
7061           windows->image.window_changes.width=(int) crop_info.width;
7062           windows->image.window_changes.height=(int) crop_info.height;
7063           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7064           (void) XConfigureImage(display,resource_info,windows,*image,
7065             exception);
7066           return(NullCommand);
7067         }
7068       XTranslateImage(display,windows,*image,key_symbol);
7069       return(NullCommand);
7070     }
7071     default:
7072       return(NullCommand);
7073   }
7074   return(NullCommand);
7075 }
7076 \f
7077 /*
7078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7079 %                                                                             %
7080 %                                                                             %
7081 %                                                                             %
7082 +   X M a g i c k C o m m a n d                                               %
7083 %                                                                             %
7084 %                                                                             %
7085 %                                                                             %
7086 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7087 %
7088 %  XMagickCommand() makes a transform to the image or Image window as
7089 %  specified by a user menu button or keyboard command.
7090 %
7091 %  The format of the XMagickCommand method is:
7092 %
7093 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7094 %        XWindows *windows,const CommandType command,Image **image,
7095 %        ExceptionInfo *exception)
7096 %
7097 %  A description of each parameter follows:
7098 %
7099 %    o display: Specifies a connection to an X server; returned from
7100 %      XOpenDisplay.
7101 %
7102 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7103 %
7104 %    o windows: Specifies a pointer to a XWindows structure.
7105 %
7106 %    o command: Specifies a command to perform.
7107 %
7108 %    o image: the image;  XMagickCommand may transform the image and return a
7109 %      new image pointer.
7110 %
7111 %    o exception: return any errors or warnings in this structure.
7112 %
7113 */
7114 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7115   XWindows *windows,const CommandType command,Image **image,
7116   ExceptionInfo *exception)
7117 {
7118   char
7119     filename[MaxTextExtent],
7120     geometry[MaxTextExtent],
7121     modulate_factors[MaxTextExtent];
7122
7123   GeometryInfo
7124     geometry_info;
7125
7126   Image
7127     *nexus;
7128
7129   ImageInfo
7130     *image_info;
7131
7132   int
7133     x,
7134     y;
7135
7136   MagickStatusType
7137     flags,
7138     status;
7139
7140   QuantizeInfo
7141     quantize_info;
7142
7143   RectangleInfo
7144     page_geometry;
7145
7146   register int
7147     i;
7148
7149   static char
7150     color[MaxTextExtent] = "gray";
7151
7152   unsigned int
7153     height,
7154     width;
7155
7156   /*
7157     Process user command.
7158   */
7159   XCheckRefreshWindows(display,windows);
7160   XImageCache(display,resource_info,windows,command,image,exception);
7161   nexus=NewImageList();
7162   windows->image.window_changes.width=windows->image.ximage->width;
7163   windows->image.window_changes.height=windows->image.ximage->height;
7164   image_info=CloneImageInfo(resource_info->image_info);
7165   SetGeometryInfo(&geometry_info);
7166   GetQuantizeInfo(&quantize_info);
7167   switch (command)
7168   {
7169     case OpenCommand:
7170     {
7171       /*
7172         Load image.
7173       */
7174       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7175       break;
7176     }
7177     case NextCommand:
7178     {
7179       /*
7180         Display next image.
7181       */
7182       for (i=0; i < resource_info->quantum; i++)
7183         XClientMessage(display,windows->image.id,windows->im_protocols,
7184           windows->im_next_image,CurrentTime);
7185       break;
7186     }
7187     case FormerCommand:
7188     {
7189       /*
7190         Display former image.
7191       */
7192       for (i=0; i < resource_info->quantum; i++)
7193         XClientMessage(display,windows->image.id,windows->im_protocols,
7194           windows->im_former_image,CurrentTime);
7195       break;
7196     }
7197     case SelectCommand:
7198     {
7199       int
7200         status;
7201
7202       /*
7203         Select image.
7204       */
7205       if (*resource_info->home_directory == '\0')
7206         (void) CopyMagickString(resource_info->home_directory,".",
7207           MaxTextExtent);
7208       status=chdir(resource_info->home_directory);
7209       if (status == -1)
7210         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7211           "UnableToOpenFile","%s",resource_info->home_directory);
7212       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7213       break;
7214     }
7215     case SaveCommand:
7216     {
7217       /*
7218         Save image.
7219       */
7220       status=XSaveImage(display,resource_info,windows,*image,exception);
7221       if (IfMagickFalse(status) )
7222         {
7223           char
7224             message[MaxTextExtent];
7225
7226           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7227             exception->reason != (char *) NULL ? exception->reason : "",
7228             exception->description != (char *) NULL ? exception->description :
7229             "");
7230           XNoticeWidget(display,windows,"Unable to save file:",message);
7231           break;
7232         }
7233       break;
7234     }
7235     case PrintCommand:
7236     {
7237       /*
7238         Print image.
7239       */
7240       status=XPrintImage(display,resource_info,windows,*image,exception);
7241       if (IfMagickFalse(status) )
7242         {
7243           char
7244             message[MaxTextExtent];
7245
7246           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7247             exception->reason != (char *) NULL ? exception->reason : "",
7248             exception->description != (char *) NULL ? exception->description :
7249             "");
7250           XNoticeWidget(display,windows,"Unable to print file:",message);
7251           break;
7252         }
7253       break;
7254     }
7255     case DeleteCommand:
7256     {
7257       static char
7258         filename[MaxTextExtent] = "\0";
7259
7260       /*
7261         Delete image file.
7262       */
7263       XFileBrowserWidget(display,windows,"Delete",filename);
7264       if (*filename == '\0')
7265         break;
7266       status=ShredFile(filename);
7267       if (IfMagickTrue(status) )
7268         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7269       break;
7270     }
7271     case NewCommand:
7272     {
7273       int
7274         status;
7275
7276       static char
7277         color[MaxTextExtent] = "gray",
7278         geometry[MaxTextExtent] = "640x480";
7279
7280       static const char
7281         *format = "gradient";
7282
7283       /*
7284         Query user for canvas geometry.
7285       */
7286       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7287         geometry);
7288       if (*geometry == '\0')
7289         break;
7290       if (status == 0)
7291         format="xc";
7292       XColorBrowserWidget(display,windows,"Select",color);
7293       if (*color == '\0')
7294         break;
7295       /*
7296         Create canvas.
7297       */
7298       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7299         "%s:%s",format,color);
7300       (void) CloneString(&image_info->size,geometry);
7301       nexus=ReadImage(image_info,exception);
7302       CatchException(exception);
7303       XClientMessage(display,windows->image.id,windows->im_protocols,
7304         windows->im_next_image,CurrentTime);
7305       break;
7306     }
7307     case VisualDirectoryCommand:
7308     {
7309       /*
7310         Visual Image directory.
7311       */
7312       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7313       break;
7314     }
7315     case QuitCommand:
7316     {
7317       /*
7318         exit program.
7319       */
7320       if (IfMagickFalse(resource_info->confirm_exit) )
7321         XClientMessage(display,windows->image.id,windows->im_protocols,
7322           windows->im_exit,CurrentTime);
7323       else
7324         {
7325           int
7326             status;
7327
7328           /*
7329             Confirm program exit.
7330           */
7331           status=XConfirmWidget(display,windows,"Do you really want to exit",
7332             resource_info->client_name);
7333           if (status > 0)
7334             XClientMessage(display,windows->image.id,windows->im_protocols,
7335               windows->im_exit,CurrentTime);
7336         }
7337       break;
7338     }
7339     case CutCommand:
7340     {
7341       /*
7342         Cut image.
7343       */
7344       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7345       break;
7346     }
7347     case CopyCommand:
7348     {
7349       /*
7350         Copy image.
7351       */
7352       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7353         exception);
7354       break;
7355     }
7356     case PasteCommand:
7357     {
7358       /*
7359         Paste image.
7360       */
7361       status=XPasteImage(display,resource_info,windows,*image,exception);
7362       if (IfMagickFalse(status) )
7363         {
7364           XNoticeWidget(display,windows,"Unable to paste X image",
7365             (*image)->filename);
7366           break;
7367         }
7368       break;
7369     }
7370     case HalfSizeCommand:
7371     {
7372       /*
7373         Half image size.
7374       */
7375       windows->image.window_changes.width=windows->image.ximage->width/2;
7376       windows->image.window_changes.height=windows->image.ximage->height/2;
7377       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7378       break;
7379     }
7380     case OriginalSizeCommand:
7381     {
7382       /*
7383         Original image size.
7384       */
7385       windows->image.window_changes.width=(int) (*image)->columns;
7386       windows->image.window_changes.height=(int) (*image)->rows;
7387       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7388       break;
7389     }
7390     case DoubleSizeCommand:
7391     {
7392       /*
7393         Double the image size.
7394       */
7395       windows->image.window_changes.width=windows->image.ximage->width << 1;
7396       windows->image.window_changes.height=windows->image.ximage->height << 1;
7397       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7398       break;
7399     }
7400     case ResizeCommand:
7401     {
7402       int
7403         status;
7404
7405       size_t
7406         height,
7407         width;
7408
7409       ssize_t
7410         x,
7411         y;
7412
7413       /*
7414         Resize image.
7415       */
7416       width=(size_t) windows->image.ximage->width;
7417       height=(size_t) windows->image.ximage->height;
7418       x=0;
7419       y=0;
7420       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7421         (double) width,(double) height);
7422       status=XDialogWidget(display,windows,"Resize",
7423         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7424       if (*geometry == '\0')
7425         break;
7426       if (status == 0)
7427         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7428       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7429       windows->image.window_changes.width=(int) width;
7430       windows->image.window_changes.height=(int) height;
7431       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7432       break;
7433     }
7434     case ApplyCommand:
7435     {
7436       char
7437         image_geometry[MaxTextExtent];
7438
7439       if ((windows->image.crop_geometry == (char *) NULL) &&
7440           ((int) (*image)->columns == windows->image.ximage->width) &&
7441           ((int) (*image)->rows == windows->image.ximage->height))
7442         break;
7443       /*
7444         Apply size transforms to image.
7445       */
7446       XSetCursorState(display,windows,MagickTrue);
7447       XCheckRefreshWindows(display,windows);
7448       /*
7449         Crop and/or scale displayed image.
7450       */
7451       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7452         windows->image.ximage->width,windows->image.ximage->height);
7453       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7454         exception);
7455       if (windows->image.crop_geometry != (char *) NULL)
7456         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7457           windows->image.crop_geometry);
7458       windows->image.x=0;
7459       windows->image.y=0;
7460       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7461       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7462       break;
7463     }
7464     case RefreshCommand:
7465     {
7466       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7467       break;
7468     }
7469     case RestoreCommand:
7470     {
7471       /*
7472         Restore Image window to its original size.
7473       */
7474       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7475           (windows->image.height == (unsigned int) (*image)->rows) &&
7476           (windows->image.crop_geometry == (char *) NULL))
7477         {
7478           (void) XBell(display,0);
7479           break;
7480         }
7481       windows->image.window_changes.width=(int) (*image)->columns;
7482       windows->image.window_changes.height=(int) (*image)->rows;
7483       if (windows->image.crop_geometry != (char *) NULL)
7484         {
7485           windows->image.crop_geometry=(char *)
7486             RelinquishMagickMemory(windows->image.crop_geometry);
7487           windows->image.crop_geometry=(char *) NULL;
7488           windows->image.x=0;
7489           windows->image.y=0;
7490         }
7491       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7492       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7493       break;
7494     }
7495     case CropCommand:
7496     {
7497       /*
7498         Crop image.
7499       */
7500       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7501         exception);
7502       break;
7503     }
7504     case ChopCommand:
7505     {
7506       /*
7507         Chop image.
7508       */
7509       status=XChopImage(display,resource_info,windows,image,exception);
7510       if (IfMagickFalse(status) )
7511         {
7512           XNoticeWidget(display,windows,"Unable to cut X image",
7513             (*image)->filename);
7514           break;
7515         }
7516       break;
7517     }
7518     case FlopCommand:
7519     {
7520       Image
7521         *flop_image;
7522
7523       /*
7524         Flop image scanlines.
7525       */
7526       XSetCursorState(display,windows,MagickTrue);
7527       XCheckRefreshWindows(display,windows);
7528       flop_image=FlopImage(*image,exception);
7529       if (flop_image != (Image *) NULL)
7530         {
7531           *image=DestroyImage(*image);
7532           *image=flop_image;
7533         }
7534       CatchException(exception);
7535       XSetCursorState(display,windows,MagickFalse);
7536       if (windows->image.crop_geometry != (char *) NULL)
7537         {
7538           /*
7539             Flop crop geometry.
7540           */
7541           width=(unsigned int) (*image)->columns;
7542           height=(unsigned int) (*image)->rows;
7543           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7544             &width,&height);
7545           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7546             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7547         }
7548       if (IfMagickTrue(windows->image.orphan) )
7549         break;
7550       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7551       break;
7552     }
7553     case FlipCommand:
7554     {
7555       Image
7556         *flip_image;
7557
7558       /*
7559         Flip image scanlines.
7560       */
7561       XSetCursorState(display,windows,MagickTrue);
7562       XCheckRefreshWindows(display,windows);
7563       flip_image=FlipImage(*image,exception);
7564       if (flip_image != (Image *) NULL)
7565         {
7566           *image=DestroyImage(*image);
7567           *image=flip_image;
7568         }
7569       CatchException(exception);
7570       XSetCursorState(display,windows,MagickFalse);
7571       if (windows->image.crop_geometry != (char *) NULL)
7572         {
7573           /*
7574             Flip crop geometry.
7575           */
7576           width=(unsigned int) (*image)->columns;
7577           height=(unsigned int) (*image)->rows;
7578           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7579             &width,&height);
7580           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7581             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7582         }
7583       if (IfMagickTrue(windows->image.orphan) )
7584         break;
7585       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7586       break;
7587     }
7588     case RotateRightCommand:
7589     {
7590       /*
7591         Rotate image 90 degrees clockwise.
7592       */
7593       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7594       if (IfMagickFalse(status) )
7595         {
7596           XNoticeWidget(display,windows,"Unable to rotate X image",
7597             (*image)->filename);
7598           break;
7599         }
7600       break;
7601     }
7602     case RotateLeftCommand:
7603     {
7604       /*
7605         Rotate image 90 degrees counter-clockwise.
7606       */
7607       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7608       if (IfMagickFalse(status) )
7609         {
7610           XNoticeWidget(display,windows,"Unable to rotate X image",
7611             (*image)->filename);
7612           break;
7613         }
7614       break;
7615     }
7616     case RotateCommand:
7617     {
7618       /*
7619         Rotate image.
7620       */
7621       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7622       if (IfMagickFalse(status) )
7623         {
7624           XNoticeWidget(display,windows,"Unable to rotate X image",
7625             (*image)->filename);
7626           break;
7627         }
7628       break;
7629     }
7630     case ShearCommand:
7631     {
7632       Image
7633         *shear_image;
7634
7635       static char
7636         geometry[MaxTextExtent] = "45.0x45.0";
7637
7638       /*
7639         Query user for shear color and geometry.
7640       */
7641       XColorBrowserWidget(display,windows,"Select",color);
7642       if (*color == '\0')
7643         break;
7644       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7645         geometry);
7646       if (*geometry == '\0')
7647         break;
7648       /*
7649         Shear image.
7650       */
7651       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7652         exception);
7653       XSetCursorState(display,windows,MagickTrue);
7654       XCheckRefreshWindows(display,windows);
7655       (void) QueryColorCompliance(color,AllCompliance,
7656         &(*image)->background_color,exception);
7657       flags=ParseGeometry(geometry,&geometry_info);
7658       if ((flags & SigmaValue) == 0)
7659         geometry_info.sigma=geometry_info.rho;
7660       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7661         exception);
7662       if (shear_image != (Image *) NULL)
7663         {
7664           *image=DestroyImage(*image);
7665           *image=shear_image;
7666         }
7667       CatchException(exception);
7668       XSetCursorState(display,windows,MagickFalse);
7669       if (IfMagickTrue(windows->image.orphan) )
7670         break;
7671       windows->image.window_changes.width=(int) (*image)->columns;
7672       windows->image.window_changes.height=(int) (*image)->rows;
7673       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7674       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7675       break;
7676     }
7677     case RollCommand:
7678     {
7679       Image
7680         *roll_image;
7681
7682       static char
7683         geometry[MaxTextExtent] = "+2+2";
7684
7685       /*
7686         Query user for the roll geometry.
7687       */
7688       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7689         geometry);
7690       if (*geometry == '\0')
7691         break;
7692       /*
7693         Roll image.
7694       */
7695       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7696         exception);
7697       XSetCursorState(display,windows,MagickTrue);
7698       XCheckRefreshWindows(display,windows);
7699       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7700         exception);
7701       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7702         exception);
7703       if (roll_image != (Image *) NULL)
7704         {
7705           *image=DestroyImage(*image);
7706           *image=roll_image;
7707         }
7708       CatchException(exception);
7709       XSetCursorState(display,windows,MagickFalse);
7710       if (IfMagickTrue(windows->image.orphan) )
7711         break;
7712       windows->image.window_changes.width=(int) (*image)->columns;
7713       windows->image.window_changes.height=(int) (*image)->rows;
7714       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7715       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7716       break;
7717     }
7718     case TrimCommand:
7719     {
7720       static char
7721         fuzz[MaxTextExtent];
7722
7723       /*
7724         Query user for the fuzz factor.
7725       */
7726       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7727         (*image)->fuzz/(QuantumRange+1.0));
7728       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7729       if (*fuzz == '\0')
7730         break;
7731       (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7732       /*
7733         Trim image.
7734       */
7735       status=XTrimImage(display,resource_info,windows,*image,exception);
7736       if (IfMagickFalse(status) )
7737         {
7738           XNoticeWidget(display,windows,"Unable to trim X image",
7739             (*image)->filename);
7740           break;
7741         }
7742       break;
7743     }
7744     case HueCommand:
7745     {
7746       static char
7747         hue_percent[MaxTextExtent] = "110";
7748
7749       /*
7750         Query user for percent hue change.
7751       */
7752       (void) XDialogWidget(display,windows,"Apply",
7753         "Enter percent change in image hue (0-200):",hue_percent);
7754       if (*hue_percent == '\0')
7755         break;
7756       /*
7757         Vary the image hue.
7758       */
7759       XSetCursorState(display,windows,MagickTrue);
7760       XCheckRefreshWindows(display,windows);
7761       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7762       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7763         MaxTextExtent);
7764       (void) ModulateImage(*image,modulate_factors,exception);
7765       XSetCursorState(display,windows,MagickFalse);
7766       if (IfMagickTrue(windows->image.orphan) )
7767         break;
7768       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7769       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7770       break;
7771     }
7772     case SaturationCommand:
7773     {
7774       static char
7775         saturation_percent[MaxTextExtent] = "110";
7776
7777       /*
7778         Query user for percent saturation change.
7779       */
7780       (void) XDialogWidget(display,windows,"Apply",
7781         "Enter percent change in color saturation (0-200):",saturation_percent);
7782       if (*saturation_percent == '\0')
7783         break;
7784       /*
7785         Vary color saturation.
7786       */
7787       XSetCursorState(display,windows,MagickTrue);
7788       XCheckRefreshWindows(display,windows);
7789       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7790       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7791         MaxTextExtent);
7792       (void) ModulateImage(*image,modulate_factors,exception);
7793       XSetCursorState(display,windows,MagickFalse);
7794       if (IfMagickTrue(windows->image.orphan) )
7795         break;
7796       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7797       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7798       break;
7799     }
7800     case BrightnessCommand:
7801     {
7802       static char
7803         brightness_percent[MaxTextExtent] = "110";
7804
7805       /*
7806         Query user for percent brightness change.
7807       */
7808       (void) XDialogWidget(display,windows,"Apply",
7809         "Enter percent change in color brightness (0-200):",brightness_percent);
7810       if (*brightness_percent == '\0')
7811         break;
7812       /*
7813         Vary the color brightness.
7814       */
7815       XSetCursorState(display,windows,MagickTrue);
7816       XCheckRefreshWindows(display,windows);
7817       (void) CopyMagickString(modulate_factors,brightness_percent,
7818         MaxTextExtent);
7819       (void) ModulateImage(*image,modulate_factors,exception);
7820       XSetCursorState(display,windows,MagickFalse);
7821       if (IfMagickTrue(windows->image.orphan) )
7822         break;
7823       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7824       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7825       break;
7826     }
7827     case GammaCommand:
7828     {
7829       static char
7830         factor[MaxTextExtent] = "1.6";
7831
7832       /*
7833         Query user for gamma value.
7834       */
7835       (void) XDialogWidget(display,windows,"Gamma",
7836         "Enter gamma value (e.g. 1.2):",factor);
7837       if (*factor == '\0')
7838         break;
7839       /*
7840         Gamma correct image.
7841       */
7842       XSetCursorState(display,windows,MagickTrue);
7843       XCheckRefreshWindows(display,windows);
7844       (void) GammaImage(*image,atof(factor),exception);
7845       XSetCursorState(display,windows,MagickFalse);
7846       if (IfMagickTrue(windows->image.orphan) )
7847         break;
7848       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7849       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7850       break;
7851     }
7852     case SpiffCommand:
7853     {
7854       /*
7855         Sharpen the image contrast.
7856       */
7857       XSetCursorState(display,windows,MagickTrue);
7858       XCheckRefreshWindows(display,windows);
7859       (void) ContrastImage(*image,MagickTrue,exception);
7860       XSetCursorState(display,windows,MagickFalse);
7861       if (IfMagickTrue(windows->image.orphan) )
7862         break;
7863       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7864       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7865       break;
7866     }
7867     case DullCommand:
7868     {
7869       /*
7870         Dull the image contrast.
7871       */
7872       XSetCursorState(display,windows,MagickTrue);
7873       XCheckRefreshWindows(display,windows);
7874       (void) ContrastImage(*image,MagickFalse,exception);
7875       XSetCursorState(display,windows,MagickFalse);
7876       if (IfMagickTrue(windows->image.orphan) )
7877         break;
7878       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7879       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7880       break;
7881     }
7882     case ContrastStretchCommand:
7883     {
7884       double
7885         black_point,
7886         white_point;
7887
7888       static char
7889         levels[MaxTextExtent] = "1%";
7890
7891       /*
7892         Query user for gamma value.
7893       */
7894       (void) XDialogWidget(display,windows,"Contrast Stretch",
7895         "Enter black and white points:",levels);
7896       if (*levels == '\0')
7897         break;
7898       /*
7899         Contrast stretch image.
7900       */
7901       XSetCursorState(display,windows,MagickTrue);
7902       XCheckRefreshWindows(display,windows);
7903       flags=ParseGeometry(levels,&geometry_info);
7904       black_point=geometry_info.rho;
7905       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7906       if ((flags & PercentValue) != 0)
7907         {
7908           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7909           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7910         }
7911       white_point=(double) (*image)->columns*(*image)->rows-white_point;
7912       (void) ContrastStretchImage(*image,black_point,white_point,
7913         exception);
7914       XSetCursorState(display,windows,MagickFalse);
7915       if (IfMagickTrue(windows->image.orphan) )
7916         break;
7917       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7918       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7919       break;
7920     }
7921     case SigmoidalContrastCommand:
7922     {
7923       GeometryInfo
7924         geometry_info;
7925
7926       MagickStatusType
7927         flags;
7928
7929       static char
7930         levels[MaxTextExtent] = "3x50%";
7931
7932       /*
7933         Query user for gamma value.
7934       */
7935       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7936         "Enter contrast and midpoint:",levels);
7937       if (*levels == '\0')
7938         break;
7939       /*
7940         Contrast stretch image.
7941       */
7942       XSetCursorState(display,windows,MagickTrue);
7943       XCheckRefreshWindows(display,windows);
7944       flags=ParseGeometry(levels,&geometry_info);
7945       if ((flags & SigmaValue) == 0)
7946         geometry_info.sigma=1.0*QuantumRange/2.0;
7947       if ((flags & PercentValue) != 0)
7948         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7949       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7950         geometry_info.sigma,exception);
7951       XSetCursorState(display,windows,MagickFalse);
7952       if (IfMagickTrue(windows->image.orphan) )
7953         break;
7954       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7955       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7956       break;
7957     }
7958     case NormalizeCommand:
7959     {
7960       /*
7961         Perform histogram normalization on the image.
7962       */
7963       XSetCursorState(display,windows,MagickTrue);
7964       XCheckRefreshWindows(display,windows);
7965       (void) NormalizeImage(*image,exception);
7966       XSetCursorState(display,windows,MagickFalse);
7967       if (IfMagickTrue(windows->image.orphan) )
7968         break;
7969       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7970       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7971       break;
7972     }
7973     case EqualizeCommand:
7974     {
7975       /*
7976         Perform histogram equalization on the image.
7977       */
7978       XSetCursorState(display,windows,MagickTrue);
7979       XCheckRefreshWindows(display,windows);
7980       (void) EqualizeImage(*image,exception);
7981       XSetCursorState(display,windows,MagickFalse);
7982       if (IfMagickTrue(windows->image.orphan) )
7983         break;
7984       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7985       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7986       break;
7987     }
7988     case NegateCommand:
7989     {
7990       /*
7991         Negate colors in image.
7992       */
7993       XSetCursorState(display,windows,MagickTrue);
7994       XCheckRefreshWindows(display,windows);
7995       (void) NegateImage(*image,MagickFalse,exception);
7996       XSetCursorState(display,windows,MagickFalse);
7997       if (IfMagickTrue(windows->image.orphan) )
7998         break;
7999       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8000       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8001       break;
8002     }
8003     case GrayscaleCommand:
8004     {
8005       /*
8006         Convert image to grayscale.
8007       */
8008       XSetCursorState(display,windows,MagickTrue);
8009       XCheckRefreshWindows(display,windows);
8010       (void) SetImageType(*image,(*image)->alpha_trait != BlendPixelTrait ?
8011         GrayscaleType : GrayscaleMatteType,exception);
8012       XSetCursorState(display,windows,MagickFalse);
8013       if (IfMagickTrue(windows->image.orphan) )
8014         break;
8015       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8016       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8017       break;
8018     }
8019     case MapCommand:
8020     {
8021       Image
8022         *affinity_image;
8023
8024       static char
8025         filename[MaxTextExtent] = "\0";
8026
8027       /*
8028         Request image file name from user.
8029       */
8030       XFileBrowserWidget(display,windows,"Map",filename);
8031       if (*filename == '\0')
8032         break;
8033       /*
8034         Map image.
8035       */
8036       XSetCursorState(display,windows,MagickTrue);
8037       XCheckRefreshWindows(display,windows);
8038       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8039       affinity_image=ReadImage(image_info,exception);
8040       if (affinity_image != (Image *) NULL)
8041         {
8042           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8043           affinity_image=DestroyImage(affinity_image);
8044         }
8045       CatchException(exception);
8046       XSetCursorState(display,windows,MagickFalse);
8047       if (IfMagickTrue(windows->image.orphan) )
8048         break;
8049       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8050       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8051       break;
8052     }
8053     case QuantizeCommand:
8054     {
8055       int
8056         status;
8057
8058       static char
8059         colors[MaxTextExtent] = "256";
8060
8061       /*
8062         Query user for maximum number of colors.
8063       */
8064       status=XDialogWidget(display,windows,"Quantize",
8065         "Maximum number of colors:",colors);
8066       if (*colors == '\0')
8067         break;
8068       /*
8069         Color reduce the image.
8070       */
8071       XSetCursorState(display,windows,MagickTrue);
8072       XCheckRefreshWindows(display,windows);
8073       quantize_info.number_colors=StringToUnsignedLong(colors);
8074       quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8075         NoDitherMethod;
8076       (void) QuantizeImage(&quantize_info,*image,exception);
8077       XSetCursorState(display,windows,MagickFalse);
8078       if (IfMagickTrue(windows->image.orphan) )
8079         break;
8080       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8081       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8082       break;
8083     }
8084     case DespeckleCommand:
8085     {
8086       Image
8087         *despeckle_image;
8088
8089       /*
8090         Despeckle image.
8091       */
8092       XSetCursorState(display,windows,MagickTrue);
8093       XCheckRefreshWindows(display,windows);
8094       despeckle_image=DespeckleImage(*image,exception);
8095       if (despeckle_image != (Image *) NULL)
8096         {
8097           *image=DestroyImage(*image);
8098           *image=despeckle_image;
8099         }
8100       CatchException(exception);
8101       XSetCursorState(display,windows,MagickFalse);
8102       if (IfMagickTrue(windows->image.orphan) )
8103         break;
8104       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8105       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8106       break;
8107     }
8108     case EmbossCommand:
8109     {
8110       Image
8111         *emboss_image;
8112
8113       static char
8114         radius[MaxTextExtent] = "0.0x1.0";
8115
8116       /*
8117         Query user for emboss radius.
8118       */
8119       (void) XDialogWidget(display,windows,"Emboss",
8120         "Enter the emboss radius and standard deviation:",radius);
8121       if (*radius == '\0')
8122         break;
8123       /*
8124         Reduce noise in the image.
8125       */
8126       XSetCursorState(display,windows,MagickTrue);
8127       XCheckRefreshWindows(display,windows);
8128       flags=ParseGeometry(radius,&geometry_info);
8129       if ((flags & SigmaValue) == 0)
8130         geometry_info.sigma=1.0;
8131       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8132         exception);
8133       if (emboss_image != (Image *) NULL)
8134         {
8135           *image=DestroyImage(*image);
8136           *image=emboss_image;
8137         }
8138       CatchException(exception);
8139       XSetCursorState(display,windows,MagickFalse);
8140       if (IfMagickTrue(windows->image.orphan) )
8141         break;
8142       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8143       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8144       break;
8145     }
8146     case ReduceNoiseCommand:
8147     {
8148       Image
8149         *noise_image;
8150
8151       static char
8152         radius[MaxTextExtent] = "0";
8153
8154       /*
8155         Query user for noise radius.
8156       */
8157       (void) XDialogWidget(display,windows,"Reduce Noise",
8158         "Enter the noise radius:",radius);
8159       if (*radius == '\0')
8160         break;
8161       /*
8162         Reduce noise in the image.
8163       */
8164       XSetCursorState(display,windows,MagickTrue);
8165       XCheckRefreshWindows(display,windows);
8166       flags=ParseGeometry(radius,&geometry_info);
8167       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8168         geometry_info.rho,(size_t) geometry_info.rho,exception);
8169       if (noise_image != (Image *) NULL)
8170         {
8171           *image=DestroyImage(*image);
8172           *image=noise_image;
8173         }
8174       CatchException(exception);
8175       XSetCursorState(display,windows,MagickFalse);
8176       if (IfMagickTrue(windows->image.orphan) )
8177         break;
8178       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8179       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8180       break;
8181     }
8182     case AddNoiseCommand:
8183     {
8184       char
8185         **noises;
8186
8187       Image
8188         *noise_image;
8189
8190       static char
8191         noise_type[MaxTextExtent] = "Gaussian";
8192
8193       /*
8194         Add noise to the image.
8195       */
8196       noises=GetCommandOptions(MagickNoiseOptions);
8197       if (noises == (char **) NULL)
8198         break;
8199       XListBrowserWidget(display,windows,&windows->widget,
8200         (const char **) noises,"Add Noise",
8201         "Select a type of noise to add to your image:",noise_type);
8202       noises=DestroyStringList(noises);
8203       if (*noise_type == '\0')
8204         break;
8205       XSetCursorState(display,windows,MagickTrue);
8206       XCheckRefreshWindows(display,windows);
8207       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8208         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8209       if (noise_image != (Image *) NULL)
8210         {
8211           *image=DestroyImage(*image);
8212           *image=noise_image;
8213         }
8214       CatchException(exception);
8215       XSetCursorState(display,windows,MagickFalse);
8216       if (IfMagickTrue(windows->image.orphan) )
8217         break;
8218       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8219       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8220       break;
8221     }
8222     case SharpenCommand:
8223     {
8224       Image
8225         *sharp_image;
8226
8227       static char
8228         radius[MaxTextExtent] = "0.0x1.0";
8229
8230       /*
8231         Query user for sharpen radius.
8232       */
8233       (void) XDialogWidget(display,windows,"Sharpen",
8234         "Enter the sharpen radius and standard deviation:",radius);
8235       if (*radius == '\0')
8236         break;
8237       /*
8238         Sharpen image scanlines.
8239       */
8240       XSetCursorState(display,windows,MagickTrue);
8241       XCheckRefreshWindows(display,windows);
8242       flags=ParseGeometry(radius,&geometry_info);
8243       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8244         exception);
8245       if (sharp_image != (Image *) NULL)
8246         {
8247           *image=DestroyImage(*image);
8248           *image=sharp_image;
8249         }
8250       CatchException(exception);
8251       XSetCursorState(display,windows,MagickFalse);
8252       if (IfMagickTrue(windows->image.orphan) )
8253         break;
8254       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8255       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8256       break;
8257     }
8258     case BlurCommand:
8259     {
8260       Image
8261         *blur_image;
8262
8263       static char
8264         radius[MaxTextExtent] = "0.0x1.0";
8265
8266       /*
8267         Query user for blur radius.
8268       */
8269       (void) XDialogWidget(display,windows,"Blur",
8270         "Enter the blur radius and standard deviation:",radius);
8271       if (*radius == '\0')
8272         break;
8273       /*
8274         Blur an image.
8275       */
8276       XSetCursorState(display,windows,MagickTrue);
8277       XCheckRefreshWindows(display,windows);
8278       flags=ParseGeometry(radius,&geometry_info);
8279       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8280         exception);
8281       if (blur_image != (Image *) NULL)
8282         {
8283           *image=DestroyImage(*image);
8284           *image=blur_image;
8285         }
8286       CatchException(exception);
8287       XSetCursorState(display,windows,MagickFalse);
8288       if (IfMagickTrue(windows->image.orphan) )
8289         break;
8290       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8291       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8292       break;
8293     }
8294     case ThresholdCommand:
8295     {
8296       double
8297         threshold;
8298
8299       static char
8300         factor[MaxTextExtent] = "128";
8301
8302       /*
8303         Query user for threshold value.
8304       */
8305       (void) XDialogWidget(display,windows,"Threshold",
8306         "Enter threshold value:",factor);
8307       if (*factor == '\0')
8308         break;
8309       /*
8310         Gamma correct image.
8311       */
8312       XSetCursorState(display,windows,MagickTrue);
8313       XCheckRefreshWindows(display,windows);
8314       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8315       (void) BilevelImage(*image,threshold,exception);
8316       XSetCursorState(display,windows,MagickFalse);
8317       if (IfMagickTrue(windows->image.orphan) )
8318         break;
8319       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8320       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8321       break;
8322     }
8323     case EdgeDetectCommand:
8324     {
8325       Image
8326         *edge_image;
8327
8328       static char
8329         radius[MaxTextExtent] = "0";
8330
8331       /*
8332         Query user for edge factor.
8333       */
8334       (void) XDialogWidget(display,windows,"Detect Edges",
8335         "Enter the edge detect radius:",radius);
8336       if (*radius == '\0')
8337         break;
8338       /*
8339         Detect edge in image.
8340       */
8341       XSetCursorState(display,windows,MagickTrue);
8342       XCheckRefreshWindows(display,windows);
8343       flags=ParseGeometry(radius,&geometry_info);
8344       edge_image=EdgeImage(*image,geometry_info.rho,exception);
8345       if (edge_image != (Image *) NULL)
8346         {
8347           *image=DestroyImage(*image);
8348           *image=edge_image;
8349         }
8350       CatchException(exception);
8351       XSetCursorState(display,windows,MagickFalse);
8352       if (IfMagickTrue(windows->image.orphan) )
8353         break;
8354       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8355       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8356       break;
8357     }
8358     case SpreadCommand:
8359     {
8360       Image
8361         *spread_image;
8362
8363       static char
8364         amount[MaxTextExtent] = "2";
8365
8366       /*
8367         Query user for spread amount.
8368       */
8369       (void) XDialogWidget(display,windows,"Spread",
8370         "Enter the displacement amount:",amount);
8371       if (*amount == '\0')
8372         break;
8373       /*
8374         Displace image pixels by a random amount.
8375       */
8376       XSetCursorState(display,windows,MagickTrue);
8377       XCheckRefreshWindows(display,windows);
8378       flags=ParseGeometry(amount,&geometry_info);
8379       spread_image=EdgeImage(*image,geometry_info.rho,exception);
8380       if (spread_image != (Image *) NULL)
8381         {
8382           *image=DestroyImage(*image);
8383           *image=spread_image;
8384         }
8385       CatchException(exception);
8386       XSetCursorState(display,windows,MagickFalse);
8387       if (IfMagickTrue(windows->image.orphan) )
8388         break;
8389       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8390       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8391       break;
8392     }
8393     case ShadeCommand:
8394     {
8395       Image
8396         *shade_image;
8397
8398       int
8399         status;
8400
8401       static char
8402         geometry[MaxTextExtent] = "30x30";
8403
8404       /*
8405         Query user for the shade geometry.
8406       */
8407       status=XDialogWidget(display,windows,"Shade",
8408         "Enter the azimuth and elevation of the light source:",geometry);
8409       if (*geometry == '\0')
8410         break;
8411       /*
8412         Shade image pixels.
8413       */
8414       XSetCursorState(display,windows,MagickTrue);
8415       XCheckRefreshWindows(display,windows);
8416       flags=ParseGeometry(geometry,&geometry_info);
8417       if ((flags & SigmaValue) == 0)
8418         geometry_info.sigma=1.0;
8419       shade_image=ShadeImage(*image,IsMagickTrue(status),
8420         geometry_info.rho,geometry_info.sigma,exception);
8421       if (shade_image != (Image *) NULL)
8422         {
8423           *image=DestroyImage(*image);
8424           *image=shade_image;
8425         }
8426       CatchException(exception);
8427       XSetCursorState(display,windows,MagickFalse);
8428       if (IfMagickTrue(windows->image.orphan) )
8429         break;
8430       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8431       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8432       break;
8433     }
8434     case RaiseCommand:
8435     {
8436       static char
8437         bevel_width[MaxTextExtent] = "10";
8438
8439       /*
8440         Query user for bevel width.
8441       */
8442       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8443       if (*bevel_width == '\0')
8444         break;
8445       /*
8446         Raise an image.
8447       */
8448       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8449         exception);
8450       XSetCursorState(display,windows,MagickTrue);
8451       XCheckRefreshWindows(display,windows);
8452       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8453         exception);
8454       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8455       XSetCursorState(display,windows,MagickFalse);
8456       if (IfMagickTrue(windows->image.orphan) )
8457         break;
8458       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8459       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8460       break;
8461     }
8462     case SegmentCommand:
8463     {
8464       static char
8465         threshold[MaxTextExtent] = "1.0x1.5";
8466
8467       /*
8468         Query user for smoothing threshold.
8469       */
8470       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8471         threshold);
8472       if (*threshold == '\0')
8473         break;
8474       /*
8475         Segment an image.
8476       */
8477       XSetCursorState(display,windows,MagickTrue);
8478       XCheckRefreshWindows(display,windows);
8479       flags=ParseGeometry(threshold,&geometry_info);
8480       if ((flags & SigmaValue) == 0)
8481         geometry_info.sigma=1.0;
8482       (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8483         geometry_info.sigma,exception);
8484       XSetCursorState(display,windows,MagickFalse);
8485       if (IfMagickTrue(windows->image.orphan) )
8486         break;
8487       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8488       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8489       break;
8490     }
8491     case SepiaToneCommand:
8492     {
8493       double
8494         threshold;
8495
8496       Image
8497         *sepia_image;
8498
8499       static char
8500         factor[MaxTextExtent] = "80%";
8501
8502       /*
8503         Query user for sepia-tone factor.
8504       */
8505       (void) XDialogWidget(display,windows,"Sepia Tone",
8506         "Enter the sepia tone factor (0 - 99.9%):",factor);
8507       if (*factor == '\0')
8508         break;
8509       /*
8510         Sepia tone image pixels.
8511       */
8512       XSetCursorState(display,windows,MagickTrue);
8513       XCheckRefreshWindows(display,windows);
8514       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8515       sepia_image=SepiaToneImage(*image,threshold,exception);
8516       if (sepia_image != (Image *) NULL)
8517         {
8518           *image=DestroyImage(*image);
8519           *image=sepia_image;
8520         }
8521       CatchException(exception);
8522       XSetCursorState(display,windows,MagickFalse);
8523       if (IfMagickTrue(windows->image.orphan) )
8524         break;
8525       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8526       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8527       break;
8528     }
8529     case SolarizeCommand:
8530     {
8531       double
8532         threshold;
8533
8534       static char
8535         factor[MaxTextExtent] = "60%";
8536
8537       /*
8538         Query user for solarize factor.
8539       */
8540       (void) XDialogWidget(display,windows,"Solarize",
8541         "Enter the solarize factor (0 - 99.9%):",factor);
8542       if (*factor == '\0')
8543         break;
8544       /*
8545         Solarize image pixels.
8546       */
8547       XSetCursorState(display,windows,MagickTrue);
8548       XCheckRefreshWindows(display,windows);
8549       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8550       (void) SolarizeImage(*image,threshold,exception);
8551       XSetCursorState(display,windows,MagickFalse);
8552       if (IfMagickTrue(windows->image.orphan) )
8553         break;
8554       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8555       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8556       break;
8557     }
8558     case SwirlCommand:
8559     {
8560       Image
8561         *swirl_image;
8562
8563       static char
8564         degrees[MaxTextExtent] = "60";
8565
8566       /*
8567         Query user for swirl angle.
8568       */
8569       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8570         degrees);
8571       if (*degrees == '\0')
8572         break;
8573       /*
8574         Swirl image pixels about the center.
8575       */
8576       XSetCursorState(display,windows,MagickTrue);
8577       XCheckRefreshWindows(display,windows);
8578       flags=ParseGeometry(degrees,&geometry_info);
8579       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8580         exception);
8581       if (swirl_image != (Image *) NULL)
8582         {
8583           *image=DestroyImage(*image);
8584           *image=swirl_image;
8585         }
8586       CatchException(exception);
8587       XSetCursorState(display,windows,MagickFalse);
8588       if (IfMagickTrue(windows->image.orphan) )
8589         break;
8590       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8591       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8592       break;
8593     }
8594     case ImplodeCommand:
8595     {
8596       Image
8597         *implode_image;
8598
8599       static char
8600         factor[MaxTextExtent] = "0.3";
8601
8602       /*
8603         Query user for implode factor.
8604       */
8605       (void) XDialogWidget(display,windows,"Implode",
8606         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8607       if (*factor == '\0')
8608         break;
8609       /*
8610         Implode image pixels about the center.
8611       */
8612       XSetCursorState(display,windows,MagickTrue);
8613       XCheckRefreshWindows(display,windows);
8614       flags=ParseGeometry(factor,&geometry_info);
8615       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8616         exception);
8617       if (implode_image != (Image *) NULL)
8618         {
8619           *image=DestroyImage(*image);
8620           *image=implode_image;
8621         }
8622       CatchException(exception);
8623       XSetCursorState(display,windows,MagickFalse);
8624       if (IfMagickTrue(windows->image.orphan) )
8625         break;
8626       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8627       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8628       break;
8629     }
8630     case VignetteCommand:
8631     {
8632       Image
8633         *vignette_image;
8634
8635       static char
8636         geometry[MaxTextExtent] = "0x20";
8637
8638       /*
8639         Query user for the vignette geometry.
8640       */
8641       (void) XDialogWidget(display,windows,"Vignette",
8642         "Enter the radius, sigma, and x and y offsets:",geometry);
8643       if (*geometry == '\0')
8644         break;
8645       /*
8646         Soften the edges of the image in vignette style
8647       */
8648       XSetCursorState(display,windows,MagickTrue);
8649       XCheckRefreshWindows(display,windows);
8650       flags=ParseGeometry(geometry,&geometry_info);
8651       if ((flags & SigmaValue) == 0)
8652         geometry_info.sigma=1.0;
8653       if ((flags & XiValue) == 0)
8654         geometry_info.xi=0.1*(*image)->columns;
8655       if ((flags & PsiValue) == 0)
8656         geometry_info.psi=0.1*(*image)->rows;
8657       vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8658         ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8659         exception);
8660       if (vignette_image != (Image *) NULL)
8661         {
8662           *image=DestroyImage(*image);
8663           *image=vignette_image;
8664         }
8665       CatchException(exception);
8666       XSetCursorState(display,windows,MagickFalse);
8667       if (IfMagickTrue(windows->image.orphan) )
8668         break;
8669       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8670       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8671       break;
8672     }
8673     case WaveCommand:
8674     {
8675       Image
8676         *wave_image;
8677
8678       static char
8679         geometry[MaxTextExtent] = "25x150";
8680
8681       /*
8682         Query user for the wave geometry.
8683       */
8684       (void) XDialogWidget(display,windows,"Wave",
8685         "Enter the amplitude and length of the wave:",geometry);
8686       if (*geometry == '\0')
8687         break;
8688       /*
8689         Alter an image along a sine wave.
8690       */
8691       XSetCursorState(display,windows,MagickTrue);
8692       XCheckRefreshWindows(display,windows);
8693       flags=ParseGeometry(geometry,&geometry_info);
8694       if ((flags & SigmaValue) == 0)
8695         geometry_info.sigma=1.0;
8696       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8697         (*image)->interpolate,exception);
8698       if (wave_image != (Image *) NULL)
8699         {
8700           *image=DestroyImage(*image);
8701           *image=wave_image;
8702         }
8703       CatchException(exception);
8704       XSetCursorState(display,windows,MagickFalse);
8705       if (IfMagickTrue(windows->image.orphan) )
8706         break;
8707       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8708       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8709       break;
8710     }
8711     case OilPaintCommand:
8712     {
8713       Image
8714         *paint_image;
8715
8716       static char
8717         radius[MaxTextExtent] = "0";
8718
8719       /*
8720         Query user for circular neighborhood radius.
8721       */
8722       (void) XDialogWidget(display,windows,"Oil Paint",
8723         "Enter the mask radius:",radius);
8724       if (*radius == '\0')
8725         break;
8726       /*
8727         OilPaint image scanlines.
8728       */
8729       XSetCursorState(display,windows,MagickTrue);
8730       XCheckRefreshWindows(display,windows);
8731       flags=ParseGeometry(radius,&geometry_info);
8732       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8733         exception);
8734       if (paint_image != (Image *) NULL)
8735         {
8736           *image=DestroyImage(*image);
8737           *image=paint_image;
8738         }
8739       CatchException(exception);
8740       XSetCursorState(display,windows,MagickFalse);
8741       if (IfMagickTrue(windows->image.orphan) )
8742         break;
8743       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8744       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8745       break;
8746     }
8747     case CharcoalDrawCommand:
8748     {
8749       Image
8750         *charcoal_image;
8751
8752       static char
8753         radius[MaxTextExtent] = "0x1";
8754
8755       /*
8756         Query user for charcoal radius.
8757       */
8758       (void) XDialogWidget(display,windows,"Charcoal Draw",
8759         "Enter the charcoal radius and sigma:",radius);
8760       if (*radius == '\0')
8761         break;
8762       /*
8763         Charcoal the image.
8764       */
8765       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8766         exception);
8767       XSetCursorState(display,windows,MagickTrue);
8768       XCheckRefreshWindows(display,windows);
8769       flags=ParseGeometry(radius,&geometry_info);
8770       if ((flags & SigmaValue) == 0)
8771         geometry_info.sigma=geometry_info.rho;
8772       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8773         exception);
8774       if (charcoal_image != (Image *) NULL)
8775         {
8776           *image=DestroyImage(*image);
8777           *image=charcoal_image;
8778         }
8779       CatchException(exception);
8780       XSetCursorState(display,windows,MagickFalse);
8781       if (IfMagickTrue(windows->image.orphan) )
8782         break;
8783       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8784       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8785       break;
8786     }
8787     case AnnotateCommand:
8788     {
8789       /*
8790         Annotate the image with text.
8791       */
8792       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8793       if (IfMagickFalse(status) )
8794         {
8795           XNoticeWidget(display,windows,"Unable to annotate X image",
8796             (*image)->filename);
8797           break;
8798         }
8799       break;
8800     }
8801     case DrawCommand:
8802     {
8803       /*
8804         Draw image.
8805       */
8806       status=XDrawEditImage(display,resource_info,windows,image,exception);
8807       if (IfMagickFalse(status) )
8808         {
8809           XNoticeWidget(display,windows,"Unable to draw on the X image",
8810             (*image)->filename);
8811           break;
8812         }
8813       break;
8814     }
8815     case ColorCommand:
8816     {
8817       /*
8818         Color edit.
8819       */
8820       status=XColorEditImage(display,resource_info,windows,image,exception);
8821       if (IfMagickFalse(status) )
8822         {
8823           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8824             (*image)->filename);
8825           break;
8826         }
8827       break;
8828     }
8829     case MatteCommand:
8830     {
8831       /*
8832         Matte edit.
8833       */
8834       status=XMatteEditImage(display,resource_info,windows,image,exception);
8835       if (IfMagickFalse(status) )
8836         {
8837           XNoticeWidget(display,windows,"Unable to matte edit X image",
8838             (*image)->filename);
8839           break;
8840         }
8841       break;
8842     }
8843     case CompositeCommand:
8844     {
8845       /*
8846         Composite image.
8847       */
8848       status=XCompositeImage(display,resource_info,windows,*image,
8849         exception);
8850       if (IfMagickFalse(status) )
8851         {
8852           XNoticeWidget(display,windows,"Unable to composite X image",
8853             (*image)->filename);
8854           break;
8855         }
8856       break;
8857     }
8858     case AddBorderCommand:
8859     {
8860       Image
8861         *border_image;
8862
8863       static char
8864         geometry[MaxTextExtent] = "6x6";
8865
8866       /*
8867         Query user for border color and geometry.
8868       */
8869       XColorBrowserWidget(display,windows,"Select",color);
8870       if (*color == '\0')
8871         break;
8872       (void) XDialogWidget(display,windows,"Add Border",
8873         "Enter border geometry:",geometry);
8874       if (*geometry == '\0')
8875         break;
8876       /*
8877         Add a border to the image.
8878       */
8879       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8880         exception);
8881       XSetCursorState(display,windows,MagickTrue);
8882       XCheckRefreshWindows(display,windows);
8883       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8884         exception);
8885       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8886         exception);
8887       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8888         exception);
8889       if (border_image != (Image *) NULL)
8890         {
8891           *image=DestroyImage(*image);
8892           *image=border_image;
8893         }
8894       CatchException(exception);
8895       XSetCursorState(display,windows,MagickFalse);
8896       if (IfMagickTrue(windows->image.orphan) )
8897         break;
8898       windows->image.window_changes.width=(int) (*image)->columns;
8899       windows->image.window_changes.height=(int) (*image)->rows;
8900       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8901       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8902       break;
8903     }
8904     case AddFrameCommand:
8905     {
8906       FrameInfo
8907         frame_info;
8908
8909       Image
8910         *frame_image;
8911
8912       static char
8913         geometry[MaxTextExtent] = "6x6";
8914
8915       /*
8916         Query user for frame color and geometry.
8917       */
8918       XColorBrowserWidget(display,windows,"Select",color);
8919       if (*color == '\0')
8920         break;
8921       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8922         geometry);
8923       if (*geometry == '\0')
8924         break;
8925       /*
8926         Surround image with an ornamental border.
8927       */
8928       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8929         exception);
8930       XSetCursorState(display,windows,MagickTrue);
8931       XCheckRefreshWindows(display,windows);
8932       (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8933         exception);
8934       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8935         exception);
8936       frame_info.width=page_geometry.width;
8937       frame_info.height=page_geometry.height;
8938       frame_info.outer_bevel=page_geometry.x;
8939       frame_info.inner_bevel=page_geometry.y;
8940       frame_info.x=(ssize_t) frame_info.width;
8941       frame_info.y=(ssize_t) frame_info.height;
8942       frame_info.width=(*image)->columns+2*frame_info.width;
8943       frame_info.height=(*image)->rows+2*frame_info.height;
8944       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8945       if (frame_image != (Image *) NULL)
8946         {
8947           *image=DestroyImage(*image);
8948           *image=frame_image;
8949         }
8950       CatchException(exception);
8951       XSetCursorState(display,windows,MagickFalse);
8952       if (IfMagickTrue(windows->image.orphan) )
8953         break;
8954       windows->image.window_changes.width=(int) (*image)->columns;
8955       windows->image.window_changes.height=(int) (*image)->rows;
8956       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8957       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8958       break;
8959     }
8960     case CommentCommand:
8961     {
8962       const char
8963         *value;
8964
8965       FILE
8966         *file;
8967
8968       int
8969         unique_file;
8970
8971       /*
8972         Edit image comment.
8973       */
8974       unique_file=AcquireUniqueFileResource(image_info->filename);
8975       if (unique_file == -1)
8976         XNoticeWidget(display,windows,"Unable to edit image comment",
8977           image_info->filename);
8978       value=GetImageProperty(*image,"comment",exception);
8979       if (value == (char *) NULL)
8980         unique_file=close(unique_file)-1;
8981       else
8982         {
8983           register const char
8984             *p;
8985
8986           file=fdopen(unique_file,"w");
8987           if (file == (FILE *) NULL)
8988             {
8989               XNoticeWidget(display,windows,"Unable to edit image comment",
8990                 image_info->filename);
8991               break;
8992             }
8993           for (p=value; *p != '\0'; p++)
8994             (void) fputc((int) *p,file);
8995           (void) fputc('\n',file);
8996           (void) fclose(file);
8997         }
8998       XSetCursorState(display,windows,MagickTrue);
8999       XCheckRefreshWindows(display,windows);
9000       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
9001         exception);
9002       if (IfMagickFalse(status) )
9003         XNoticeWidget(display,windows,"Unable to edit image comment",
9004           (char *) NULL);
9005       else
9006         {
9007           char
9008             *comment;
9009
9010           comment=FileToString(image_info->filename,~0UL,exception);
9011           if (comment != (char *) NULL)
9012             {
9013               (void) SetImageProperty(*image,"comment",comment,exception);
9014               (*image)->taint=MagickTrue;
9015             }
9016         }
9017       (void) RelinquishUniqueFileResource(image_info->filename);
9018       XSetCursorState(display,windows,MagickFalse);
9019       break;
9020     }
9021     case LaunchCommand:
9022     {
9023       /*
9024         Launch program.
9025       */
9026       XSetCursorState(display,windows,MagickTrue);
9027       XCheckRefreshWindows(display,windows);
9028       (void) AcquireUniqueFilename(filename);
9029       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9030         filename);
9031       status=WriteImage(image_info,*image,exception);
9032       if (IfMagickFalse(status) )
9033         XNoticeWidget(display,windows,"Unable to launch image editor",
9034           (char *) NULL);
9035       else
9036         {
9037           nexus=ReadImage(resource_info->image_info,exception);
9038           CatchException(exception);
9039           XClientMessage(display,windows->image.id,windows->im_protocols,
9040             windows->im_next_image,CurrentTime);
9041         }
9042       (void) RelinquishUniqueFileResource(filename);
9043       XSetCursorState(display,windows,MagickFalse);
9044       break;
9045     }
9046     case RegionofInterestCommand:
9047     {
9048       /*
9049         Apply an image processing technique to a region of interest.
9050       */
9051       (void) XROIImage(display,resource_info,windows,image,exception);
9052       break;
9053     }
9054     case InfoCommand:
9055       break;
9056     case ZoomCommand:
9057     {
9058       /*
9059         Zoom image.
9060       */
9061       if (IfMagickTrue(windows->magnify.mapped) )
9062         (void) XRaiseWindow(display,windows->magnify.id);
9063       else
9064         {
9065           /*
9066             Make magnify image.
9067           */
9068           XSetCursorState(display,windows,MagickTrue);
9069           (void) XMapRaised(display,windows->magnify.id);
9070           XSetCursorState(display,windows,MagickFalse);
9071         }
9072       break;
9073     }
9074     case ShowPreviewCommand:
9075     {
9076       char
9077         **previews;
9078
9079       Image
9080         *preview_image;
9081
9082       static char
9083         preview_type[MaxTextExtent] = "Gamma";
9084
9085       /*
9086         Select preview type from menu.
9087       */
9088       previews=GetCommandOptions(MagickPreviewOptions);
9089       if (previews == (char **) NULL)
9090         break;
9091       XListBrowserWidget(display,windows,&windows->widget,
9092         (const char **) previews,"Preview",
9093         "Select an enhancement, effect, or F/X:",preview_type);
9094       previews=DestroyStringList(previews);
9095       if (*preview_type == '\0')
9096         break;
9097       /*
9098         Show image preview.
9099       */
9100       XSetCursorState(display,windows,MagickTrue);
9101       XCheckRefreshWindows(display,windows);
9102       image_info->preview_type=(PreviewType)
9103         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9104       image_info->group=(ssize_t) windows->image.id;
9105       (void) DeleteImageProperty(*image,"label");
9106       (void) SetImageProperty(*image,"label","Preview",exception);
9107       (void) AcquireUniqueFilename(filename);
9108       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9109         filename);
9110       status=WriteImage(image_info,*image,exception);
9111       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9112       preview_image=ReadImage(image_info,exception);
9113       (void) RelinquishUniqueFileResource(filename);
9114       if (preview_image == (Image *) NULL)
9115         break;
9116       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9117         filename);
9118       status=WriteImage(image_info,preview_image,exception);
9119       preview_image=DestroyImage(preview_image);
9120       if (IfMagickFalse(status) )
9121         XNoticeWidget(display,windows,"Unable to show image preview",
9122           (*image)->filename);
9123       XDelay(display,1500);
9124       XSetCursorState(display,windows,MagickFalse);
9125       break;
9126     }
9127     case ShowHistogramCommand:
9128     {
9129       Image
9130         *histogram_image;
9131
9132       /*
9133         Show image histogram.
9134       */
9135       XSetCursorState(display,windows,MagickTrue);
9136       XCheckRefreshWindows(display,windows);
9137       image_info->group=(ssize_t) windows->image.id;
9138       (void) DeleteImageProperty(*image,"label");
9139       (void) SetImageProperty(*image,"label","Histogram",exception);
9140       (void) AcquireUniqueFilename(filename);
9141       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9142         filename);
9143       status=WriteImage(image_info,*image,exception);
9144       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9145       histogram_image=ReadImage(image_info,exception);
9146       (void) RelinquishUniqueFileResource(filename);
9147       if (histogram_image == (Image *) NULL)
9148         break;
9149       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9150         "show:%s",filename);
9151       status=WriteImage(image_info,histogram_image,exception);
9152       histogram_image=DestroyImage(histogram_image);
9153       if (IfMagickFalse(status) )
9154         XNoticeWidget(display,windows,"Unable to show histogram",
9155           (*image)->filename);
9156       XDelay(display,1500);
9157       XSetCursorState(display,windows,MagickFalse);
9158       break;
9159     }
9160     case ShowMatteCommand:
9161     {
9162       Image
9163         *matte_image;
9164
9165       if ((*image)->alpha_trait != BlendPixelTrait)
9166         {
9167           XNoticeWidget(display,windows,
9168             "Image does not have any matte information",(*image)->filename);
9169           break;
9170         }
9171       /*
9172         Show image matte.
9173       */
9174       XSetCursorState(display,windows,MagickTrue);
9175       XCheckRefreshWindows(display,windows);
9176       image_info->group=(ssize_t) windows->image.id;
9177       (void) DeleteImageProperty(*image,"label");
9178       (void) SetImageProperty(*image,"label","Matte",exception);
9179       (void) AcquireUniqueFilename(filename);
9180       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9181         filename);
9182       status=WriteImage(image_info,*image,exception);
9183       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9184       matte_image=ReadImage(image_info,exception);
9185       (void) RelinquishUniqueFileResource(filename);
9186       if (matte_image == (Image *) NULL)
9187         break;
9188       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9189         filename);
9190       status=WriteImage(image_info,matte_image,exception);
9191       matte_image=DestroyImage(matte_image);
9192       if (IfMagickFalse(status) )
9193         XNoticeWidget(display,windows,"Unable to show matte",
9194           (*image)->filename);
9195       XDelay(display,1500);
9196       XSetCursorState(display,windows,MagickFalse);
9197       break;
9198     }
9199     case BackgroundCommand:
9200     {
9201       /*
9202         Background image.
9203       */
9204       status=XBackgroundImage(display,resource_info,windows,image,exception);
9205       if (IfMagickFalse(status) )
9206         break;
9207       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9208       if (nexus != (Image *) NULL)
9209         XClientMessage(display,windows->image.id,windows->im_protocols,
9210           windows->im_next_image,CurrentTime);
9211       break;
9212     }
9213     case SlideShowCommand:
9214     {
9215       static char
9216         delay[MaxTextExtent] = "5";
9217
9218       /*
9219         Display next image after pausing.
9220       */
9221       (void) XDialogWidget(display,windows,"Slide Show",
9222         "Pause how many 1/100ths of a second between images:",delay);
9223       if (*delay == '\0')
9224         break;
9225       resource_info->delay=StringToUnsignedLong(delay);
9226       XClientMessage(display,windows->image.id,windows->im_protocols,
9227         windows->im_next_image,CurrentTime);
9228       break;
9229     }
9230     case PreferencesCommand:
9231     {
9232       /*
9233         Set user preferences.
9234       */
9235       status=XPreferencesWidget(display,resource_info,windows);
9236       if (IfMagickFalse(status) )
9237         break;
9238       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9239       if (nexus != (Image *) NULL)
9240         XClientMessage(display,windows->image.id,windows->im_protocols,
9241           windows->im_next_image,CurrentTime);
9242       break;
9243     }
9244     case HelpCommand:
9245     {
9246       /*
9247         User requested help.
9248       */
9249       XTextViewWidget(display,resource_info,windows,MagickFalse,
9250         "Help Viewer - Display",DisplayHelp);
9251       break;
9252     }
9253     case BrowseDocumentationCommand:
9254     {
9255       Atom
9256         mozilla_atom;
9257
9258       Window
9259         mozilla_window,
9260         root_window;
9261
9262       /*
9263         Browse the ImageMagick documentation.
9264       */
9265       root_window=XRootWindow(display,XDefaultScreen(display));
9266       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9267       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9268       if (mozilla_window != (Window) NULL)
9269         {
9270           char
9271             command[MaxTextExtent],
9272             *url;
9273
9274           /*
9275             Display documentation using Netscape remote control.
9276           */
9277           url=GetMagickHomeURL();
9278           (void) FormatLocaleString(command,MaxTextExtent,
9279             "openurl(%s,new-tab)",url);
9280           url=DestroyString(url);
9281           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9282           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9283             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9284           XSetCursorState(display,windows,MagickFalse);
9285           break;
9286         }
9287       XSetCursorState(display,windows,MagickTrue);
9288       XCheckRefreshWindows(display,windows);
9289       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9290         exception);
9291       if (IfMagickFalse(status) )
9292         XNoticeWidget(display,windows,"Unable to browse documentation",
9293           (char *) NULL);
9294       XDelay(display,1500);
9295       XSetCursorState(display,windows,MagickFalse);
9296       break;
9297     }
9298     case VersionCommand:
9299     {
9300       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9301         GetMagickCopyright());
9302       break;
9303     }
9304     case SaveToUndoBufferCommand:
9305       break;
9306     default:
9307     {
9308       (void) XBell(display,0);
9309       break;
9310     }
9311   }
9312   image_info=DestroyImageInfo(image_info);
9313   return(nexus);
9314 }
9315 \f
9316 /*
9317 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9318 %                                                                             %
9319 %                                                                             %
9320 %                                                                             %
9321 +   X M a g n i f y I m a g e                                                 %
9322 %                                                                             %
9323 %                                                                             %
9324 %                                                                             %
9325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9326 %
9327 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9328 %  The magnified portion is displayed in a separate window.
9329 %
9330 %  The format of the XMagnifyImage method is:
9331 %
9332 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9333 %        ExceptionInfo *exception)
9334 %
9335 %  A description of each parameter follows:
9336 %
9337 %    o display: Specifies a connection to an X server;  returned from
9338 %      XOpenDisplay.
9339 %
9340 %    o windows: Specifies a pointer to a XWindows structure.
9341 %
9342 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9343 %      the entire image is refreshed.
9344 %
9345 %    o exception: return any errors or warnings in this structure.
9346 %
9347 */
9348 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9349   ExceptionInfo *exception)
9350 {
9351   char
9352     text[MaxTextExtent];
9353
9354   register int
9355     x,
9356     y;
9357
9358   size_t
9359     state;
9360
9361   /*
9362     Update magnified image until the mouse button is released.
9363   */
9364   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9365   state=DefaultState;
9366   x=event->xbutton.x;
9367   y=event->xbutton.y;
9368   windows->magnify.x=(int) windows->image.x+x;
9369   windows->magnify.y=(int) windows->image.y+y;
9370   do
9371   {
9372     /*
9373       Map and unmap Info widget as text cursor crosses its boundaries.
9374     */
9375     if (IfMagickTrue(windows->info.mapped) )
9376       {
9377         if ((x < (int) (windows->info.x+windows->info.width)) &&
9378             (y < (int) (windows->info.y+windows->info.height)))
9379           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9380       }
9381     else
9382       if ((x > (int) (windows->info.x+windows->info.width)) ||
9383           (y > (int) (windows->info.y+windows->info.height)))
9384         (void) XMapWindow(display,windows->info.id);
9385     if (IfMagickTrue(windows->info.mapped) )
9386       {
9387         /*
9388           Display pointer position.
9389         */
9390         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9391           windows->magnify.x,windows->magnify.y);
9392         XInfoWidget(display,windows,text);
9393       }
9394     /*
9395       Wait for next event.
9396     */
9397     XScreenEvent(display,windows,event,exception);
9398     switch (event->type)
9399     {
9400       case ButtonPress:
9401         break;
9402       case ButtonRelease:
9403       {
9404         /*
9405           User has finished magnifying image.
9406         */
9407         x=event->xbutton.x;
9408         y=event->xbutton.y;
9409         state|=ExitState;
9410         break;
9411       }
9412       case Expose:
9413         break;
9414       case MotionNotify:
9415       {
9416         x=event->xmotion.x;
9417         y=event->xmotion.y;
9418         break;
9419       }
9420       default:
9421         break;
9422     }
9423     /*
9424       Check boundary conditions.
9425     */
9426     if (x < 0)
9427       x=0;
9428     else
9429       if (x >= (int) windows->image.width)
9430         x=(int) windows->image.width-1;
9431     if (y < 0)
9432       y=0;
9433     else
9434      if (y >= (int) windows->image.height)
9435        y=(int) windows->image.height-1;
9436   } while ((state & ExitState) == 0);
9437   /*
9438     Display magnified image.
9439   */
9440   XSetCursorState(display,windows,MagickFalse);
9441 }
9442 \f
9443 /*
9444 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9445 %                                                                             %
9446 %                                                                             %
9447 %                                                                             %
9448 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9449 %                                                                             %
9450 %                                                                             %
9451 %                                                                             %
9452 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9453 %
9454 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9455 %  pixel as specified by the key symbol.
9456 %
9457 %  The format of the XMagnifyWindowCommand method is:
9458 %
9459 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9460 %        const MagickStatusType state,const KeySym key_symbol,
9461 %        ExceptionInfo *exception)
9462 %
9463 %  A description of each parameter follows:
9464 %
9465 %    o display: Specifies a connection to an X server; returned from
9466 %      XOpenDisplay.
9467 %
9468 %    o windows: Specifies a pointer to a XWindows structure.
9469 %
9470 %    o state: key mask.
9471 %
9472 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9473 %      to trim.
9474 %
9475 %    o exception: return any errors or warnings in this structure.
9476 %
9477 */
9478 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9479   const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9480 {
9481   unsigned int
9482     quantum;
9483
9484   /*
9485     User specified a magnify factor or position.
9486   */
9487   quantum=1;
9488   if ((state & Mod1Mask) != 0)
9489     quantum=10;
9490   switch ((int) key_symbol)
9491   {
9492     case QuitCommand:
9493     {
9494       (void) XWithdrawWindow(display,windows->magnify.id,
9495         windows->magnify.screen);
9496       break;
9497     }
9498     case XK_Home:
9499     case XK_KP_Home:
9500     {
9501       windows->magnify.x=(int) windows->image.width/2;
9502       windows->magnify.y=(int) windows->image.height/2;
9503       break;
9504     }
9505     case XK_Left:
9506     case XK_KP_Left:
9507     {
9508       if (windows->magnify.x > 0)
9509         windows->magnify.x-=quantum;
9510       break;
9511     }
9512     case XK_Up:
9513     case XK_KP_Up:
9514     {
9515       if (windows->magnify.y > 0)
9516         windows->magnify.y-=quantum;
9517       break;
9518     }
9519     case XK_Right:
9520     case XK_KP_Right:
9521     {
9522       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9523         windows->magnify.x+=quantum;
9524       break;
9525     }
9526     case XK_Down:
9527     case XK_KP_Down:
9528     {
9529       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9530         windows->magnify.y+=quantum;
9531       break;
9532     }
9533     case XK_0:
9534     case XK_1:
9535     case XK_2:
9536     case XK_3:
9537     case XK_4:
9538     case XK_5:
9539     case XK_6:
9540     case XK_7:
9541     case XK_8:
9542     case XK_9:
9543     {
9544       windows->magnify.data=(key_symbol-XK_0);
9545       break;
9546     }
9547     case XK_KP_0:
9548     case XK_KP_1:
9549     case XK_KP_2:
9550     case XK_KP_3:
9551     case XK_KP_4:
9552     case XK_KP_5:
9553     case XK_KP_6:
9554     case XK_KP_7:
9555     case XK_KP_8:
9556     case XK_KP_9:
9557     {
9558       windows->magnify.data=(key_symbol-XK_KP_0);
9559       break;
9560     }
9561     default:
9562       break;
9563   }
9564   XMakeMagnifyImage(display,windows,exception);
9565 }
9566 \f
9567 /*
9568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9569 %                                                                             %
9570 %                                                                             %
9571 %                                                                             %
9572 +   X M a k e P a n I m a g e                                                 %
9573 %                                                                             %
9574 %                                                                             %
9575 %                                                                             %
9576 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9577 %
9578 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9579 %  icon window.
9580 %
9581 %  The format of the XMakePanImage method is:
9582 %
9583 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9584 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9585 %
9586 %  A description of each parameter follows:
9587 %
9588 %    o display: Specifies a connection to an X server;  returned from
9589 %      XOpenDisplay.
9590 %
9591 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9592 %
9593 %    o windows: Specifies a pointer to a XWindows structure.
9594 %
9595 %    o image: the image.
9596 %
9597 %    o exception: return any errors or warnings in this structure.
9598 %
9599 */
9600 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9601   XWindows *windows,Image *image,ExceptionInfo *exception)
9602 {
9603   MagickStatusType
9604     status;
9605
9606   /*
9607     Create and display image for panning icon.
9608   */
9609   XSetCursorState(display,windows,MagickTrue);
9610   XCheckRefreshWindows(display,windows);
9611   windows->pan.x=(int) windows->image.x;
9612   windows->pan.y=(int) windows->image.y;
9613   status=XMakeImage(display,resource_info,&windows->pan,image,
9614     windows->pan.width,windows->pan.height,exception);
9615   if (IfMagickFalse(status) )
9616     ThrowXWindowException(ResourceLimitError,
9617      "MemoryAllocationFailed",image->filename);
9618   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9619     windows->pan.pixmap);
9620   (void) XClearWindow(display,windows->pan.id);
9621   XDrawPanRectangle(display,windows);
9622   XSetCursorState(display,windows,MagickFalse);
9623 }
9624 \f
9625 /*
9626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9627 %                                                                             %
9628 %                                                                             %
9629 %                                                                             %
9630 +   X M a t t a E d i t I m a g e                                             %
9631 %                                                                             %
9632 %                                                                             %
9633 %                                                                             %
9634 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9635 %
9636 %  XMatteEditImage() allows the user to interactively change the Matte channel
9637 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9638 %  before the matte information is stored.
9639 %
9640 %  The format of the XMatteEditImage method is:
9641 %
9642 %      MagickBooleanType XMatteEditImage(Display *display,
9643 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9644 %        ExceptionInfo *exception)
9645 %
9646 %  A description of each parameter follows:
9647 %
9648 %    o display: Specifies a connection to an X server;  returned from
9649 %      XOpenDisplay.
9650 %
9651 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9652 %
9653 %    o windows: Specifies a pointer to a XWindows structure.
9654 %
9655 %    o image: the image; returned from ReadImage.
9656 %
9657 %    o exception: return any errors or warnings in this structure.
9658 %
9659 */
9660 static MagickBooleanType XMatteEditImage(Display *display,
9661   XResourceInfo *resource_info,XWindows *windows,Image **image,
9662   ExceptionInfo *exception)
9663 {
9664   static char
9665     matte[MaxTextExtent] = "0";
9666
9667   static const char
9668     *MatteEditMenu[] =
9669     {
9670       "Method",
9671       "Border Color",
9672       "Fuzz",
9673       "Matte Value",
9674       "Undo",
9675       "Help",
9676       "Dismiss",
9677       (char *) NULL
9678     };
9679
9680   static const ModeType
9681     MatteEditCommands[] =
9682     {
9683       MatteEditMethod,
9684       MatteEditBorderCommand,
9685       MatteEditFuzzCommand,
9686       MatteEditValueCommand,
9687       MatteEditUndoCommand,
9688       MatteEditHelpCommand,
9689       MatteEditDismissCommand
9690     };
9691
9692   static PaintMethod
9693     method = PointMethod;
9694
9695   static XColor
9696     border_color = { 0, 0, 0, 0, 0, 0 };
9697
9698   char
9699     command[MaxTextExtent],
9700     text[MaxTextExtent];
9701
9702   Cursor
9703     cursor;
9704
9705   int
9706     entry,
9707     id,
9708     x,
9709     x_offset,
9710     y,
9711     y_offset;
9712
9713   register int
9714     i;
9715
9716   register Quantum
9717     *q;
9718
9719   unsigned int
9720     height,
9721     width;
9722
9723   size_t
9724     state;
9725
9726   XEvent
9727     event;
9728
9729   /*
9730     Map Command widget.
9731   */
9732   (void) CloneString(&windows->command.name,"Matte Edit");
9733   windows->command.data=4;
9734   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9735   (void) XMapRaised(display,windows->command.id);
9736   XClientMessage(display,windows->image.id,windows->im_protocols,
9737     windows->im_update_widget,CurrentTime);
9738   /*
9739     Make cursor.
9740   */
9741   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9742     resource_info->background_color,resource_info->foreground_color);
9743   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9744   /*
9745     Track pointer until button 1 is pressed.
9746   */
9747   XQueryPosition(display,windows->image.id,&x,&y);
9748   (void) XSelectInput(display,windows->image.id,
9749     windows->image.attributes.event_mask | PointerMotionMask);
9750   state=DefaultState;
9751   do
9752   {
9753     if (IfMagickTrue(windows->info.mapped) )
9754       {
9755         /*
9756           Display pointer position.
9757         */
9758         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9759           x+windows->image.x,y+windows->image.y);
9760         XInfoWidget(display,windows,text);
9761       }
9762     /*
9763       Wait for next event.
9764     */
9765     XScreenEvent(display,windows,&event,exception);
9766     if (event.xany.window == windows->command.id)
9767       {
9768         /*
9769           Select a command from the Command widget.
9770         */
9771         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9772         if (id < 0)
9773           {
9774             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9775             continue;
9776           }
9777         switch (MatteEditCommands[id])
9778         {
9779           case MatteEditMethod:
9780           {
9781             char
9782               **methods;
9783
9784             /*
9785               Select a method from the pop-up menu.
9786             */
9787             methods=GetCommandOptions(MagickMethodOptions);
9788             if (methods == (char **) NULL)
9789               break;
9790             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9791               (const char **) methods,command);
9792             if (entry >= 0)
9793               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9794                 MagickFalse,methods[entry]);
9795             methods=DestroyStringList(methods);
9796             break;
9797           }
9798           case MatteEditBorderCommand:
9799           {
9800             const char
9801               *ColorMenu[MaxNumberPens];
9802
9803             int
9804               pen_number;
9805
9806             /*
9807               Initialize menu selections.
9808             */
9809             for (i=0; i < (int) (MaxNumberPens-2); i++)
9810               ColorMenu[i]=resource_info->pen_colors[i];
9811             ColorMenu[MaxNumberPens-2]="Browser...";
9812             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9813             /*
9814               Select a pen color from the pop-up menu.
9815             */
9816             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9817               (const char **) ColorMenu,command);
9818             if (pen_number < 0)
9819               break;
9820             if (pen_number == (MaxNumberPens-2))
9821               {
9822                 static char
9823                   color_name[MaxTextExtent] = "gray";
9824
9825                 /*
9826                   Select a pen color from a dialog.
9827                 */
9828                 resource_info->pen_colors[pen_number]=color_name;
9829                 XColorBrowserWidget(display,windows,"Select",color_name);
9830                 if (*color_name == '\0')
9831                   break;
9832               }
9833             /*
9834               Set border color.
9835             */
9836             (void) XParseColor(display,windows->map_info->colormap,
9837               resource_info->pen_colors[pen_number],&border_color);
9838             break;
9839           }
9840           case MatteEditFuzzCommand:
9841           {
9842             static char
9843               fuzz[MaxTextExtent];
9844
9845             static const char
9846               *FuzzMenu[] =
9847               {
9848                 "0%",
9849                 "2%",
9850                 "5%",
9851                 "10%",
9852                 "15%",
9853                 "Dialog...",
9854                 (char *) NULL,
9855               };
9856
9857             /*
9858               Select a command from the pop-up menu.
9859             */
9860             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9861               command);
9862             if (entry < 0)
9863               break;
9864             if (entry != 5)
9865               {
9866                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9867                   QuantumRange+1.0);
9868                 break;
9869               }
9870             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9871             (void) XDialogWidget(display,windows,"Ok",
9872               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9873             if (*fuzz == '\0')
9874               break;
9875             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9876             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9877               1.0);
9878             break;
9879           }
9880           case MatteEditValueCommand:
9881           {
9882             static char
9883               message[MaxTextExtent];
9884
9885             static const char
9886               *MatteMenu[] =
9887               {
9888                 "Opaque",
9889                 "Transparent",
9890                 "Dialog...",
9891                 (char *) NULL,
9892               };
9893
9894             /*
9895               Select a command from the pop-up menu.
9896             */
9897             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9898               command);
9899             if (entry < 0)
9900               break;
9901             if (entry != 2)
9902               {
9903                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9904                   OpaqueAlpha);
9905                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9906                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9907                     (Quantum) TransparentAlpha);
9908                 break;
9909               }
9910             (void) FormatLocaleString(message,MaxTextExtent,
9911               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9912               QuantumRange);
9913             (void) XDialogWidget(display,windows,"Matte",message,matte);
9914             if (*matte == '\0')
9915               break;
9916             break;
9917           }
9918           case MatteEditUndoCommand:
9919           {
9920             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9921               image,exception);
9922             break;
9923           }
9924           case MatteEditHelpCommand:
9925           {
9926             XTextViewWidget(display,resource_info,windows,MagickFalse,
9927               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9928             break;
9929           }
9930           case MatteEditDismissCommand:
9931           {
9932             /*
9933               Prematurely exit.
9934             */
9935             state|=EscapeState;
9936             state|=ExitState;
9937             break;
9938           }
9939           default:
9940             break;
9941         }
9942         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9943         continue;
9944       }
9945     switch (event.type)
9946     {
9947       case ButtonPress:
9948       {
9949         if (event.xbutton.button != Button1)
9950           break;
9951         if ((event.xbutton.window != windows->image.id) &&
9952             (event.xbutton.window != windows->magnify.id))
9953           break;
9954         /*
9955           Update matte data.
9956         */
9957         x=event.xbutton.x;
9958         y=event.xbutton.y;
9959         (void) XMagickCommand(display,resource_info,windows,
9960           SaveToUndoBufferCommand,image,exception);
9961         state|=UpdateConfigurationState;
9962         break;
9963       }
9964       case ButtonRelease:
9965       {
9966         if (event.xbutton.button != Button1)
9967           break;
9968         if ((event.xbutton.window != windows->image.id) &&
9969             (event.xbutton.window != windows->magnify.id))
9970           break;
9971         /*
9972           Update colormap information.
9973         */
9974         x=event.xbutton.x;
9975         y=event.xbutton.y;
9976         XConfigureImageColormap(display,resource_info,windows,*image,exception);
9977         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9978         XInfoWidget(display,windows,text);
9979         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9980         state&=(~UpdateConfigurationState);
9981         break;
9982       }
9983       case Expose:
9984         break;
9985       case KeyPress:
9986       {
9987         char
9988           command[MaxTextExtent];
9989
9990         KeySym
9991           key_symbol;
9992
9993         if (event.xkey.window == windows->magnify.id)
9994           {
9995             Window
9996               window;
9997
9998             window=windows->magnify.id;
9999             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
10000           }
10001         if (event.xkey.window != windows->image.id)
10002           break;
10003         /*
10004           Respond to a user key press.
10005         */
10006         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10007           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10008         switch ((int) key_symbol)
10009         {
10010           case XK_Escape:
10011           case XK_F20:
10012           {
10013             /*
10014               Prematurely exit.
10015             */
10016             state|=ExitState;
10017             break;
10018           }
10019           case XK_F1:
10020           case XK_Help:
10021           {
10022             XTextViewWidget(display,resource_info,windows,MagickFalse,
10023               "Help Viewer - Matte Edit",ImageMatteEditHelp);
10024             break;
10025           }
10026           default:
10027           {
10028             (void) XBell(display,0);
10029             break;
10030           }
10031         }
10032         break;
10033       }
10034       case MotionNotify:
10035       {
10036         /*
10037           Map and unmap Info widget as cursor crosses its boundaries.
10038         */
10039         x=event.xmotion.x;
10040         y=event.xmotion.y;
10041         if (IfMagickTrue(windows->info.mapped) )
10042           {
10043             if ((x < (int) (windows->info.x+windows->info.width)) &&
10044                 (y < (int) (windows->info.y+windows->info.height)))
10045               (void) XWithdrawWindow(display,windows->info.id,
10046                 windows->info.screen);
10047           }
10048         else
10049           if ((x > (int) (windows->info.x+windows->info.width)) ||
10050               (y > (int) (windows->info.y+windows->info.height)))
10051             (void) XMapWindow(display,windows->info.id);
10052         break;
10053       }
10054       default:
10055         break;
10056     }
10057     if (event.xany.window == windows->magnify.id)
10058       {
10059         x=windows->magnify.x-windows->image.x;
10060         y=windows->magnify.y-windows->image.y;
10061       }
10062     x_offset=x;
10063     y_offset=y;
10064     if ((state & UpdateConfigurationState) != 0)
10065       {
10066         CacheView
10067           *image_view;
10068
10069         int
10070           x,
10071           y;
10072
10073         /*
10074           Matte edit is relative to image configuration.
10075         */
10076         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10077           MagickTrue);
10078         XPutPixel(windows->image.ximage,x_offset,y_offset,
10079           windows->pixel_info->background_color.pixel);
10080         width=(unsigned int) (*image)->columns;
10081         height=(unsigned int) (*image)->rows;
10082         x=0;
10083         y=0;
10084         if (windows->image.crop_geometry != (char *) NULL)
10085           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10086             &height);
10087         x_offset=(int) (width*(windows->image.x+x_offset)/
10088           windows->image.ximage->width+x);
10089         y_offset=(int) (height*(windows->image.y+y_offset)/
10090           windows->image.ximage->height+y);
10091         if ((x_offset < 0) || (y_offset < 0))
10092           continue;
10093         if ((x_offset >= (int) (*image)->columns) ||
10094             (y_offset >= (int) (*image)->rows))
10095           continue;
10096         if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10097           return(MagickFalse);
10098         if ((*image)->alpha_trait != BlendPixelTrait)
10099           (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10100         image_view=AcquireAuthenticCacheView(*image,exception);
10101         switch (method)
10102         {
10103           case PointMethod:
10104           default:
10105           {
10106             /*
10107               Update matte information using point algorithm.
10108             */
10109             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10110               (ssize_t) y_offset,1,1,exception);
10111             if (q == (Quantum *) NULL)
10112               break;
10113             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10114             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10115             break;
10116           }
10117           case ReplaceMethod:
10118           {
10119             PixelInfo
10120               pixel,
10121               target;
10122
10123             /*
10124               Update matte information using replace algorithm.
10125             */
10126             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10127               x_offset,(ssize_t) y_offset,&target,exception);
10128             for (y=0; y < (int) (*image)->rows; y++)
10129             {
10130               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10131                 (*image)->columns,1,exception);
10132               if (q == (Quantum *) NULL)
10133                 break;
10134               for (x=0; x < (int) (*image)->columns; x++)
10135               {
10136                 GetPixelInfoPixel(*image,q,&pixel);
10137                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10138                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10139                 q+=GetPixelChannels(*image);
10140               }
10141               if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10142                 break;
10143             }
10144             break;
10145           }
10146           case FloodfillMethod:
10147           case FillToBorderMethod:
10148           {
10149             ChannelType
10150               channel_mask;
10151
10152             DrawInfo
10153               *draw_info;
10154
10155             PixelInfo
10156               target;
10157
10158             /*
10159               Update matte information using floodfill algorithm.
10160             */
10161             (void) GetOneVirtualPixelInfo(*image,
10162               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10163               y_offset,&target,exception);
10164             if (method == FillToBorderMethod)
10165               {
10166                 target.red=(double) ScaleShortToQuantum(
10167                   border_color.red);
10168                 target.green=(double) ScaleShortToQuantum(
10169                   border_color.green);
10170                 target.blue=(double) ScaleShortToQuantum(
10171                   border_color.blue);
10172               }
10173             draw_info=CloneDrawInfo(resource_info->image_info,
10174               (DrawInfo *) NULL);
10175             draw_info->fill.alpha=(double) ClampToQuantum(
10176               StringToDouble(matte,(char **) NULL));
10177             channel_mask=SetImageChannelMask(*image,AlphaChannel);
10178             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10179               x_offset,(ssize_t) y_offset,
10180               IsMagickFalse(method == FloodfillMethod),exception);
10181             (void) SetPixelChannelMask(*image,channel_mask);
10182             draw_info=DestroyDrawInfo(draw_info);
10183             break;
10184           }
10185           case ResetMethod:
10186           {
10187             /*
10188               Update matte information using reset algorithm.
10189             */
10190             if (IfMagickFalse(SetImageStorageClass(*image,DirectClass,exception)) )
10191               return(MagickFalse);
10192             for (y=0; y < (int) (*image)->rows; y++)
10193             {
10194               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10195                 (*image)->columns,1,exception);
10196               if (q == (Quantum *) NULL)
10197                 break;
10198               for (x=0; x < (int) (*image)->columns; x++)
10199               {
10200                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10201                 q+=GetPixelChannels(*image);
10202               }
10203               if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
10204                 break;
10205             }
10206             if (StringToLong(matte) == (long) OpaqueAlpha)
10207               (*image)->alpha_trait=UndefinedPixelTrait;
10208             break;
10209           }
10210         }
10211         image_view=DestroyCacheView(image_view);
10212         state&=(~UpdateConfigurationState);
10213       }
10214   } while ((state & ExitState) == 0);
10215   (void) XSelectInput(display,windows->image.id,
10216     windows->image.attributes.event_mask);
10217   XSetCursorState(display,windows,MagickFalse);
10218   (void) XFreeCursor(display,cursor);
10219   return(MagickTrue);
10220 }
10221 \f
10222 /*
10223 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10224 %                                                                             %
10225 %                                                                             %
10226 %                                                                             %
10227 +   X O p e n I m a g e                                                       %
10228 %                                                                             %
10229 %                                                                             %
10230 %                                                                             %
10231 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10232 %
10233 %  XOpenImage() loads an image from a file.
10234 %
10235 %  The format of the XOpenImage method is:
10236 %
10237 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10238 %       XWindows *windows,const unsigned int command)
10239 %
10240 %  A description of each parameter follows:
10241 %
10242 %    o display: Specifies a connection to an X server; returned from
10243 %      XOpenDisplay.
10244 %
10245 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10246 %
10247 %    o windows: Specifies a pointer to a XWindows structure.
10248 %
10249 %    o command: A value other than zero indicates that the file is selected
10250 %      from the command line argument list.
10251 %
10252 */
10253 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10254   XWindows *windows,const MagickBooleanType command)
10255 {
10256   const MagickInfo
10257     *magick_info;
10258
10259   ExceptionInfo
10260     *exception;
10261
10262   Image
10263     *nexus;
10264
10265   ImageInfo
10266     *image_info;
10267
10268   static char
10269     filename[MaxTextExtent] = "\0";
10270
10271   /*
10272     Request file name from user.
10273   */
10274   if (IfMagickFalse(command) )
10275     XFileBrowserWidget(display,windows,"Open",filename);
10276   else
10277     {
10278       char
10279         **filelist,
10280         **files;
10281
10282       int
10283         count,
10284         status;
10285
10286       register int
10287         i,
10288         j;
10289
10290       /*
10291         Select next image from the command line.
10292       */
10293       status=XGetCommand(display,windows->image.id,&files,&count);
10294       if (status == 0)
10295         {
10296           ThrowXWindowException(XServerError,"UnableToGetProperty","...");
10297           return((Image *) NULL);
10298         }
10299       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10300       if (filelist == (char **) NULL)
10301         {
10302           ThrowXWindowException(ResourceLimitError,
10303             "MemoryAllocationFailed","...");
10304           (void) XFreeStringList(files);
10305           return((Image *) NULL);
10306         }
10307       j=0;
10308       for (i=1; i < count; i++)
10309         if (*files[i] != '-')
10310           filelist[j++]=files[i];
10311       filelist[j]=(char *) NULL;
10312       XListBrowserWidget(display,windows,&windows->widget,
10313         (const char **) filelist,"Load","Select Image to Load:",filename);
10314       filelist=(char **) RelinquishMagickMemory(filelist);
10315       (void) XFreeStringList(files);
10316     }
10317   if (*filename == '\0')
10318     return((Image *) NULL);
10319   image_info=CloneImageInfo(resource_info->image_info);
10320   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10321     (void *) NULL);
10322   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10323   exception=AcquireExceptionInfo();
10324   (void) SetImageInfo(image_info,0,exception);
10325   if (LocaleCompare(image_info->magick,"X") == 0)
10326     {
10327       char
10328         seconds[MaxTextExtent];
10329
10330       /*
10331         User may want to delay the X server screen grab.
10332       */
10333       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10334       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10335         seconds);
10336       if (*seconds == '\0')
10337         return((Image *) NULL);
10338       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10339     }
10340   magick_info=GetMagickInfo(image_info->magick,exception);
10341   if ((magick_info != (const MagickInfo *) NULL) &&
10342       IfMagickTrue(magick_info->raw))
10343     {
10344       char
10345         geometry[MaxTextExtent];
10346
10347       /*
10348         Request image size from the user.
10349       */
10350       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10351       if (image_info->size != (char *) NULL)
10352         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10353       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10354         geometry);
10355       (void) CloneString(&image_info->size,geometry);
10356     }
10357   /*
10358     Load the image.
10359   */
10360   XSetCursorState(display,windows,MagickTrue);
10361   XCheckRefreshWindows(display,windows);
10362   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10363   nexus=ReadImage(image_info,exception);
10364   CatchException(exception);
10365   XSetCursorState(display,windows,MagickFalse);
10366   if (nexus != (Image *) NULL)
10367     XClientMessage(display,windows->image.id,windows->im_protocols,
10368       windows->im_next_image,CurrentTime);
10369   else
10370     {
10371       char
10372         *text,
10373         **textlist;
10374
10375       /*
10376         Unknown image format.
10377       */
10378       text=FileToString(filename,~0UL,exception);
10379       if (text == (char *) NULL)
10380         return((Image *) NULL);
10381       textlist=StringToList(text);
10382       if (textlist != (char **) NULL)
10383         {
10384           char
10385             title[MaxTextExtent];
10386
10387           register int
10388             i;
10389
10390           (void) FormatLocaleString(title,MaxTextExtent,
10391             "Unknown format: %s",filename);
10392           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10393             (const char **) textlist);
10394           for (i=0; textlist[i] != (char *) NULL; i++)
10395             textlist[i]=DestroyString(textlist[i]);
10396           textlist=(char **) RelinquishMagickMemory(textlist);
10397         }
10398       text=DestroyString(text);
10399     }
10400   exception=DestroyExceptionInfo(exception);
10401   image_info=DestroyImageInfo(image_info);
10402   return(nexus);
10403 }
10404 \f
10405 /*
10406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10407 %                                                                             %
10408 %                                                                             %
10409 %                                                                             %
10410 +   X P a n I m a g e                                                         %
10411 %                                                                             %
10412 %                                                                             %
10413 %                                                                             %
10414 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10415 %
10416 %  XPanImage() pans the image until the mouse button is released.
10417 %
10418 %  The format of the XPanImage method is:
10419 %
10420 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10421 %        ExceptionInfo *exception)
10422 %
10423 %  A description of each parameter follows:
10424 %
10425 %    o display: Specifies a connection to an X server;  returned from
10426 %      XOpenDisplay.
10427 %
10428 %    o windows: Specifies a pointer to a XWindows structure.
10429 %
10430 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10431 %      the entire image is refreshed.
10432 %
10433 %    o exception: return any errors or warnings in this structure.
10434 %
10435 */
10436 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10437   ExceptionInfo *exception)
10438 {
10439   char
10440     text[MaxTextExtent];
10441
10442   Cursor
10443     cursor;
10444
10445   double
10446     x_factor,
10447     y_factor;
10448
10449   RectangleInfo
10450     pan_info;
10451
10452   size_t
10453     state;
10454
10455   /*
10456     Define cursor.
10457   */
10458   if ((windows->image.ximage->width > (int) windows->image.width) &&
10459       (windows->image.ximage->height > (int) windows->image.height))
10460     cursor=XCreateFontCursor(display,XC_fleur);
10461   else
10462     if (windows->image.ximage->width > (int) windows->image.width)
10463       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10464     else
10465       if (windows->image.ximage->height > (int) windows->image.height)
10466         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10467       else
10468         cursor=XCreateFontCursor(display,XC_arrow);
10469   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10470   /*
10471     Pan image as pointer moves until the mouse button is released.
10472   */
10473   x_factor=(double) windows->image.ximage->width/windows->pan.width;
10474   y_factor=(double) windows->image.ximage->height/windows->pan.height;
10475   pan_info.width=windows->pan.width*windows->image.width/
10476     windows->image.ximage->width;
10477   pan_info.height=windows->pan.height*windows->image.height/
10478     windows->image.ximage->height;
10479   pan_info.x=0;
10480   pan_info.y=0;
10481   state=UpdateConfigurationState;
10482   do
10483   {
10484     switch (event->type)
10485     {
10486       case ButtonPress:
10487       {
10488         /*
10489           User choose an initial pan location.
10490         */
10491         pan_info.x=(ssize_t) event->xbutton.x;
10492         pan_info.y=(ssize_t) event->xbutton.y;
10493         state|=UpdateConfigurationState;
10494         break;
10495       }
10496       case ButtonRelease:
10497       {
10498         /*
10499           User has finished panning the image.
10500         */
10501         pan_info.x=(ssize_t) event->xbutton.x;
10502         pan_info.y=(ssize_t) event->xbutton.y;
10503         state|=UpdateConfigurationState | ExitState;
10504         break;
10505       }
10506       case MotionNotify:
10507       {
10508         pan_info.x=(ssize_t) event->xmotion.x;
10509         pan_info.y=(ssize_t) event->xmotion.y;
10510         state|=UpdateConfigurationState;
10511       }
10512       default:
10513         break;
10514     }
10515     if ((state & UpdateConfigurationState) != 0)
10516       {
10517         /*
10518           Check boundary conditions.
10519         */
10520         if (pan_info.x < (ssize_t) (pan_info.width/2))
10521           pan_info.x=0;
10522         else
10523           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10524         if (pan_info.x < 0)
10525           pan_info.x=0;
10526         else
10527           if ((int) (pan_info.x+windows->image.width) >
10528               windows->image.ximage->width)
10529             pan_info.x=(ssize_t)
10530               (windows->image.ximage->width-windows->image.width);
10531         if (pan_info.y < (ssize_t) (pan_info.height/2))
10532           pan_info.y=0;
10533         else
10534           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10535         if (pan_info.y < 0)
10536           pan_info.y=0;
10537         else
10538           if ((int) (pan_info.y+windows->image.height) >
10539               windows->image.ximage->height)
10540             pan_info.y=(ssize_t)
10541               (windows->image.ximage->height-windows->image.height);
10542         if ((windows->image.x != (int) pan_info.x) ||
10543             (windows->image.y != (int) pan_info.y))
10544           {
10545             /*
10546               Display image pan offset.
10547             */
10548             windows->image.x=(int) pan_info.x;
10549             windows->image.y=(int) pan_info.y;
10550             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10551               windows->image.width,windows->image.height,windows->image.x,
10552               windows->image.y);
10553             XInfoWidget(display,windows,text);
10554             /*
10555               Refresh Image window.
10556             */
10557             XDrawPanRectangle(display,windows);
10558             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10559           }
10560         state&=(~UpdateConfigurationState);
10561       }
10562     /*
10563       Wait for next event.
10564     */
10565     if ((state & ExitState) == 0)
10566       XScreenEvent(display,windows,event,exception);
10567   } while ((state & ExitState) == 0);
10568   /*
10569     Restore cursor.
10570   */
10571   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10572   (void) XFreeCursor(display,cursor);
10573   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10574 }
10575 \f
10576 /*
10577 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10578 %                                                                             %
10579 %                                                                             %
10580 %                                                                             %
10581 +   X P a s t e I m a g e                                                     %
10582 %                                                                             %
10583 %                                                                             %
10584 %                                                                             %
10585 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10586 %
10587 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10588 %  window image at a location the user chooses with the pointer.
10589 %
10590 %  The format of the XPasteImage method is:
10591 %
10592 %      MagickBooleanType XPasteImage(Display *display,
10593 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10594 %        ExceptionInfo *exception)
10595 %
10596 %  A description of each parameter follows:
10597 %
10598 %    o display: Specifies a connection to an X server;  returned from
10599 %      XOpenDisplay.
10600 %
10601 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10602 %
10603 %    o windows: Specifies a pointer to a XWindows structure.
10604 %
10605 %    o image: the image; returned from ReadImage.
10606 %
10607 %    o exception: return any errors or warnings in this structure.
10608 %
10609 */
10610 static MagickBooleanType XPasteImage(Display *display,
10611   XResourceInfo *resource_info,XWindows *windows,Image *image,
10612   ExceptionInfo *exception)
10613 {
10614   static const char
10615     *PasteMenu[] =
10616     {
10617       "Operator",
10618       "Help",
10619       "Dismiss",
10620       (char *) NULL
10621     };
10622
10623   static const ModeType
10624     PasteCommands[] =
10625     {
10626       PasteOperatorsCommand,
10627       PasteHelpCommand,
10628       PasteDismissCommand
10629     };
10630
10631   static CompositeOperator
10632     compose = CopyCompositeOp;
10633
10634   char
10635     text[MaxTextExtent];
10636
10637   Cursor
10638     cursor;
10639
10640   Image
10641     *paste_image;
10642
10643   int
10644     entry,
10645     id,
10646     x,
10647     y;
10648
10649   double
10650     scale_factor;
10651
10652   RectangleInfo
10653     highlight_info,
10654     paste_info;
10655
10656   unsigned int
10657     height,
10658     width;
10659
10660   size_t
10661     state;
10662
10663   XEvent
10664     event;
10665
10666   /*
10667     Copy image.
10668   */
10669   if (resource_info->copy_image == (Image *) NULL)
10670     return(MagickFalse);
10671   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10672   /*
10673     Map Command widget.
10674   */
10675   (void) CloneString(&windows->command.name,"Paste");
10676   windows->command.data=1;
10677   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10678   (void) XMapRaised(display,windows->command.id);
10679   XClientMessage(display,windows->image.id,windows->im_protocols,
10680     windows->im_update_widget,CurrentTime);
10681   /*
10682     Track pointer until button 1 is pressed.
10683   */
10684   XSetCursorState(display,windows,MagickFalse);
10685   XQueryPosition(display,windows->image.id,&x,&y);
10686   (void) XSelectInput(display,windows->image.id,
10687     windows->image.attributes.event_mask | PointerMotionMask);
10688   paste_info.x=(ssize_t) windows->image.x+x;
10689   paste_info.y=(ssize_t) windows->image.y+y;
10690   paste_info.width=0;
10691   paste_info.height=0;
10692   cursor=XCreateFontCursor(display,XC_ul_angle);
10693   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10694   state=DefaultState;
10695   do
10696   {
10697     if (IfMagickTrue(windows->info.mapped) )
10698       {
10699         /*
10700           Display pointer position.
10701         */
10702         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10703           (long) paste_info.x,(long) paste_info.y);
10704         XInfoWidget(display,windows,text);
10705       }
10706     highlight_info=paste_info;
10707     highlight_info.x=paste_info.x-windows->image.x;
10708     highlight_info.y=paste_info.y-windows->image.y;
10709     XHighlightRectangle(display,windows->image.id,
10710       windows->image.highlight_context,&highlight_info);
10711     /*
10712       Wait for next event.
10713     */
10714     XScreenEvent(display,windows,&event,exception);
10715     XHighlightRectangle(display,windows->image.id,
10716       windows->image.highlight_context,&highlight_info);
10717     if (event.xany.window == windows->command.id)
10718       {
10719         /*
10720           Select a command from the Command widget.
10721         */
10722         id=XCommandWidget(display,windows,PasteMenu,&event);
10723         if (id < 0)
10724           continue;
10725         switch (PasteCommands[id])
10726         {
10727           case PasteOperatorsCommand:
10728           {
10729             char
10730               command[MaxTextExtent],
10731               **operators;
10732
10733             /*
10734               Select a command from the pop-up menu.
10735             */
10736             operators=GetCommandOptions(MagickComposeOptions);
10737             if (operators == (char **) NULL)
10738               break;
10739             entry=XMenuWidget(display,windows,PasteMenu[id],
10740               (const char **) operators,command);
10741             if (entry >= 0)
10742               compose=(CompositeOperator) ParseCommandOption(
10743                 MagickComposeOptions,MagickFalse,operators[entry]);
10744             operators=DestroyStringList(operators);
10745             break;
10746           }
10747           case PasteHelpCommand:
10748           {
10749             XTextViewWidget(display,resource_info,windows,MagickFalse,
10750               "Help Viewer - Image Composite",ImagePasteHelp);
10751             break;
10752           }
10753           case PasteDismissCommand:
10754           {
10755             /*
10756               Prematurely exit.
10757             */
10758             state|=EscapeState;
10759             state|=ExitState;
10760             break;
10761           }
10762           default:
10763             break;
10764         }
10765         continue;
10766       }
10767     switch (event.type)
10768     {
10769       case ButtonPress:
10770       {
10771         if (IfMagickTrue(image->debug) )
10772           (void) LogMagickEvent(X11Event,GetMagickModule(),
10773             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10774             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10775         if (event.xbutton.button != Button1)
10776           break;
10777         if (event.xbutton.window != windows->image.id)
10778           break;
10779         /*
10780           Paste rectangle is relative to image configuration.
10781         */
10782         width=(unsigned int) image->columns;
10783         height=(unsigned int) image->rows;
10784         x=0;
10785         y=0;
10786         if (windows->image.crop_geometry != (char *) NULL)
10787           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10788             &width,&height);
10789         scale_factor=(double) windows->image.ximage->width/width;
10790         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10791         scale_factor=(double) windows->image.ximage->height/height;
10792         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10793         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10794         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10795         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10796         break;
10797       }
10798       case ButtonRelease:
10799       {
10800         if (IfMagickTrue(image->debug) )
10801           (void) LogMagickEvent(X11Event,GetMagickModule(),
10802             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10803             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10804         if (event.xbutton.button != Button1)
10805           break;
10806         if (event.xbutton.window != windows->image.id)
10807           break;
10808         if ((paste_info.width != 0) && (paste_info.height != 0))
10809           {
10810             /*
10811               User has selected the location of the paste image.
10812             */
10813             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10814             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10815             state|=ExitState;
10816           }
10817         break;
10818       }
10819       case Expose:
10820         break;
10821       case KeyPress:
10822       {
10823         char
10824           command[MaxTextExtent];
10825
10826         KeySym
10827           key_symbol;
10828
10829         int
10830           length;
10831
10832         if (event.xkey.window != windows->image.id)
10833           break;
10834         /*
10835           Respond to a user key press.
10836         */
10837         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10838           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10839         *(command+length)='\0';
10840         if (IfMagickTrue(image->debug) )
10841           (void) LogMagickEvent(X11Event,GetMagickModule(),
10842             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10843         switch ((int) key_symbol)
10844         {
10845           case XK_Escape:
10846           case XK_F20:
10847           {
10848             /*
10849               Prematurely exit.
10850             */
10851             paste_image=DestroyImage(paste_image);
10852             state|=EscapeState;
10853             state|=ExitState;
10854             break;
10855           }
10856           case XK_F1:
10857           case XK_Help:
10858           {
10859             (void) XSetFunction(display,windows->image.highlight_context,
10860               GXcopy);
10861             XTextViewWidget(display,resource_info,windows,MagickFalse,
10862               "Help Viewer - Image Composite",ImagePasteHelp);
10863             (void) XSetFunction(display,windows->image.highlight_context,
10864               GXinvert);
10865             break;
10866           }
10867           default:
10868           {
10869             (void) XBell(display,0);
10870             break;
10871           }
10872         }
10873         break;
10874       }
10875       case MotionNotify:
10876       {
10877         /*
10878           Map and unmap Info widget as text cursor crosses its boundaries.
10879         */
10880         x=event.xmotion.x;
10881         y=event.xmotion.y;
10882         if (IfMagickTrue(windows->info.mapped) )
10883           {
10884             if ((x < (int) (windows->info.x+windows->info.width)) &&
10885                 (y < (int) (windows->info.y+windows->info.height)))
10886               (void) XWithdrawWindow(display,windows->info.id,
10887                 windows->info.screen);
10888           }
10889         else
10890           if ((x > (int) (windows->info.x+windows->info.width)) ||
10891               (y > (int) (windows->info.y+windows->info.height)))
10892             (void) XMapWindow(display,windows->info.id);
10893         paste_info.x=(ssize_t) windows->image.x+x;
10894         paste_info.y=(ssize_t) windows->image.y+y;
10895         break;
10896       }
10897       default:
10898       {
10899         if (IfMagickTrue(image->debug) )
10900           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10901             event.type);
10902         break;
10903       }
10904     }
10905   } while ((state & ExitState) == 0);
10906   (void) XSelectInput(display,windows->image.id,
10907     windows->image.attributes.event_mask);
10908   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10909   XSetCursorState(display,windows,MagickFalse);
10910   (void) XFreeCursor(display,cursor);
10911   if ((state & EscapeState) != 0)
10912     return(MagickTrue);
10913   /*
10914     Image pasting is relative to image configuration.
10915   */
10916   XSetCursorState(display,windows,MagickTrue);
10917   XCheckRefreshWindows(display,windows);
10918   width=(unsigned int) image->columns;
10919   height=(unsigned int) image->rows;
10920   x=0;
10921   y=0;
10922   if (windows->image.crop_geometry != (char *) NULL)
10923     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10924   scale_factor=(double) width/windows->image.ximage->width;
10925   paste_info.x+=x;
10926   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10927   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10928   scale_factor=(double) height/windows->image.ximage->height;
10929   paste_info.y+=y;
10930   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10931   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10932   /*
10933     Paste image with X Image window.
10934   */
10935   (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10936     paste_info.y,exception);
10937   paste_image=DestroyImage(paste_image);
10938   XSetCursorState(display,windows,MagickFalse);
10939   /*
10940     Update image colormap.
10941   */
10942   XConfigureImageColormap(display,resource_info,windows,image,exception);
10943   (void) XConfigureImage(display,resource_info,windows,image,exception);
10944   return(MagickTrue);
10945 }
10946 \f
10947 /*
10948 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10949 %                                                                             %
10950 %                                                                             %
10951 %                                                                             %
10952 +   X P r i n t I m a g e                                                     %
10953 %                                                                             %
10954 %                                                                             %
10955 %                                                                             %
10956 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10957 %
10958 %  XPrintImage() prints an image to a Postscript printer.
10959 %
10960 %  The format of the XPrintImage method is:
10961 %
10962 %      MagickBooleanType XPrintImage(Display *display,
10963 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10964 %        ExceptionInfo *exception)
10965 %
10966 %  A description of each parameter follows:
10967 %
10968 %    o display: Specifies a connection to an X server; returned from
10969 %      XOpenDisplay.
10970 %
10971 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10972 %
10973 %    o windows: Specifies a pointer to a XWindows structure.
10974 %
10975 %    o image: the image.
10976 %
10977 %    o exception: return any errors or warnings in this structure.
10978 %
10979 */
10980 static MagickBooleanType XPrintImage(Display *display,
10981   XResourceInfo *resource_info,XWindows *windows,Image *image,
10982   ExceptionInfo *exception)
10983 {
10984   char
10985     filename[MaxTextExtent],
10986     geometry[MaxTextExtent];
10987
10988   Image
10989     *print_image;
10990
10991   ImageInfo
10992     *image_info;
10993
10994   MagickStatusType
10995     status;
10996
10997   /*
10998     Request Postscript page geometry from user.
10999   */
11000   image_info=CloneImageInfo(resource_info->image_info);
11001   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
11002   if (image_info->page != (char *) NULL)
11003     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11004   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11005     "Select Postscript Page Geometry:",geometry);
11006   if (*geometry == '\0')
11007     return(MagickTrue);
11008   image_info->page=GetPageGeometry(geometry);
11009   /*
11010     Apply image transforms.
11011   */
11012   XSetCursorState(display,windows,MagickTrue);
11013   XCheckRefreshWindows(display,windows);
11014   print_image=CloneImage(image,0,0,MagickTrue,exception);
11015   if (print_image == (Image *) NULL)
11016     return(MagickFalse);
11017   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11018     windows->image.ximage->width,windows->image.ximage->height);
11019   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11020     exception);
11021   /*
11022     Print image.
11023   */
11024   (void) AcquireUniqueFilename(filename);
11025   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11026     filename);
11027   status=WriteImage(image_info,print_image,exception);
11028   (void) RelinquishUniqueFileResource(filename);
11029   print_image=DestroyImage(print_image);
11030   image_info=DestroyImageInfo(image_info);
11031   XSetCursorState(display,windows,MagickFalse);
11032   return(IsMagickTrue(status));
11033 }
11034 \f
11035 /*
11036 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11037 %                                                                             %
11038 %                                                                             %
11039 %                                                                             %
11040 +   X R O I I m a g e                                                         %
11041 %                                                                             %
11042 %                                                                             %
11043 %                                                                             %
11044 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11045 %
11046 %  XROIImage() applies an image processing technique to a region of interest.
11047 %
11048 %  The format of the XROIImage method is:
11049 %
11050 %      MagickBooleanType XROIImage(Display *display,
11051 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11052 %        ExceptionInfo *exception)
11053 %
11054 %  A description of each parameter follows:
11055 %
11056 %    o display: Specifies a connection to an X server; returned from
11057 %      XOpenDisplay.
11058 %
11059 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11060 %
11061 %    o windows: Specifies a pointer to a XWindows structure.
11062 %
11063 %    o image: the image; returned from ReadImage.
11064 %
11065 %    o exception: return any errors or warnings in this structure.
11066 %
11067 */
11068 static MagickBooleanType XROIImage(Display *display,
11069   XResourceInfo *resource_info,XWindows *windows,Image **image,
11070   ExceptionInfo *exception)
11071 {
11072 #define ApplyMenus  7
11073
11074   static const char
11075     *ROIMenu[] =
11076     {
11077       "Help",
11078       "Dismiss",
11079       (char *) NULL
11080     },
11081     *ApplyMenu[] =
11082     {
11083       "File",
11084       "Edit",
11085       "Transform",
11086       "Enhance",
11087       "Effects",
11088       "F/X",
11089       "Miscellany",
11090       "Help",
11091       "Dismiss",
11092       (char *) NULL
11093     },
11094     *FileMenu[] =
11095     {
11096       "Save...",
11097       "Print...",
11098       (char *) NULL
11099     },
11100     *EditMenu[] =
11101     {
11102       "Undo",
11103       "Redo",
11104       (char *) NULL
11105     },
11106     *TransformMenu[] =
11107     {
11108       "Flop",
11109       "Flip",
11110       "Rotate Right",
11111       "Rotate Left",
11112       (char *) NULL
11113     },
11114     *EnhanceMenu[] =
11115     {
11116       "Hue...",
11117       "Saturation...",
11118       "Brightness...",
11119       "Gamma...",
11120       "Spiff",
11121       "Dull",
11122       "Contrast Stretch...",
11123       "Sigmoidal Contrast...",
11124       "Normalize",
11125       "Equalize",
11126       "Negate",
11127       "Grayscale",
11128       "Map...",
11129       "Quantize...",
11130       (char *) NULL
11131     },
11132     *EffectsMenu[] =
11133     {
11134       "Despeckle",
11135       "Emboss",
11136       "Reduce Noise",
11137       "Add Noise",
11138       "Sharpen...",
11139       "Blur...",
11140       "Threshold...",
11141       "Edge Detect...",
11142       "Spread...",
11143       "Shade...",
11144       "Raise...",
11145       "Segment...",
11146       (char *) NULL
11147     },
11148     *FXMenu[] =
11149     {
11150       "Solarize...",
11151       "Sepia Tone...",
11152       "Swirl...",
11153       "Implode...",
11154       "Vignette...",
11155       "Wave...",
11156       "Oil Paint...",
11157       "Charcoal Draw...",
11158       (char *) NULL
11159     },
11160     *MiscellanyMenu[] =
11161     {
11162       "Image Info",
11163       "Zoom Image",
11164       "Show Preview...",
11165       "Show Histogram",
11166       "Show Matte",
11167       (char *) NULL
11168     };
11169
11170   static const char
11171     **Menus[ApplyMenus] =
11172     {
11173       FileMenu,
11174       EditMenu,
11175       TransformMenu,
11176       EnhanceMenu,
11177       EffectsMenu,
11178       FXMenu,
11179       MiscellanyMenu
11180     };
11181
11182   static const CommandType
11183     ApplyCommands[] =
11184     {
11185       NullCommand,
11186       NullCommand,
11187       NullCommand,
11188       NullCommand,
11189       NullCommand,
11190       NullCommand,
11191       NullCommand,
11192       HelpCommand,
11193       QuitCommand
11194     },
11195     FileCommands[] =
11196     {
11197       SaveCommand,
11198       PrintCommand
11199     },
11200     EditCommands[] =
11201     {
11202       UndoCommand,
11203       RedoCommand
11204     },
11205     TransformCommands[] =
11206     {
11207       FlopCommand,
11208       FlipCommand,
11209       RotateRightCommand,
11210       RotateLeftCommand
11211     },
11212     EnhanceCommands[] =
11213     {
11214       HueCommand,
11215       SaturationCommand,
11216       BrightnessCommand,
11217       GammaCommand,
11218       SpiffCommand,
11219       DullCommand,
11220       ContrastStretchCommand,
11221       SigmoidalContrastCommand,
11222       NormalizeCommand,
11223       EqualizeCommand,
11224       NegateCommand,
11225       GrayscaleCommand,
11226       MapCommand,
11227       QuantizeCommand
11228     },
11229     EffectsCommands[] =
11230     {
11231       DespeckleCommand,
11232       EmbossCommand,
11233       ReduceNoiseCommand,
11234       AddNoiseCommand,
11235       SharpenCommand,
11236       BlurCommand,
11237       EdgeDetectCommand,
11238       SpreadCommand,
11239       ShadeCommand,
11240       RaiseCommand,
11241       SegmentCommand
11242     },
11243     FXCommands[] =
11244     {
11245       SolarizeCommand,
11246       SepiaToneCommand,
11247       SwirlCommand,
11248       ImplodeCommand,
11249       VignetteCommand,
11250       WaveCommand,
11251       OilPaintCommand,
11252       CharcoalDrawCommand
11253     },
11254     MiscellanyCommands[] =
11255     {
11256       InfoCommand,
11257       ZoomCommand,
11258       ShowPreviewCommand,
11259       ShowHistogramCommand,
11260       ShowMatteCommand
11261     },
11262     ROICommands[] =
11263     {
11264       ROIHelpCommand,
11265       ROIDismissCommand
11266     };
11267
11268   static const CommandType
11269     *Commands[ApplyMenus] =
11270     {
11271       FileCommands,
11272       EditCommands,
11273       TransformCommands,
11274       EnhanceCommands,
11275       EffectsCommands,
11276       FXCommands,
11277       MiscellanyCommands
11278     };
11279
11280   char
11281     command[MaxTextExtent],
11282     text[MaxTextExtent];
11283
11284   CommandType
11285     command_type;
11286
11287   Cursor
11288     cursor;
11289
11290   Image
11291     *roi_image;
11292
11293   int
11294     entry,
11295     id,
11296     x,
11297     y;
11298
11299   double
11300     scale_factor;
11301
11302   MagickProgressMonitor
11303     progress_monitor;
11304
11305   RectangleInfo
11306     crop_info,
11307     highlight_info,
11308     roi_info;
11309
11310   unsigned int
11311     height,
11312     width;
11313
11314   size_t
11315     state;
11316
11317   XEvent
11318     event;
11319
11320   /*
11321     Map Command widget.
11322   */
11323   (void) CloneString(&windows->command.name,"ROI");
11324   windows->command.data=0;
11325   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11326   (void) XMapRaised(display,windows->command.id);
11327   XClientMessage(display,windows->image.id,windows->im_protocols,
11328     windows->im_update_widget,CurrentTime);
11329   /*
11330     Track pointer until button 1 is pressed.
11331   */
11332   XQueryPosition(display,windows->image.id,&x,&y);
11333   (void) XSelectInput(display,windows->image.id,
11334     windows->image.attributes.event_mask | PointerMotionMask);
11335   roi_info.x=(ssize_t) windows->image.x+x;
11336   roi_info.y=(ssize_t) windows->image.y+y;
11337   roi_info.width=0;
11338   roi_info.height=0;
11339   cursor=XCreateFontCursor(display,XC_fleur);
11340   state=DefaultState;
11341   do
11342   {
11343     if (IfMagickTrue(windows->info.mapped) )
11344       {
11345         /*
11346           Display pointer position.
11347         */
11348         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11349           (long) roi_info.x,(long) roi_info.y);
11350         XInfoWidget(display,windows,text);
11351       }
11352     /*
11353       Wait for next event.
11354     */
11355     XScreenEvent(display,windows,&event,exception);
11356     if (event.xany.window == windows->command.id)
11357       {
11358         /*
11359           Select a command from the Command widget.
11360         */
11361         id=XCommandWidget(display,windows,ROIMenu,&event);
11362         if (id < 0)
11363           continue;
11364         switch (ROICommands[id])
11365         {
11366           case ROIHelpCommand:
11367           {
11368             XTextViewWidget(display,resource_info,windows,MagickFalse,
11369               "Help Viewer - Region of Interest",ImageROIHelp);
11370             break;
11371           }
11372           case ROIDismissCommand:
11373           {
11374             /*
11375               Prematurely exit.
11376             */
11377             state|=EscapeState;
11378             state|=ExitState;
11379             break;
11380           }
11381           default:
11382             break;
11383         }
11384         continue;
11385       }
11386     switch (event.type)
11387     {
11388       case ButtonPress:
11389       {
11390         if (event.xbutton.button != Button1)
11391           break;
11392         if (event.xbutton.window != windows->image.id)
11393           break;
11394         /*
11395           Note first corner of region of interest rectangle-- exit loop.
11396         */
11397         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11398         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11399         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11400         state|=ExitState;
11401         break;
11402       }
11403       case ButtonRelease:
11404         break;
11405       case Expose:
11406         break;
11407       case KeyPress:
11408       {
11409         KeySym
11410           key_symbol;
11411
11412         if (event.xkey.window != windows->image.id)
11413           break;
11414         /*
11415           Respond to a user key press.
11416         */
11417         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11418           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11419         switch ((int) key_symbol)
11420         {
11421           case XK_Escape:
11422           case XK_F20:
11423           {
11424             /*
11425               Prematurely exit.
11426             */
11427             state|=EscapeState;
11428             state|=ExitState;
11429             break;
11430           }
11431           case XK_F1:
11432           case XK_Help:
11433           {
11434             XTextViewWidget(display,resource_info,windows,MagickFalse,
11435               "Help Viewer - Region of Interest",ImageROIHelp);
11436             break;
11437           }
11438           default:
11439           {
11440             (void) XBell(display,0);
11441             break;
11442           }
11443         }
11444         break;
11445       }
11446       case MotionNotify:
11447       {
11448         /*
11449           Map and unmap Info widget as text cursor crosses its boundaries.
11450         */
11451         x=event.xmotion.x;
11452         y=event.xmotion.y;
11453         if (IfMagickTrue(windows->info.mapped) )
11454           {
11455             if ((x < (int) (windows->info.x+windows->info.width)) &&
11456                 (y < (int) (windows->info.y+windows->info.height)))
11457               (void) XWithdrawWindow(display,windows->info.id,
11458                 windows->info.screen);
11459           }
11460         else
11461           if ((x > (int) (windows->info.x+windows->info.width)) ||
11462               (y > (int) (windows->info.y+windows->info.height)))
11463             (void) XMapWindow(display,windows->info.id);
11464         roi_info.x=(ssize_t) windows->image.x+x;
11465         roi_info.y=(ssize_t) windows->image.y+y;
11466         break;
11467       }
11468       default:
11469         break;
11470     }
11471   } while ((state & ExitState) == 0);
11472   (void) XSelectInput(display,windows->image.id,
11473     windows->image.attributes.event_mask);
11474   if ((state & EscapeState) != 0)
11475     {
11476       /*
11477         User want to exit without region of interest.
11478       */
11479       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11480       (void) XFreeCursor(display,cursor);
11481       return(MagickTrue);
11482     }
11483   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11484   do
11485   {
11486     /*
11487       Size rectangle as pointer moves until the mouse button is released.
11488     */
11489     x=(int) roi_info.x;
11490     y=(int) roi_info.y;
11491     roi_info.width=0;
11492     roi_info.height=0;
11493     state=DefaultState;
11494     do
11495     {
11496       highlight_info=roi_info;
11497       highlight_info.x=roi_info.x-windows->image.x;
11498       highlight_info.y=roi_info.y-windows->image.y;
11499       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11500         {
11501           /*
11502             Display info and draw region of interest rectangle.
11503           */
11504           if (IfMagickFalse(windows->info.mapped) )
11505             (void) XMapWindow(display,windows->info.id);
11506           (void) FormatLocaleString(text,MaxTextExtent,
11507             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11508             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11509           XInfoWidget(display,windows,text);
11510           XHighlightRectangle(display,windows->image.id,
11511             windows->image.highlight_context,&highlight_info);
11512         }
11513       else
11514         if (IfMagickTrue(windows->info.mapped) )
11515           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11516       /*
11517         Wait for next event.
11518       */
11519       XScreenEvent(display,windows,&event,exception);
11520       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11521         XHighlightRectangle(display,windows->image.id,
11522           windows->image.highlight_context,&highlight_info);
11523       switch (event.type)
11524       {
11525         case ButtonPress:
11526         {
11527           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11528           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11529           break;
11530         }
11531         case ButtonRelease:
11532         {
11533           /*
11534             User has committed to region of interest rectangle.
11535           */
11536           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11537           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11538           XSetCursorState(display,windows,MagickFalse);
11539           state|=ExitState;
11540           if (LocaleCompare(windows->command.name,"Apply") == 0)
11541             break;
11542           (void) CloneString(&windows->command.name,"Apply");
11543           windows->command.data=ApplyMenus;
11544           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11545           break;
11546         }
11547         case Expose:
11548           break;
11549         case MotionNotify:
11550         {
11551           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11552           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11553         }
11554         default:
11555           break;
11556       }
11557       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11558           ((state & ExitState) != 0))
11559         {
11560           /*
11561             Check boundary conditions.
11562           */
11563           if (roi_info.x < 0)
11564             roi_info.x=0;
11565           else
11566             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11567               roi_info.x=(ssize_t) windows->image.ximage->width;
11568           if ((int) roi_info.x < x)
11569             roi_info.width=(unsigned int) (x-roi_info.x);
11570           else
11571             {
11572               roi_info.width=(unsigned int) (roi_info.x-x);
11573               roi_info.x=(ssize_t) x;
11574             }
11575           if (roi_info.y < 0)
11576             roi_info.y=0;
11577           else
11578             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11579               roi_info.y=(ssize_t) windows->image.ximage->height;
11580           if ((int) roi_info.y < y)
11581             roi_info.height=(unsigned int) (y-roi_info.y);
11582           else
11583             {
11584               roi_info.height=(unsigned int) (roi_info.y-y);
11585               roi_info.y=(ssize_t) y;
11586             }
11587         }
11588     } while ((state & ExitState) == 0);
11589     /*
11590       Wait for user to grab a corner of the rectangle or press return.
11591     */
11592     state=DefaultState;
11593     command_type=NullCommand;
11594     crop_info.x=0;
11595     crop_info.y=0;
11596     (void) XMapWindow(display,windows->info.id);
11597     do
11598     {
11599       if (IfMagickTrue(windows->info.mapped) )
11600         {
11601           /*
11602             Display pointer position.
11603           */
11604           (void) FormatLocaleString(text,MaxTextExtent,
11605             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11606             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11607           XInfoWidget(display,windows,text);
11608         }
11609       highlight_info=roi_info;
11610       highlight_info.x=roi_info.x-windows->image.x;
11611       highlight_info.y=roi_info.y-windows->image.y;
11612       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11613         {
11614           state|=EscapeState;
11615           state|=ExitState;
11616           break;
11617         }
11618       if ((state & UpdateRegionState) != 0)
11619         {
11620           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11621           switch (command_type)
11622           {
11623             case UndoCommand:
11624             case RedoCommand:
11625             {
11626               (void) XMagickCommand(display,resource_info,windows,command_type,
11627                 image,exception);
11628               break;
11629             }
11630             default:
11631             {
11632               /*
11633                 Region of interest is relative to image configuration.
11634               */
11635               progress_monitor=SetImageProgressMonitor(*image,
11636                 (MagickProgressMonitor) NULL,(*image)->client_data);
11637               crop_info=roi_info;
11638               width=(unsigned int) (*image)->columns;
11639               height=(unsigned int) (*image)->rows;
11640               x=0;
11641               y=0;
11642               if (windows->image.crop_geometry != (char *) NULL)
11643                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11644                   &width,&height);
11645               scale_factor=(double) width/windows->image.ximage->width;
11646               crop_info.x+=x;
11647               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11648               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11649               scale_factor=(double)
11650                 height/windows->image.ximage->height;
11651               crop_info.y+=y;
11652               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11653               crop_info.height=(unsigned int)
11654                 (scale_factor*crop_info.height+0.5);
11655               roi_image=CropImage(*image,&crop_info,exception);
11656               (void) SetImageProgressMonitor(*image,progress_monitor,
11657                 (*image)->client_data);
11658               if (roi_image == (Image *) NULL)
11659                 continue;
11660               /*
11661                 Apply image processing technique to the region of interest.
11662               */
11663               windows->image.orphan=MagickTrue;
11664               (void) XMagickCommand(display,resource_info,windows,command_type,
11665                 &roi_image,exception);
11666               progress_monitor=SetImageProgressMonitor(*image,
11667                 (MagickProgressMonitor) NULL,(*image)->client_data);
11668               (void) XMagickCommand(display,resource_info,windows,
11669                 SaveToUndoBufferCommand,image,exception);
11670               windows->image.orphan=MagickFalse;
11671               (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11672                 MagickTrue,crop_info.x,crop_info.y,exception);
11673               roi_image=DestroyImage(roi_image);
11674               (void) SetImageProgressMonitor(*image,progress_monitor,
11675                 (*image)->client_data);
11676               break;
11677             }
11678           }
11679           if (command_type != InfoCommand)
11680             {
11681               XConfigureImageColormap(display,resource_info,windows,*image,
11682                 exception);
11683               (void) XConfigureImage(display,resource_info,windows,*image,
11684                 exception);
11685             }
11686           XCheckRefreshWindows(display,windows);
11687           XInfoWidget(display,windows,text);
11688           (void) XSetFunction(display,windows->image.highlight_context,
11689             GXinvert);
11690           state&=(~UpdateRegionState);
11691         }
11692       XHighlightRectangle(display,windows->image.id,
11693         windows->image.highlight_context,&highlight_info);
11694       XScreenEvent(display,windows,&event,exception);
11695       if (event.xany.window == windows->command.id)
11696         {
11697           /*
11698             Select a command from the Command widget.
11699           */
11700           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11701           command_type=NullCommand;
11702           id=XCommandWidget(display,windows,ApplyMenu,&event);
11703           if (id >= 0)
11704             {
11705               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11706               command_type=ApplyCommands[id];
11707               if (id < ApplyMenus)
11708                 {
11709                   /*
11710                     Select a command from a pop-up menu.
11711                   */
11712                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11713                     (const char **) Menus[id],command);
11714                   if (entry >= 0)
11715                     {
11716                       (void) CopyMagickString(command,Menus[id][entry],
11717                         MaxTextExtent);
11718                       command_type=Commands[id][entry];
11719                     }
11720                 }
11721             }
11722           (void) XSetFunction(display,windows->image.highlight_context,
11723             GXinvert);
11724           XHighlightRectangle(display,windows->image.id,
11725             windows->image.highlight_context,&highlight_info);
11726           if (command_type == HelpCommand)
11727             {
11728               (void) XSetFunction(display,windows->image.highlight_context,
11729                 GXcopy);
11730               XTextViewWidget(display,resource_info,windows,MagickFalse,
11731                 "Help Viewer - Region of Interest",ImageROIHelp);
11732               (void) XSetFunction(display,windows->image.highlight_context,
11733                 GXinvert);
11734               continue;
11735             }
11736           if (command_type == QuitCommand)
11737             {
11738               /*
11739                 exit.
11740               */
11741               state|=EscapeState;
11742               state|=ExitState;
11743               continue;
11744             }
11745           if (command_type != NullCommand)
11746             state|=UpdateRegionState;
11747           continue;
11748         }
11749       XHighlightRectangle(display,windows->image.id,
11750         windows->image.highlight_context,&highlight_info);
11751       switch (event.type)
11752       {
11753         case ButtonPress:
11754         {
11755           x=windows->image.x;
11756           y=windows->image.y;
11757           if (event.xbutton.button != Button1)
11758             break;
11759           if (event.xbutton.window != windows->image.id)
11760             break;
11761           x=windows->image.x+event.xbutton.x;
11762           y=windows->image.y+event.xbutton.y;
11763           if ((x < (int) (roi_info.x+RoiDelta)) &&
11764               (x > (int) (roi_info.x-RoiDelta)) &&
11765               (y < (int) (roi_info.y+RoiDelta)) &&
11766               (y > (int) (roi_info.y-RoiDelta)))
11767             {
11768               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11769               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11770               state|=UpdateConfigurationState;
11771               break;
11772             }
11773           if ((x < (int) (roi_info.x+RoiDelta)) &&
11774               (x > (int) (roi_info.x-RoiDelta)) &&
11775               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11776               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11777             {
11778               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11779               state|=UpdateConfigurationState;
11780               break;
11781             }
11782           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11783               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11784               (y < (int) (roi_info.y+RoiDelta)) &&
11785               (y > (int) (roi_info.y-RoiDelta)))
11786             {
11787               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11788               state|=UpdateConfigurationState;
11789               break;
11790             }
11791           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11792               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11793               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11794               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11795             {
11796               state|=UpdateConfigurationState;
11797               break;
11798             }
11799         }
11800         case ButtonRelease:
11801         {
11802           if (event.xbutton.window == windows->pan.id)
11803             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11804                 (highlight_info.y != crop_info.y-windows->image.y))
11805               XHighlightRectangle(display,windows->image.id,
11806                 windows->image.highlight_context,&highlight_info);
11807           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11808             event.xbutton.time);
11809           break;
11810         }
11811         case Expose:
11812         {
11813           if (event.xexpose.window == windows->image.id)
11814             if (event.xexpose.count == 0)
11815               {
11816                 event.xexpose.x=(int) highlight_info.x;
11817                 event.xexpose.y=(int) highlight_info.y;
11818                 event.xexpose.width=(int) highlight_info.width;
11819                 event.xexpose.height=(int) highlight_info.height;
11820                 XRefreshWindow(display,&windows->image,&event);
11821               }
11822           if (event.xexpose.window == windows->info.id)
11823             if (event.xexpose.count == 0)
11824               XInfoWidget(display,windows,text);
11825           break;
11826         }
11827         case KeyPress:
11828         {
11829           KeySym
11830             key_symbol;
11831
11832           if (event.xkey.window != windows->image.id)
11833             break;
11834           /*
11835             Respond to a user key press.
11836           */
11837           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11838             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11839           switch ((int) key_symbol)
11840           {
11841             case XK_Shift_L:
11842             case XK_Shift_R:
11843               break;
11844             case XK_Escape:
11845             case XK_F20:
11846               state|=EscapeState;
11847             case XK_Return:
11848             {
11849               state|=ExitState;
11850               break;
11851             }
11852             case XK_Home:
11853             case XK_KP_Home:
11854             {
11855               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11856               roi_info.y=(ssize_t) (windows->image.height/2L-
11857                 roi_info.height/2L);
11858               break;
11859             }
11860             case XK_Left:
11861             case XK_KP_Left:
11862             {
11863               roi_info.x--;
11864               break;
11865             }
11866             case XK_Up:
11867             case XK_KP_Up:
11868             case XK_Next:
11869             {
11870               roi_info.y--;
11871               break;
11872             }
11873             case XK_Right:
11874             case XK_KP_Right:
11875             {
11876               roi_info.x++;
11877               break;
11878             }
11879             case XK_Prior:
11880             case XK_Down:
11881             case XK_KP_Down:
11882             {
11883               roi_info.y++;
11884               break;
11885             }
11886             case XK_F1:
11887             case XK_Help:
11888             {
11889               (void) XSetFunction(display,windows->image.highlight_context,
11890                 GXcopy);
11891               XTextViewWidget(display,resource_info,windows,MagickFalse,
11892                 "Help Viewer - Region of Interest",ImageROIHelp);
11893               (void) XSetFunction(display,windows->image.highlight_context,
11894                 GXinvert);
11895               break;
11896             }
11897             default:
11898             {
11899               command_type=XImageWindowCommand(display,resource_info,windows,
11900                 event.xkey.state,key_symbol,image,exception);
11901               if (command_type != NullCommand)
11902                 state|=UpdateRegionState;
11903               break;
11904             }
11905           }
11906           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11907             event.xkey.time);
11908           break;
11909         }
11910         case KeyRelease:
11911           break;
11912         case MotionNotify:
11913         {
11914           if (event.xbutton.window != windows->image.id)
11915             break;
11916           /*
11917             Map and unmap Info widget as text cursor crosses its boundaries.
11918           */
11919           x=event.xmotion.x;
11920           y=event.xmotion.y;
11921           if (IfMagickTrue(windows->info.mapped) )
11922             {
11923               if ((x < (int) (windows->info.x+windows->info.width)) &&
11924                   (y < (int) (windows->info.y+windows->info.height)))
11925                 (void) XWithdrawWindow(display,windows->info.id,
11926                   windows->info.screen);
11927             }
11928           else
11929             if ((x > (int) (windows->info.x+windows->info.width)) ||
11930                 (y > (int) (windows->info.y+windows->info.height)))
11931               (void) XMapWindow(display,windows->info.id);
11932           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11933           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11934           break;
11935         }
11936         case SelectionRequest:
11937         {
11938           XSelectionEvent
11939             notify;
11940
11941           XSelectionRequestEvent
11942             *request;
11943
11944           /*
11945             Set primary selection.
11946           */
11947           (void) FormatLocaleString(text,MaxTextExtent,
11948             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11949             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11950           request=(&(event.xselectionrequest));
11951           (void) XChangeProperty(request->display,request->requestor,
11952             request->property,request->target,8,PropModeReplace,
11953             (unsigned char *) text,(int) strlen(text));
11954           notify.type=SelectionNotify;
11955           notify.display=request->display;
11956           notify.requestor=request->requestor;
11957           notify.selection=request->selection;
11958           notify.target=request->target;
11959           notify.time=request->time;
11960           if (request->property == None)
11961             notify.property=request->target;
11962           else
11963             notify.property=request->property;
11964           (void) XSendEvent(request->display,request->requestor,False,0,
11965             (XEvent *) &notify);
11966         }
11967         default:
11968           break;
11969       }
11970       if ((state & UpdateConfigurationState) != 0)
11971         {
11972           (void) XPutBackEvent(display,&event);
11973           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11974           break;
11975         }
11976     } while ((state & ExitState) == 0);
11977   } while ((state & ExitState) == 0);
11978   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11979   XSetCursorState(display,windows,MagickFalse);
11980   if ((state & EscapeState) != 0)
11981     return(MagickTrue);
11982   return(MagickTrue);
11983 }
11984 \f
11985 /*
11986 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11987 %                                                                             %
11988 %                                                                             %
11989 %                                                                             %
11990 +   X R o t a t e I m a g e                                                   %
11991 %                                                                             %
11992 %                                                                             %
11993 %                                                                             %
11994 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11995 %
11996 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11997 %  rotation angle is computed from the slope of a line drawn by the user.
11998 %
11999 %  The format of the XRotateImage method is:
12000 %
12001 %      MagickBooleanType XRotateImage(Display *display,
12002 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
12003 %        Image **image,ExceptionInfo *exception)
12004 %
12005 %  A description of each parameter follows:
12006 %
12007 %    o display: Specifies a connection to an X server; returned from
12008 %      XOpenDisplay.
12009 %
12010 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12011 %
12012 %    o windows: Specifies a pointer to a XWindows structure.
12013 %
12014 %    o degrees: Specifies the number of degrees to rotate the image.
12015 %
12016 %    o image: the image.
12017 %
12018 %    o exception: return any errors or warnings in this structure.
12019 %
12020 */
12021 static MagickBooleanType XRotateImage(Display *display,
12022   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12023   ExceptionInfo *exception)
12024 {
12025   static const char
12026     *RotateMenu[] =
12027     {
12028       "Pixel Color",
12029       "Direction",
12030       "Help",
12031       "Dismiss",
12032       (char *) NULL
12033     };
12034
12035   static ModeType
12036     direction = HorizontalRotateCommand;
12037
12038   static const ModeType
12039     DirectionCommands[] =
12040     {
12041       HorizontalRotateCommand,
12042       VerticalRotateCommand
12043     },
12044     RotateCommands[] =
12045     {
12046       RotateColorCommand,
12047       RotateDirectionCommand,
12048       RotateHelpCommand,
12049       RotateDismissCommand
12050     };
12051
12052   static unsigned int
12053     pen_id = 0;
12054
12055   char
12056     command[MaxTextExtent],
12057     text[MaxTextExtent];
12058
12059   Image
12060     *rotate_image;
12061
12062   int
12063     id,
12064     x,
12065     y;
12066
12067   double
12068     normalized_degrees;
12069
12070   register int
12071     i;
12072
12073   unsigned int
12074     height,
12075     rotations,
12076     width;
12077
12078   if (degrees == 0.0)
12079     {
12080       unsigned int
12081         distance;
12082
12083       size_t
12084         state;
12085
12086       XEvent
12087         event;
12088
12089       XSegment
12090         rotate_info;
12091
12092       /*
12093         Map Command widget.
12094       */
12095       (void) CloneString(&windows->command.name,"Rotate");
12096       windows->command.data=2;
12097       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12098       (void) XMapRaised(display,windows->command.id);
12099       XClientMessage(display,windows->image.id,windows->im_protocols,
12100         windows->im_update_widget,CurrentTime);
12101       /*
12102         Wait for first button press.
12103       */
12104       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12105       XQueryPosition(display,windows->image.id,&x,&y);
12106       rotate_info.x1=x;
12107       rotate_info.y1=y;
12108       rotate_info.x2=x;
12109       rotate_info.y2=y;
12110       state=DefaultState;
12111       do
12112       {
12113         XHighlightLine(display,windows->image.id,
12114           windows->image.highlight_context,&rotate_info);
12115         /*
12116           Wait for next event.
12117         */
12118         XScreenEvent(display,windows,&event,exception);
12119         XHighlightLine(display,windows->image.id,
12120           windows->image.highlight_context,&rotate_info);
12121         if (event.xany.window == windows->command.id)
12122           {
12123             /*
12124               Select a command from the Command widget.
12125             */
12126             id=XCommandWidget(display,windows,RotateMenu,&event);
12127             if (id < 0)
12128               continue;
12129             (void) XSetFunction(display,windows->image.highlight_context,
12130               GXcopy);
12131             switch (RotateCommands[id])
12132             {
12133               case RotateColorCommand:
12134               {
12135                 const char
12136                   *ColorMenu[MaxNumberPens];
12137
12138                 int
12139                   pen_number;
12140
12141                 XColor
12142                   color;
12143
12144                 /*
12145                   Initialize menu selections.
12146                 */
12147                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12148                   ColorMenu[i]=resource_info->pen_colors[i];
12149                 ColorMenu[MaxNumberPens-2]="Browser...";
12150                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12151                 /*
12152                   Select a pen color from the pop-up menu.
12153                 */
12154                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12155                   (const char **) ColorMenu,command);
12156                 if (pen_number < 0)
12157                   break;
12158                 if (pen_number == (MaxNumberPens-2))
12159                   {
12160                     static char
12161                       color_name[MaxTextExtent] = "gray";
12162
12163                     /*
12164                       Select a pen color from a dialog.
12165                     */
12166                     resource_info->pen_colors[pen_number]=color_name;
12167                     XColorBrowserWidget(display,windows,"Select",color_name);
12168                     if (*color_name == '\0')
12169                       break;
12170                   }
12171                 /*
12172                   Set pen color.
12173                 */
12174                 (void) XParseColor(display,windows->map_info->colormap,
12175                   resource_info->pen_colors[pen_number],&color);
12176                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12177                   (unsigned int) MaxColors,&color);
12178                 windows->pixel_info->pen_colors[pen_number]=color;
12179                 pen_id=(unsigned int) pen_number;
12180                 break;
12181               }
12182               case RotateDirectionCommand:
12183               {
12184                 static const char
12185                   *Directions[] =
12186                   {
12187                     "horizontal",
12188                     "vertical",
12189                     (char *) NULL,
12190                   };
12191
12192                 /*
12193                   Select a command from the pop-up menu.
12194                 */
12195                 id=XMenuWidget(display,windows,RotateMenu[id],
12196                   Directions,command);
12197                 if (id >= 0)
12198                   direction=DirectionCommands[id];
12199                 break;
12200               }
12201               case RotateHelpCommand:
12202               {
12203                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12204                   "Help Viewer - Image Rotation",ImageRotateHelp);
12205                 break;
12206               }
12207               case RotateDismissCommand:
12208               {
12209                 /*
12210                   Prematurely exit.
12211                 */
12212                 state|=EscapeState;
12213                 state|=ExitState;
12214                 break;
12215               }
12216               default:
12217                 break;
12218             }
12219             (void) XSetFunction(display,windows->image.highlight_context,
12220               GXinvert);
12221             continue;
12222           }
12223         switch (event.type)
12224         {
12225           case ButtonPress:
12226           {
12227             if (event.xbutton.button != Button1)
12228               break;
12229             if (event.xbutton.window != windows->image.id)
12230               break;
12231             /*
12232               exit loop.
12233             */
12234             (void) XSetFunction(display,windows->image.highlight_context,
12235               GXcopy);
12236             rotate_info.x1=event.xbutton.x;
12237             rotate_info.y1=event.xbutton.y;
12238             state|=ExitState;
12239             break;
12240           }
12241           case ButtonRelease:
12242             break;
12243           case Expose:
12244             break;
12245           case KeyPress:
12246           {
12247             char
12248               command[MaxTextExtent];
12249
12250             KeySym
12251               key_symbol;
12252
12253             if (event.xkey.window != windows->image.id)
12254               break;
12255             /*
12256               Respond to a user key press.
12257             */
12258             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12259               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12260             switch ((int) key_symbol)
12261             {
12262               case XK_Escape:
12263               case XK_F20:
12264               {
12265                 /*
12266                   Prematurely exit.
12267                 */
12268                 state|=EscapeState;
12269                 state|=ExitState;
12270                 break;
12271               }
12272               case XK_F1:
12273               case XK_Help:
12274               {
12275                 (void) XSetFunction(display,windows->image.highlight_context,
12276                   GXcopy);
12277                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12278                   "Help Viewer - Image Rotation",ImageRotateHelp);
12279                 (void) XSetFunction(display,windows->image.highlight_context,
12280                   GXinvert);
12281                 break;
12282               }
12283               default:
12284               {
12285                 (void) XBell(display,0);
12286                 break;
12287               }
12288             }
12289             break;
12290           }
12291           case MotionNotify:
12292           {
12293             rotate_info.x1=event.xmotion.x;
12294             rotate_info.y1=event.xmotion.y;
12295           }
12296         }
12297         rotate_info.x2=rotate_info.x1;
12298         rotate_info.y2=rotate_info.y1;
12299         if (direction == HorizontalRotateCommand)
12300           rotate_info.x2+=32;
12301         else
12302           rotate_info.y2-=32;
12303       } while ((state & ExitState) == 0);
12304       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12305       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12306       if ((state & EscapeState) != 0)
12307         return(MagickTrue);
12308       /*
12309         Draw line as pointer moves until the mouse button is released.
12310       */
12311       distance=0;
12312       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12313       state=DefaultState;
12314       do
12315       {
12316         if (distance > 9)
12317           {
12318             /*
12319               Display info and draw rotation line.
12320             */
12321             if (IfMagickFalse(windows->info.mapped) )
12322               (void) XMapWindow(display,windows->info.id);
12323             (void) FormatLocaleString(text,MaxTextExtent," %g",
12324               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12325             XInfoWidget(display,windows,text);
12326             XHighlightLine(display,windows->image.id,
12327               windows->image.highlight_context,&rotate_info);
12328           }
12329         else
12330           if (IfMagickTrue(windows->info.mapped) )
12331             (void) XWithdrawWindow(display,windows->info.id,
12332               windows->info.screen);
12333         /*
12334           Wait for next event.
12335         */
12336         XScreenEvent(display,windows,&event,exception);
12337         if (distance > 9)
12338           XHighlightLine(display,windows->image.id,
12339             windows->image.highlight_context,&rotate_info);
12340         switch (event.type)
12341         {
12342           case ButtonPress:
12343             break;
12344           case ButtonRelease:
12345           {
12346             /*
12347               User has committed to rotation line.
12348             */
12349             rotate_info.x2=event.xbutton.x;
12350             rotate_info.y2=event.xbutton.y;
12351             state|=ExitState;
12352             break;
12353           }
12354           case Expose:
12355             break;
12356           case MotionNotify:
12357           {
12358             rotate_info.x2=event.xmotion.x;
12359             rotate_info.y2=event.xmotion.y;
12360           }
12361           default:
12362             break;
12363         }
12364         /*
12365           Check boundary conditions.
12366         */
12367         if (rotate_info.x2 < 0)
12368           rotate_info.x2=0;
12369         else
12370           if (rotate_info.x2 > (int) windows->image.width)
12371             rotate_info.x2=(short) windows->image.width;
12372         if (rotate_info.y2 < 0)
12373           rotate_info.y2=0;
12374         else
12375           if (rotate_info.y2 > (int) windows->image.height)
12376             rotate_info.y2=(short) windows->image.height;
12377         /*
12378           Compute rotation angle from the slope of the line.
12379         */
12380         degrees=0.0;
12381         distance=(unsigned int)
12382           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12383           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12384         if (distance > 9)
12385           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12386             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12387       } while ((state & ExitState) == 0);
12388       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12389       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12390       if (distance <= 9)
12391         return(MagickTrue);
12392     }
12393   if (direction == VerticalRotateCommand)
12394     degrees-=90.0;
12395   if (degrees == 0.0)
12396     return(MagickTrue);
12397   /*
12398     Rotate image.
12399   */
12400   normalized_degrees=degrees;
12401   while (normalized_degrees < -45.0)
12402     normalized_degrees+=360.0;
12403   for (rotations=0; normalized_degrees > 45.0; rotations++)
12404     normalized_degrees-=90.0;
12405   if (normalized_degrees != 0.0)
12406     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12407       exception);
12408   XSetCursorState(display,windows,MagickTrue);
12409   XCheckRefreshWindows(display,windows);
12410   (*image)->background_color.red=(double) ScaleShortToQuantum(
12411     windows->pixel_info->pen_colors[pen_id].red);
12412   (*image)->background_color.green=(double) ScaleShortToQuantum(
12413     windows->pixel_info->pen_colors[pen_id].green);
12414   (*image)->background_color.blue=(double) ScaleShortToQuantum(
12415     windows->pixel_info->pen_colors[pen_id].blue);
12416   rotate_image=RotateImage(*image,degrees,exception);
12417   XSetCursorState(display,windows,MagickFalse);
12418   if (rotate_image == (Image *) NULL)
12419     return(MagickFalse);
12420   *image=DestroyImage(*image);
12421   *image=rotate_image;
12422   if (windows->image.crop_geometry != (char *) NULL)
12423     {
12424       /*
12425         Rotate crop geometry.
12426       */
12427       width=(unsigned int) (*image)->columns;
12428       height=(unsigned int) (*image)->rows;
12429       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12430       switch (rotations % 4)
12431       {
12432         default:
12433         case 0:
12434           break;
12435         case 1:
12436         {
12437           /*
12438             Rotate 90 degrees.
12439           */
12440           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12441             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12442             (int) height-y,x);
12443           break;
12444         }
12445         case 2:
12446         {
12447           /*
12448             Rotate 180 degrees.
12449           */
12450           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12451             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12452           break;
12453         }
12454         case 3:
12455         {
12456           /*
12457             Rotate 270 degrees.
12458           */
12459           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12460             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12461           break;
12462         }
12463       }
12464     }
12465   if (IfMagickTrue(windows->image.orphan) )
12466     return(MagickTrue);
12467   if (normalized_degrees != 0.0)
12468     {
12469       /*
12470         Update image colormap.
12471       */
12472       windows->image.window_changes.width=(int) (*image)->columns;
12473       windows->image.window_changes.height=(int) (*image)->rows;
12474       if (windows->image.crop_geometry != (char *) NULL)
12475         {
12476           /*
12477             Obtain dimensions of image from crop geometry.
12478           */
12479           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12480             &width,&height);
12481           windows->image.window_changes.width=(int) width;
12482           windows->image.window_changes.height=(int) height;
12483         }
12484       XConfigureImageColormap(display,resource_info,windows,*image,exception);
12485     }
12486   else
12487     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12488       {
12489         windows->image.window_changes.width=windows->image.ximage->height;
12490         windows->image.window_changes.height=windows->image.ximage->width;
12491       }
12492   /*
12493     Update image configuration.
12494   */
12495   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12496   return(MagickTrue);
12497 }
12498 \f
12499 /*
12500 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12501 %                                                                             %
12502 %                                                                             %
12503 %                                                                             %
12504 +   X S a v e I m a g e                                                       %
12505 %                                                                             %
12506 %                                                                             %
12507 %                                                                             %
12508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12509 %
12510 %  XSaveImage() saves an image to a file.
12511 %
12512 %  The format of the XSaveImage method is:
12513 %
12514 %      MagickBooleanType XSaveImage(Display *display,
12515 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12516 %        ExceptionInfo *exception)
12517 %
12518 %  A description of each parameter follows:
12519 %
12520 %    o display: Specifies a connection to an X server; returned from
12521 %      XOpenDisplay.
12522 %
12523 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12524 %
12525 %    o windows: Specifies a pointer to a XWindows structure.
12526 %
12527 %    o image: the image.
12528 %
12529 %    o exception: return any errors or warnings in this structure.
12530 %
12531 */
12532 static MagickBooleanType XSaveImage(Display *display,
12533   XResourceInfo *resource_info,XWindows *windows,Image *image,
12534   ExceptionInfo *exception)
12535 {
12536   char
12537     filename[MaxTextExtent],
12538     geometry[MaxTextExtent];
12539
12540   Image
12541     *save_image;
12542
12543   ImageInfo
12544     *image_info;
12545
12546   MagickStatusType
12547     status;
12548
12549   /*
12550     Request file name from user.
12551   */
12552   if (resource_info->write_filename != (char *) NULL)
12553     (void) CopyMagickString(filename,resource_info->write_filename,
12554       MaxTextExtent);
12555   else
12556     {
12557       char
12558         path[MaxTextExtent];
12559
12560       int
12561         status;
12562
12563       GetPathComponent(image->filename,HeadPath,path);
12564       GetPathComponent(image->filename,TailPath,filename);
12565       if (*path != '\0')
12566         {
12567           status=chdir(path);
12568           if (status == -1)
12569             (void) ThrowMagickException(exception,GetMagickModule(),
12570               FileOpenError,"UnableToOpenFile","%s",path);
12571         }
12572     }
12573   XFileBrowserWidget(display,windows,"Save",filename);
12574   if (*filename == '\0')
12575     return(MagickTrue);
12576   if (IfMagickTrue(IsPathAccessible(filename)) )
12577     {
12578       int
12579         status;
12580
12581       /*
12582         File exists-- seek user's permission before overwriting.
12583       */
12584       status=XConfirmWidget(display,windows,"Overwrite",filename);
12585       if (status <= 0)
12586         return(MagickTrue);
12587     }
12588   image_info=CloneImageInfo(resource_info->image_info);
12589   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12590   (void) SetImageInfo(image_info,1,exception);
12591   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12592       (LocaleCompare(image_info->magick,"JPG") == 0))
12593     {
12594       char
12595         quality[MaxTextExtent];
12596
12597       int
12598         status;
12599
12600       /*
12601         Request JPEG quality from user.
12602       */
12603       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12604         image->quality);
12605       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12606         quality);
12607       if (*quality == '\0')
12608         return(MagickTrue);
12609       image->quality=StringToUnsignedLong(quality);
12610       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12611     }
12612   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12613       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12614       (LocaleCompare(image_info->magick,"PS") == 0) ||
12615       (LocaleCompare(image_info->magick,"PS2") == 0))
12616     {
12617       char
12618         geometry[MaxTextExtent];
12619
12620       /*
12621         Request page geometry from user.
12622       */
12623       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12624       if (LocaleCompare(image_info->magick,"PDF") == 0)
12625         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12626       if (image_info->page != (char *) NULL)
12627         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12628       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12629         "Select page geometry:",geometry);
12630       if (*geometry != '\0')
12631         image_info->page=GetPageGeometry(geometry);
12632     }
12633   /*
12634     Apply image transforms.
12635   */
12636   XSetCursorState(display,windows,MagickTrue);
12637   XCheckRefreshWindows(display,windows);
12638   save_image=CloneImage(image,0,0,MagickTrue,exception);
12639   if (save_image == (Image *) NULL)
12640     return(MagickFalse);
12641   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12642     windows->image.ximage->width,windows->image.ximage->height);
12643   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12644     exception);
12645   /*
12646     Write image.
12647   */
12648   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12649   status=WriteImage(image_info,save_image,exception);
12650   if (IfMagickTrue(status) )
12651     image->taint=MagickFalse;
12652   save_image=DestroyImage(save_image);
12653   image_info=DestroyImageInfo(image_info);
12654   XSetCursorState(display,windows,MagickFalse);
12655   return(IsMagickTrue(status));
12656 }
12657 \f
12658 /*
12659 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12660 %                                                                             %
12661 %                                                                             %
12662 %                                                                             %
12663 +   X S c r e e n E v e n t                                                   %
12664 %                                                                             %
12665 %                                                                             %
12666 %                                                                             %
12667 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12668 %
12669 %  XScreenEvent() handles global events associated with the Pan and Magnify
12670 %  windows.
12671 %
12672 %  The format of the XScreenEvent function is:
12673 %
12674 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12675 %        ExceptionInfo *exception)
12676 %
12677 %  A description of each parameter follows:
12678 %
12679 %    o display: Specifies a pointer to the Display structure;  returned from
12680 %      XOpenDisplay.
12681 %
12682 %    o windows: Specifies a pointer to a XWindows structure.
12683 %
12684 %    o event: Specifies a pointer to a X11 XEvent structure.
12685 %
12686 %    o exception: return any errors or warnings in this structure.
12687 %
12688 */
12689
12690 #if defined(__cplusplus) || defined(c_plusplus)
12691 extern "C" {
12692 #endif
12693
12694 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12695 {
12696   register XWindows
12697     *windows;
12698
12699   windows=(XWindows *) data;
12700   if ((event->type == ClientMessage) &&
12701       (event->xclient.window == windows->image.id))
12702     return(MagickFalse);
12703   return(MagickTrue);
12704 }
12705
12706 #if defined(__cplusplus) || defined(c_plusplus)
12707 }
12708 #endif
12709
12710 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12711   ExceptionInfo *exception)
12712 {
12713   register int
12714     x,
12715     y;
12716
12717   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12718   if (event->xany.window == windows->command.id)
12719     return;
12720   switch (event->type)
12721   {
12722     case ButtonPress:
12723     case ButtonRelease:
12724     {
12725       if ((event->xbutton.button == Button3) &&
12726           (event->xbutton.state & Mod1Mask))
12727         {
12728           /*
12729             Convert Alt-Button3 to Button2.
12730           */
12731           event->xbutton.button=Button2;
12732           event->xbutton.state&=(~Mod1Mask);
12733         }
12734       if (event->xbutton.window == windows->backdrop.id)
12735         {
12736           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12737             event->xbutton.time);
12738           break;
12739         }
12740       if (event->xbutton.window == windows->pan.id)
12741         {
12742           XPanImage(display,windows,event,exception);
12743           break;
12744         }
12745       if (event->xbutton.window == windows->image.id)
12746         if (event->xbutton.button == Button2)
12747           {
12748             /*
12749               Update magnified image.
12750             */
12751             x=event->xbutton.x;
12752             y=event->xbutton.y;
12753             if (x < 0)
12754               x=0;
12755             else
12756               if (x >= (int) windows->image.width)
12757                 x=(int) (windows->image.width-1);
12758             windows->magnify.x=(int) windows->image.x+x;
12759             if (y < 0)
12760               y=0;
12761             else
12762              if (y >= (int) windows->image.height)
12763                y=(int) (windows->image.height-1);
12764             windows->magnify.y=windows->image.y+y;
12765             if (IfMagickFalse(windows->magnify.mapped) )
12766               (void) XMapRaised(display,windows->magnify.id);
12767             XMakeMagnifyImage(display,windows,exception);
12768             if (event->type == ButtonRelease)
12769               (void) XWithdrawWindow(display,windows->info.id,
12770                 windows->info.screen);
12771             break;
12772           }
12773       break;
12774     }
12775     case ClientMessage:
12776     {
12777       /*
12778         If client window delete message, exit.
12779       */
12780       if (event->xclient.message_type != windows->wm_protocols)
12781         break;
12782       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12783         break;
12784       if (event->xclient.window == windows->magnify.id)
12785         {
12786           (void) XWithdrawWindow(display,windows->magnify.id,
12787             windows->magnify.screen);
12788           break;
12789         }
12790       break;
12791     }
12792     case ConfigureNotify:
12793     {
12794       if (event->xconfigure.window == windows->magnify.id)
12795         {
12796           unsigned int
12797             magnify;
12798
12799           /*
12800             Magnify window has a new configuration.
12801           */
12802           windows->magnify.width=(unsigned int) event->xconfigure.width;
12803           windows->magnify.height=(unsigned int) event->xconfigure.height;
12804           if (IfMagickFalse(windows->magnify.mapped) )
12805             break;
12806           magnify=1;
12807           while ((int) magnify <= event->xconfigure.width)
12808             magnify<<=1;
12809           while ((int) magnify <= event->xconfigure.height)
12810             magnify<<=1;
12811           magnify>>=1;
12812           if (((int) magnify != event->xconfigure.width) ||
12813               ((int) magnify != event->xconfigure.height))
12814             {
12815               XWindowChanges
12816                 window_changes;
12817
12818               window_changes.width=(int) magnify;
12819               window_changes.height=(int) magnify;
12820               (void) XReconfigureWMWindow(display,windows->magnify.id,
12821                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12822                 &window_changes);
12823               break;
12824             }
12825           XMakeMagnifyImage(display,windows,exception);
12826           break;
12827         }
12828       break;
12829     }
12830     case Expose:
12831     {
12832       if (event->xexpose.window == windows->image.id)
12833         {
12834           XRefreshWindow(display,&windows->image,event);
12835           break;
12836         }
12837       if (event->xexpose.window == windows->pan.id)
12838         if (event->xexpose.count == 0)
12839           {
12840             XDrawPanRectangle(display,windows);
12841             break;
12842           }
12843       if (event->xexpose.window == windows->magnify.id)
12844         if (event->xexpose.count == 0)
12845           {
12846             XMakeMagnifyImage(display,windows,exception);
12847             break;
12848           }
12849       break;
12850     }
12851     case KeyPress:
12852     {
12853       char
12854         command[MaxTextExtent];
12855
12856       KeySym
12857         key_symbol;
12858
12859       if (event->xkey.window != windows->magnify.id)
12860         break;
12861       /*
12862         Respond to a user key press.
12863       */
12864       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12865         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12866       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12867         exception);
12868       break;
12869     }
12870     case MapNotify:
12871     {
12872       if (event->xmap.window == windows->magnify.id)
12873         {
12874           windows->magnify.mapped=MagickTrue;
12875           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12876           break;
12877         }
12878       if (event->xmap.window == windows->info.id)
12879         {
12880           windows->info.mapped=MagickTrue;
12881           break;
12882         }
12883       break;
12884     }
12885     case MotionNotify:
12886     {
12887       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12888       if (event->xmotion.window == windows->image.id)
12889         if (IfMagickTrue(windows->magnify.mapped) )
12890           {
12891             /*
12892               Update magnified image.
12893             */
12894             x=event->xmotion.x;
12895             y=event->xmotion.y;
12896             if (x < 0)
12897               x=0;
12898             else
12899               if (x >= (int) windows->image.width)
12900                 x=(int) (windows->image.width-1);
12901             windows->magnify.x=(int) windows->image.x+x;
12902             if (y < 0)
12903               y=0;
12904             else
12905              if (y >= (int) windows->image.height)
12906                y=(int) (windows->image.height-1);
12907             windows->magnify.y=windows->image.y+y;
12908             XMakeMagnifyImage(display,windows,exception);
12909           }
12910       break;
12911     }
12912     case UnmapNotify:
12913     {
12914       if (event->xunmap.window == windows->magnify.id)
12915         {
12916           windows->magnify.mapped=MagickFalse;
12917           break;
12918         }
12919       if (event->xunmap.window == windows->info.id)
12920         {
12921           windows->info.mapped=MagickFalse;
12922           break;
12923         }
12924       break;
12925     }
12926     default:
12927       break;
12928   }
12929 }
12930 \f
12931 /*
12932 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12933 %                                                                             %
12934 %                                                                             %
12935 %                                                                             %
12936 +   X S e t C r o p G e o m e t r y                                           %
12937 %                                                                             %
12938 %                                                                             %
12939 %                                                                             %
12940 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12941 %
12942 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12943 %  and translates it to a cropping geometry relative to the image.
12944 %
12945 %  The format of the XSetCropGeometry method is:
12946 %
12947 %      void XSetCropGeometry(Display *display,XWindows *windows,
12948 %        RectangleInfo *crop_info,Image *image)
12949 %
12950 %  A description of each parameter follows:
12951 %
12952 %    o display: Specifies a connection to an X server; returned from
12953 %      XOpenDisplay.
12954 %
12955 %    o windows: Specifies a pointer to a XWindows structure.
12956 %
12957 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12958 %      Image window to crop.
12959 %
12960 %    o image: the image.
12961 %
12962 */
12963 static void XSetCropGeometry(Display *display,XWindows *windows,
12964   RectangleInfo *crop_info,Image *image)
12965 {
12966   char
12967     text[MaxTextExtent];
12968
12969   int
12970     x,
12971     y;
12972
12973   double
12974     scale_factor;
12975
12976   unsigned int
12977     height,
12978     width;
12979
12980   if (IfMagickTrue(windows->info.mapped) )
12981     {
12982       /*
12983         Display info on cropping rectangle.
12984       */
12985       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12986         (double) crop_info->width,(double) crop_info->height,(double)
12987         crop_info->x,(double) crop_info->y);
12988       XInfoWidget(display,windows,text);
12989     }
12990   /*
12991     Cropping geometry is relative to any previous crop geometry.
12992   */
12993   x=0;
12994   y=0;
12995   width=(unsigned int) image->columns;
12996   height=(unsigned int) image->rows;
12997   if (windows->image.crop_geometry != (char *) NULL)
12998     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12999   else
13000     windows->image.crop_geometry=AcquireString((char *) NULL);
13001   /*
13002     Define the crop geometry string from the cropping rectangle.
13003   */
13004   scale_factor=(double) width/windows->image.ximage->width;
13005   if (crop_info->x > 0)
13006     x+=(int) (scale_factor*crop_info->x+0.5);
13007   width=(unsigned int) (scale_factor*crop_info->width+0.5);
13008   if (width == 0)
13009     width=1;
13010   scale_factor=(double) height/windows->image.ximage->height;
13011   if (crop_info->y > 0)
13012     y+=(int) (scale_factor*crop_info->y+0.5);
13013   height=(unsigned int) (scale_factor*crop_info->height+0.5);
13014   if (height == 0)
13015     height=1;
13016   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13017     "%ux%u%+d%+d",width,height,x,y);
13018 }
13019 \f
13020 /*
13021 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13022 %                                                                             %
13023 %                                                                             %
13024 %                                                                             %
13025 +   X T i l e I m a g e                                                       %
13026 %                                                                             %
13027 %                                                                             %
13028 %                                                                             %
13029 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13030 %
13031 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13032 %  The load or delete command is chosen from a menu.
13033 %
13034 %  The format of the XTileImage method is:
13035 %
13036 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13037 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13038 %
13039 %  A description of each parameter follows:
13040 %
13041 %    o tile_image:  XTileImage reads or deletes the tile image
13042 %      and returns it.  A null image is returned if an error occurs.
13043 %
13044 %    o display: Specifies a connection to an X server;  returned from
13045 %      XOpenDisplay.
13046 %
13047 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13048 %
13049 %    o windows: Specifies a pointer to a XWindows structure.
13050 %
13051 %    o image: the image; returned from ReadImage.
13052 %
13053 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13054 %      the entire image is refreshed.
13055 %
13056 %    o exception: return any errors or warnings in this structure.
13057 %
13058 */
13059 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13060   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13061 {
13062   static const char
13063     *VerbMenu[] =
13064     {
13065       "Load",
13066       "Next",
13067       "Former",
13068       "Delete",
13069       "Update",
13070       (char *) NULL,
13071     };
13072
13073   static const ModeType
13074     TileCommands[] =
13075     {
13076       TileLoadCommand,
13077       TileNextCommand,
13078       TileFormerCommand,
13079       TileDeleteCommand,
13080       TileUpdateCommand
13081     };
13082
13083   char
13084     command[MaxTextExtent],
13085     filename[MaxTextExtent];
13086
13087   Image
13088     *tile_image;
13089
13090   int
13091     id,
13092     status,
13093     tile,
13094     x,
13095     y;
13096
13097   double
13098     scale_factor;
13099
13100   register char
13101     *p,
13102     *q;
13103
13104   register int
13105     i;
13106
13107   unsigned int
13108     height,
13109     width;
13110
13111   /*
13112     Tile image is relative to montage image configuration.
13113   */
13114   x=0;
13115   y=0;
13116   width=(unsigned int) image->columns;
13117   height=(unsigned int) image->rows;
13118   if (windows->image.crop_geometry != (char *) NULL)
13119     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13120   scale_factor=(double) width/windows->image.ximage->width;
13121   event->xbutton.x+=windows->image.x;
13122   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13123   scale_factor=(double) height/windows->image.ximage->height;
13124   event->xbutton.y+=windows->image.y;
13125   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13126   /*
13127     Determine size and location of each tile in the visual image directory.
13128   */
13129   width=(unsigned int) image->columns;
13130   height=(unsigned int) image->rows;
13131   x=0;
13132   y=0;
13133   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13134   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13135     (event->xbutton.x-x)/width;
13136   if (tile < 0)
13137     {
13138       /*
13139         Button press is outside any tile.
13140       */
13141       (void) XBell(display,0);
13142       return((Image *) NULL);
13143     }
13144   /*
13145     Determine file name from the tile directory.
13146   */
13147   p=image->directory;
13148   for (i=tile; (i != 0) && (*p != '\0'); )
13149   {
13150     if (*p == '\n')
13151       i--;
13152     p++;
13153   }
13154   if (*p == '\0')
13155     {
13156       /*
13157         Button press is outside any tile.
13158       */
13159       (void) XBell(display,0);
13160       return((Image *) NULL);
13161     }
13162   /*
13163     Select a command from the pop-up menu.
13164   */
13165   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13166   if (id < 0)
13167     return((Image *) NULL);
13168   q=p;
13169   while ((*q != '\n') && (*q != '\0'))
13170     q++;
13171   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13172   /*
13173     Perform command for the selected tile.
13174   */
13175   XSetCursorState(display,windows,MagickTrue);
13176   XCheckRefreshWindows(display,windows);
13177   tile_image=NewImageList();
13178   switch (TileCommands[id])
13179   {
13180     case TileLoadCommand:
13181     {
13182       /*
13183         Load tile image.
13184       */
13185       XCheckRefreshWindows(display,windows);
13186       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13187         MaxTextExtent);
13188       (void) CopyMagickString(resource_info->image_info->filename,filename,
13189         MaxTextExtent);
13190       tile_image=ReadImage(resource_info->image_info,exception);
13191       CatchException(exception);
13192       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13193       break;
13194     }
13195     case TileNextCommand:
13196     {
13197       /*
13198         Display next image.
13199       */
13200       XClientMessage(display,windows->image.id,windows->im_protocols,
13201         windows->im_next_image,CurrentTime);
13202       break;
13203     }
13204     case TileFormerCommand:
13205     {
13206       /*
13207         Display former image.
13208       */
13209       XClientMessage(display,windows->image.id,windows->im_protocols,
13210         windows->im_former_image,CurrentTime);
13211       break;
13212     }
13213     case TileDeleteCommand:
13214     {
13215       /*
13216         Delete tile image.
13217       */
13218       if (IfMagickFalse(IsPathAccessible(filename)) )
13219         {
13220           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13221           break;
13222         }
13223       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13224       if (status <= 0)
13225         break;
13226       status=ShredFile(filename);
13227       if (IfMagickTrue(status) )
13228         {
13229           XNoticeWidget(display,windows,"Unable to delete image file:",
13230             filename);
13231           break;
13232         }
13233     }
13234     case TileUpdateCommand:
13235     {
13236       int
13237         x_offset,
13238         y_offset;
13239
13240       PixelInfo
13241         pixel;
13242
13243       register int
13244         j;
13245
13246       register Quantum
13247         *s;
13248
13249       /*
13250         Ensure all the images exist.
13251       */
13252       tile=0;
13253       GetPixelInfo(image,&pixel);
13254       for (p=image->directory; *p != '\0'; p++)
13255       {
13256         CacheView
13257           *image_view;
13258
13259         q=p;
13260         while ((*q != '\n') && (*q != '\0'))
13261           q++;
13262         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13263         p=q;
13264         if (IfMagickTrue(IsPathAccessible(filename)) )
13265           {
13266             tile++;
13267             continue;
13268           }
13269         /*
13270           Overwrite tile with background color.
13271         */
13272         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13273         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13274         image_view=AcquireAuthenticCacheView(image,exception);
13275         (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13276         for (i=0; i < (int) height; i++)
13277         {
13278           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13279             y_offset+i,width,1,exception);
13280           if (s == (Quantum *) NULL)
13281             break;
13282           for (j=0; j < (int) width; j++)
13283           {
13284             SetPixelInfoPixel(image,&pixel,s);
13285             s+=GetPixelChannels(image);
13286           }
13287           if (IfMagickFalse(SyncCacheViewAuthenticPixels(image_view,exception)) )
13288             break;
13289         }
13290         image_view=DestroyCacheView(image_view);
13291         tile++;
13292       }
13293       windows->image.window_changes.width=(int) image->columns;
13294       windows->image.window_changes.height=(int) image->rows;
13295       XConfigureImageColormap(display,resource_info,windows,image,exception);
13296       (void) XConfigureImage(display,resource_info,windows,image,exception);
13297       break;
13298     }
13299     default:
13300       break;
13301   }
13302   XSetCursorState(display,windows,MagickFalse);
13303   return(tile_image);
13304 }
13305 \f
13306 /*
13307 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13308 %                                                                             %
13309 %                                                                             %
13310 %                                                                             %
13311 +   X T r a n s l a t e I m a g e                                             %
13312 %                                                                             %
13313 %                                                                             %
13314 %                                                                             %
13315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13316 %
13317 %  XTranslateImage() translates the image within an Image window by one pixel
13318 %  as specified by the key symbol.  If the image has a montage string the
13319 %  translation is respect to the width and height contained within the string.
13320 %
13321 %  The format of the XTranslateImage method is:
13322 %
13323 %      void XTranslateImage(Display *display,XWindows *windows,
13324 %        Image *image,const KeySym key_symbol)
13325 %
13326 %  A description of each parameter follows:
13327 %
13328 %    o display: Specifies a connection to an X server; returned from
13329 %      XOpenDisplay.
13330 %
13331 %    o windows: Specifies a pointer to a XWindows structure.
13332 %
13333 %    o image: the image.
13334 %
13335 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13336 %      to trim.
13337 %
13338 */
13339 static void XTranslateImage(Display *display,XWindows *windows,
13340   Image *image,const KeySym key_symbol)
13341 {
13342   char
13343     text[MaxTextExtent];
13344
13345   int
13346     x,
13347     y;
13348
13349   unsigned int
13350     x_offset,
13351     y_offset;
13352
13353   /*
13354     User specified a pan position offset.
13355   */
13356   x_offset=windows->image.width;
13357   y_offset=windows->image.height;
13358   if (image->montage != (char *) NULL)
13359     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13360   switch ((int) key_symbol)
13361   {
13362     case XK_Home:
13363     case XK_KP_Home:
13364     {
13365       windows->image.x=(int) windows->image.width/2;
13366       windows->image.y=(int) windows->image.height/2;
13367       break;
13368     }
13369     case XK_Left:
13370     case XK_KP_Left:
13371     {
13372       windows->image.x-=x_offset;
13373       break;
13374     }
13375     case XK_Next:
13376     case XK_Up:
13377     case XK_KP_Up:
13378     {
13379       windows->image.y-=y_offset;
13380       break;
13381     }
13382     case XK_Right:
13383     case XK_KP_Right:
13384     {
13385       windows->image.x+=x_offset;
13386       break;
13387     }
13388     case XK_Prior:
13389     case XK_Down:
13390     case XK_KP_Down:
13391     {
13392       windows->image.y+=y_offset;
13393       break;
13394     }
13395     default:
13396       return;
13397   }
13398   /*
13399     Check boundary conditions.
13400   */
13401   if (windows->image.x < 0)
13402     windows->image.x=0;
13403   else
13404     if ((int) (windows->image.x+windows->image.width) >
13405         windows->image.ximage->width)
13406       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13407   if (windows->image.y < 0)
13408     windows->image.y=0;
13409   else
13410     if ((int) (windows->image.y+windows->image.height) >
13411         windows->image.ximage->height)
13412       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13413   /*
13414     Refresh Image window.
13415   */
13416   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13417     windows->image.width,windows->image.height,windows->image.x,
13418     windows->image.y);
13419   XInfoWidget(display,windows,text);
13420   XCheckRefreshWindows(display,windows);
13421   XDrawPanRectangle(display,windows);
13422   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13423   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13424 }
13425 \f
13426 /*
13427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13428 %                                                                             %
13429 %                                                                             %
13430 %                                                                             %
13431 +   X T r i m I m a g e                                                       %
13432 %                                                                             %
13433 %                                                                             %
13434 %                                                                             %
13435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13436 %
13437 %  XTrimImage() trims the edges from the Image window.
13438 %
13439 %  The format of the XTrimImage method is:
13440 %
13441 %      MagickBooleanType XTrimImage(Display *display,
13442 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13443 %        ExceptionInfo *exception)
13444 %
13445 %  A description of each parameter follows:
13446 %
13447 %    o display: Specifies a connection to an X server; returned from
13448 %      XOpenDisplay.
13449 %
13450 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13451 %
13452 %    o windows: Specifies a pointer to a XWindows structure.
13453 %
13454 %    o image: the image.
13455 %
13456 %    o exception: return any errors or warnings in this structure.
13457 %
13458 */
13459 static MagickBooleanType XTrimImage(Display *display,
13460   XResourceInfo *resource_info,XWindows *windows,Image *image,
13461   ExceptionInfo *exception)
13462 {
13463   RectangleInfo
13464     trim_info;
13465
13466   register int
13467     x,
13468     y;
13469
13470   size_t
13471     background,
13472     pixel;
13473
13474   /*
13475     Trim edges from image.
13476   */
13477   XSetCursorState(display,windows,MagickTrue);
13478   XCheckRefreshWindows(display,windows);
13479   /*
13480     Crop the left edge.
13481   */
13482   background=XGetPixel(windows->image.ximage,0,0);
13483   trim_info.width=(size_t) windows->image.ximage->width;
13484   for (x=0; x < windows->image.ximage->width; x++)
13485   {
13486     for (y=0; y < windows->image.ximage->height; y++)
13487     {
13488       pixel=XGetPixel(windows->image.ximage,x,y);
13489       if (pixel != background)
13490         break;
13491     }
13492     if (y < windows->image.ximage->height)
13493       break;
13494   }
13495   trim_info.x=(ssize_t) x;
13496   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13497     {
13498       XSetCursorState(display,windows,MagickFalse);
13499       return(MagickFalse);
13500     }
13501   /*
13502     Crop the right edge.
13503   */
13504   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13505   for (x=windows->image.ximage->width-1; x != 0; x--)
13506   {
13507     for (y=0; y < windows->image.ximage->height; y++)
13508     {
13509       pixel=XGetPixel(windows->image.ximage,x,y);
13510       if (pixel != background)
13511         break;
13512     }
13513     if (y < windows->image.ximage->height)
13514       break;
13515   }
13516   trim_info.width=(size_t) (x-trim_info.x+1);
13517   /*
13518     Crop the top edge.
13519   */
13520   background=XGetPixel(windows->image.ximage,0,0);
13521   trim_info.height=(size_t) windows->image.ximage->height;
13522   for (y=0; y < windows->image.ximage->height; y++)
13523   {
13524     for (x=0; x < windows->image.ximage->width; x++)
13525     {
13526       pixel=XGetPixel(windows->image.ximage,x,y);
13527       if (pixel != background)
13528         break;
13529     }
13530     if (x < windows->image.ximage->width)
13531       break;
13532   }
13533   trim_info.y=(ssize_t) y;
13534   /*
13535     Crop the bottom edge.
13536   */
13537   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13538   for (y=windows->image.ximage->height-1; y != 0; y--)
13539   {
13540     for (x=0; x < windows->image.ximage->width; x++)
13541     {
13542       pixel=XGetPixel(windows->image.ximage,x,y);
13543       if (pixel != background)
13544         break;
13545     }
13546     if (x < windows->image.ximage->width)
13547       break;
13548   }
13549   trim_info.height=(size_t) y-trim_info.y+1;
13550   if (((unsigned int) trim_info.width != windows->image.width) ||
13551       ((unsigned int) trim_info.height != windows->image.height))
13552     {
13553       /*
13554         Reconfigure Image window as defined by the trimming rectangle.
13555       */
13556       XSetCropGeometry(display,windows,&trim_info,image);
13557       windows->image.window_changes.width=(int) trim_info.width;
13558       windows->image.window_changes.height=(int) trim_info.height;
13559       (void) XConfigureImage(display,resource_info,windows,image,exception);
13560     }
13561   XSetCursorState(display,windows,MagickFalse);
13562   return(MagickTrue);
13563 }
13564 \f
13565 /*
13566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13567 %                                                                             %
13568 %                                                                             %
13569 %                                                                             %
13570 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13571 %                                                                             %
13572 %                                                                             %
13573 %                                                                             %
13574 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13575 %
13576 %  XVisualDirectoryImage() creates a Visual Image Directory.
13577 %
13578 %  The format of the XVisualDirectoryImage method is:
13579 %
13580 %      Image *XVisualDirectoryImage(Display *display,
13581 %        XResourceInfo *resource_info,XWindows *windows,
13582 %        ExceptionInfo *exception)
13583 %
13584 %  A description of each parameter follows:
13585 %
13586 %    o display: Specifies a connection to an X server; returned from
13587 %      XOpenDisplay.
13588 %
13589 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13590 %
13591 %    o windows: Specifies a pointer to a XWindows structure.
13592 %
13593 %    o exception: return any errors or warnings in this structure.
13594 %
13595 */
13596 static Image *XVisualDirectoryImage(Display *display,
13597   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13598 {
13599 #define TileImageTag  "Scale/Image"
13600 #define XClientName  "montage"
13601
13602   char
13603     **filelist;
13604
13605   Image
13606     *images,
13607     *montage_image,
13608     *next_image,
13609     *thumbnail_image;
13610
13611   ImageInfo
13612     *read_info;
13613
13614   int
13615     number_files;
13616
13617   MagickBooleanType
13618     backdrop;
13619
13620   MagickStatusType
13621     status;
13622
13623   MontageInfo
13624     *montage_info;
13625
13626   RectangleInfo
13627     geometry;
13628
13629   register int
13630     i;
13631
13632   static char
13633     filename[MaxTextExtent] = "\0",
13634     filenames[MaxTextExtent] = "*";
13635
13636   XResourceInfo
13637     background_resources;
13638
13639   /*
13640     Request file name from user.
13641   */
13642   XFileBrowserWidget(display,windows,"Directory",filenames);
13643   if (*filenames == '\0')
13644     return((Image *) NULL);
13645   /*
13646     Expand the filenames.
13647   */
13648   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13649   if (filelist == (char **) NULL)
13650     {
13651       ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13652         filenames);
13653       return((Image *) NULL);
13654     }
13655   number_files=1;
13656   filelist[0]=filenames;
13657   status=ExpandFilenames(&number_files,&filelist);
13658   if (IfMagickFalse(status) || (number_files == 0))
13659     {
13660       if (number_files == 0)
13661         ThrowXWindowException(ImageError,"NoImagesWereFound",filenames)
13662       else
13663         ThrowXWindowException(ResourceLimitError,"MemoryAllocationFailed",
13664           filenames);
13665       return((Image *) NULL);
13666     }
13667   /*
13668     Set image background resources.
13669   */
13670   background_resources=(*resource_info);
13671   background_resources.window_id=AcquireString("");
13672   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13673     "0x%lx",windows->image.id);
13674   background_resources.backdrop=MagickTrue;
13675   /*
13676     Read each image and convert them to a tile.
13677   */
13678   backdrop=IsMagickTrue( (windows->visual_info->klass == TrueColor) ||
13679     (windows->visual_info->klass == DirectColor) );
13680   read_info=CloneImageInfo(resource_info->image_info);
13681   (void) SetImageOption(read_info,"jpeg:size","120x120");
13682   (void) CloneString(&read_info->size,DefaultTileGeometry);
13683   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13684     (void *) NULL);
13685   images=NewImageList();
13686   XSetCursorState(display,windows,MagickTrue);
13687   XCheckRefreshWindows(display,windows);
13688   for (i=0; i < (int) number_files; i++)
13689   {
13690     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13691     filelist[i]=DestroyString(filelist[i]);
13692     *read_info->magick='\0';
13693     next_image=ReadImage(read_info,exception);
13694     CatchException(exception);
13695     if (next_image != (Image *) NULL)
13696       {
13697         (void) DeleteImageProperty(next_image,"label");
13698         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13699           read_info,next_image,DefaultTileLabel,exception),exception);
13700         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13701           exception);
13702         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13703           geometry.height,exception);
13704         if (thumbnail_image != (Image *) NULL)
13705           {
13706             next_image=DestroyImage(next_image);
13707             next_image=thumbnail_image;
13708           }
13709         if (backdrop)
13710           {
13711             (void) XDisplayBackgroundImage(display,&background_resources,
13712               next_image,exception);
13713             XSetCursorState(display,windows,MagickTrue);
13714           }
13715         AppendImageToList(&images,next_image);
13716         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13717           {
13718             MagickBooleanType
13719               proceed;
13720
13721             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13722               (MagickSizeType) number_files);
13723             if (IfMagickFalse(proceed) )
13724               break;
13725           }
13726       }
13727   }
13728   filelist=(char **) RelinquishMagickMemory(filelist);
13729   if (images == (Image *) NULL)
13730     {
13731       read_info=DestroyImageInfo(read_info);
13732       XSetCursorState(display,windows,MagickFalse);
13733       ThrowXWindowException(ImageError,"NoImagesWereLoaded",filenames);
13734       return((Image *) NULL);
13735     }
13736   /*
13737     Create the Visual Image Directory.
13738   */
13739   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13740   montage_info->pointsize=10;
13741   if (resource_info->font != (char *) NULL)
13742     (void) CloneString(&montage_info->font,resource_info->font);
13743   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13744   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13745     images),exception);
13746   images=DestroyImageList(images);
13747   montage_info=DestroyMontageInfo(montage_info);
13748   read_info=DestroyImageInfo(read_info);
13749   XSetCursorState(display,windows,MagickFalse);
13750   if (montage_image == (Image *) NULL)
13751     return(montage_image);
13752   XClientMessage(display,windows->image.id,windows->im_protocols,
13753     windows->im_next_image,CurrentTime);
13754   return(montage_image);
13755 }
13756 \f
13757 /*
13758 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13759 %                                                                             %
13760 %                                                                             %
13761 %                                                                             %
13762 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13763 %                                                                             %
13764 %                                                                             %
13765 %                                                                             %
13766 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13767 %
13768 %  XDisplayBackgroundImage() displays an image in the background of a window.
13769 %
13770 %  The format of the XDisplayBackgroundImage method is:
13771 %
13772 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13773 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13774 %
13775 %  A description of each parameter follows:
13776 %
13777 %    o display: Specifies a connection to an X server;  returned from
13778 %      XOpenDisplay.
13779 %
13780 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13781 %
13782 %    o image: the image.
13783 %
13784 %    o exception: return any errors or warnings in this structure.
13785 %
13786 */
13787 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13788   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13789 {
13790   char
13791     geometry[MaxTextExtent],
13792     visual_type[MaxTextExtent];
13793
13794   int
13795     height,
13796     status,
13797     width;
13798
13799   RectangleInfo
13800     geometry_info;
13801
13802   static XPixelInfo
13803     pixel;
13804
13805   static XStandardColormap
13806     *map_info;
13807
13808   static XVisualInfo
13809     *visual_info = (XVisualInfo *) NULL;
13810
13811   static XWindowInfo
13812     window_info;
13813
13814   size_t
13815     delay;
13816
13817   Window
13818     root_window;
13819
13820   XGCValues
13821     context_values;
13822
13823   XResourceInfo
13824     resources;
13825
13826   XWindowAttributes
13827     window_attributes;
13828
13829   /*
13830     Determine target window.
13831   */
13832   assert(image != (Image *) NULL);
13833   assert(image->signature == MagickSignature);
13834   if (IfMagickTrue(image->debug) )
13835     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13836   resources=(*resource_info);
13837   window_info.id=(Window) NULL;
13838   root_window=XRootWindow(display,XDefaultScreen(display));
13839   if (LocaleCompare(resources.window_id,"root") == 0)
13840     window_info.id=root_window;
13841   else
13842     {
13843       if (isdigit((int) ((unsigned char) *resources.window_id)) != 0)
13844         window_info.id=XWindowByID(display,root_window,
13845           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13846       if (window_info.id == (Window) NULL)
13847         window_info.id=XWindowByName(display,root_window,resources.window_id);
13848     }
13849   if (window_info.id == (Window) NULL)
13850     {
13851       ThrowXWindowException(XServerError,"NoWindowWithSpecifiedIDExists",
13852         resources.window_id);
13853       return(MagickFalse);
13854     }
13855   /*
13856     Determine window visual id.
13857   */
13858   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13859   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13860   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13861   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13862   if (status != 0)
13863     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13864       XVisualIDFromVisual(window_attributes.visual));
13865   if (visual_info == (XVisualInfo *) NULL)
13866     {
13867       /*
13868         Allocate standard colormap.
13869       */
13870       map_info=XAllocStandardColormap();
13871       if (map_info == (XStandardColormap *) NULL)
13872         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13873           image->filename);
13874       map_info->colormap=(Colormap) NULL;
13875       pixel.pixels=(unsigned long *) NULL;
13876       /*
13877         Initialize visual info.
13878       */
13879       resources.map_type=(char *) NULL;
13880       resources.visual_type=visual_type;
13881       visual_info=XBestVisualInfo(display,map_info,&resources);
13882       if (visual_info == (XVisualInfo *) NULL)
13883         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13884           resources.visual_type);
13885       /*
13886         Initialize window info.
13887       */
13888       window_info.ximage=(XImage *) NULL;
13889       window_info.matte_image=(XImage *) NULL;
13890       window_info.pixmap=(Pixmap) NULL;
13891       window_info.matte_pixmap=(Pixmap) NULL;
13892     }
13893   /*
13894     Free previous root colors.
13895   */
13896   if (window_info.id == root_window)
13897     (void) XDestroyWindowColors(display,root_window);
13898   /*
13899     Initialize Standard Colormap.
13900   */
13901   resources.colormap=SharedColormap;
13902   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13903     exception);
13904   /*
13905     Graphic context superclass.
13906   */
13907   context_values.background=pixel.background_color.pixel;
13908   context_values.foreground=pixel.foreground_color.pixel;
13909   pixel.annotate_context=XCreateGC(display,window_info.id,
13910     (size_t) (GCBackground | GCForeground),&context_values);
13911   if (pixel.annotate_context == (GC) NULL)
13912     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13913       image->filename);
13914   /*
13915     Initialize Image window attributes.
13916   */
13917   window_info.name=AcquireString("\0");
13918   window_info.icon_name=AcquireString("\0");
13919   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13920     &resources,&window_info);
13921   /*
13922     Create the X image.
13923   */
13924   window_info.width=(unsigned int) image->columns;
13925   window_info.height=(unsigned int) image->rows;
13926   if ((image->columns != window_info.width) ||
13927       (image->rows != window_info.height))
13928     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13929       image->filename);
13930   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13931     window_attributes.width,window_attributes.height);
13932   geometry_info.width=window_info.width;
13933   geometry_info.height=window_info.height;
13934   geometry_info.x=(ssize_t) window_info.x;
13935   geometry_info.y=(ssize_t) window_info.y;
13936   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13937     &geometry_info.width,&geometry_info.height);
13938   window_info.width=(unsigned int) geometry_info.width;
13939   window_info.height=(unsigned int) geometry_info.height;
13940   window_info.x=(int) geometry_info.x;
13941   window_info.y=(int) geometry_info.y;
13942   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13943     window_info.height,exception);
13944   if (IfMagickFalse(status) )
13945     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13946       image->filename);
13947   window_info.x=0;
13948   window_info.y=0;
13949   if (IfMagickTrue(image->debug) )
13950     {
13951       (void) LogMagickEvent(X11Event,GetMagickModule(),
13952         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13953         (double) image->columns,(double) image->rows);
13954       if (image->colors != 0)
13955         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13956           image->colors);
13957       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13958     }
13959   /*
13960     Adjust image dimensions as specified by backdrop or geometry options.
13961   */
13962   width=(int) window_info.width;
13963   height=(int) window_info.height;
13964   if (IfMagickTrue(resources.backdrop) )
13965     {
13966       /*
13967         Center image on window.
13968       */
13969       window_info.x=(window_attributes.width/2)-
13970         (window_info.ximage->width/2);
13971       window_info.y=(window_attributes.height/2)-
13972         (window_info.ximage->height/2);
13973       width=window_attributes.width;
13974       height=window_attributes.height;
13975     }
13976   if ((resources.image_geometry != (char *) NULL) &&
13977       (*resources.image_geometry != '\0'))
13978     {
13979       char
13980         default_geometry[MaxTextExtent];
13981
13982       int
13983         flags,
13984         gravity;
13985
13986       XSizeHints
13987         *size_hints;
13988
13989       /*
13990         User specified geometry.
13991       */
13992       size_hints=XAllocSizeHints();
13993       if (size_hints == (XSizeHints *) NULL)
13994         ThrowXWindowFatalException(ResourceLimitFatalError,
13995           "MemoryAllocationFailed",image->filename);
13996       size_hints->flags=0L;
13997       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13998         width,height);
13999       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
14000         default_geometry,window_info.border_width,size_hints,&window_info.x,
14001         &window_info.y,&width,&height,&gravity);
14002       if (flags & (XValue | YValue))
14003         {
14004           width=window_attributes.width;
14005           height=window_attributes.height;
14006         }
14007       (void) XFree((void *) size_hints);
14008     }
14009   /*
14010     Create the X pixmap.
14011   */
14012   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14013     (unsigned int) height,window_info.depth);
14014   if (window_info.pixmap == (Pixmap) NULL)
14015     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14016       image->filename);
14017   /*
14018     Display pixmap on the window.
14019   */
14020   if (((unsigned int) width > window_info.width) ||
14021       ((unsigned int) height > window_info.height))
14022     (void) XFillRectangle(display,window_info.pixmap,
14023       window_info.annotate_context,0,0,(unsigned int) width,
14024       (unsigned int) height);
14025   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14026     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14027     window_info.width,(unsigned int) window_info.height);
14028   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14029   (void) XClearWindow(display,window_info.id);
14030   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14031   XDelay(display,delay == 0UL ? 10UL : delay);
14032   (void) XSync(display,MagickFalse);
14033   return(IsMagickTrue(window_info.id == root_window));
14034 }
14035 \f
14036 /*
14037 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14038 %                                                                             %
14039 %                                                                             %
14040 %                                                                             %
14041 +   X D i s p l a y I m a g e                                                 %
14042 %                                                                             %
14043 %                                                                             %
14044 %                                                                             %
14045 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14046 %
14047 %  XDisplayImage() displays an image via X11.  A new image is created and
14048 %  returned if the user interactively transforms the displayed image.
14049 %
14050 %  The format of the XDisplayImage method is:
14051 %
14052 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14053 %        char **argv,int argc,Image **image,size_t *state,
14054 %        ExceptionInfo *exception)
14055 %
14056 %  A description of each parameter follows:
14057 %
14058 %    o nexus:  Method XDisplayImage returns an image when the
14059 %      user chooses 'Open Image' from the command menu or picks a tile
14060 %      from the image directory.  Otherwise a null image is returned.
14061 %
14062 %    o display: Specifies a connection to an X server;  returned from
14063 %      XOpenDisplay.
14064 %
14065 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14066 %
14067 %    o argv: Specifies the application's argument list.
14068 %
14069 %    o argc: Specifies the number of arguments.
14070 %
14071 %    o image: Specifies an address to an address of an Image structure;
14072 %
14073 %    o exception: return any errors or warnings in this structure.
14074 %
14075 */
14076 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14077   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14078 {
14079 #define MagnifySize  256  /* must be a power of 2 */
14080 #define MagickMenus  10
14081 #define MagickTitle  "Commands"
14082
14083   static const char
14084     *CommandMenu[] =
14085     {
14086       "File",
14087       "Edit",
14088       "View",
14089       "Transform",
14090       "Enhance",
14091       "Effects",
14092       "F/X",
14093       "Image Edit",
14094       "Miscellany",
14095       "Help",
14096       (char *) NULL
14097     },
14098     *FileMenu[] =
14099     {
14100       "Open...",
14101       "Next",
14102       "Former",
14103       "Select...",
14104       "Save...",
14105       "Print...",
14106       "Delete...",
14107       "New...",
14108       "Visual Directory...",
14109       "Quit",
14110       (char *) NULL
14111     },
14112     *EditMenu[] =
14113     {
14114       "Undo",
14115       "Redo",
14116       "Cut",
14117       "Copy",
14118       "Paste",
14119       (char *) NULL
14120     },
14121     *ViewMenu[] =
14122     {
14123       "Half Size",
14124       "Original Size",
14125       "Double Size",
14126       "Resize...",
14127       "Apply",
14128       "Refresh",
14129       "Restore",
14130       (char *) NULL
14131     },
14132     *TransformMenu[] =
14133     {
14134       "Crop",
14135       "Chop",
14136       "Flop",
14137       "Flip",
14138       "Rotate Right",
14139       "Rotate Left",
14140       "Rotate...",
14141       "Shear...",
14142       "Roll...",
14143       "Trim Edges",
14144       (char *) NULL
14145     },
14146     *EnhanceMenu[] =
14147     {
14148       "Hue...",
14149       "Saturation...",
14150       "Brightness...",
14151       "Gamma...",
14152       "Spiff",
14153       "Dull",
14154       "Contrast Stretch...",
14155       "Sigmoidal Contrast...",
14156       "Normalize",
14157       "Equalize",
14158       "Negate",
14159       "Grayscale",
14160       "Map...",
14161       "Quantize...",
14162       (char *) NULL
14163     },
14164     *EffectsMenu[] =
14165     {
14166       "Despeckle",
14167       "Emboss",
14168       "Reduce Noise",
14169       "Add Noise...",
14170       "Sharpen...",
14171       "Blur...",
14172       "Threshold...",
14173       "Edge Detect...",
14174       "Spread...",
14175       "Shade...",
14176       "Raise...",
14177       "Segment...",
14178       (char *) NULL
14179     },
14180     *FXMenu[] =
14181     {
14182       "Solarize...",
14183       "Sepia Tone...",
14184       "Swirl...",
14185       "Implode...",
14186       "Vignette...",
14187       "Wave...",
14188       "Oil Paint...",
14189       "Charcoal Draw...",
14190       (char *) NULL
14191     },
14192     *ImageEditMenu[] =
14193     {
14194       "Annotate...",
14195       "Draw...",
14196       "Color...",
14197       "Matte...",
14198       "Composite...",
14199       "Add Border...",
14200       "Add Frame...",
14201       "Comment...",
14202       "Launch...",
14203       "Region of Interest...",
14204       (char *) NULL
14205     },
14206     *MiscellanyMenu[] =
14207     {
14208       "Image Info",
14209       "Zoom Image",
14210       "Show Preview...",
14211       "Show Histogram",
14212       "Show Matte",
14213       "Background...",
14214       "Slide Show...",
14215       "Preferences...",
14216       (char *) NULL
14217     },
14218     *HelpMenu[] =
14219     {
14220       "Overview",
14221       "Browse Documentation",
14222       "About Display",
14223       (char *) NULL
14224     },
14225     *ShortCutsMenu[] =
14226     {
14227       "Next",
14228       "Former",
14229       "Open...",
14230       "Save...",
14231       "Print...",
14232       "Undo",
14233       "Restore",
14234       "Image Info",
14235       "Quit",
14236       (char *) NULL
14237     },
14238     *VirtualMenu[] =
14239     {
14240       "Image Info",
14241       "Print",
14242       "Next",
14243       "Quit",
14244       (char *) NULL
14245     };
14246
14247   static const char
14248     **Menus[MagickMenus] =
14249     {
14250       FileMenu,
14251       EditMenu,
14252       ViewMenu,
14253       TransformMenu,
14254       EnhanceMenu,
14255       EffectsMenu,
14256       FXMenu,
14257       ImageEditMenu,
14258       MiscellanyMenu,
14259       HelpMenu
14260     };
14261
14262   static CommandType
14263     CommandMenus[] =
14264     {
14265       NullCommand,
14266       NullCommand,
14267       NullCommand,
14268       NullCommand,
14269       NullCommand,
14270       NullCommand,
14271       NullCommand,
14272       NullCommand,
14273       NullCommand,
14274       NullCommand,
14275     },
14276     FileCommands[] =
14277     {
14278       OpenCommand,
14279       NextCommand,
14280       FormerCommand,
14281       SelectCommand,
14282       SaveCommand,
14283       PrintCommand,
14284       DeleteCommand,
14285       NewCommand,
14286       VisualDirectoryCommand,
14287       QuitCommand
14288     },
14289     EditCommands[] =
14290     {
14291       UndoCommand,
14292       RedoCommand,
14293       CutCommand,
14294       CopyCommand,
14295       PasteCommand
14296     },
14297     ViewCommands[] =
14298     {
14299       HalfSizeCommand,
14300       OriginalSizeCommand,
14301       DoubleSizeCommand,
14302       ResizeCommand,
14303       ApplyCommand,
14304       RefreshCommand,
14305       RestoreCommand
14306     },
14307     TransformCommands[] =
14308     {
14309       CropCommand,
14310       ChopCommand,
14311       FlopCommand,
14312       FlipCommand,
14313       RotateRightCommand,
14314       RotateLeftCommand,
14315       RotateCommand,
14316       ShearCommand,
14317       RollCommand,
14318       TrimCommand
14319     },
14320     EnhanceCommands[] =
14321     {
14322       HueCommand,
14323       SaturationCommand,
14324       BrightnessCommand,
14325       GammaCommand,
14326       SpiffCommand,
14327       DullCommand,
14328       ContrastStretchCommand,
14329       SigmoidalContrastCommand,
14330       NormalizeCommand,
14331       EqualizeCommand,
14332       NegateCommand,
14333       GrayscaleCommand,
14334       MapCommand,
14335       QuantizeCommand
14336     },
14337     EffectsCommands[] =
14338     {
14339       DespeckleCommand,
14340       EmbossCommand,
14341       ReduceNoiseCommand,
14342       AddNoiseCommand,
14343       SharpenCommand,
14344       BlurCommand,
14345       ThresholdCommand,
14346       EdgeDetectCommand,
14347       SpreadCommand,
14348       ShadeCommand,
14349       RaiseCommand,
14350       SegmentCommand
14351     },
14352     FXCommands[] =
14353     {
14354       SolarizeCommand,
14355       SepiaToneCommand,
14356       SwirlCommand,
14357       ImplodeCommand,
14358       VignetteCommand,
14359       WaveCommand,
14360       OilPaintCommand,
14361       CharcoalDrawCommand
14362     },
14363     ImageEditCommands[] =
14364     {
14365       AnnotateCommand,
14366       DrawCommand,
14367       ColorCommand,
14368       MatteCommand,
14369       CompositeCommand,
14370       AddBorderCommand,
14371       AddFrameCommand,
14372       CommentCommand,
14373       LaunchCommand,
14374       RegionofInterestCommand
14375     },
14376     MiscellanyCommands[] =
14377     {
14378       InfoCommand,
14379       ZoomCommand,
14380       ShowPreviewCommand,
14381       ShowHistogramCommand,
14382       ShowMatteCommand,
14383       BackgroundCommand,
14384       SlideShowCommand,
14385       PreferencesCommand
14386     },
14387     HelpCommands[] =
14388     {
14389       HelpCommand,
14390       BrowseDocumentationCommand,
14391       VersionCommand
14392     },
14393     ShortCutsCommands[] =
14394     {
14395       NextCommand,
14396       FormerCommand,
14397       OpenCommand,
14398       SaveCommand,
14399       PrintCommand,
14400       UndoCommand,
14401       RestoreCommand,
14402       InfoCommand,
14403       QuitCommand
14404     },
14405     VirtualCommands[] =
14406     {
14407       InfoCommand,
14408       PrintCommand,
14409       NextCommand,
14410       QuitCommand
14411     };
14412
14413   static CommandType
14414     *Commands[MagickMenus] =
14415     {
14416       FileCommands,
14417       EditCommands,
14418       ViewCommands,
14419       TransformCommands,
14420       EnhanceCommands,
14421       EffectsCommands,
14422       FXCommands,
14423       ImageEditCommands,
14424       MiscellanyCommands,
14425       HelpCommands
14426     };
14427
14428   char
14429     command[MaxTextExtent],
14430     *directory,
14431     geometry[MaxTextExtent],
14432     resource_name[MaxTextExtent];
14433
14434   CommandType
14435     command_type;
14436
14437   Image
14438     *display_image,
14439     *nexus;
14440
14441   int
14442     entry,
14443     id;
14444
14445   KeySym
14446     key_symbol;
14447
14448   MagickStatusType
14449     context_mask,
14450     status;
14451
14452   RectangleInfo
14453     geometry_info;
14454
14455   register int
14456     i;
14457
14458   static char
14459     working_directory[MaxTextExtent];
14460
14461   static XPoint
14462     vid_info;
14463
14464   static XWindowInfo
14465     *magick_windows[MaxXWindows];
14466
14467   static unsigned int
14468     number_windows;
14469
14470   struct stat
14471     attributes;
14472
14473   time_t
14474     timer,
14475     timestamp,
14476     update_time;
14477
14478   unsigned int
14479     height,
14480     width;
14481
14482   size_t
14483     delay;
14484
14485   WarningHandler
14486     warning_handler;
14487
14488   Window
14489     root_window;
14490
14491   XClassHint
14492     *class_hints;
14493
14494   XEvent
14495     event;
14496
14497   XFontStruct
14498     *font_info;
14499
14500   XGCValues
14501     context_values;
14502
14503   XPixelInfo
14504     *icon_pixel,
14505     *pixel;
14506
14507   XResourceInfo
14508     *icon_resources;
14509
14510   XStandardColormap
14511     *icon_map,
14512     *map_info;
14513
14514   XVisualInfo
14515     *icon_visual,
14516     *visual_info;
14517
14518   XWindowChanges
14519     window_changes;
14520
14521   XWindows
14522     *windows;
14523
14524   XWMHints
14525     *manager_hints;
14526
14527   assert(image != (Image **) NULL);
14528   assert((*image)->signature == MagickSignature);
14529   if (IfMagickTrue((*image)->debug) )
14530     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14531   display_image=(*image);
14532   warning_handler=(WarningHandler) NULL;
14533   windows=XSetWindows((XWindows *) ~0);
14534   if (windows != (XWindows *) NULL)
14535     {
14536       int
14537         status;
14538
14539       if (*working_directory == '\0')
14540         (void) CopyMagickString(working_directory,".",MaxTextExtent);
14541       status=chdir(working_directory);
14542       if (status == -1)
14543         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14544           "UnableToOpenFile","%s",working_directory);
14545       warning_handler=resource_info->display_warnings ?
14546         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14547       warning_handler=resource_info->display_warnings ?
14548         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14549     }
14550   else
14551     {
14552       /*
14553         Allocate windows structure.
14554       */
14555       resource_info->colors=display_image->colors;
14556       windows=XSetWindows(XInitializeWindows(display,resource_info));
14557       if (windows == (XWindows *) NULL)
14558         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14559           (*image)->filename);
14560       /*
14561         Initialize window id's.
14562       */
14563       number_windows=0;
14564       magick_windows[number_windows++]=(&windows->icon);
14565       magick_windows[number_windows++]=(&windows->backdrop);
14566       magick_windows[number_windows++]=(&windows->image);
14567       magick_windows[number_windows++]=(&windows->info);
14568       magick_windows[number_windows++]=(&windows->command);
14569       magick_windows[number_windows++]=(&windows->widget);
14570       magick_windows[number_windows++]=(&windows->popup);
14571       magick_windows[number_windows++]=(&windows->magnify);
14572       magick_windows[number_windows++]=(&windows->pan);
14573       for (i=0; i < (int) number_windows; i++)
14574         magick_windows[i]->id=(Window) NULL;
14575       vid_info.x=0;
14576       vid_info.y=0;
14577     }
14578   /*
14579     Initialize font info.
14580   */
14581   if (windows->font_info != (XFontStruct *) NULL)
14582     (void) XFreeFont(display,windows->font_info);
14583   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14584   if (windows->font_info == (XFontStruct *) NULL)
14585     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14586       resource_info->font);
14587   /*
14588     Initialize Standard Colormap.
14589   */
14590   map_info=windows->map_info;
14591   icon_map=windows->icon_map;
14592   visual_info=windows->visual_info;
14593   icon_visual=windows->icon_visual;
14594   pixel=windows->pixel_info;
14595   icon_pixel=windows->icon_pixel;
14596   font_info=windows->font_info;
14597   icon_resources=windows->icon_resources;
14598   class_hints=windows->class_hints;
14599   manager_hints=windows->manager_hints;
14600   root_window=XRootWindow(display,visual_info->screen);
14601   nexus=NewImageList();
14602   if (IfMagickTrue(display_image->debug) )
14603     {
14604       (void) LogMagickEvent(X11Event,GetMagickModule(),
14605         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14606         (double) display_image->scene,(double) display_image->columns,
14607         (double) display_image->rows);
14608       if (display_image->colors != 0)
14609         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14610           display_image->colors);
14611       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14612         display_image->magick);
14613     }
14614   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14615     map_info,pixel,exception);
14616   display_image->taint=MagickFalse;
14617   /*
14618     Initialize graphic context.
14619   */
14620   windows->context.id=(Window) NULL;
14621   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14622     resource_info,&windows->context);
14623   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14624   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14625   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14626   manager_hints->flags=InputHint | StateHint;
14627   manager_hints->input=MagickFalse;
14628   manager_hints->initial_state=WithdrawnState;
14629   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14630     &windows->context);
14631   if (IfMagickTrue(display_image->debug) )
14632     (void) LogMagickEvent(X11Event,GetMagickModule(),
14633       "Window id: 0x%lx (context)",windows->context.id);
14634   context_values.background=pixel->background_color.pixel;
14635   context_values.font=font_info->fid;
14636   context_values.foreground=pixel->foreground_color.pixel;
14637   context_values.graphics_exposures=MagickFalse;
14638   context_mask=(MagickStatusType)
14639     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14640   if (pixel->annotate_context != (GC) NULL)
14641     (void) XFreeGC(display,pixel->annotate_context);
14642   pixel->annotate_context=XCreateGC(display,windows->context.id,
14643     context_mask,&context_values);
14644   if (pixel->annotate_context == (GC) NULL)
14645     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14646       display_image->filename);
14647   context_values.background=pixel->depth_color.pixel;
14648   if (pixel->widget_context != (GC) NULL)
14649     (void) XFreeGC(display,pixel->widget_context);
14650   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14651     &context_values);
14652   if (pixel->widget_context == (GC) NULL)
14653     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14654       display_image->filename);
14655   context_values.background=pixel->foreground_color.pixel;
14656   context_values.foreground=pixel->background_color.pixel;
14657   context_values.plane_mask=context_values.background ^
14658     context_values.foreground;
14659   if (pixel->highlight_context != (GC) NULL)
14660     (void) XFreeGC(display,pixel->highlight_context);
14661   pixel->highlight_context=XCreateGC(display,windows->context.id,
14662     (size_t) (context_mask | GCPlaneMask),&context_values);
14663   if (pixel->highlight_context == (GC) NULL)
14664     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14665       display_image->filename);
14666   (void) XDestroyWindow(display,windows->context.id);
14667   /*
14668     Initialize icon window.
14669   */
14670   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14671     icon_resources,&windows->icon);
14672   windows->icon.geometry=resource_info->icon_geometry;
14673   XBestIconSize(display,&windows->icon,display_image);
14674   windows->icon.attributes.colormap=XDefaultColormap(display,
14675     icon_visual->screen);
14676   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14677   manager_hints->flags=InputHint | StateHint;
14678   manager_hints->input=MagickFalse;
14679   manager_hints->initial_state=IconicState;
14680   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14681     &windows->icon);
14682   if (IfMagickTrue(display_image->debug) )
14683     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14684       windows->icon.id);
14685   /*
14686     Initialize graphic context for icon window.
14687   */
14688   if (icon_pixel->annotate_context != (GC) NULL)
14689     (void) XFreeGC(display,icon_pixel->annotate_context);
14690   context_values.background=icon_pixel->background_color.pixel;
14691   context_values.foreground=icon_pixel->foreground_color.pixel;
14692   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14693     (size_t) (GCBackground | GCForeground),&context_values);
14694   if (icon_pixel->annotate_context == (GC) NULL)
14695     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14696       display_image->filename);
14697   windows->icon.annotate_context=icon_pixel->annotate_context;
14698   /*
14699     Initialize Image window.
14700   */
14701   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14702     &windows->image);
14703   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14704   if (IfMagickFalse(resource_info->use_shared_memory) )
14705     windows->image.shared_memory=MagickFalse;
14706   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14707     {
14708       char
14709         *title;
14710
14711       title=InterpretImageProperties(resource_info->image_info,display_image,
14712         resource_info->title,exception);
14713       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14714       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14715       title=DestroyString(title);
14716     }
14717   else
14718     {
14719       char
14720         filename[MaxTextExtent];
14721
14722       /*
14723         Window name is the base of the filename.
14724       */
14725       GetPathComponent(display_image->magick_filename,TailPath,filename);
14726       if (display_image->scene == 0)
14727         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14728           "%s: %s",MagickPackageName,filename);
14729       else
14730         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14731           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14732           (double) display_image->scene,(double) GetImageListLength(
14733           display_image));
14734       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14735     }
14736   if (resource_info->immutable)
14737     windows->image.immutable=MagickTrue;
14738   windows->image.use_pixmap=resource_info->use_pixmap;
14739   windows->image.geometry=resource_info->image_geometry;
14740   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14741     XDisplayWidth(display,visual_info->screen),
14742     XDisplayHeight(display,visual_info->screen));
14743   geometry_info.width=display_image->columns;
14744   geometry_info.height=display_image->rows;
14745   geometry_info.x=0;
14746   geometry_info.y=0;
14747   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14748     &geometry_info.width,&geometry_info.height);
14749   windows->image.width=(unsigned int) geometry_info.width;
14750   windows->image.height=(unsigned int) geometry_info.height;
14751   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14752     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14753     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14754     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14755   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14756     resource_info,&windows->backdrop);
14757   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14758     {
14759       /*
14760         Initialize backdrop window.
14761       */
14762       windows->backdrop.x=0;
14763       windows->backdrop.y=0;
14764       (void) CloneString(&windows->backdrop.name,"Backdrop");
14765       windows->backdrop.flags=(size_t) (USSize | USPosition);
14766       windows->backdrop.width=(unsigned int)
14767         XDisplayWidth(display,visual_info->screen);
14768       windows->backdrop.height=(unsigned int)
14769         XDisplayHeight(display,visual_info->screen);
14770       windows->backdrop.border_width=0;
14771       windows->backdrop.immutable=MagickTrue;
14772       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14773         ButtonReleaseMask;
14774       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14775         StructureNotifyMask;
14776       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14777       manager_hints->icon_window=windows->icon.id;
14778       manager_hints->input=MagickTrue;
14779       manager_hints->initial_state=resource_info->iconic ? IconicState :
14780         NormalState;
14781       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14782         &windows->backdrop);
14783       if (IfMagickTrue(display_image->debug) )
14784         (void) LogMagickEvent(X11Event,GetMagickModule(),
14785           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14786       (void) XMapWindow(display,windows->backdrop.id);
14787       (void) XClearWindow(display,windows->backdrop.id);
14788       if (windows->image.id != (Window) NULL)
14789         {
14790           (void) XDestroyWindow(display,windows->image.id);
14791           windows->image.id=(Window) NULL;
14792         }
14793       /*
14794         Position image in the center the backdrop.
14795       */
14796       windows->image.flags|=USPosition;
14797       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14798         (windows->image.width/2);
14799       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14800         (windows->image.height/2);
14801     }
14802   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14803   manager_hints->icon_window=windows->icon.id;
14804   manager_hints->input=MagickTrue;
14805   manager_hints->initial_state=resource_info->iconic ? IconicState :
14806     NormalState;
14807   if (windows->group_leader.id != (Window) NULL)
14808     {
14809       /*
14810         Follow the leader.
14811       */
14812       manager_hints->flags|=WindowGroupHint;
14813       manager_hints->window_group=windows->group_leader.id;
14814       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14815       if (IfMagickTrue(display_image->debug) )
14816         (void) LogMagickEvent(X11Event,GetMagickModule(),
14817           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14818     }
14819   XMakeWindow(display,
14820     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14821     argv,argc,class_hints,manager_hints,&windows->image);
14822   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14823     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14824   if (windows->group_leader.id != (Window) NULL)
14825     (void) XSetTransientForHint(display,windows->image.id,
14826       windows->group_leader.id);
14827   if (IfMagickTrue(display_image->debug) )
14828     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14829       windows->image.id);
14830   /*
14831     Initialize Info widget.
14832   */
14833   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14834     &windows->info);
14835   (void) CloneString(&windows->info.name,"Info");
14836   (void) CloneString(&windows->info.icon_name,"Info");
14837   windows->info.border_width=1;
14838   windows->info.x=2;
14839   windows->info.y=2;
14840   windows->info.flags|=PPosition;
14841   windows->info.attributes.win_gravity=UnmapGravity;
14842   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14843     StructureNotifyMask;
14844   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14845   manager_hints->input=MagickFalse;
14846   manager_hints->initial_state=NormalState;
14847   manager_hints->window_group=windows->image.id;
14848   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14849     &windows->info);
14850   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14851     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14852   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14853     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14854   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14855   if (IfMagickTrue(windows->image.mapped) )
14856     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14857   if (IfMagickTrue(display_image->debug) )
14858     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14859       windows->info.id);
14860   /*
14861     Initialize Command widget.
14862   */
14863   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14864     resource_info,&windows->command);
14865   windows->command.data=MagickMenus;
14866   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14867   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14868     resource_info->client_name);
14869   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14870     resource_name,"geometry",(char *) NULL);
14871   (void) CloneString(&windows->command.name,MagickTitle);
14872   windows->command.border_width=0;
14873   windows->command.flags|=PPosition;
14874   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14875     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14876     OwnerGrabButtonMask | StructureNotifyMask;
14877   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14878   manager_hints->input=MagickTrue;
14879   manager_hints->initial_state=NormalState;
14880   manager_hints->window_group=windows->image.id;
14881   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14882     &windows->command);
14883   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14884     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14885     HighlightHeight);
14886   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14887     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14888   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14889   if (IfMagickTrue(windows->command.mapped) )
14890     (void) XMapRaised(display,windows->command.id);
14891   if (IfMagickTrue(display_image->debug) )
14892     (void) LogMagickEvent(X11Event,GetMagickModule(),
14893       "Window id: 0x%lx (command)",windows->command.id);
14894   /*
14895     Initialize Widget window.
14896   */
14897   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14898     resource_info,&windows->widget);
14899   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14900     resource_info->client_name);
14901   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14902     resource_name,"geometry",(char *) NULL);
14903   windows->widget.border_width=0;
14904   windows->widget.flags|=PPosition;
14905   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14906     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14907     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14908     StructureNotifyMask;
14909   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14910   manager_hints->input=MagickTrue;
14911   manager_hints->initial_state=NormalState;
14912   manager_hints->window_group=windows->image.id;
14913   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14914     &windows->widget);
14915   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14916     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14917   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14918     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14919   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14920   if (IfMagickTrue(display_image->debug) )
14921     (void) LogMagickEvent(X11Event,GetMagickModule(),
14922       "Window id: 0x%lx (widget)",windows->widget.id);
14923   /*
14924     Initialize popup window.
14925   */
14926   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14927     resource_info,&windows->popup);
14928   windows->popup.border_width=0;
14929   windows->popup.flags|=PPosition;
14930   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14931     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14932     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14933   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14934   manager_hints->input=MagickTrue;
14935   manager_hints->initial_state=NormalState;
14936   manager_hints->window_group=windows->image.id;
14937   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14938     &windows->popup);
14939   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14940     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14941   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14942     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14943   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14944   if (IfMagickTrue(display_image->debug) )
14945     (void) LogMagickEvent(X11Event,GetMagickModule(),
14946       "Window id: 0x%lx (pop up)",windows->popup.id);
14947   /*
14948     Initialize Magnify window and cursor.
14949   */
14950   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14951     resource_info,&windows->magnify);
14952   if (IfMagickFalse(resource_info->use_shared_memory) )
14953     windows->magnify.shared_memory=MagickFalse;
14954   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14955     resource_info->client_name);
14956   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14957     resource_name,"geometry",(char *) NULL);
14958   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14959     resource_info->magnify);
14960   if (windows->magnify.cursor != (Cursor) NULL)
14961     (void) XFreeCursor(display,windows->magnify.cursor);
14962   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14963     map_info->colormap,resource_info->background_color,
14964     resource_info->foreground_color);
14965   if (windows->magnify.cursor == (Cursor) NULL)
14966     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14967       display_image->filename);
14968   windows->magnify.width=MagnifySize;
14969   windows->magnify.height=MagnifySize;
14970   windows->magnify.flags|=PPosition;
14971   windows->magnify.min_width=MagnifySize;
14972   windows->magnify.min_height=MagnifySize;
14973   windows->magnify.width_inc=MagnifySize;
14974   windows->magnify.height_inc=MagnifySize;
14975   windows->magnify.data=resource_info->magnify;
14976   windows->magnify.attributes.cursor=windows->magnify.cursor;
14977   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14978     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14979     StructureNotifyMask;
14980   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14981   manager_hints->input=MagickTrue;
14982   manager_hints->initial_state=NormalState;
14983   manager_hints->window_group=windows->image.id;
14984   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14985     &windows->magnify);
14986   if (IfMagickTrue(display_image->debug) )
14987     (void) LogMagickEvent(X11Event,GetMagickModule(),
14988       "Window id: 0x%lx (magnify)",windows->magnify.id);
14989   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14990   /*
14991     Initialize panning window.
14992   */
14993   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14994     resource_info,&windows->pan);
14995   (void) CloneString(&windows->pan.name,"Pan Icon");
14996   windows->pan.width=windows->icon.width;
14997   windows->pan.height=windows->icon.height;
14998   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14999     resource_info->client_name);
15000   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
15001     resource_name,"geometry",(char *) NULL);
15002   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
15003     &windows->pan.width,&windows->pan.height);
15004   windows->pan.flags|=PPosition;
15005   windows->pan.immutable=MagickTrue;
15006   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15007     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15008     StructureNotifyMask;
15009   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15010   manager_hints->input=MagickFalse;
15011   manager_hints->initial_state=NormalState;
15012   manager_hints->window_group=windows->image.id;
15013   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15014     &windows->pan);
15015   if (IfMagickTrue(display_image->debug) )
15016     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15017       windows->pan.id);
15018   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15019   if (IfMagickTrue(windows->info.mapped) )
15020     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15021   if (IfMagickFalse(windows->image.mapped) ||
15022       (windows->backdrop.id != (Window) NULL))
15023     (void) XMapWindow(display,windows->image.id);
15024   /*
15025     Set our progress monitor and warning handlers.
15026   */
15027   if (warning_handler == (WarningHandler) NULL)
15028     {
15029       warning_handler=resource_info->display_warnings ?
15030         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15031       warning_handler=resource_info->display_warnings ?
15032         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15033     }
15034   /*
15035     Initialize Image and Magnify X images.
15036   */
15037   windows->image.x=0;
15038   windows->image.y=0;
15039   windows->magnify.shape=MagickFalse;
15040   width=(unsigned int) display_image->columns;
15041   height=(unsigned int) display_image->rows;
15042   if ((display_image->columns != width) || (display_image->rows != height))
15043     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15044       display_image->filename);
15045   status=XMakeImage(display,resource_info,&windows->image,display_image,
15046     width,height,exception);
15047   if (IfMagickFalse(status) )
15048     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15049       display_image->filename);
15050   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15051     windows->magnify.width,windows->magnify.height,exception);
15052   if (IfMagickFalse(status))
15053     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15054       display_image->filename);
15055   if (IfMagickTrue(windows->magnify.mapped) )
15056     (void) XMapRaised(display,windows->magnify.id);
15057   if (IfMagickTrue(windows->pan.mapped) )
15058     (void) XMapRaised(display,windows->pan.id);
15059   windows->image.window_changes.width=(int) display_image->columns;
15060   windows->image.window_changes.height=(int) display_image->rows;
15061   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15062   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15063   (void) XSync(display,MagickFalse);
15064   /*
15065     Respond to events.
15066   */
15067   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15068   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15069   update_time=0;
15070   if (IfMagickTrue(resource_info->update) )
15071     {
15072       MagickBooleanType
15073         status;
15074
15075       /*
15076         Determine when file data was last modified.
15077       */
15078       status=GetPathAttributes(display_image->filename,&attributes);
15079       if (IfMagickTrue(status) )
15080         update_time=attributes.st_mtime;
15081     }
15082   *state&=(~FormerImageState);
15083   *state&=(~MontageImageState);
15084   *state&=(~NextImageState);
15085   do
15086   {
15087     /*
15088       Handle a window event.
15089     */
15090     if (IfMagickTrue(windows->image.mapped) )
15091       if ((display_image->delay != 0) || (resource_info->update != 0))
15092         {
15093           if (timer < time((time_t *) NULL))
15094             {
15095               if (IfMagickFalse(resource_info->update) )
15096                 *state|=NextImageState | ExitState;
15097               else
15098                 {
15099                   MagickBooleanType
15100                     status;
15101
15102                   /*
15103                     Determine if image file was modified.
15104                   */
15105                   status=GetPathAttributes(display_image->filename,&attributes);
15106                   if (IfMagickTrue(status) )
15107                     if (update_time != attributes.st_mtime)
15108                       {
15109                         /*
15110                           Redisplay image.
15111                         */
15112                         (void) FormatLocaleString(
15113                           resource_info->image_info->filename,MaxTextExtent,
15114                           "%s:%s",display_image->magick,
15115                           display_image->filename);
15116                         nexus=ReadImage(resource_info->image_info,exception);
15117                         if (nexus != (Image *) NULL)
15118                           {
15119                             nexus=DestroyImage(nexus);
15120                             *state|=NextImageState | ExitState;
15121                           }
15122                       }
15123                   delay=display_image->delay/MagickMax(
15124                     display_image->ticks_per_second,1L);
15125                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15126                 }
15127             }
15128           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15129             {
15130               /*
15131                 Do not block if delay > 0.
15132               */
15133               XDelay(display,SuspendTime << 2);
15134               continue;
15135             }
15136         }
15137     timestamp=time((time_t *) NULL);
15138     (void) XNextEvent(display,&event);
15139     if (IfMagickFalse(windows->image.stasis) )
15140       windows->image.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15141     if (IfMagickFalse(windows->magnify.stasis) )
15142       windows->magnify.stasis=IsMagickTrue((time((time_t *) NULL)-timestamp) > 0);
15143     if (event.xany.window == windows->command.id)
15144       {
15145         /*
15146           Select a command from the Command widget.
15147         */
15148         id=XCommandWidget(display,windows,CommandMenu,&event);
15149         if (id < 0)
15150           continue;
15151         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15152         command_type=CommandMenus[id];
15153         if (id < MagickMenus)
15154           {
15155             /*
15156               Select a command from a pop-up menu.
15157             */
15158             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15159               command);
15160             if (entry < 0)
15161               continue;
15162             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15163             command_type=Commands[id][entry];
15164           }
15165         if (command_type != NullCommand)
15166           nexus=XMagickCommand(display,resource_info,windows,command_type,
15167             &display_image,exception);
15168         continue;
15169       }
15170     switch (event.type)
15171     {
15172       case ButtonPress:
15173       {
15174         if (IfMagickTrue(display_image->debug) )
15175           (void) LogMagickEvent(X11Event,GetMagickModule(),
15176             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15177             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15178         if ((event.xbutton.button == Button3) &&
15179             (event.xbutton.state & Mod1Mask))
15180           {
15181             /*
15182               Convert Alt-Button3 to Button2.
15183             */
15184             event.xbutton.button=Button2;
15185             event.xbutton.state&=(~Mod1Mask);
15186           }
15187         if (event.xbutton.window == windows->backdrop.id)
15188           {
15189             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15190               event.xbutton.time);
15191             break;
15192           }
15193         if (event.xbutton.window == windows->image.id)
15194           {
15195             switch (event.xbutton.button)
15196             {
15197               case Button1:
15198               {
15199                 if (resource_info->immutable)
15200                   {
15201                     /*
15202                       Select a command from the Virtual menu.
15203                     */
15204                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15205                       command);
15206                     if (entry >= 0)
15207                       nexus=XMagickCommand(display,resource_info,windows,
15208                         VirtualCommands[entry],&display_image,exception);
15209                     break;
15210                   }
15211                 /*
15212                   Map/unmap Command widget.
15213                 */
15214                 if (IfMagickTrue(windows->command.mapped) )
15215                   (void) XWithdrawWindow(display,windows->command.id,
15216                     windows->command.screen);
15217                 else
15218                   {
15219                     (void) XCommandWidget(display,windows,CommandMenu,
15220                       (XEvent *) NULL);
15221                     (void) XMapRaised(display,windows->command.id);
15222                   }
15223                 break;
15224               }
15225               case Button2:
15226               {
15227                 /*
15228                   User pressed the image magnify button.
15229                 */
15230                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15231                   &display_image,exception);
15232                 XMagnifyImage(display,windows,&event,exception);
15233                 break;
15234               }
15235               case Button3:
15236               {
15237                 if (resource_info->immutable)
15238                   {
15239                     /*
15240                       Select a command from the Virtual menu.
15241                     */
15242                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15243                       command);
15244                     if (entry >= 0)
15245                       nexus=XMagickCommand(display,resource_info,windows,
15246                         VirtualCommands[entry],&display_image,exception);
15247                     break;
15248                   }
15249                 if (display_image->montage != (char *) NULL)
15250                   {
15251                     /*
15252                       Open or delete a tile from a visual image directory.
15253                     */
15254                     nexus=XTileImage(display,resource_info,windows,
15255                       display_image,&event,exception);
15256                     if (nexus != (Image *) NULL)
15257                       *state|=MontageImageState | NextImageState | ExitState;
15258                     vid_info.x=(short int) windows->image.x;
15259                     vid_info.y=(short int) windows->image.y;
15260                     break;
15261                   }
15262                 /*
15263                   Select a command from the Short Cuts menu.
15264                 */
15265                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15266                   command);
15267                 if (entry >= 0)
15268                   nexus=XMagickCommand(display,resource_info,windows,
15269                     ShortCutsCommands[entry],&display_image,exception);
15270                 break;
15271               }
15272               case Button4:
15273               {
15274                 /*
15275                   Wheel up.
15276                 */
15277                 XTranslateImage(display,windows,*image,XK_Up);
15278                 break;
15279               }
15280               case Button5:
15281               {
15282                 /*
15283                   Wheel down.
15284                 */
15285                 XTranslateImage(display,windows,*image,XK_Down);
15286                 break;
15287               }
15288               default:
15289                 break;
15290             }
15291             break;
15292           }
15293         if (event.xbutton.window == windows->magnify.id)
15294           {
15295             int
15296               factor;
15297
15298             static const char
15299               *MagnifyMenu[] =
15300               {
15301                 "2",
15302                 "4",
15303                 "5",
15304                 "6",
15305                 "7",
15306                 "8",
15307                 "9",
15308                 "3",
15309                 (char *) NULL,
15310               };
15311
15312             static KeySym
15313               MagnifyCommands[] =
15314               {
15315                 XK_2,
15316                 XK_4,
15317                 XK_5,
15318                 XK_6,
15319                 XK_7,
15320                 XK_8,
15321                 XK_9,
15322                 XK_3
15323               };
15324
15325             /*
15326               Select a magnify factor from the pop-up menu.
15327             */
15328             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15329             if (factor >= 0)
15330               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15331                 exception);
15332             break;
15333           }
15334         if (event.xbutton.window == windows->pan.id)
15335           {
15336             switch (event.xbutton.button)
15337             {
15338               case Button4:
15339               {
15340                 /*
15341                   Wheel up.
15342                 */
15343                 XTranslateImage(display,windows,*image,XK_Up);
15344                 break;
15345               }
15346               case Button5:
15347               {
15348                 /*
15349                   Wheel down.
15350                 */
15351                 XTranslateImage(display,windows,*image,XK_Down);
15352                 break;
15353               }
15354               default:
15355               {
15356                 XPanImage(display,windows,&event,exception);
15357                 break;
15358               }
15359             }
15360             break;
15361           }
15362         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15363           1L);
15364         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15365         break;
15366       }
15367       case ButtonRelease:
15368       {
15369         if (IfMagickTrue(display_image->debug) )
15370           (void) LogMagickEvent(X11Event,GetMagickModule(),
15371             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15372             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15373         break;
15374       }
15375       case ClientMessage:
15376       {
15377         if (IfMagickTrue(display_image->debug) )
15378           (void) LogMagickEvent(X11Event,GetMagickModule(),
15379             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15380             event.xclient.message_type,event.xclient.format,(unsigned long)
15381             event.xclient.data.l[0]);
15382         if (event.xclient.message_type == windows->im_protocols)
15383           {
15384             if (*event.xclient.data.l == (long) windows->im_update_widget)
15385               {
15386                 (void) CloneString(&windows->command.name,MagickTitle);
15387                 windows->command.data=MagickMenus;
15388                 (void) XCommandWidget(display,windows,CommandMenu,
15389                   (XEvent *) NULL);
15390                 break;
15391               }
15392             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15393               {
15394                 /*
15395                   Update graphic context and window colormap.
15396                 */
15397                 for (i=0; i < (int) number_windows; i++)
15398                 {
15399                   if (magick_windows[i]->id == windows->icon.id)
15400                     continue;
15401                   context_values.background=pixel->background_color.pixel;
15402                   context_values.foreground=pixel->foreground_color.pixel;
15403                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15404                     context_mask,&context_values);
15405                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15406                     context_mask,&context_values);
15407                   context_values.background=pixel->foreground_color.pixel;
15408                   context_values.foreground=pixel->background_color.pixel;
15409                   context_values.plane_mask=context_values.background ^
15410                     context_values.foreground;
15411                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15412                     (size_t) (context_mask | GCPlaneMask),
15413                     &context_values);
15414                   magick_windows[i]->attributes.background_pixel=
15415                     pixel->background_color.pixel;
15416                   magick_windows[i]->attributes.border_pixel=
15417                     pixel->border_color.pixel;
15418                   magick_windows[i]->attributes.colormap=map_info->colormap;
15419                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15420                     (unsigned long) magick_windows[i]->mask,
15421                     &magick_windows[i]->attributes);
15422                 }
15423                 if (IfMagickTrue(windows->pan.mapped) )
15424                   {
15425                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15426                       windows->pan.pixmap);
15427                     (void) XClearWindow(display,windows->pan.id);
15428                     XDrawPanRectangle(display,windows);
15429                   }
15430                 if (windows->backdrop.id != (Window) NULL)
15431                   (void) XInstallColormap(display,map_info->colormap);
15432                 break;
15433               }
15434             if (*event.xclient.data.l == (long) windows->im_former_image)
15435               {
15436                 *state|=FormerImageState | ExitState;
15437                 break;
15438               }
15439             if (*event.xclient.data.l == (long) windows->im_next_image)
15440               {
15441                 *state|=NextImageState | ExitState;
15442                 break;
15443               }
15444             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15445               {
15446                 *state|=RetainColorsState;
15447                 break;
15448               }
15449             if (*event.xclient.data.l == (long) windows->im_exit)
15450               {
15451                 *state|=ExitState;
15452                 break;
15453               }
15454             break;
15455           }
15456         if (event.xclient.message_type == windows->dnd_protocols)
15457           {
15458             Atom
15459               selection,
15460               type;
15461
15462             int
15463               format,
15464               status;
15465
15466             unsigned char
15467               *data;
15468
15469             unsigned long
15470               after,
15471               length;
15472
15473             /*
15474               Display image named by the Drag-and-Drop selection.
15475             */
15476             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15477               break;
15478             selection=XInternAtom(display,"DndSelection",MagickFalse);
15479             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15480               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15481               &length,&after,&data);
15482             if ((status != Success) || (length == 0))
15483               break;
15484             if (*event.xclient.data.l == 2)
15485               {
15486                 /*
15487                   Offix DND.
15488                 */
15489                 (void) CopyMagickString(resource_info->image_info->filename,
15490                   (char *) data,MaxTextExtent);
15491               }
15492             else
15493               {
15494                 /*
15495                   XDND.
15496                 */
15497                 if (strncmp((char *) data, "file:", 5) != 0)
15498                   {
15499                     (void) XFree((void *) data);
15500                     break;
15501                   }
15502                 (void) CopyMagickString(resource_info->image_info->filename,
15503                   ((char *) data)+5,MaxTextExtent);
15504               }
15505             nexus=ReadImage(resource_info->image_info,exception);
15506             CatchException(exception);
15507             if (nexus != (Image *) NULL)
15508               *state|=NextImageState | ExitState;
15509             (void) XFree((void *) data);
15510             break;
15511           }
15512         /*
15513           If client window delete message, exit.
15514         */
15515         if (event.xclient.message_type != windows->wm_protocols)
15516           break;
15517         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15518           break;
15519         (void) XWithdrawWindow(display,event.xclient.window,
15520           visual_info->screen);
15521         if (event.xclient.window == windows->image.id)
15522           {
15523             *state|=ExitState;
15524             break;
15525           }
15526         if (event.xclient.window == windows->pan.id)
15527           {
15528             /*
15529               Restore original image size when pan window is deleted.
15530             */
15531             windows->image.window_changes.width=windows->image.ximage->width;
15532             windows->image.window_changes.height=windows->image.ximage->height;
15533             (void) XConfigureImage(display,resource_info,windows,
15534               display_image,exception);
15535           }
15536         break;
15537       }
15538       case ConfigureNotify:
15539       {
15540         if (IfMagickTrue(display_image->debug) )
15541           (void) LogMagickEvent(X11Event,GetMagickModule(),
15542             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15543             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15544             event.xconfigure.y,event.xconfigure.send_event);
15545         if (event.xconfigure.window == windows->image.id)
15546           {
15547             /*
15548               Image window has a new configuration.
15549             */
15550             if (event.xconfigure.send_event != 0)
15551               {
15552                 XWindowChanges
15553                   window_changes;
15554
15555                 /*
15556                   Position the transient windows relative of the Image window.
15557                 */
15558                 if (windows->command.geometry == (char *) NULL)
15559                   if (IfMagickFalse(windows->command.mapped) )
15560                     {
15561                       windows->command.x=event.xconfigure.x-
15562                         windows->command.width-25;
15563                       windows->command.y=event.xconfigure.y;
15564                       XConstrainWindowPosition(display,&windows->command);
15565                       window_changes.x=windows->command.x;
15566                       window_changes.y=windows->command.y;
15567                       (void) XReconfigureWMWindow(display,windows->command.id,
15568                         windows->command.screen,(unsigned int) (CWX | CWY),
15569                         &window_changes);
15570                     }
15571                 if (windows->widget.geometry == (char *) NULL)
15572                   if (IfMagickFalse(windows->widget.mapped) )
15573                     {
15574                       windows->widget.x=event.xconfigure.x+
15575                         event.xconfigure.width/10;
15576                       windows->widget.y=event.xconfigure.y+
15577                         event.xconfigure.height/10;
15578                       XConstrainWindowPosition(display,&windows->widget);
15579                       window_changes.x=windows->widget.x;
15580                       window_changes.y=windows->widget.y;
15581                       (void) XReconfigureWMWindow(display,windows->widget.id,
15582                         windows->widget.screen,(unsigned int) (CWX | CWY),
15583                         &window_changes);
15584                     }
15585                 if (windows->magnify.geometry == (char *) NULL)
15586                   if (IfMagickFalse(windows->magnify.mapped) )
15587                     {
15588                       windows->magnify.x=event.xconfigure.x+
15589                         event.xconfigure.width+25;
15590                       windows->magnify.y=event.xconfigure.y;
15591                       XConstrainWindowPosition(display,&windows->magnify);
15592                       window_changes.x=windows->magnify.x;
15593                       window_changes.y=windows->magnify.y;
15594                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15595                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15596                         &window_changes);
15597                     }
15598                 if (windows->pan.geometry == (char *) NULL)
15599                   if (IfMagickFalse(windows->pan.mapped) )
15600                     {
15601                       windows->pan.x=event.xconfigure.x+
15602                         event.xconfigure.width+25;
15603                       windows->pan.y=event.xconfigure.y+
15604                         windows->magnify.height+50;
15605                       XConstrainWindowPosition(display,&windows->pan);
15606                       window_changes.x=windows->pan.x;
15607                       window_changes.y=windows->pan.y;
15608                       (void) XReconfigureWMWindow(display,windows->pan.id,
15609                         windows->pan.screen,(unsigned int) (CWX | CWY),
15610                         &window_changes);
15611                     }
15612               }
15613             if ((event.xconfigure.width == (int) windows->image.width) &&
15614                 (event.xconfigure.height == (int) windows->image.height))
15615               break;
15616             windows->image.width=(unsigned int) event.xconfigure.width;
15617             windows->image.height=(unsigned int) event.xconfigure.height;
15618             windows->image.x=0;
15619             windows->image.y=0;
15620             if (display_image->montage != (char *) NULL)
15621               {
15622                 windows->image.x=vid_info.x;
15623                 windows->image.y=vid_info.y;
15624               }
15625             if (IfMagickTrue(windows->image.mapped) &&
15626                 IfMagickTrue(windows->image.stasis) )
15627               {
15628                 /*
15629                   Update image window configuration.
15630                 */
15631                 windows->image.window_changes.width=event.xconfigure.width;
15632                 windows->image.window_changes.height=event.xconfigure.height;
15633                 (void) XConfigureImage(display,resource_info,windows,
15634                   display_image,exception);
15635               }
15636             /*
15637               Update pan window configuration.
15638             */
15639             if ((event.xconfigure.width < windows->image.ximage->width) ||
15640                 (event.xconfigure.height < windows->image.ximage->height))
15641               {
15642                 (void) XMapRaised(display,windows->pan.id);
15643                 XDrawPanRectangle(display,windows);
15644               }
15645             else
15646               if (IfMagickTrue(windows->pan.mapped) )
15647                 (void) XWithdrawWindow(display,windows->pan.id,
15648                   windows->pan.screen);
15649             break;
15650           }
15651         if (event.xconfigure.window == windows->magnify.id)
15652           {
15653             unsigned int
15654               magnify;
15655
15656             /*
15657               Magnify window has a new configuration.
15658             */
15659             windows->magnify.width=(unsigned int) event.xconfigure.width;
15660             windows->magnify.height=(unsigned int) event.xconfigure.height;
15661             if (IfMagickFalse(windows->magnify.mapped) )
15662               break;
15663             magnify=1;
15664             while ((int) magnify <= event.xconfigure.width)
15665               magnify<<=1;
15666             while ((int) magnify <= event.xconfigure.height)
15667               magnify<<=1;
15668             magnify>>=1;
15669             if (((int) magnify != event.xconfigure.width) ||
15670                 ((int) magnify != event.xconfigure.height))
15671               {
15672                 window_changes.width=(int) magnify;
15673                 window_changes.height=(int) magnify;
15674                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15675                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15676                   &window_changes);
15677                 break;
15678               }
15679             if (IfMagickTrue(windows->magnify.mapped) &&
15680                 IfMagickTrue(windows->magnify.stasis) )
15681               {
15682                 status=XMakeImage(display,resource_info,&windows->magnify,
15683                   display_image,windows->magnify.width,windows->magnify.height,
15684                   exception);
15685                 XMakeMagnifyImage(display,windows,exception);
15686               }
15687             break;
15688           }
15689         if (IfMagickTrue(windows->magnify.mapped) &&
15690             (event.xconfigure.window == windows->pan.id))
15691           {
15692             /*
15693               Pan icon window has a new configuration.
15694             */
15695             if (event.xconfigure.send_event != 0)
15696               {
15697                 windows->pan.x=event.xconfigure.x;
15698                 windows->pan.y=event.xconfigure.y;
15699               }
15700             windows->pan.width=(unsigned int) event.xconfigure.width;
15701             windows->pan.height=(unsigned int) event.xconfigure.height;
15702             break;
15703           }
15704         if (event.xconfigure.window == windows->icon.id)
15705           {
15706             /*
15707               Icon window has a new configuration.
15708             */
15709             windows->icon.width=(unsigned int) event.xconfigure.width;
15710             windows->icon.height=(unsigned int) event.xconfigure.height;
15711             break;
15712           }
15713         break;
15714       }
15715       case DestroyNotify:
15716       {
15717         /*
15718           Group leader has exited.
15719         */
15720         if (IfMagickTrue(display_image->debug) )
15721           (void) LogMagickEvent(X11Event,GetMagickModule(),
15722             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15723         if (event.xdestroywindow.window == windows->group_leader.id)
15724           {
15725             *state|=ExitState;
15726             break;
15727           }
15728         break;
15729       }
15730       case EnterNotify:
15731       {
15732         /*
15733           Selectively install colormap.
15734         */
15735         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15736           if (event.xcrossing.mode != NotifyUngrab)
15737             XInstallColormap(display,map_info->colormap);
15738         break;
15739       }
15740       case Expose:
15741       {
15742         if (IfMagickTrue(display_image->debug) )
15743           (void) LogMagickEvent(X11Event,GetMagickModule(),
15744             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15745             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15746             event.xexpose.y);
15747         /*
15748           Refresh windows that are now exposed.
15749         */
15750         if ((event.xexpose.window == windows->image.id) &&
15751             IfMagickTrue(windows->image.mapped) )
15752           {
15753             XRefreshWindow(display,&windows->image,&event);
15754             delay=display_image->delay/MagickMax(
15755               display_image->ticks_per_second,1L);
15756             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15757             break;
15758           }
15759         if ((event.xexpose.window == windows->magnify.id) &&
15760             IfMagickTrue(windows->magnify.mapped))
15761           {
15762             XMakeMagnifyImage(display,windows,exception);
15763             break;
15764           }
15765         if (event.xexpose.window == windows->pan.id)
15766           {
15767             XDrawPanRectangle(display,windows);
15768             break;
15769           }
15770         if (event.xexpose.window == windows->icon.id)
15771           {
15772             XRefreshWindow(display,&windows->icon,&event);
15773             break;
15774           }
15775         break;
15776       }
15777       case KeyPress:
15778       {
15779         int
15780           length;
15781
15782         /*
15783           Respond to a user key press.
15784         */
15785         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15786           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15787         *(command+length)='\0';
15788         if (IfMagickTrue(display_image->debug) )
15789           (void) LogMagickEvent(X11Event,GetMagickModule(),
15790             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15791             key_symbol,command);
15792         if (event.xkey.window == windows->image.id)
15793           {
15794             command_type=XImageWindowCommand(display,resource_info,windows,
15795               event.xkey.state,key_symbol,&display_image,exception);
15796             if (command_type != NullCommand)
15797               nexus=XMagickCommand(display,resource_info,windows,command_type,
15798                 &display_image,exception);
15799           }
15800         if (event.xkey.window == windows->magnify.id)
15801           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15802             exception);
15803         if (event.xkey.window == windows->pan.id)
15804           {
15805             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15806               (void) XWithdrawWindow(display,windows->pan.id,
15807                 windows->pan.screen);
15808             else
15809               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15810                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15811                   "Help Viewer - Image Pan",ImagePanHelp);
15812               else
15813                 XTranslateImage(display,windows,*image,key_symbol);
15814           }
15815         delay=display_image->delay/MagickMax(
15816           display_image->ticks_per_second,1L);
15817         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15818         break;
15819       }
15820       case KeyRelease:
15821       {
15822         /*
15823           Respond to a user key release.
15824         */
15825         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15826           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15827         if (IfMagickTrue(display_image->debug) )
15828           (void) LogMagickEvent(X11Event,GetMagickModule(),
15829             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15830         break;
15831       }
15832       case LeaveNotify:
15833       {
15834         /*
15835           Selectively uninstall colormap.
15836         */
15837         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15838           if (event.xcrossing.mode != NotifyUngrab)
15839             XUninstallColormap(display,map_info->colormap);
15840         break;
15841       }
15842       case MapNotify:
15843       {
15844         if (IfMagickTrue(display_image->debug) )
15845           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15846             event.xmap.window);
15847         if (event.xmap.window == windows->backdrop.id)
15848           {
15849             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15850               CurrentTime);
15851             windows->backdrop.mapped=MagickTrue;
15852             break;
15853           }
15854         if (event.xmap.window == windows->image.id)
15855           {
15856             if (windows->backdrop.id != (Window) NULL)
15857               (void) XInstallColormap(display,map_info->colormap);
15858             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15859               {
15860                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15861                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15862               }
15863             if (((int) windows->image.width < windows->image.ximage->width) ||
15864                 ((int) windows->image.height < windows->image.ximage->height))
15865               (void) XMapRaised(display,windows->pan.id);
15866             windows->image.mapped=MagickTrue;
15867             break;
15868           }
15869         if (event.xmap.window == windows->magnify.id)
15870           {
15871             XMakeMagnifyImage(display,windows,exception);
15872             windows->magnify.mapped=MagickTrue;
15873             (void) XWithdrawWindow(display,windows->info.id,
15874               windows->info.screen);
15875             break;
15876           }
15877         if (event.xmap.window == windows->pan.id)
15878           {
15879             XMakePanImage(display,resource_info,windows,display_image,
15880               exception);
15881             windows->pan.mapped=MagickTrue;
15882             break;
15883           }
15884         if (event.xmap.window == windows->info.id)
15885           {
15886             windows->info.mapped=MagickTrue;
15887             break;
15888           }
15889         if (event.xmap.window == windows->icon.id)
15890           {
15891             MagickBooleanType
15892               taint;
15893
15894             /*
15895               Create an icon image.
15896             */
15897             taint=display_image->taint;
15898             XMakeStandardColormap(display,icon_visual,icon_resources,
15899               display_image,icon_map,icon_pixel,exception);
15900             (void) XMakeImage(display,icon_resources,&windows->icon,
15901               display_image,windows->icon.width,windows->icon.height,
15902               exception);
15903             display_image->taint=taint;
15904             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15905               windows->icon.pixmap);
15906             (void) XClearWindow(display,windows->icon.id);
15907             (void) XWithdrawWindow(display,windows->info.id,
15908               windows->info.screen);
15909             windows->icon.mapped=MagickTrue;
15910             break;
15911           }
15912         if (event.xmap.window == windows->command.id)
15913           {
15914             windows->command.mapped=MagickTrue;
15915             break;
15916           }
15917         if (event.xmap.window == windows->popup.id)
15918           {
15919             windows->popup.mapped=MagickTrue;
15920             break;
15921           }
15922         if (event.xmap.window == windows->widget.id)
15923           {
15924             windows->widget.mapped=MagickTrue;
15925             break;
15926           }
15927         break;
15928       }
15929       case MappingNotify:
15930       {
15931         (void) XRefreshKeyboardMapping(&event.xmapping);
15932         break;
15933       }
15934       case NoExpose:
15935         break;
15936       case PropertyNotify:
15937       {
15938         Atom
15939           type;
15940
15941         int
15942           format,
15943           status;
15944
15945         unsigned char
15946           *data;
15947
15948         unsigned long
15949           after,
15950           length;
15951
15952         if (IfMagickTrue(display_image->debug) )
15953           (void) LogMagickEvent(X11Event,GetMagickModule(),
15954             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15955             event.xproperty.atom,event.xproperty.state);
15956         if (event.xproperty.atom != windows->im_remote_command)
15957           break;
15958         /*
15959           Display image named by the remote command protocol.
15960         */
15961         status=XGetWindowProperty(display,event.xproperty.window,
15962           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15963           AnyPropertyType,&type,&format,&length,&after,&data);
15964         if ((status != Success) || (length == 0))
15965           break;
15966         if (LocaleCompare((char *) data,"-quit") == 0)
15967           {
15968             XClientMessage(display,windows->image.id,windows->im_protocols,
15969               windows->im_exit,CurrentTime);
15970             (void) XFree((void *) data);
15971             break;
15972           }
15973         (void) CopyMagickString(resource_info->image_info->filename,
15974           (char *) data,MaxTextExtent);
15975         (void) XFree((void *) data);
15976         nexus=ReadImage(resource_info->image_info,exception);
15977         CatchException(exception);
15978         if (nexus != (Image *) NULL)
15979           *state|=NextImageState | ExitState;
15980         break;
15981       }
15982       case ReparentNotify:
15983       {
15984         if (IfMagickTrue(display_image->debug) )
15985           (void) LogMagickEvent(X11Event,GetMagickModule(),
15986             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15987             event.xreparent.window);
15988         break;
15989       }
15990       case UnmapNotify:
15991       {
15992         if (IfMagickTrue(display_image->debug) )
15993           (void) LogMagickEvent(X11Event,GetMagickModule(),
15994             "Unmap Notify: 0x%lx",event.xunmap.window);
15995         if (event.xunmap.window == windows->backdrop.id)
15996           {
15997             windows->backdrop.mapped=MagickFalse;
15998             break;
15999           }
16000         if (event.xunmap.window == windows->image.id)
16001           {
16002             windows->image.mapped=MagickFalse;
16003             break;
16004           }
16005         if (event.xunmap.window == windows->magnify.id)
16006           {
16007             windows->magnify.mapped=MagickFalse;
16008             break;
16009           }
16010         if (event.xunmap.window == windows->pan.id)
16011           {
16012             windows->pan.mapped=MagickFalse;
16013             break;
16014           }
16015         if (event.xunmap.window == windows->info.id)
16016           {
16017             windows->info.mapped=MagickFalse;
16018             break;
16019           }
16020         if (event.xunmap.window == windows->icon.id)
16021           {
16022             if (map_info->colormap == icon_map->colormap)
16023               XConfigureImageColormap(display,resource_info,windows,
16024                 display_image,exception);
16025             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16026               icon_pixel);
16027             windows->icon.mapped=MagickFalse;
16028             break;
16029           }
16030         if (event.xunmap.window == windows->command.id)
16031           {
16032             windows->command.mapped=MagickFalse;
16033             break;
16034           }
16035         if (event.xunmap.window == windows->popup.id)
16036           {
16037             if (windows->backdrop.id != (Window) NULL)
16038               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16039                 CurrentTime);
16040             windows->popup.mapped=MagickFalse;
16041             break;
16042           }
16043         if (event.xunmap.window == windows->widget.id)
16044           {
16045             if (windows->backdrop.id != (Window) NULL)
16046               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16047                 CurrentTime);
16048             windows->widget.mapped=MagickFalse;
16049             break;
16050           }
16051         break;
16052       }
16053       default:
16054       {
16055         if (IfMagickTrue(display_image->debug) )
16056           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16057             event.type);
16058         break;
16059       }
16060     }
16061   } while (!(*state & ExitState));
16062   if ((*state & ExitState) == 0)
16063     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16064       &display_image,exception);
16065   else
16066     if (IfMagickTrue(resource_info->confirm_edit) )
16067       {
16068         /*
16069           Query user if image has changed.
16070         */
16071         if (IfMagickFalse(resource_info->immutable) &&
16072             IfMagickTrue(display_image->taint))
16073           {
16074             int
16075               status;
16076
16077             status=XConfirmWidget(display,windows,"Your image changed.",
16078               "Do you want to save it");
16079             if (status == 0)
16080               *state&=(~ExitState);
16081             else
16082               if (status > 0)
16083                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16084                   &display_image,exception);
16085           }
16086       }
16087   if ((windows->visual_info->klass == GrayScale) ||
16088       (windows->visual_info->klass == PseudoColor) ||
16089       (windows->visual_info->klass == DirectColor))
16090     {
16091       /*
16092         Withdraw pan and Magnify window.
16093       */
16094       if (IfMagickTrue(windows->info.mapped) )
16095         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16096       if (IfMagickTrue(windows->magnify.mapped) )
16097         (void) XWithdrawWindow(display,windows->magnify.id,
16098           windows->magnify.screen);
16099       if (IfMagickTrue(windows->command.mapped) )
16100         (void) XWithdrawWindow(display,windows->command.id,
16101           windows->command.screen);
16102     }
16103   if (IfMagickTrue(windows->pan.mapped) )
16104     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16105   if (IfMagickFalse(resource_info->backdrop) )
16106     if (windows->backdrop.mapped)
16107       {
16108         (void) XWithdrawWindow(display,windows->backdrop.id,
16109           windows->backdrop.screen);
16110         (void) XDestroyWindow(display,windows->backdrop.id);
16111         windows->backdrop.id=(Window) NULL;
16112         (void) XWithdrawWindow(display,windows->image.id,
16113           windows->image.screen);
16114         (void) XDestroyWindow(display,windows->image.id);
16115         windows->image.id=(Window) NULL;
16116       }
16117   XSetCursorState(display,windows,MagickTrue);
16118   XCheckRefreshWindows(display,windows);
16119   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16120     *state&=(~ExitState);
16121   if (*state & ExitState)
16122     {
16123       /*
16124         Free Standard Colormap.
16125       */
16126       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16127       if (resource_info->map_type == (char *) NULL)
16128         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16129       /*
16130         Free X resources.
16131       */
16132       if (resource_info->copy_image != (Image *) NULL)
16133         {
16134           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16135           resource_info->copy_image=NewImageList();
16136         }
16137       DestroyXResources();
16138     }
16139   (void) XSync(display,MagickFalse);
16140   /*
16141     Restore our progress monitor and warning handlers.
16142   */
16143   (void) SetErrorHandler(warning_handler);
16144   (void) SetWarningHandler(warning_handler);
16145   /*
16146     Change to home directory.
16147   */
16148   directory=getcwd(working_directory,MaxTextExtent);
16149   (void) directory;
16150   {
16151     int
16152       status;
16153
16154     if (*resource_info->home_directory == '\0')
16155       (void) CopyMagickString(resource_info->home_directory,".",MaxTextExtent);
16156     status=chdir(resource_info->home_directory);
16157     if (status == -1)
16158       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16159         "UnableToOpenFile","%s",resource_info->home_directory);
16160   }
16161   *image=display_image;
16162   return(nexus);
16163 }
16164 #else
16165 \f
16166 /*
16167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16168 %                                                                             %
16169 %                                                                             %
16170 %                                                                             %
16171 +   D i s p l a y I m a g e s                                                 %
16172 %                                                                             %
16173 %                                                                             %
16174 %                                                                             %
16175 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16176 %
16177 %  DisplayImages() displays an image sequence to any X window screen.  It
16178 %  returns a value other than 0 if successful.  Check the exception member
16179 %  of image to determine the reason for any failure.
16180 %
16181 %  The format of the DisplayImages method is:
16182 %
16183 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16184 %        Image *images,ExceptionInfo *exception)
16185 %
16186 %  A description of each parameter follows:
16187 %
16188 %    o image_info: the image info.
16189 %
16190 %    o image: the image.
16191 %
16192 %    o exception: return any errors or warnings in this structure.
16193 %
16194 */
16195 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16196   Image *image,ExceptionInfo *exception)
16197 {
16198   assert(image_info != (const ImageInfo *) NULL);
16199   assert(image_info->signature == MagickSignature);
16200   assert(image != (Image *) NULL);
16201   assert(image->signature == MagickSignature);
16202   (void) image_info;
16203   if (IfMagickTrue(image->debug) )
16204     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16205   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16206     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16207   return(MagickFalse);
16208 }
16209 \f
16210 /*
16211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16212 %                                                                             %
16213 %                                                                             %
16214 %                                                                             %
16215 +   R e m o t e D i s p l a y C o m m a n d                                   %
16216 %                                                                             %
16217 %                                                                             %
16218 %                                                                             %
16219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16220 %
16221 %  RemoteDisplayCommand() encourages a remote display program to display the
16222 %  specified image filename.
16223 %
16224 %  The format of the RemoteDisplayCommand method is:
16225 %
16226 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16227 %        const char *window,const char *filename,ExceptionInfo *exception)
16228 %
16229 %  A description of each parameter follows:
16230 %
16231 %    o image_info: the image info.
16232 %
16233 %    o window: Specifies the name or id of an X window.
16234 %
16235 %    o filename: the name of the image filename to display.
16236 %
16237 %    o exception: return any errors or warnings in this structure.
16238 %
16239 */
16240 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16241   const char *window,const char *filename,ExceptionInfo *exception)
16242 {
16243   assert(image_info != (const ImageInfo *) NULL);
16244   assert(image_info->signature == MagickSignature);
16245   assert(filename != (char *) NULL);
16246   (void) window;
16247   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16248   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16249     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16250   return(MagickFalse);
16251 }
16252 #endif