]> granicus.if.org Git - imagemagick/blob - MagickCore/display.c
(no commit message)
[imagemagick] / MagickCore / display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7 %               D   D    I    SS     P   P  L      A   A   Y Y                %
8 %               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9 %               D   D    I       SS  P      L      A   A    Y                 %
10 %               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11 %                                                                             %
12 %                                                                             %
13 %        MagickCore Methods to Interactively Display and Edit an Image        %
14 %                                                                             %
15 %                             Software Design                                 %
16 %                               John Cristy                                   %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-private.h"
47 #include "MagickCore/client.h"
48 #include "MagickCore/color.h"
49 #include "MagickCore/colorspace.h"
50 #include "MagickCore/composite.h"
51 #include "MagickCore/constitute.h"
52 #include "MagickCore/decorate.h"
53 #include "MagickCore/delegate.h"
54 #include "MagickCore/display.h"
55 #include "MagickCore/display-private.h"
56 #include "MagickCore/distort.h"
57 #include "MagickCore/draw.h"
58 #include "MagickCore/effect.h"
59 #include "MagickCore/enhance.h"
60 #include "MagickCore/exception.h"
61 #include "MagickCore/exception-private.h"
62 #include "MagickCore/fx.h"
63 #include "MagickCore/geometry.h"
64 #include "MagickCore/image.h"
65 #include "MagickCore/image-private.h"
66 #include "MagickCore/list.h"
67 #include "MagickCore/log.h"
68 #include "MagickCore/magick.h"
69 #include "MagickCore/memory_.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/montage.h"
73 #include "MagickCore/option.h"
74 #include "MagickCore/paint.h"
75 #include "MagickCore/pixel.h"
76 #include "MagickCore/pixel-accessor.h"
77 #include "MagickCore/PreRvIcccm.h"
78 #include "MagickCore/property.h"
79 #include "MagickCore/quantum.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/resize.h"
82 #include "MagickCore/resource_.h"
83 #include "MagickCore/shear.h"
84 #include "MagickCore/segment.h"
85 #include "MagickCore/statistic.h"
86 #include "MagickCore/string_.h"
87 #include "MagickCore/string-private.h"
88 #include "MagickCore/transform.h"
89 #include "MagickCore/threshold.h"
90 #include "MagickCore/utility.h"
91 #include "MagickCore/utility-private.h"
92 #include "MagickCore/version.h"
93 #include "MagickCore/widget.h"
94 #include "MagickCore/widget-private.h"
95 #include "MagickCore/xwindow.h"
96 #include "MagickCore/xwindow-private.h"
97 \f
98 #if defined(MAGICKCORE_X11_DELEGATE)
99 /*
100   Define declarations.
101 */
102 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
103 \f
104 /*
105   Constant declarations.
106 */
107 static const unsigned char
108   HighlightBitmap[8] =
109   {
110     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
111   },
112   OpaqueBitmap[8] =
113   {
114     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
115   },
116   ShadowBitmap[8] =
117   {
118     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
119   };
120
121 static const char
122   *PageSizes[] =
123   {
124     "Letter",
125     "Tabloid",
126     "Ledger",
127     "Legal",
128     "Statement",
129     "Executive",
130     "A3",
131     "A4",
132     "A5",
133     "B4",
134     "B5",
135     "Folio",
136     "Quarto",
137     "10x14",
138     (char *) NULL
139   };
140 \f
141 /*
142   Help widget declarations.
143 */
144 static const char
145   *ImageAnnotateHelp[] =
146   {
147     "In annotate mode, the Command widget has these options:",
148     "",
149     "    Font Name",
150     "      fixed",
151     "      variable",
152     "      5x8",
153     "      6x10",
154     "      7x13bold",
155     "      8x13bold",
156     "      9x15bold",
157     "      10x20",
158     "      12x24",
159     "      Browser...",
160     "    Font Color",
161     "      black",
162     "      blue",
163     "      cyan",
164     "      green",
165     "      gray",
166     "      red",
167     "      magenta",
168     "      yellow",
169     "      white",
170     "      transparent",
171     "      Browser...",
172     "    Font Color",
173     "      black",
174     "      blue",
175     "      cyan",
176     "      green",
177     "      gray",
178     "      red",
179     "      magenta",
180     "      yellow",
181     "      white",
182     "      transparent",
183     "      Browser...",
184     "    Rotate Text",
185     "      -90",
186     "      -45",
187     "      -30",
188     "      0",
189     "      30",
190     "      45",
191     "      90",
192     "      180",
193     "      Dialog...",
194     "    Help",
195     "    Dismiss",
196     "",
197     "Choose a font name from the Font Name sub-menu.  Additional",
198     "font names can be specified with the font browser.  You can",
199     "change the menu names by setting the X resources font1",
200     "through font9.",
201     "",
202     "Choose a font color from the Font Color sub-menu.",
203     "Additional font colors can be specified with the color",
204     "browser.  You can change the menu colors by setting the X",
205     "resources pen1 through pen9.",
206     "",
207     "If you select the color browser and press Grab, you can",
208     "choose the font color by moving the pointer to the desired",
209     "color on the screen and press any button.",
210     "",
211     "If you choose to rotate the text, choose Rotate Text from the",
212     "menu and select an angle.  Typically you will only want to",
213     "rotate one line of text at a time.  Depending on the angle you",
214     "choose, subsequent lines may end up overwriting each other.",
215     "",
216     "Choosing a font and its color is optional.  The default font",
217     "is fixed and the default color is black.  However, you must",
218     "choose a location to begin entering text and press button 1.",
219     "An underscore character will appear at the location of the",
220     "pointer.  The cursor changes to a pencil to indicate you are",
221     "in text mode.  To exit immediately, press Dismiss.",
222     "",
223     "In text mode, any key presses will display the character at",
224     "the location of the underscore and advance the underscore",
225     "cursor.  Enter your text and once completed press Apply to",
226     "finish your image annotation.  To correct errors press BACK",
227     "SPACE.  To delete an entire line of text, press DELETE.  Any",
228     "text that exceeds the boundaries of the image window is",
229     "automagically continued onto the next line.",
230     "",
231     "The actual color you request for the font is saved in the",
232     "image.  However, the color that appears in your image window",
233     "may be different.  For example, on a monochrome screen the",
234     "text will appear black or white even if you choose the color",
235     "red as the font color.  However, the image saved to a file",
236     "with -write is written with red lettering.  To assure the",
237     "correct color text in the final image, any PseudoClass image",
238     "is promoted to DirectClass (see miff(5)).  To force a",
239     "PseudoClass image to remain PseudoClass, use -colors.",
240     (char *) NULL,
241   },
242   *ImageChopHelp[] =
243   {
244     "In chop mode, the Command widget has these options:",
245     "",
246     "    Direction",
247     "      horizontal",
248     "      vertical",
249     "    Help",
250     "    Dismiss",
251     "",
252     "If the you choose the horizontal direction (this the",
253     "default), the area of the image between the two horizontal",
254     "endpoints of the chop line is removed.  Otherwise, the area",
255     "of the image between the two vertical endpoints of the chop",
256     "line is removed.",
257     "",
258     "Select a location within the image window to begin your chop,",
259     "press and hold any button.  Next, move the pointer to",
260     "another location in the image.  As you move a line will",
261     "connect the initial location and the pointer.  When you",
262     "release the button, the area within the image to chop is",
263     "determined by which direction you choose from the Command",
264     "widget.",
265     "",
266     "To cancel the image chopping, move the pointer back to the",
267     "starting point of the line and release the button.",
268     (char *) NULL,
269   },
270   *ImageColorEditHelp[] =
271   {
272     "In color edit mode, the Command widget has these options:",
273     "",
274     "    Method",
275     "      point",
276     "      replace",
277     "      floodfill",
278     "      filltoborder",
279     "      reset",
280     "    Pixel Color",
281     "      black",
282     "      blue",
283     "      cyan",
284     "      green",
285     "      gray",
286     "      red",
287     "      magenta",
288     "      yellow",
289     "      white",
290     "      Browser...",
291     "    Border Color",
292     "      black",
293     "      blue",
294     "      cyan",
295     "      green",
296     "      gray",
297     "      red",
298     "      magenta",
299     "      yellow",
300     "      white",
301     "      Browser...",
302     "    Fuzz",
303     "      0%",
304     "      2%",
305     "      5%",
306     "      10%",
307     "      15%",
308     "      Dialog...",
309     "    Undo",
310     "    Help",
311     "    Dismiss",
312     "",
313     "Choose a color editing method from the Method sub-menu",
314     "of the Command widget.  The point method recolors any pixel",
315     "selected with the pointer until the button is released.  The",
316     "replace method recolors any pixel that matches the color of",
317     "the pixel you select with a button press.  Floodfill recolors",
318     "any pixel that matches the color of the pixel you select with",
319     "a button press and is a neighbor.  Whereas filltoborder recolors",
320     "any neighbor pixel that is not the border color.  Finally reset",
321     "changes the entire image to the designated color.",
322     "",
323     "Next, choose a pixel color from the Pixel Color sub-menu.",
324     "Additional pixel colors can be specified with the color",
325     "browser.  You can change the menu colors by setting the X",
326     "resources pen1 through pen9.",
327     "",
328     "Now press button 1 to select a pixel within the image window",
329     "to change its color.  Additional pixels may be recolored as",
330     "prescribed by the method you choose.",
331     "",
332     "If the Magnify widget is mapped, it can be helpful in positioning",
333     "your pointer within the image (refer to button 2).",
334     "",
335     "The actual color you request for the pixels is saved in the",
336     "image.  However, the color that appears in your image window",
337     "may be different.  For example, on a monochrome screen the",
338     "pixel will appear black or white even if you choose the",
339     "color red as the pixel color.  However, the image saved to a",
340     "file with -write is written with red pixels.  To assure the",
341     "correct color text in the final image, any PseudoClass image",
342     "is promoted to DirectClass (see miff(5)).  To force a",
343     "PseudoClass image to remain PseudoClass, use -colors.",
344     (char *) NULL,
345   },
346   *ImageCompositeHelp[] =
347   {
348     "First a widget window is displayed requesting you to enter an",
349     "image name. Press Composite, Grab or type a file name.",
350     "Press Cancel if you choose not to create a composite image.",
351     "When you choose Grab, move the pointer to the desired window",
352     "and press any button.",
353     "",
354     "If the Composite image does not have any matte information,",
355     "you are informed and the file browser is displayed again.",
356     "Enter the name of a mask image.  The image is typically",
357     "grayscale and the same size as the composite image.  If the",
358     "image is not grayscale, it is converted to grayscale and the",
359     "resulting intensities are used as matte information.",
360     "",
361     "A small window appears showing the location of the cursor in",
362     "the image window. You are now in composite mode.  To exit",
363     "immediately, press Dismiss.  In composite mode, the Command",
364     "widget has these options:",
365     "",
366     "    Operators",
367     "      Over",
368     "      In",
369     "      Out",
370     "      Atop",
371     "      Xor",
372     "      Plus",
373     "      Minus",
374     "      Add",
375     "      Subtract",
376     "      Difference",
377     "      Multiply",
378     "      Bumpmap",
379     "      Copy",
380     "      CopyRed",
381     "      CopyGreen",
382     "      CopyBlue",
383     "      CopyOpacity",
384     "      Clear",
385     "    Dissolve",
386     "    Displace",
387     "    Help",
388     "    Dismiss",
389     "",
390     "Choose a composite operation from the Operators sub-menu of",
391     "the Command widget.  How each operator behaves is described",
392     "below.  Image window is the image currently displayed on",
393     "your X server and image is the image obtained with the File",
394     "Browser widget.",
395     "",
396     "Over     The result is the union of the two image shapes,",
397     "         with image obscuring image window in the region of",
398     "         overlap.",
399     "",
400     "In       The result is simply image cut by the shape of",
401     "         image window.  None of the image data of image",
402     "         window is in the result.",
403     "",
404     "Out      The resulting image is image with the shape of",
405     "         image window cut out.",
406     "",
407     "Atop     The result is the same shape as image image window,",
408     "         with image obscuring image window where the image",
409     "         shapes overlap.  Note this differs from over",
410     "         because the portion of image outside image window's",
411     "         shape does not appear in the result.",
412     "",
413     "Xor      The result is the image data from both image and",
414     "         image window that is outside the overlap region.",
415     "         The overlap region is blank.",
416     "",
417     "Plus     The result is just the sum of the image data.",
418     "         Output values are cropped to QuantumRange (no overflow).",
419     "",
420     "Minus    The result of image - image window, with underflow",
421     "         cropped to zero.",
422     "",
423     "Add      The result of image + image window, with overflow",
424     "         wrapping around (mod 256).",
425     "",
426     "Subtract The result of image - image window, with underflow",
427     "         wrapping around (mod 256).  The add and subtract",
428     "         operators can be used to perform reversible",
429     "         transformations.",
430     "",
431     "Difference",
432     "         The result of abs(image - image window).  This",
433     "         useful for comparing two very similar images.",
434     "",
435     "Multiply",
436     "         The result of image * image window.  This",
437     "         useful for the creation of drop-shadows.",
438     "",
439     "Bumpmap  The result of surface normals from image * image",
440     "         window.",
441     "",
442     "Copy     The resulting image is image window replaced with",
443     "         image.  Here the matte information is ignored.",
444     "",
445     "CopyRed  The red layer of the image window is replace with",
446     "         the red layer of the image.  The other layers are",
447     "         untouched.",
448     "",
449     "CopyGreen",
450     "         The green layer of the image window is replace with",
451     "         the green layer of the image.  The other layers are",
452     "         untouched.",
453     "",
454     "CopyBlue The blue layer of the image window is replace with",
455     "         the blue layer of the image.  The other layers are",
456     "         untouched.",
457     "",
458     "CopyOpacity",
459     "         The matte layer of the image window is replace with",
460     "         the matte layer of the image.  The other layers are",
461     "         untouched.",
462     "",
463     "The image compositor requires a matte, or alpha channel in",
464     "the image for some operations.  This extra channel usually",
465     "defines a mask which represents a sort of a cookie-cutter",
466     "for the image.  This the case when matte is opaque (full",
467     "coverage) for pixels inside the shape, zero outside, and",
468     "between 0 and QuantumRange on the boundary.  If image does not",
469     "have a matte channel, it is initialized with 0 for any pixel",
470     "matching in color to pixel location (0,0), otherwise QuantumRange.",
471     "",
472     "If you choose Dissolve, the composite operator becomes Over.  The",
473     "image matte channel percent transparency is initialized to factor.",
474     "The image window is initialized to (100-factor). Where factor is the",
475     "value you specify in the Dialog widget.",
476     "",
477     "Displace shifts the image pixels as defined by a displacement",
478     "map.  With this option, image is used as a displacement map.",
479     "Black, within the displacement map, is a maximum positive",
480     "displacement.  White is a maximum negative displacement and",
481     "middle gray is neutral.  The displacement is scaled to determine",
482     "the pixel shift.  By default, the displacement applies in both the",
483     "horizontal and vertical directions.  However, if you specify a mask,",
484     "image is the horizontal X displacement and mask the vertical Y",
485     "displacement.",
486     "",
487     "Note that matte information for image window is not retained",
488     "for colormapped X server visuals (e.g. StaticColor,",
489     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
490     "behavior may require a TrueColor or DirectColor visual or a",
491     "Standard Colormap.",
492     "",
493     "Choosing a composite operator is optional.  The default",
494     "operator is replace.  However, you must choose a location to",
495     "composite your image and press button 1.  Press and hold the",
496     "button before releasing and an outline of the image will",
497     "appear to help you identify your location.",
498     "",
499     "The actual colors of the composite image is saved.  However,",
500     "the color that appears in image window may be different.",
501     "For example, on a monochrome screen image window will appear",
502     "black or white even though your composited image may have",
503     "many colors.  If the image is saved to a file it is written",
504     "with the correct colors.  To assure the correct colors are",
505     "saved in the final image, any PseudoClass image is promoted",
506     "to DirectClass (see miff(5)).  To force a PseudoClass image",
507     "to remain PseudoClass, use -colors.",
508     (char *) NULL,
509   },
510   *ImageCutHelp[] =
511   {
512     "In cut mode, the Command widget has these options:",
513     "",
514     "    Help",
515     "    Dismiss",
516     "",
517     "To define a cut region, press button 1 and drag.  The",
518     "cut region is defined by a highlighted rectangle that",
519     "expands or contracts as it follows the pointer.  Once you",
520     "are satisfied with the cut region, release the button.",
521     "You are now in rectify mode.  In rectify mode, the Command",
522     "widget has these options:",
523     "",
524     "    Cut",
525     "    Help",
526     "    Dismiss",
527     "",
528     "You can make adjustments by moving the pointer to one of the",
529     "cut rectangle corners, pressing a button, and dragging.",
530     "Finally, press Cut to commit your copy region.  To",
531     "exit without cutting the image, press Dismiss.",
532     (char *) NULL,
533   },
534   *ImageCopyHelp[] =
535   {
536     "In copy mode, the Command widget has these options:",
537     "",
538     "    Help",
539     "    Dismiss",
540     "",
541     "To define a copy region, press button 1 and drag.  The",
542     "copy region is defined by a highlighted rectangle that",
543     "expands or contracts as it follows the pointer.  Once you",
544     "are satisfied with the copy region, release the button.",
545     "You are now in rectify mode.  In rectify mode, the Command",
546     "widget has these options:",
547     "",
548     "    Copy",
549     "    Help",
550     "    Dismiss",
551     "",
552     "You can make adjustments by moving the pointer to one of the",
553     "copy rectangle corners, pressing a button, and dragging.",
554     "Finally, press Copy to commit your copy region.  To",
555     "exit without copying the image, press Dismiss.",
556     (char *) NULL,
557   },
558   *ImageCropHelp[] =
559   {
560     "In crop mode, the Command widget has these options:",
561     "",
562     "    Help",
563     "    Dismiss",
564     "",
565     "To define a cropping region, press button 1 and drag.  The",
566     "cropping region is defined by a highlighted rectangle that",
567     "expands or contracts as it follows the pointer.  Once you",
568     "are satisfied with the cropping region, release the button.",
569     "You are now in rectify mode.  In rectify mode, the Command",
570     "widget has these options:",
571     "",
572     "    Crop",
573     "    Help",
574     "    Dismiss",
575     "",
576     "You can make adjustments by moving the pointer to one of the",
577     "cropping rectangle corners, pressing a button, and dragging.",
578     "Finally, press Crop to commit your cropping region.  To",
579     "exit without cropping the image, press Dismiss.",
580     (char *) NULL,
581   },
582   *ImageDrawHelp[] =
583   {
584     "The cursor changes to a crosshair to indicate you are in",
585     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
586     "the Command widget has these options:",
587     "",
588     "    Element",
589     "      point",
590     "      line",
591     "      rectangle",
592     "      fill rectangle",
593     "      circle",
594     "      fill circle",
595     "      ellipse",
596     "      fill ellipse",
597     "      polygon",
598     "      fill polygon",
599     "    Color",
600     "      black",
601     "      blue",
602     "      cyan",
603     "      green",
604     "      gray",
605     "      red",
606     "      magenta",
607     "      yellow",
608     "      white",
609     "      transparent",
610     "      Browser...",
611     "    Stipple",
612     "      Brick",
613     "      Diagonal",
614     "      Scales",
615     "      Vertical",
616     "      Wavy",
617     "      Translucent",
618     "      Opaque",
619     "      Open...",
620     "    Width",
621     "      1",
622     "      2",
623     "      4",
624     "      8",
625     "      16",
626     "      Dialog...",
627     "    Undo",
628     "    Help",
629     "    Dismiss",
630     "",
631     "Choose a drawing primitive from the Element sub-menu.",
632     "",
633     "Choose a color from the Color sub-menu.  Additional",
634     "colors can be specified with the color browser.",
635     "",
636     "If you choose the color browser and press Grab, you can",
637     "select the color by moving the pointer to the desired",
638     "color on the screen and press any button.  The transparent",
639     "color updates the image matte channel and is useful for",
640     "image compositing.",
641     "",
642     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
643     "Additional stipples can be specified with the file browser.",
644     "Stipples obtained from the file browser must be on disk in the",
645     "X11 bitmap format.",
646     "",
647     "Choose a width, if appropriate, from the Width sub-menu.  To",
648     "choose a specific width select the Dialog widget.",
649     "",
650     "Choose a point in the Image window and press button 1 and",
651     "hold.  Next, move the pointer to another location in the",
652     "image.  As you move, a line connects the initial location and",
653     "the pointer.  When you release the button, the image is",
654     "updated with the primitive you just drew.  For polygons, the",
655     "image is updated when you press and release the button without",
656     "moving the pointer.",
657     "",
658     "To cancel image drawing, move the pointer back to the",
659     "starting point of the line and release the button.",
660     (char *) NULL,
661   },
662   *DisplayHelp[] =
663   {
664     "BUTTONS",
665     "  The effects of each button press is described below.  Three",
666     "  buttons are required.  If you have a two button mouse,",
667     "  button 1 and 3 are returned.  Press ALT and button 3 to",
668     "  simulate button 2.",
669     "",
670     "  1    Press this button to map or unmap the Command widget.",
671     "",
672     "  2    Press and drag to define a region of the image to",
673     "       magnify.",
674     "",
675     "  3    Press and drag to choose from a select set of commands.",
676     "       This button behaves differently if the image being",
677     "       displayed is a visual image directory.  Here, choose a",
678     "       particular tile of the directory and press this button and",
679     "       drag to select a command from a pop-up menu.  Choose from",
680     "       these menu items:",
681     "",
682     "           Open",
683     "           Next",
684     "           Former",
685     "           Delete",
686     "           Update",
687     "",
688     "       If you choose Open, the image represented by the tile is",
689     "       displayed.  To return to the visual image directory, choose",
690     "       Next from the Command widget.  Next and Former moves to the",
691     "       next or former image respectively.  Choose Delete to delete",
692     "       a particular image tile.  Finally, choose Update to",
693     "       synchronize all the image tiles with their respective",
694     "       images.",
695     "",
696     "COMMAND WIDGET",
697     "  The Command widget lists a number of sub-menus and commands.",
698     "  They are",
699     "",
700     "      File",
701     "        Open...",
702     "        Next",
703     "        Former",
704     "        Select...",
705     "        Save...",
706     "        Print...",
707     "        Delete...",
708     "        New...",
709     "        Visual Directory...",
710     "        Quit",
711     "      Edit",
712     "        Undo",
713     "        Redo",
714     "        Cut",
715     "        Copy",
716     "        Paste",
717     "      View",
718     "        Half Size",
719     "        Original Size",
720     "        Double Size",
721     "        Resize...",
722     "        Apply",
723     "        Refresh",
724     "        Restore",
725     "      Transform",
726     "        Crop",
727     "        Chop",
728     "        Flop",
729     "        Flip",
730     "        Rotate Right",
731     "        Rotate Left",
732     "        Rotate...",
733     "        Shear...",
734     "        Roll...",
735     "        Trim Edges",
736     "      Enhance",
737     "        Brightness...",
738     "        Saturation...",
739     "        Hue...",
740     "        Gamma...",
741     "        Sharpen...",
742     "        Dull",
743     "        Contrast Stretch...",
744     "        Sigmoidal Contrast...",
745     "        Normalize",
746     "        Equalize",
747     "        Negate",
748     "        Grayscale",
749     "        Map...",
750     "        Quantize...",
751     "      Effects",
752     "        Despeckle",
753     "        Emboss",
754     "        Reduce Noise",
755     "        Add Noise",
756     "        Sharpen...",
757     "        Blur...",
758     "        Threshold...",
759     "        Edge Detect...",
760     "        Spread...",
761     "        Shade...",
762     "        Painting...",
763     "        Segment...",
764     "      F/X",
765     "        Solarize...",
766     "        Sepia Tone...",
767     "        Swirl...",
768     "        Implode...",
769     "        Vignette...",
770     "        Wave...",
771     "        Oil Painting...",
772     "        Charcoal Drawing...",
773     "      Image Edit",
774     "        Annotate...",
775     "        Draw...",
776     "        Color...",
777     "        Matte...",
778     "        Composite...",
779     "        Add Border...",
780     "        Add Frame...",
781     "        Comment...",
782     "        Launch...",
783     "        Region of Interest...",
784     "      Miscellany",
785     "        Image Info",
786     "        Zoom Image",
787     "        Show Preview...",
788     "        Show Histogram",
789     "        Show Matte",
790     "        Background...",
791     "        Slide Show",
792     "        Preferences...",
793     "      Help",
794     "        Overview",
795     "        Browse Documentation",
796     "        About Display",
797     "",
798     "  Menu items with a indented triangle have a sub-menu.  They",
799     "  are represented above as the indented items.  To access a",
800     "  sub-menu item, move the pointer to the appropriate menu and",
801     "  press a button and drag.  When you find the desired sub-menu",
802     "  item, release the button and the command is executed.  Move",
803     "  the pointer away from the sub-menu if you decide not to",
804     "  execute a particular command.",
805     "",
806     "KEYBOARD ACCELERATORS",
807     "  Accelerators are one or two key presses that effect a",
808     "  particular command.  The keyboard accelerators that",
809     "  display(1) understands is:",
810     "",
811     "  Ctl+O     Press to open an image from a file.",
812     "",
813     "  space     Press to display the next image.",
814     "",
815     "            If the image is a multi-paged document such as a Postscript",
816     "            document, you can skip ahead several pages by preceding",
817     "            this command with a number.  For example to display the",
818     "            third page beyond the current page, press 3<space>.",
819     "",
820     "  backspace Press to display the former image.",
821     "",
822     "            If the image is a multi-paged document such as a Postscript",
823     "            document, you can skip behind several pages by preceding",
824     "            this command with a number.  For example to display the",
825     "            third page preceding the current page, press 3<backspace>.",
826     "",
827     "  Ctl+S     Press to write the image to a file.",
828     "",
829     "  Ctl+P     Press to print the image to a Postscript printer.",
830     "",
831     "  Ctl+D     Press to delete an image file.",
832     "",
833     "  Ctl+N     Press to create a blank canvas.",
834     "",
835     "  Ctl+Q     Press to discard all images and exit program.",
836     "",
837     "  Ctl+Z     Press to undo last image transformation.",
838     "",
839     "  Ctl+R     Press to redo last image transformation.",
840     "",
841     "  Ctl+X     Press to cut a region of the image.",
842     "",
843     "  Ctl+C     Press to copy a region of the image.",
844     "",
845     "  Ctl+V     Press to paste a region to the image.",
846     "",
847     "  <         Press to half the image size.",
848     "",
849     "  -         Press to return to the original image size.",
850     "",
851     "  >         Press to double the image size.",
852     "",
853     "  %         Press to resize the image to a width and height you",
854     "            specify.",
855     "",
856     "Cmd-A       Press to make any image transformations permanent."
857     "",
858     "            By default, any image size transformations are applied",
859     "            to the original image to create the image displayed on",
860     "            the X server.  However, the transformations are not",
861     "            permanent (i.e. the original image does not change",
862     "            size only the X image does).  For example, if you",
863     "            press > the X image will appear to double in size,",
864     "            but the original image will in fact remain the same size.",
865     "            To force the original image to double in size, press >",
866     "            followed by Cmd-A.",
867     "",
868     "  @         Press to refresh the image window.",
869     "",
870     "  C         Press to cut out a rectangular region of the image.",
871     "",
872     "  [         Press to chop the image.",
873     "",
874     "  H         Press to flop image in the horizontal direction.",
875     "",
876     "  V         Press to flip image in the vertical direction.",
877     "",
878     "  /         Press to rotate the image 90 degrees clockwise.",
879     "",
880     " \\         Press to rotate the image 90 degrees counter-clockwise.",
881     "",
882     "  *         Press to rotate the image the number of degrees you",
883     "            specify.",
884     "",
885     "  S         Press to shear the image the number of degrees you",
886     "            specify.",
887     "",
888     "  R         Press to roll the image.",
889     "",
890     "  T         Press to trim the image edges.",
891     "",
892     "  Shft-H    Press to vary the image hue.",
893     "",
894     "  Shft-S    Press to vary the color saturation.",
895     "",
896     "  Shft-L    Press to vary the color brightness.",
897     "",
898     "  Shft-G    Press to gamma correct the image.",
899     "",
900     "  Shft-C    Press to sharpen the image contrast.",
901     "",
902     "  Shft-Z    Press to dull the image contrast.",
903     "",
904     "  =         Press to perform histogram equalization on the image.",
905     "",
906     "  Shft-N    Press to perform histogram normalization on the image.",
907     "",
908     "  Shft-~    Press to negate the colors of the image.",
909     "",
910     "  .         Press to convert the image colors to gray.",
911     "",
912     "  Shft-#    Press to set the maximum number of unique colors in the",
913     "            image.",
914     "",
915     "  F2        Press to reduce the speckles in an image.",
916     "",
917     "  F3        Press to eliminate peak noise from an image.",
918     "",
919     "  F4        Press to add noise to an image.",
920     "",
921     "  F5        Press to sharpen an image.",
922     "",
923     "  F6        Press to delete an image file.",
924     "",
925     "  F7        Press to threshold the image.",
926     "",
927     "  F8        Press to detect edges within an image.",
928     "",
929     "  F9        Press to emboss an image.",
930     "",
931     "  F10       Press to displace pixels by a random amount.",
932     "",
933     "  F11       Press to negate all pixels above the threshold level.",
934     "",
935     "  F12       Press to shade the image using a distant light source.",
936     "",
937     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
938     "",
939     "  F14       Press to segment the image by color.",
940     "",
941     "  Meta-S    Press to swirl image pixels about the center.",
942     "",
943     "  Meta-I    Press to implode image pixels about the center.",
944     "",
945     "  Meta-W    Press to alter an image along a sine wave.",
946     "",
947     "  Meta-P    Press to simulate an oil painting.",
948     "",
949     "  Meta-C    Press to simulate a charcoal drawing.",
950     "",
951     "  Alt-A     Press to annotate the image with text.",
952     "",
953     "  Alt-D     Press to draw on an image.",
954     "",
955     "  Alt-P     Press to edit an image pixel color.",
956     "",
957     "  Alt-M     Press to edit the image matte information.",
958     "",
959     "  Alt-V     Press to composite the image with another.",
960     "",
961     "  Alt-B     Press to add a border to the image.",
962     "",
963     "  Alt-F     Press to add an ornamental border to the image.",
964     "",
965     "  Alt-Shft-!",
966     "            Press to add an image comment.",
967     "",
968     "  Ctl-A     Press to apply image processing techniques to a region",
969     "            of interest.",
970     "",
971     "  Shft-?    Press to display information about the image.",
972     "",
973     "  Shft-+    Press to map the zoom image window.",
974     "",
975     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
976     "",
977     "  F1        Press to display helpful information about display(1).",
978     "",
979     "  Find      Press to browse documentation about ImageMagick.",
980     "",
981     "  1-9       Press to change the level of magnification.",
982     "",
983     "  Use the arrow keys to move the image one pixel up, down,",
984     "  left, or right within the magnify window.  Be sure to first",
985     "  map the magnify window by pressing button 2.",
986     "",
987     "  Press ALT and one of the arrow keys to trim off one pixel",
988     "  from any side of the image.",
989     (char *) NULL,
990   },
991   *ImageMatteEditHelp[] =
992   {
993     "Matte information within an image is useful for some",
994     "operations such as image compositing (See IMAGE",
995     "COMPOSITING).  This extra channel usually defines a mask",
996     "which represents a sort of a cookie-cutter for the image.",
997     "This the case when matte is opaque (full coverage) for",
998     "pixels inside the shape, zero outside, and between 0 and",
999     "QuantumRange on the boundary.",
1000     "",
1001     "A small window appears showing the location of the cursor in",
1002     "the image window. You are now in matte edit mode.  To exit",
1003     "immediately, press Dismiss.  In matte edit mode, the Command",
1004     "widget has these options:",
1005     "",
1006     "    Method",
1007     "      point",
1008     "      replace",
1009     "      floodfill",
1010     "      filltoborder",
1011     "      reset",
1012     "    Border Color",
1013     "      black",
1014     "      blue",
1015     "      cyan",
1016     "      green",
1017     "      gray",
1018     "      red",
1019     "      magenta",
1020     "      yellow",
1021     "      white",
1022     "      Browser...",
1023     "    Fuzz",
1024     "      0%",
1025     "      2%",
1026     "      5%",
1027     "      10%",
1028     "      15%",
1029     "      Dialog...",
1030     "    Matte",
1031     "      Opaque",
1032     "      Transparent",
1033     "      Dialog...",
1034     "    Undo",
1035     "    Help",
1036     "    Dismiss",
1037     "",
1038     "Choose a matte editing method from the Method sub-menu of",
1039     "the Command widget.  The point method changes the matte value",
1040     "of any pixel selected with the pointer until the button is",
1041     "is released.  The replace method changes the matte value of",
1042     "any pixel that matches the color of the pixel you select with",
1043     "a button press.  Floodfill changes the matte value of any pixel",
1044     "that matches the color of the pixel you select with a button",
1045     "press and is a neighbor.  Whereas filltoborder changes the matte",
1046     "value any neighbor pixel that is not the border color.  Finally",
1047     "reset changes the entire image to the designated matte value.",
1048     "",
1049     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1050     "select the Dialog entry.  Here a dialog appears requesting a matte",
1051     "value.  The value you select is assigned as the opacity value of the",
1052     "selected pixel or pixels.",
1053     "",
1054     "Now, press any button to select a pixel within the image",
1055     "window to change its matte value.",
1056     "",
1057     "If the Magnify widget is mapped, it can be helpful in positioning",
1058     "your pointer within the image (refer to button 2).",
1059     "",
1060     "Matte information is only valid in a DirectClass image.",
1061     "Therefore, any PseudoClass image is promoted to DirectClass",
1062     "(see miff(5)).  Note that matte information for PseudoClass",
1063     "is not retained for colormapped X server visuals (e.g.",
1064     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1065     "immediately save your image to a file (refer to Write).",
1066     "Correct matte editing behavior may require a TrueColor or",
1067     "DirectColor visual or a Standard Colormap.",
1068     (char *) NULL,
1069   },
1070   *ImagePanHelp[] =
1071   {
1072     "When an image exceeds the width or height of the X server",
1073     "screen, display maps a small panning icon.  The rectangle",
1074     "within the panning icon shows the area that is currently",
1075     "displayed in the image window.  To pan about the image,",
1076     "press any button and drag the pointer within the panning",
1077     "icon.  The pan rectangle moves with the pointer and the",
1078     "image window is updated to reflect the location of the",
1079     "rectangle within the panning icon.  When you have selected",
1080     "the area of the image you wish to view, release the button.",
1081     "",
1082     "Use the arrow keys to pan the image one pixel up, down,",
1083     "left, or right within the image window.",
1084     "",
1085     "The panning icon is withdrawn if the image becomes smaller",
1086     "than the dimensions of the X server screen.",
1087     (char *) NULL,
1088   },
1089   *ImagePasteHelp[] =
1090   {
1091     "A small window appears showing the location of the cursor in",
1092     "the image window. You are now in paste mode.  To exit",
1093     "immediately, press Dismiss.  In paste mode, the Command",
1094     "widget has these options:",
1095     "",
1096     "    Operators",
1097     "      over",
1098     "      in",
1099     "      out",
1100     "      atop",
1101     "      xor",
1102     "      plus",
1103     "      minus",
1104     "      add",
1105     "      subtract",
1106     "      difference",
1107     "      replace",
1108     "    Help",
1109     "    Dismiss",
1110     "",
1111     "Choose a composite operation from the Operators sub-menu of",
1112     "the Command widget.  How each operator behaves is described",
1113     "below.  Image window is the image currently displayed on",
1114     "your X server and image is the image obtained with the File",
1115     "Browser widget.",
1116     "",
1117     "Over     The result is the union of the two image shapes,",
1118     "         with image obscuring image window in the region of",
1119     "         overlap.",
1120     "",
1121     "In       The result is simply image cut by the shape of",
1122     "         image window.  None of the image data of image",
1123     "         window is in the result.",
1124     "",
1125     "Out      The resulting image is image with the shape of",
1126     "         image window cut out.",
1127     "",
1128     "Atop     The result is the same shape as image image window,",
1129     "         with image obscuring image window where the image",
1130     "         shapes overlap.  Note this differs from over",
1131     "         because the portion of image outside image window's",
1132     "         shape does not appear in the result.",
1133     "",
1134     "Xor      The result is the image data from both image and",
1135     "         image window that is outside the overlap region.",
1136     "         The overlap region is blank.",
1137     "",
1138     "Plus     The result is just the sum of the image data.",
1139     "         Output values are cropped to QuantumRange (no overflow).",
1140     "         This operation is independent of the matte",
1141     "         channels.",
1142     "",
1143     "Minus    The result of image - image window, with underflow",
1144     "         cropped to zero.",
1145     "",
1146     "Add      The result of image + image window, with overflow",
1147     "         wrapping around (mod 256).",
1148     "",
1149     "Subtract The result of image - image window, with underflow",
1150     "         wrapping around (mod 256).  The add and subtract",
1151     "         operators can be used to perform reversible",
1152     "         transformations.",
1153     "",
1154     "Difference",
1155     "         The result of abs(image - image window).  This",
1156     "         useful for comparing two very similar images.",
1157     "",
1158     "Copy     The resulting image is image window replaced with",
1159     "         image.  Here the matte information is ignored.",
1160     "",
1161     "CopyRed  The red layer of the image window is replace with",
1162     "         the red layer of the image.  The other layers are",
1163     "         untouched.",
1164     "",
1165     "CopyGreen",
1166     "         The green layer of the image window is replace with",
1167     "         the green layer of the image.  The other layers are",
1168     "         untouched.",
1169     "",
1170     "CopyBlue The blue layer of the image window is replace with",
1171     "         the blue layer of the image.  The other layers are",
1172     "         untouched.",
1173     "",
1174     "CopyOpacity",
1175     "         The matte layer of the image window is replace with",
1176     "         the matte layer of the image.  The other layers are",
1177     "         untouched.",
1178     "",
1179     "The image compositor requires a matte, or alpha channel in",
1180     "the image for some operations.  This extra channel usually",
1181     "defines a mask which represents a sort of a cookie-cutter",
1182     "for the image.  This the case when matte is opaque (full",
1183     "coverage) for pixels inside the shape, zero outside, and",
1184     "between 0 and QuantumRange on the boundary.  If image does not",
1185     "have a matte channel, it is initialized with 0 for any pixel",
1186     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1187     "",
1188     "Note that matte information for image window is not retained",
1189     "for colormapped X server visuals (e.g. StaticColor,",
1190     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1191     "behavior may require a TrueColor or DirectColor visual or a",
1192     "Standard Colormap.",
1193     "",
1194     "Choosing a composite operator is optional.  The default",
1195     "operator is replace.  However, you must choose a location to",
1196     "paste your image and press button 1.  Press and hold the",
1197     "button before releasing and an outline of the image will",
1198     "appear to help you identify your location.",
1199     "",
1200     "The actual colors of the pasted image is saved.  However,",
1201     "the color that appears in image window may be different.",
1202     "For example, on a monochrome screen image window will appear",
1203     "black or white even though your pasted image may have",
1204     "many colors.  If the image is saved to a file it is written",
1205     "with the correct colors.  To assure the correct colors are",
1206     "saved in the final image, any PseudoClass image is promoted",
1207     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1208     "to remain PseudoClass, use -colors.",
1209     (char *) NULL,
1210   },
1211   *ImageROIHelp[] =
1212   {
1213     "In region of interest mode, the Command widget has these",
1214     "options:",
1215     "",
1216     "    Help",
1217     "    Dismiss",
1218     "",
1219     "To define a region of interest, press button 1 and drag.",
1220     "The region of interest is defined by a highlighted rectangle",
1221     "that expands or contracts as it follows the pointer.  Once",
1222     "you are satisfied with the region of interest, release the",
1223     "button.  You are now in apply mode.  In apply mode the",
1224     "Command widget has these options:",
1225     "",
1226     "      File",
1227     "        Save...",
1228     "        Print...",
1229     "      Edit",
1230     "        Undo",
1231     "        Redo",
1232     "      Transform",
1233     "        Flop",
1234     "        Flip",
1235     "        Rotate Right",
1236     "        Rotate Left",
1237     "      Enhance",
1238     "        Hue...",
1239     "        Saturation...",
1240     "        Brightness...",
1241     "        Gamma...",
1242     "        Spiff",
1243     "        Dull",
1244     "        Contrast Stretch",
1245     "        Sigmoidal Contrast...",
1246     "        Normalize",
1247     "        Equalize",
1248     "        Negate",
1249     "        Grayscale",
1250     "        Map...",
1251     "        Quantize...",
1252     "      Effects",
1253     "        Despeckle",
1254     "        Emboss",
1255     "        Reduce Noise",
1256     "        Sharpen...",
1257     "        Blur...",
1258     "        Threshold...",
1259     "        Edge Detect...",
1260     "        Spread...",
1261     "        Shade...",
1262     "        Raise...",
1263     "        Segment...",
1264     "      F/X",
1265     "        Solarize...",
1266     "        Sepia Tone...",
1267     "        Swirl...",
1268     "        Implode...",
1269     "        Vignette...",
1270     "        Wave...",
1271     "        Oil Painting...",
1272     "        Charcoal Drawing...",
1273     "      Miscellany",
1274     "        Image Info",
1275     "        Zoom Image",
1276     "        Show Preview...",
1277     "        Show Histogram",
1278     "        Show Matte",
1279     "      Help",
1280     "      Dismiss",
1281     "",
1282     "You can make adjustments to the region of interest by moving",
1283     "the pointer to one of the rectangle corners, pressing a",
1284     "button, and dragging.  Finally, choose an image processing",
1285     "technique from the Command widget.  You can choose more than",
1286     "one image processing technique to apply to an area.",
1287     "Alternatively, you can move the region of interest before",
1288     "applying another image processing technique.  To exit, press",
1289     "Dismiss.",
1290     (char *) NULL,
1291   },
1292   *ImageRotateHelp[] =
1293   {
1294     "In rotate mode, the Command widget has these options:",
1295     "",
1296     "    Pixel Color",
1297     "      black",
1298     "      blue",
1299     "      cyan",
1300     "      green",
1301     "      gray",
1302     "      red",
1303     "      magenta",
1304     "      yellow",
1305     "      white",
1306     "      Browser...",
1307     "    Direction",
1308     "      horizontal",
1309     "      vertical",
1310     "    Help",
1311     "    Dismiss",
1312     "",
1313     "Choose a background color from the Pixel Color sub-menu.",
1314     "Additional background colors can be specified with the color",
1315     "browser.  You can change the menu colors by setting the X",
1316     "resources pen1 through pen9.",
1317     "",
1318     "If you choose the color browser and press Grab, you can",
1319     "select the background color by moving the pointer to the",
1320     "desired color on the screen and press any button.",
1321     "",
1322     "Choose a point in the image window and press this button and",
1323     "hold.  Next, move the pointer to another location in the",
1324     "image.  As you move a line connects the initial location and",
1325     "the pointer.  When you release the button, the degree of",
1326     "image rotation is determined by the slope of the line you",
1327     "just drew.  The slope is relative to the direction you",
1328     "choose from the Direction sub-menu of the Command widget.",
1329     "",
1330     "To cancel the image rotation, move the pointer back to the",
1331     "starting point of the line and release the button.",
1332     (char *) NULL,
1333   };
1334 \f
1335 /*
1336   Enumeration declarations.
1337 */
1338 typedef enum
1339 {
1340   CopyMode,
1341   CropMode,
1342   CutMode
1343 } ClipboardMode;
1344
1345 typedef enum
1346 {
1347   OpenCommand,
1348   NextCommand,
1349   FormerCommand,
1350   SelectCommand,
1351   SaveCommand,
1352   PrintCommand,
1353   DeleteCommand,
1354   NewCommand,
1355   VisualDirectoryCommand,
1356   QuitCommand,
1357   UndoCommand,
1358   RedoCommand,
1359   CutCommand,
1360   CopyCommand,
1361   PasteCommand,
1362   HalfSizeCommand,
1363   OriginalSizeCommand,
1364   DoubleSizeCommand,
1365   ResizeCommand,
1366   ApplyCommand,
1367   RefreshCommand,
1368   RestoreCommand,
1369   CropCommand,
1370   ChopCommand,
1371   FlopCommand,
1372   FlipCommand,
1373   RotateRightCommand,
1374   RotateLeftCommand,
1375   RotateCommand,
1376   ShearCommand,
1377   RollCommand,
1378   TrimCommand,
1379   HueCommand,
1380   SaturationCommand,
1381   BrightnessCommand,
1382   GammaCommand,
1383   SpiffCommand,
1384   DullCommand,
1385   ContrastStretchCommand,
1386   SigmoidalContrastCommand,
1387   NormalizeCommand,
1388   EqualizeCommand,
1389   NegateCommand,
1390   GrayscaleCommand,
1391   MapCommand,
1392   QuantizeCommand,
1393   DespeckleCommand,
1394   EmbossCommand,
1395   ReduceNoiseCommand,
1396   AddNoiseCommand,
1397   SharpenCommand,
1398   BlurCommand,
1399   ThresholdCommand,
1400   EdgeDetectCommand,
1401   SpreadCommand,
1402   ShadeCommand,
1403   RaiseCommand,
1404   SegmentCommand,
1405   SolarizeCommand,
1406   SepiaToneCommand,
1407   SwirlCommand,
1408   ImplodeCommand,
1409   VignetteCommand,
1410   WaveCommand,
1411   OilPaintCommand,
1412   CharcoalDrawCommand,
1413   AnnotateCommand,
1414   DrawCommand,
1415   ColorCommand,
1416   MatteCommand,
1417   CompositeCommand,
1418   AddBorderCommand,
1419   AddFrameCommand,
1420   CommentCommand,
1421   LaunchCommand,
1422   RegionofInterestCommand,
1423   ROIHelpCommand,
1424   ROIDismissCommand,
1425   InfoCommand,
1426   ZoomCommand,
1427   ShowPreviewCommand,
1428   ShowHistogramCommand,
1429   ShowMatteCommand,
1430   BackgroundCommand,
1431   SlideShowCommand,
1432   PreferencesCommand,
1433   HelpCommand,
1434   BrowseDocumentationCommand,
1435   VersionCommand,
1436   SaveToUndoBufferCommand,
1437   FreeBuffersCommand,
1438   NullCommand
1439 } CommandType;
1440
1441 typedef enum
1442 {
1443   AnnotateNameCommand,
1444   AnnotateFontColorCommand,
1445   AnnotateBackgroundColorCommand,
1446   AnnotateRotateCommand,
1447   AnnotateHelpCommand,
1448   AnnotateDismissCommand,
1449   TextHelpCommand,
1450   TextApplyCommand,
1451   ChopDirectionCommand,
1452   ChopHelpCommand,
1453   ChopDismissCommand,
1454   HorizontalChopCommand,
1455   VerticalChopCommand,
1456   ColorEditMethodCommand,
1457   ColorEditColorCommand,
1458   ColorEditBorderCommand,
1459   ColorEditFuzzCommand,
1460   ColorEditUndoCommand,
1461   ColorEditHelpCommand,
1462   ColorEditDismissCommand,
1463   CompositeOperatorsCommand,
1464   CompositeDissolveCommand,
1465   CompositeDisplaceCommand,
1466   CompositeHelpCommand,
1467   CompositeDismissCommand,
1468   CropHelpCommand,
1469   CropDismissCommand,
1470   RectifyCopyCommand,
1471   RectifyHelpCommand,
1472   RectifyDismissCommand,
1473   DrawElementCommand,
1474   DrawColorCommand,
1475   DrawStippleCommand,
1476   DrawWidthCommand,
1477   DrawUndoCommand,
1478   DrawHelpCommand,
1479   DrawDismissCommand,
1480   MatteEditMethod,
1481   MatteEditBorderCommand,
1482   MatteEditFuzzCommand,
1483   MatteEditValueCommand,
1484   MatteEditUndoCommand,
1485   MatteEditHelpCommand,
1486   MatteEditDismissCommand,
1487   PasteOperatorsCommand,
1488   PasteHelpCommand,
1489   PasteDismissCommand,
1490   RotateColorCommand,
1491   RotateDirectionCommand,
1492   RotateCropCommand,
1493   RotateSharpenCommand,
1494   RotateHelpCommand,
1495   RotateDismissCommand,
1496   HorizontalRotateCommand,
1497   VerticalRotateCommand,
1498   TileLoadCommand,
1499   TileNextCommand,
1500   TileFormerCommand,
1501   TileDeleteCommand,
1502   TileUpdateCommand
1503 } ModeType;
1504 \f
1505 /*
1506   Stipples.
1507 */
1508 #define BricksWidth  20
1509 #define BricksHeight  20
1510 #define DiagonalWidth  16
1511 #define DiagonalHeight  16
1512 #define HighlightWidth  8
1513 #define HighlightHeight  8
1514 #define OpaqueWidth  8
1515 #define OpaqueHeight  8
1516 #define ScalesWidth  16
1517 #define ScalesHeight  16
1518 #define ShadowWidth  8
1519 #define ShadowHeight  8
1520 #define VerticalWidth  16
1521 #define VerticalHeight  16
1522 #define WavyWidth  16
1523 #define WavyHeight  16
1524 \f
1525 /*
1526   Constant declaration.
1527 */
1528 static const int
1529   RoiDelta = 8;
1530
1531 static const unsigned char
1532   BricksBitmap[] =
1533   {
1534     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1535     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1536     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1537     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1538     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1539   },
1540   DiagonalBitmap[] =
1541   {
1542     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1543     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1544     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1545   },
1546   ScalesBitmap[] =
1547   {
1548     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1549     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1550     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1551   },
1552   VerticalBitmap[] =
1553   {
1554     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1555     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1556     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1557   },
1558   WavyBitmap[] =
1559   {
1560     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1561     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1562     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1563   };
1564 \f
1565 /*
1566   Function prototypes.
1567 */
1568 static CommandType
1569   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1570     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1571
1572 static Image
1573   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1574     Image **,ExceptionInfo *),
1575   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1576   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1577     ExceptionInfo *),
1578   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1579     ExceptionInfo *);
1580
1581 static MagickBooleanType
1582   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1583     ExceptionInfo *),
1584   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1585     ExceptionInfo *),
1586   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1587     ExceptionInfo *),
1588   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1589     ExceptionInfo *),
1590   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1591     ExceptionInfo *),
1592   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1593     ExceptionInfo *),
1594   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1595   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1596     ExceptionInfo *),
1597   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1598     ExceptionInfo *),
1599   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1600   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1601   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1602     ExceptionInfo *),
1603   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1604   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1605   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1606
1607 static void
1608   XDrawPanRectangle(Display *,XWindows *),
1609   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1610     ExceptionInfo *),
1611   XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1612   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1613   XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1614   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1615     const KeySym,ExceptionInfo *),
1616   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1617   XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1618   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1619 \f
1620 /*
1621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1622 %                                                                             %
1623 %                                                                             %
1624 %                                                                             %
1625 %   D i s p l a y I m a g e s                                                 %
1626 %                                                                             %
1627 %                                                                             %
1628 %                                                                             %
1629 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1630 %
1631 %  DisplayImages() displays an image sequence to any X window screen.  It
1632 %  returns a value other than 0 if successful.  Check the exception member
1633 %  of image to determine the reason for any failure.
1634 %
1635 %  The format of the DisplayImages method is:
1636 %
1637 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1638 %        Image *images,ExceptionInfo *exception)
1639 %
1640 %  A description of each parameter follows:
1641 %
1642 %    o image_info: the image info.
1643 %
1644 %    o image: the image.
1645 %
1646 %    o exception: return any errors or warnings in this structure.
1647 %
1648 */
1649 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1650   Image *images,ExceptionInfo *exception)
1651 {
1652   char
1653     *argv[1];
1654
1655   Display
1656     *display;
1657
1658   Image
1659     *image;
1660
1661   register ssize_t
1662     i;
1663
1664   size_t
1665     state;
1666
1667   XrmDatabase
1668     resource_database;
1669
1670   XResourceInfo
1671     resource_info;
1672
1673   assert(image_info != (const ImageInfo *) NULL);
1674   assert(image_info->signature == MagickSignature);
1675   assert(images != (Image *) NULL);
1676   assert(images->signature == MagickSignature);
1677   if (images->debug != MagickFalse)
1678     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1679   display=XOpenDisplay(image_info->server_name);
1680   if (display == (Display *) NULL)
1681     {
1682       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1683         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1684       return(MagickFalse);
1685     }
1686   if (exception->severity != UndefinedException)
1687     CatchException(exception);
1688   (void) XSetErrorHandler(XError);
1689   resource_database=XGetResourceDatabase(display,GetClientName());
1690   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1691   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1692   if (image_info->page != (char *) NULL)
1693     resource_info.image_geometry=AcquireString(image_info->page);
1694   resource_info.immutable=MagickTrue;
1695   argv[0]=AcquireString(GetClientName());
1696   state=DefaultState;
1697   for (i=0; (state & ExitState) == 0; i++)
1698   {
1699     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1700       break;
1701     image=GetImageFromList(images,i % GetImageListLength(images));
1702     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1703   }
1704   (void) SetErrorHandler((ErrorHandler) NULL);
1705   (void) SetWarningHandler((WarningHandler) NULL);
1706   argv[0]=DestroyString(argv[0]);
1707   (void) XCloseDisplay(display);
1708   XDestroyResourceInfo(&resource_info);
1709   if (exception->severity != UndefinedException)
1710     return(MagickFalse);
1711   return(MagickTrue);
1712 }
1713 \f
1714 /*
1715 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1716 %                                                                             %
1717 %                                                                             %
1718 %                                                                             %
1719 %   R e m o t e D i s p l a y C o m m a n d                                   %
1720 %                                                                             %
1721 %                                                                             %
1722 %                                                                             %
1723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1724 %
1725 %  RemoteDisplayCommand() encourages a remote display program to display the
1726 %  specified image filename.
1727 %
1728 %  The format of the RemoteDisplayCommand method is:
1729 %
1730 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1731 %        const char *window,const char *filename,ExceptionInfo *exception)
1732 %
1733 %  A description of each parameter follows:
1734 %
1735 %    o image_info: the image info.
1736 %
1737 %    o window: Specifies the name or id of an X window.
1738 %
1739 %    o filename: the name of the image filename to display.
1740 %
1741 %    o exception: return any errors or warnings in this structure.
1742 %
1743 */
1744 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1745   const char *window,const char *filename,ExceptionInfo *exception)
1746 {
1747   Display
1748     *display;
1749
1750   MagickStatusType
1751     status;
1752
1753   assert(image_info != (const ImageInfo *) NULL);
1754   assert(image_info->signature == MagickSignature);
1755   assert(filename != (char *) NULL);
1756   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1757   display=XOpenDisplay(image_info->server_name);
1758   if (display == (Display *) NULL)
1759     {
1760       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1761         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1762       return(MagickFalse);
1763     }
1764   (void) XSetErrorHandler(XError);
1765   status=XRemoteCommand(display,window,filename);
1766   (void) XCloseDisplay(display);
1767   return(status != 0 ? MagickTrue : MagickFalse);
1768 }
1769 \f
1770 /*
1771 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1772 %                                                                             %
1773 %                                                                             %
1774 %                                                                             %
1775 +   X A n n o t a t e E d i t I m a g e                                       %
1776 %                                                                             %
1777 %                                                                             %
1778 %                                                                             %
1779 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1780 %
1781 %  XAnnotateEditImage() annotates the image with text.
1782 %
1783 %  The format of the XAnnotateEditImage method is:
1784 %
1785 %      MagickBooleanType XAnnotateEditImage(Display *display,
1786 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1787 %        ExceptionInfo *exception)
1788 %
1789 %  A description of each parameter follows:
1790 %
1791 %    o display: Specifies a connection to an X server;  returned from
1792 %      XOpenDisplay.
1793 %
1794 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1795 %
1796 %    o windows: Specifies a pointer to a XWindows structure.
1797 %
1798 %    o image: the image; returned from ReadImage.
1799 %
1800 */
1801
1802 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1803 {
1804   if (x > y)
1805     return(x);
1806   return(y);
1807 }
1808
1809 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1810 {
1811   if (x < y)
1812     return(x);
1813   return(y);
1814 }
1815
1816 static MagickBooleanType XAnnotateEditImage(Display *display,
1817   XResourceInfo *resource_info,XWindows *windows,Image *image,
1818   ExceptionInfo *exception)
1819 {
1820   static const char
1821     *AnnotateMenu[] =
1822     {
1823       "Font Name",
1824       "Font Color",
1825       "Box Color",
1826       "Rotate Text",
1827       "Help",
1828       "Dismiss",
1829       (char *) NULL
1830     },
1831     *TextMenu[] =
1832     {
1833       "Help",
1834       "Apply",
1835       (char *) NULL
1836     };
1837
1838   static const ModeType
1839     AnnotateCommands[] =
1840     {
1841       AnnotateNameCommand,
1842       AnnotateFontColorCommand,
1843       AnnotateBackgroundColorCommand,
1844       AnnotateRotateCommand,
1845       AnnotateHelpCommand,
1846       AnnotateDismissCommand
1847     },
1848     TextCommands[] =
1849     {
1850       TextHelpCommand,
1851       TextApplyCommand
1852     };
1853
1854   static MagickBooleanType
1855     transparent_box = MagickTrue,
1856     transparent_pen = MagickFalse;
1857
1858   static MagickRealType
1859     degrees = 0.0;
1860
1861   static unsigned int
1862     box_id = MaxNumberPens-2,
1863     font_id = 0,
1864     pen_id = 0;
1865
1866   char
1867     command[MaxTextExtent],
1868     text[MaxTextExtent];
1869
1870   const char
1871     *ColorMenu[MaxNumberPens+1];
1872
1873   Cursor
1874     cursor;
1875
1876   GC
1877     annotate_context;
1878
1879   int
1880     id,
1881     pen_number,
1882     status,
1883     x,
1884     y;
1885
1886   KeySym
1887     key_symbol;
1888
1889   register char
1890     *p;
1891
1892   register ssize_t
1893     i;
1894
1895   unsigned int
1896     height,
1897     width;
1898
1899   size_t
1900     state;
1901
1902   XAnnotateInfo
1903     *annotate_info,
1904     *previous_info;
1905
1906   XColor
1907     color;
1908
1909   XFontStruct
1910     *font_info;
1911
1912   XEvent
1913     event,
1914     text_event;
1915
1916   /*
1917     Map Command widget.
1918   */
1919   (void) CloneString(&windows->command.name,"Annotate");
1920   windows->command.data=4;
1921   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1922   (void) XMapRaised(display,windows->command.id);
1923   XClientMessage(display,windows->image.id,windows->im_protocols,
1924     windows->im_update_widget,CurrentTime);
1925   /*
1926     Track pointer until button 1 is pressed.
1927   */
1928   XQueryPosition(display,windows->image.id,&x,&y);
1929   (void) XSelectInput(display,windows->image.id,
1930     windows->image.attributes.event_mask | PointerMotionMask);
1931   cursor=XCreateFontCursor(display,XC_left_side);
1932   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1933   state=DefaultState;
1934   do
1935   {
1936     if (windows->info.mapped != MagickFalse)
1937       {
1938         /*
1939           Display pointer position.
1940         */
1941         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1942           x+windows->image.x,y+windows->image.y);
1943         XInfoWidget(display,windows,text);
1944       }
1945     /*
1946       Wait for next event.
1947     */
1948     XScreenEvent(display,windows,&event,exception);
1949     if (event.xany.window == windows->command.id)
1950       {
1951         /*
1952           Select a command from the Command widget.
1953         */
1954         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1955         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1956         if (id < 0)
1957           continue;
1958         switch (AnnotateCommands[id])
1959         {
1960           case AnnotateNameCommand:
1961           {
1962             const char
1963               *FontMenu[MaxNumberFonts];
1964
1965             int
1966               font_number;
1967
1968             /*
1969               Initialize menu selections.
1970             */
1971             for (i=0; i < MaxNumberFonts; i++)
1972               FontMenu[i]=resource_info->font_name[i];
1973             FontMenu[MaxNumberFonts-2]="Browser...";
1974             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1975             /*
1976               Select a font name from the pop-up menu.
1977             */
1978             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1979               (const char **) FontMenu,command);
1980             if (font_number < 0)
1981               break;
1982             if (font_number == (MaxNumberFonts-2))
1983               {
1984                 static char
1985                   font_name[MaxTextExtent] = "fixed";
1986
1987                 /*
1988                   Select a font name from a browser.
1989                 */
1990                 resource_info->font_name[font_number]=font_name;
1991                 XFontBrowserWidget(display,windows,"Select",font_name);
1992                 if (*font_name == '\0')
1993                   break;
1994               }
1995             /*
1996               Initialize font info.
1997             */
1998             font_info=XLoadQueryFont(display,resource_info->font_name[
1999               font_number]);
2000             if (font_info == (XFontStruct *) NULL)
2001               {
2002                 XNoticeWidget(display,windows,"Unable to load font:",
2003                   resource_info->font_name[font_number]);
2004                 break;
2005               }
2006             font_id=(unsigned int) font_number;
2007             (void) XFreeFont(display,font_info);
2008             break;
2009           }
2010           case AnnotateFontColorCommand:
2011           {
2012             /*
2013               Initialize menu selections.
2014             */
2015             for (i=0; i < (int) (MaxNumberPens-2); i++)
2016               ColorMenu[i]=resource_info->pen_colors[i];
2017             ColorMenu[MaxNumberPens-2]="transparent";
2018             ColorMenu[MaxNumberPens-1]="Browser...";
2019             ColorMenu[MaxNumberPens]=(const char *) NULL;
2020             /*
2021               Select a pen color from the pop-up menu.
2022             */
2023             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2024               (const char **) ColorMenu,command);
2025             if (pen_number < 0)
2026               break;
2027             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2028               MagickFalse;
2029             if (transparent_pen != MagickFalse)
2030               break;
2031             if (pen_number == (MaxNumberPens-1))
2032               {
2033                 static char
2034                   color_name[MaxTextExtent] = "gray";
2035
2036                 /*
2037                   Select a pen color from a dialog.
2038                 */
2039                 resource_info->pen_colors[pen_number]=color_name;
2040                 XColorBrowserWidget(display,windows,"Select",color_name);
2041                 if (*color_name == '\0')
2042                   break;
2043               }
2044             /*
2045               Set pen color.
2046             */
2047             (void) XParseColor(display,windows->map_info->colormap,
2048               resource_info->pen_colors[pen_number],&color);
2049             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2050               (unsigned int) MaxColors,&color);
2051             windows->pixel_info->pen_colors[pen_number]=color;
2052             pen_id=(unsigned int) pen_number;
2053             break;
2054           }
2055           case AnnotateBackgroundColorCommand:
2056           {
2057             /*
2058               Initialize menu selections.
2059             */
2060             for (i=0; i < (int) (MaxNumberPens-2); i++)
2061               ColorMenu[i]=resource_info->pen_colors[i];
2062             ColorMenu[MaxNumberPens-2]="transparent";
2063             ColorMenu[MaxNumberPens-1]="Browser...";
2064             ColorMenu[MaxNumberPens]=(const char *) NULL;
2065             /*
2066               Select a pen color from the pop-up menu.
2067             */
2068             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2069               (const char **) ColorMenu,command);
2070             if (pen_number < 0)
2071               break;
2072             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2073               MagickFalse;
2074             if (transparent_box != MagickFalse)
2075               break;
2076             if (pen_number == (MaxNumberPens-1))
2077               {
2078                 static char
2079                   color_name[MaxTextExtent] = "gray";
2080
2081                 /*
2082                   Select a pen color from a dialog.
2083                 */
2084                 resource_info->pen_colors[pen_number]=color_name;
2085                 XColorBrowserWidget(display,windows,"Select",color_name);
2086                 if (*color_name == '\0')
2087                   break;
2088               }
2089             /*
2090               Set pen color.
2091             */
2092             (void) XParseColor(display,windows->map_info->colormap,
2093               resource_info->pen_colors[pen_number],&color);
2094             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2095               (unsigned int) MaxColors,&color);
2096             windows->pixel_info->pen_colors[pen_number]=color;
2097             box_id=(unsigned int) pen_number;
2098             break;
2099           }
2100           case AnnotateRotateCommand:
2101           {
2102             int
2103               entry;
2104
2105             static char
2106               angle[MaxTextExtent] = "30.0";
2107
2108             static const char
2109               *RotateMenu[] =
2110               {
2111                 "-90",
2112                 "-45",
2113                 "-30",
2114                 "0",
2115                 "30",
2116                 "45",
2117                 "90",
2118                 "180",
2119                 "Dialog...",
2120                 (char *) NULL,
2121               };
2122
2123             /*
2124               Select a command from the pop-up menu.
2125             */
2126             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2127               command);
2128             if (entry < 0)
2129               break;
2130             if (entry != 8)
2131               {
2132                 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2133                 break;
2134               }
2135             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2136               angle);
2137             if (*angle == '\0')
2138               break;
2139             degrees=StringToDouble(angle,(char **) NULL);
2140             break;
2141           }
2142           case AnnotateHelpCommand:
2143           {
2144             XTextViewWidget(display,resource_info,windows,MagickFalse,
2145               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2146             break;
2147           }
2148           case AnnotateDismissCommand:
2149           {
2150             /*
2151               Prematurely exit.
2152             */
2153             state|=EscapeState;
2154             state|=ExitState;
2155             break;
2156           }
2157           default:
2158             break;
2159         }
2160         continue;
2161       }
2162     switch (event.type)
2163     {
2164       case ButtonPress:
2165       {
2166         if (event.xbutton.button != Button1)
2167           break;
2168         if (event.xbutton.window != windows->image.id)
2169           break;
2170         /*
2171           Change to text entering mode.
2172         */
2173         x=event.xbutton.x;
2174         y=event.xbutton.y;
2175         state|=ExitState;
2176         break;
2177       }
2178       case ButtonRelease:
2179         break;
2180       case Expose:
2181         break;
2182       case KeyPress:
2183       {
2184         if (event.xkey.window != windows->image.id)
2185           break;
2186         /*
2187           Respond to a user key press.
2188         */
2189         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2190           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2191         switch ((int) key_symbol)
2192         {
2193           case XK_Escape:
2194           case XK_F20:
2195           {
2196             /*
2197               Prematurely exit.
2198             */
2199             state|=EscapeState;
2200             state|=ExitState;
2201             break;
2202           }
2203           case XK_F1:
2204           case XK_Help:
2205           {
2206             XTextViewWidget(display,resource_info,windows,MagickFalse,
2207               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2208             break;
2209           }
2210           default:
2211           {
2212             (void) XBell(display,0);
2213             break;
2214           }
2215         }
2216         break;
2217       }
2218       case MotionNotify:
2219       {
2220         /*
2221           Map and unmap Info widget as cursor crosses its boundaries.
2222         */
2223         x=event.xmotion.x;
2224         y=event.xmotion.y;
2225         if (windows->info.mapped != MagickFalse)
2226           {
2227             if ((x < (int) (windows->info.x+windows->info.width)) &&
2228                 (y < (int) (windows->info.y+windows->info.height)))
2229               (void) XWithdrawWindow(display,windows->info.id,
2230                 windows->info.screen);
2231           }
2232         else
2233           if ((x > (int) (windows->info.x+windows->info.width)) ||
2234               (y > (int) (windows->info.y+windows->info.height)))
2235             (void) XMapWindow(display,windows->info.id);
2236         break;
2237       }
2238       default:
2239         break;
2240     }
2241   } while ((state & ExitState) == 0);
2242   (void) XSelectInput(display,windows->image.id,
2243     windows->image.attributes.event_mask);
2244   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2245   if ((state & EscapeState) != 0)
2246     return(MagickTrue);
2247   /*
2248     Set font info and check boundary conditions.
2249   */
2250   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2251   if (font_info == (XFontStruct *) NULL)
2252     {
2253       XNoticeWidget(display,windows,"Unable to load font:",
2254         resource_info->font_name[font_id]);
2255       font_info=windows->font_info;
2256     }
2257   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2258     x=(int) windows->image.width-font_info->max_bounds.width;
2259   if (y < (int) (font_info->ascent+font_info->descent))
2260     y=(int) font_info->ascent+font_info->descent;
2261   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2262       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2263     return(MagickFalse);
2264   /*
2265     Initialize annotate structure.
2266   */
2267   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2268   if (annotate_info == (XAnnotateInfo *) NULL)
2269     return(MagickFalse);
2270   XGetAnnotateInfo(annotate_info);
2271   annotate_info->x=x;
2272   annotate_info->y=y;
2273   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2274     annotate_info->stencil=OpaqueStencil;
2275   else
2276     if (transparent_box == MagickFalse)
2277       annotate_info->stencil=BackgroundStencil;
2278     else
2279       annotate_info->stencil=ForegroundStencil;
2280   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2281   annotate_info->degrees=degrees;
2282   annotate_info->font_info=font_info;
2283   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2284     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2285     sizeof(*annotate_info->text));
2286   if (annotate_info->text == (char *) NULL)
2287     return(MagickFalse);
2288   /*
2289     Create cursor and set graphic context.
2290   */
2291   cursor=XCreateFontCursor(display,XC_pencil);
2292   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2293   annotate_context=windows->image.annotate_context;
2294   (void) XSetFont(display,annotate_context,font_info->fid);
2295   (void) XSetBackground(display,annotate_context,
2296     windows->pixel_info->pen_colors[box_id].pixel);
2297   (void) XSetForeground(display,annotate_context,
2298     windows->pixel_info->pen_colors[pen_id].pixel);
2299   /*
2300     Begin annotating the image with text.
2301   */
2302   (void) CloneString(&windows->command.name,"Text");
2303   windows->command.data=0;
2304   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2305   state=DefaultState;
2306   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2307   text_event.xexpose.width=(int) font_info->max_bounds.width;
2308   text_event.xexpose.height=font_info->max_bounds.ascent+
2309     font_info->max_bounds.descent;
2310   p=annotate_info->text;
2311   do
2312   {
2313     /*
2314       Display text cursor.
2315     */
2316     *p='\0';
2317     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2318     /*
2319       Wait for next event.
2320     */
2321     XScreenEvent(display,windows,&event,exception);
2322     if (event.xany.window == windows->command.id)
2323       {
2324         /*
2325           Select a command from the Command widget.
2326         */
2327         (void) XSetBackground(display,annotate_context,
2328           windows->pixel_info->background_color.pixel);
2329         (void) XSetForeground(display,annotate_context,
2330           windows->pixel_info->foreground_color.pixel);
2331         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2332         (void) XSetBackground(display,annotate_context,
2333           windows->pixel_info->pen_colors[box_id].pixel);
2334         (void) XSetForeground(display,annotate_context,
2335           windows->pixel_info->pen_colors[pen_id].pixel);
2336         if (id < 0)
2337           continue;
2338         switch (TextCommands[id])
2339         {
2340           case TextHelpCommand:
2341           {
2342             XTextViewWidget(display,resource_info,windows,MagickFalse,
2343               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2344             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2345             break;
2346           }
2347           case TextApplyCommand:
2348           {
2349             /*
2350               Finished annotating.
2351             */
2352             annotate_info->width=(unsigned int) XTextWidth(font_info,
2353               annotate_info->text,(int) strlen(annotate_info->text));
2354             XRefreshWindow(display,&windows->image,&text_event);
2355             state|=ExitState;
2356             break;
2357           }
2358           default:
2359             break;
2360         }
2361         continue;
2362       }
2363     /*
2364       Erase text cursor.
2365     */
2366     text_event.xexpose.x=x;
2367     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2368     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2369       (unsigned int) text_event.xexpose.width,(unsigned int)
2370       text_event.xexpose.height,MagickFalse);
2371     XRefreshWindow(display,&windows->image,&text_event);
2372     switch (event.type)
2373     {
2374       case ButtonPress:
2375       {
2376         if (event.xbutton.window != windows->image.id)
2377           break;
2378         if (event.xbutton.button == Button2)
2379           {
2380             /*
2381               Request primary selection.
2382             */
2383             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2384               windows->image.id,CurrentTime);
2385             break;
2386           }
2387         break;
2388       }
2389       case Expose:
2390       {
2391         if (event.xexpose.count == 0)
2392           {
2393             XAnnotateInfo
2394               *text_info;
2395
2396             /*
2397               Refresh Image window.
2398             */
2399             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2400             text_info=annotate_info;
2401             while (text_info != (XAnnotateInfo *) NULL)
2402             {
2403               if (annotate_info->stencil == ForegroundStencil)
2404                 (void) XDrawString(display,windows->image.id,annotate_context,
2405                   text_info->x,text_info->y,text_info->text,
2406                   (int) strlen(text_info->text));
2407               else
2408                 (void) XDrawImageString(display,windows->image.id,
2409                   annotate_context,text_info->x,text_info->y,text_info->text,
2410                   (int) strlen(text_info->text));
2411               text_info=text_info->previous;
2412             }
2413             (void) XDrawString(display,windows->image.id,annotate_context,
2414               x,y,"_",1);
2415           }
2416         break;
2417       }
2418       case KeyPress:
2419       {
2420         int
2421           length;
2422
2423         if (event.xkey.window != windows->image.id)
2424           break;
2425         /*
2426           Respond to a user key press.
2427         */
2428         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2429           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2430         *(command+length)='\0';
2431         if (((event.xkey.state & ControlMask) != 0) ||
2432             ((event.xkey.state & Mod1Mask) != 0))
2433           state|=ModifierState;
2434         if ((state & ModifierState) != 0)
2435           switch ((int) key_symbol)
2436           {
2437             case XK_u:
2438             case XK_U:
2439             {
2440               key_symbol=DeleteCommand;
2441               break;
2442             }
2443             default:
2444               break;
2445           }
2446         switch ((int) key_symbol)
2447         {
2448           case XK_BackSpace:
2449           {
2450             /*
2451               Erase one character.
2452             */
2453             if (p == annotate_info->text)
2454               {
2455                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2456                   break;
2457                 else
2458                   {
2459                     /*
2460                       Go to end of the previous line of text.
2461                     */
2462                     annotate_info=annotate_info->previous;
2463                     p=annotate_info->text;
2464                     x=annotate_info->x+annotate_info->width;
2465                     y=annotate_info->y;
2466                     if (annotate_info->width != 0)
2467                       p+=strlen(annotate_info->text);
2468                     break;
2469                   }
2470               }
2471             p--;
2472             x-=XTextWidth(font_info,p,1);
2473             text_event.xexpose.x=x;
2474             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2475             XRefreshWindow(display,&windows->image,&text_event);
2476             break;
2477           }
2478           case XK_bracketleft:
2479           {
2480             key_symbol=XK_Escape;
2481             break;
2482           }
2483           case DeleteCommand:
2484           {
2485             /*
2486               Erase the entire line of text.
2487             */
2488             while (p != annotate_info->text)
2489             {
2490               p--;
2491               x-=XTextWidth(font_info,p,1);
2492               text_event.xexpose.x=x;
2493               XRefreshWindow(display,&windows->image,&text_event);
2494             }
2495             break;
2496           }
2497           case XK_Escape:
2498           case XK_F20:
2499           {
2500             /*
2501               Finished annotating.
2502             */
2503             annotate_info->width=(unsigned int) XTextWidth(font_info,
2504               annotate_info->text,(int) strlen(annotate_info->text));
2505             XRefreshWindow(display,&windows->image,&text_event);
2506             state|=ExitState;
2507             break;
2508           }
2509           default:
2510           {
2511             /*
2512               Draw a single character on the Image window.
2513             */
2514             if ((state & ModifierState) != 0)
2515               break;
2516             if (*command == '\0')
2517               break;
2518             *p=(*command);
2519             if (annotate_info->stencil == ForegroundStencil)
2520               (void) XDrawString(display,windows->image.id,annotate_context,
2521                 x,y,p,1);
2522             else
2523               (void) XDrawImageString(display,windows->image.id,
2524                 annotate_context,x,y,p,1);
2525             x+=XTextWidth(font_info,p,1);
2526             p++;
2527             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2528               break;
2529           }
2530           case XK_Return:
2531           case XK_KP_Enter:
2532           {
2533             /*
2534               Advance to the next line of text.
2535             */
2536             *p='\0';
2537             annotate_info->width=(unsigned int) XTextWidth(font_info,
2538               annotate_info->text,(int) strlen(annotate_info->text));
2539             if (annotate_info->next != (XAnnotateInfo *) NULL)
2540               {
2541                 /*
2542                   Line of text already exists.
2543                 */
2544                 annotate_info=annotate_info->next;
2545                 x=annotate_info->x;
2546                 y=annotate_info->y;
2547                 p=annotate_info->text;
2548                 break;
2549               }
2550             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2551               sizeof(*annotate_info->next));
2552             if (annotate_info->next == (XAnnotateInfo *) NULL)
2553               return(MagickFalse);
2554             *annotate_info->next=(*annotate_info);
2555             annotate_info->next->previous=annotate_info;
2556             annotate_info=annotate_info->next;
2557             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2558               windows->image.width/MagickMax((ssize_t)
2559               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2560             if (annotate_info->text == (char *) NULL)
2561               return(MagickFalse);
2562             annotate_info->y+=annotate_info->height;
2563             if (annotate_info->y > (int) windows->image.height)
2564               annotate_info->y=(int) annotate_info->height;
2565             annotate_info->next=(XAnnotateInfo *) NULL;
2566             x=annotate_info->x;
2567             y=annotate_info->y;
2568             p=annotate_info->text;
2569             break;
2570           }
2571         }
2572         break;
2573       }
2574       case KeyRelease:
2575       {
2576         /*
2577           Respond to a user key release.
2578         */
2579         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2580           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2581         state&=(~ModifierState);
2582         break;
2583       }
2584       case SelectionNotify:
2585       {
2586         Atom
2587           type;
2588
2589         int
2590           format;
2591
2592         unsigned char
2593           *data;
2594
2595         unsigned long
2596           after,
2597           length;
2598
2599         /*
2600           Obtain response from primary selection.
2601         */
2602         if (event.xselection.property == (Atom) None)
2603           break;
2604         status=XGetWindowProperty(display,event.xselection.requestor,
2605           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2606           &type,&format,&length,&after,&data);
2607         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2608             (length == 0))
2609           break;
2610         /*
2611           Annotate Image window with primary selection.
2612         */
2613         for (i=0; i < (ssize_t) length; i++)
2614         {
2615           if ((char) data[i] != '\n')
2616             {
2617               /*
2618                 Draw a single character on the Image window.
2619               */
2620               *p=(char) data[i];
2621               (void) XDrawString(display,windows->image.id,annotate_context,
2622                 x,y,p,1);
2623               x+=XTextWidth(font_info,p,1);
2624               p++;
2625               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2626                 continue;
2627             }
2628           /*
2629             Advance to the next line of text.
2630           */
2631           *p='\0';
2632           annotate_info->width=(unsigned int) XTextWidth(font_info,
2633             annotate_info->text,(int) strlen(annotate_info->text));
2634           if (annotate_info->next != (XAnnotateInfo *) NULL)
2635             {
2636               /*
2637                 Line of text already exists.
2638               */
2639               annotate_info=annotate_info->next;
2640               x=annotate_info->x;
2641               y=annotate_info->y;
2642               p=annotate_info->text;
2643               continue;
2644             }
2645           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2646             sizeof(*annotate_info->next));
2647           if (annotate_info->next == (XAnnotateInfo *) NULL)
2648             return(MagickFalse);
2649           *annotate_info->next=(*annotate_info);
2650           annotate_info->next->previous=annotate_info;
2651           annotate_info=annotate_info->next;
2652           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2653             windows->image.width/MagickMax((ssize_t)
2654             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2655           if (annotate_info->text == (char *) NULL)
2656             return(MagickFalse);
2657           annotate_info->y+=annotate_info->height;
2658           if (annotate_info->y > (int) windows->image.height)
2659             annotate_info->y=(int) annotate_info->height;
2660           annotate_info->next=(XAnnotateInfo *) NULL;
2661           x=annotate_info->x;
2662           y=annotate_info->y;
2663           p=annotate_info->text;
2664         }
2665         (void) XFree((void *) data);
2666         break;
2667       }
2668       default:
2669         break;
2670     }
2671   } while ((state & ExitState) == 0);
2672   (void) XFreeCursor(display,cursor);
2673   /*
2674     Annotation is relative to image configuration.
2675   */
2676   width=(unsigned int) image->columns;
2677   height=(unsigned int) image->rows;
2678   x=0;
2679   y=0;
2680   if (windows->image.crop_geometry != (char *) NULL)
2681     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2682   /*
2683     Initialize annotated image.
2684   */
2685   XSetCursorState(display,windows,MagickTrue);
2686   XCheckRefreshWindows(display,windows);
2687   while (annotate_info != (XAnnotateInfo *) NULL)
2688   {
2689     if (annotate_info->width == 0)
2690       {
2691         /*
2692           No text on this line--  go to the next line of text.
2693         */
2694         previous_info=annotate_info->previous;
2695         annotate_info->text=(char *)
2696           RelinquishMagickMemory(annotate_info->text);
2697         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2698         annotate_info=previous_info;
2699         continue;
2700       }
2701     /*
2702       Determine pixel index for box and pen color.
2703     */
2704     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2705     if (windows->pixel_info->colors != 0)
2706       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2707         if (windows->pixel_info->pixels[i] ==
2708             windows->pixel_info->pen_colors[box_id].pixel)
2709           {
2710             windows->pixel_info->box_index=(unsigned short) i;
2711             break;
2712           }
2713     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2714     if (windows->pixel_info->colors != 0)
2715       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2716         if (windows->pixel_info->pixels[i] ==
2717             windows->pixel_info->pen_colors[pen_id].pixel)
2718           {
2719             windows->pixel_info->pen_index=(unsigned short) i;
2720             break;
2721           }
2722     /*
2723       Define the annotate geometry string.
2724     */
2725     annotate_info->x=(int)
2726       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2727     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2728       windows->image.y)/windows->image.ximage->height;
2729     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2730       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2731       height*annotate_info->height/windows->image.ximage->height,
2732       annotate_info->x+x,annotate_info->y+y);
2733     /*
2734       Annotate image with text.
2735     */
2736     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2737       exception);
2738     if (status == 0)
2739       return(MagickFalse);
2740     /*
2741       Free up memory.
2742     */
2743     previous_info=annotate_info->previous;
2744     annotate_info->text=DestroyString(annotate_info->text);
2745     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2746     annotate_info=previous_info;
2747   }
2748   (void) XSetForeground(display,annotate_context,
2749     windows->pixel_info->foreground_color.pixel);
2750   (void) XSetBackground(display,annotate_context,
2751     windows->pixel_info->background_color.pixel);
2752   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2753   XSetCursorState(display,windows,MagickFalse);
2754   (void) XFreeFont(display,font_info);
2755   /*
2756     Update image configuration.
2757   */
2758   XConfigureImageColormap(display,resource_info,windows,image,exception);
2759   (void) XConfigureImage(display,resource_info,windows,image,exception);
2760   return(MagickTrue);
2761 }
2762 \f
2763 /*
2764 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2765 %                                                                             %
2766 %                                                                             %
2767 %                                                                             %
2768 +   X B a c k g r o u n d I m a g e                                           %
2769 %                                                                             %
2770 %                                                                             %
2771 %                                                                             %
2772 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2773 %
2774 %  XBackgroundImage() displays the image in the background of a window.
2775 %
2776 %  The format of the XBackgroundImage method is:
2777 %
2778 %      MagickBooleanType XBackgroundImage(Display *display,
2779 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2780 %        ExceptionInfo *exception)
2781 %
2782 %  A description of each parameter follows:
2783 %
2784 %    o display: Specifies a connection to an X server; returned from
2785 %      XOpenDisplay.
2786 %
2787 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2788 %
2789 %    o windows: Specifies a pointer to a XWindows structure.
2790 %
2791 %    o image: the image.
2792 %
2793 %    o exception: return any errors or warnings in this structure.
2794 %
2795 */
2796 static MagickBooleanType XBackgroundImage(Display *display,
2797   XResourceInfo *resource_info,XWindows *windows,Image **image,
2798   ExceptionInfo *exception)
2799 {
2800 #define BackgroundImageTag  "Background/Image"
2801
2802   int
2803     status;
2804
2805   static char
2806     window_id[MaxTextExtent] = "root";
2807
2808   XResourceInfo
2809     background_resources;
2810
2811   /*
2812     Put image in background.
2813   */
2814   status=XDialogWidget(display,windows,"Background",
2815     "Enter window id (id 0x00 selects window with pointer):",window_id);
2816   if (*window_id == '\0')
2817     return(MagickFalse);
2818   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2819     exception);
2820   XInfoWidget(display,windows,BackgroundImageTag);
2821   XSetCursorState(display,windows,MagickTrue);
2822   XCheckRefreshWindows(display,windows);
2823   background_resources=(*resource_info);
2824   background_resources.window_id=window_id;
2825   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2826   status=XDisplayBackgroundImage(display,&background_resources,*image,
2827     exception);
2828   if (status != MagickFalse)
2829     XClientMessage(display,windows->image.id,windows->im_protocols,
2830       windows->im_retain_colors,CurrentTime);
2831   XSetCursorState(display,windows,MagickFalse);
2832   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2833     exception);
2834   return(MagickTrue);
2835 }
2836 \f
2837 /*
2838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2839 %                                                                             %
2840 %                                                                             %
2841 %                                                                             %
2842 +   X C h o p I m a g e                                                       %
2843 %                                                                             %
2844 %                                                                             %
2845 %                                                                             %
2846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2847 %
2848 %  XChopImage() chops the X image.
2849 %
2850 %  The format of the XChopImage method is:
2851 %
2852 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2853 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2854 %
2855 %  A description of each parameter follows:
2856 %
2857 %    o display: Specifies a connection to an X server; returned from
2858 %      XOpenDisplay.
2859 %
2860 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2861 %
2862 %    o windows: Specifies a pointer to a XWindows structure.
2863 %
2864 %    o image: the image.
2865 %
2866 %    o exception: return any errors or warnings in this structure.
2867 %
2868 */
2869 static MagickBooleanType XChopImage(Display *display,
2870   XResourceInfo *resource_info,XWindows *windows,Image **image,
2871   ExceptionInfo *exception)
2872 {
2873   static const char
2874     *ChopMenu[] =
2875     {
2876       "Direction",
2877       "Help",
2878       "Dismiss",
2879       (char *) NULL
2880     };
2881
2882   static ModeType
2883     direction = HorizontalChopCommand;
2884
2885   static const ModeType
2886     ChopCommands[] =
2887     {
2888       ChopDirectionCommand,
2889       ChopHelpCommand,
2890       ChopDismissCommand
2891     },
2892     DirectionCommands[] =
2893     {
2894       HorizontalChopCommand,
2895       VerticalChopCommand
2896     };
2897
2898   char
2899     text[MaxTextExtent];
2900
2901   Image
2902     *chop_image;
2903
2904   int
2905     id,
2906     x,
2907     y;
2908
2909   MagickRealType
2910     scale_factor;
2911
2912   RectangleInfo
2913     chop_info;
2914
2915   unsigned int
2916     distance,
2917     height,
2918     width;
2919
2920   size_t
2921     state;
2922
2923   XEvent
2924     event;
2925
2926   XSegment
2927     segment_info;
2928
2929   /*
2930     Map Command widget.
2931   */
2932   (void) CloneString(&windows->command.name,"Chop");
2933   windows->command.data=1;
2934   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2935   (void) XMapRaised(display,windows->command.id);
2936   XClientMessage(display,windows->image.id,windows->im_protocols,
2937     windows->im_update_widget,CurrentTime);
2938   /*
2939     Track pointer until button 1 is pressed.
2940   */
2941   XQueryPosition(display,windows->image.id,&x,&y);
2942   (void) XSelectInput(display,windows->image.id,
2943     windows->image.attributes.event_mask | PointerMotionMask);
2944   state=DefaultState;
2945   do
2946   {
2947     if (windows->info.mapped != MagickFalse)
2948       {
2949         /*
2950           Display pointer position.
2951         */
2952         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2953           x+windows->image.x,y+windows->image.y);
2954         XInfoWidget(display,windows,text);
2955       }
2956     /*
2957       Wait for next event.
2958     */
2959     XScreenEvent(display,windows,&event,exception);
2960     if (event.xany.window == windows->command.id)
2961       {
2962         /*
2963           Select a command from the Command widget.
2964         */
2965         id=XCommandWidget(display,windows,ChopMenu,&event);
2966         if (id < 0)
2967           continue;
2968         switch (ChopCommands[id])
2969         {
2970           case ChopDirectionCommand:
2971           {
2972             char
2973               command[MaxTextExtent];
2974
2975             static const char
2976               *Directions[] =
2977               {
2978                 "horizontal",
2979                 "vertical",
2980                 (char *) NULL,
2981               };
2982
2983             /*
2984               Select a command from the pop-up menu.
2985             */
2986             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2987             if (id >= 0)
2988               direction=DirectionCommands[id];
2989             break;
2990           }
2991           case ChopHelpCommand:
2992           {
2993             XTextViewWidget(display,resource_info,windows,MagickFalse,
2994               "Help Viewer - Image Chop",ImageChopHelp);
2995             break;
2996           }
2997           case ChopDismissCommand:
2998           {
2999             /*
3000               Prematurely exit.
3001             */
3002             state|=EscapeState;
3003             state|=ExitState;
3004             break;
3005           }
3006           default:
3007             break;
3008         }
3009         continue;
3010       }
3011     switch (event.type)
3012     {
3013       case ButtonPress:
3014       {
3015         if (event.xbutton.button != Button1)
3016           break;
3017         if (event.xbutton.window != windows->image.id)
3018           break;
3019         /*
3020           User has committed to start point of chopping line.
3021         */
3022         segment_info.x1=(short int) event.xbutton.x;
3023         segment_info.x2=(short int) event.xbutton.x;
3024         segment_info.y1=(short int) event.xbutton.y;
3025         segment_info.y2=(short int) event.xbutton.y;
3026         state|=ExitState;
3027         break;
3028       }
3029       case ButtonRelease:
3030         break;
3031       case Expose:
3032         break;
3033       case KeyPress:
3034       {
3035         char
3036           command[MaxTextExtent];
3037
3038         KeySym
3039           key_symbol;
3040
3041         if (event.xkey.window != windows->image.id)
3042           break;
3043         /*
3044           Respond to a user key press.
3045         */
3046         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3047           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3048         switch ((int) key_symbol)
3049         {
3050           case XK_Escape:
3051           case XK_F20:
3052           {
3053             /*
3054               Prematurely exit.
3055             */
3056             state|=EscapeState;
3057             state|=ExitState;
3058             break;
3059           }
3060           case XK_F1:
3061           case XK_Help:
3062           {
3063             (void) XSetFunction(display,windows->image.highlight_context,
3064               GXcopy);
3065             XTextViewWidget(display,resource_info,windows,MagickFalse,
3066               "Help Viewer - Image Chop",ImageChopHelp);
3067             (void) XSetFunction(display,windows->image.highlight_context,
3068               GXinvert);
3069             break;
3070           }
3071           default:
3072           {
3073             (void) XBell(display,0);
3074             break;
3075           }
3076         }
3077         break;
3078       }
3079       case MotionNotify:
3080       {
3081         /*
3082           Map and unmap Info widget as text cursor crosses its boundaries.
3083         */
3084         x=event.xmotion.x;
3085         y=event.xmotion.y;
3086         if (windows->info.mapped != MagickFalse)
3087           {
3088             if ((x < (int) (windows->info.x+windows->info.width)) &&
3089                 (y < (int) (windows->info.y+windows->info.height)))
3090               (void) XWithdrawWindow(display,windows->info.id,
3091                 windows->info.screen);
3092           }
3093         else
3094           if ((x > (int) (windows->info.x+windows->info.width)) ||
3095               (y > (int) (windows->info.y+windows->info.height)))
3096             (void) XMapWindow(display,windows->info.id);
3097       }
3098     }
3099   } while ((state & ExitState) == 0);
3100   (void) XSelectInput(display,windows->image.id,
3101     windows->image.attributes.event_mask);
3102   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3103   if ((state & EscapeState) != 0)
3104     return(MagickTrue);
3105   /*
3106     Draw line as pointer moves until the mouse button is released.
3107   */
3108   chop_info.width=0;
3109   chop_info.height=0;
3110   chop_info.x=0;
3111   chop_info.y=0;
3112   distance=0;
3113   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3114   state=DefaultState;
3115   do
3116   {
3117     if (distance > 9)
3118       {
3119         /*
3120           Display info and draw chopping line.
3121         */
3122         if (windows->info.mapped == MagickFalse)
3123           (void) XMapWindow(display,windows->info.id);
3124         (void) FormatLocaleString(text,MaxTextExtent,
3125           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3126           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3127         XInfoWidget(display,windows,text);
3128         XHighlightLine(display,windows->image.id,
3129           windows->image.highlight_context,&segment_info);
3130       }
3131     else
3132       if (windows->info.mapped != MagickFalse)
3133         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3134     /*
3135       Wait for next event.
3136     */
3137     XScreenEvent(display,windows,&event,exception);
3138     if (distance > 9)
3139       XHighlightLine(display,windows->image.id,
3140         windows->image.highlight_context,&segment_info);
3141     switch (event.type)
3142     {
3143       case ButtonPress:
3144       {
3145         segment_info.x2=(short int) event.xmotion.x;
3146         segment_info.y2=(short int) event.xmotion.y;
3147         break;
3148       }
3149       case ButtonRelease:
3150       {
3151         /*
3152           User has committed to chopping line.
3153         */
3154         segment_info.x2=(short int) event.xbutton.x;
3155         segment_info.y2=(short int) event.xbutton.y;
3156         state|=ExitState;
3157         break;
3158       }
3159       case Expose:
3160         break;
3161       case MotionNotify:
3162       {
3163         segment_info.x2=(short int) event.xmotion.x;
3164         segment_info.y2=(short int) event.xmotion.y;
3165       }
3166       default:
3167         break;
3168     }
3169     /*
3170       Check boundary conditions.
3171     */
3172     if (segment_info.x2 < 0)
3173       segment_info.x2=0;
3174     else
3175       if (segment_info.x2 > windows->image.ximage->width)
3176         segment_info.x2=windows->image.ximage->width;
3177     if (segment_info.y2 < 0)
3178       segment_info.y2=0;
3179     else
3180       if (segment_info.y2 > windows->image.ximage->height)
3181         segment_info.y2=windows->image.ximage->height;
3182     distance=(unsigned int)
3183       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3184        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3185     /*
3186       Compute chopping geometry.
3187     */
3188     if (direction == HorizontalChopCommand)
3189       {
3190         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3191         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3192         chop_info.height=0;
3193         chop_info.y=0;
3194         if (segment_info.x1 > (int) segment_info.x2)
3195           {
3196             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3197             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3198           }
3199       }
3200     else
3201       {
3202         chop_info.width=0;
3203         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3204         chop_info.x=0;
3205         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3206         if (segment_info.y1 > segment_info.y2)
3207           {
3208             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3209             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3210           }
3211       }
3212   } while ((state & ExitState) == 0);
3213   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3214   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3215   if (distance <= 9)
3216     return(MagickTrue);
3217   /*
3218     Image chopping is relative to image configuration.
3219   */
3220   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3221     exception);
3222   XSetCursorState(display,windows,MagickTrue);
3223   XCheckRefreshWindows(display,windows);
3224   windows->image.window_changes.width=windows->image.ximage->width-
3225     (unsigned int) chop_info.width;
3226   windows->image.window_changes.height=windows->image.ximage->height-
3227     (unsigned int) chop_info.height;
3228   width=(unsigned int) (*image)->columns;
3229   height=(unsigned int) (*image)->rows;
3230   x=0;
3231   y=0;
3232   if (windows->image.crop_geometry != (char *) NULL)
3233     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3234   scale_factor=(MagickRealType) width/windows->image.ximage->width;
3235   chop_info.x+=x;
3236   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3237   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3238   scale_factor=(MagickRealType) height/windows->image.ximage->height;
3239   chop_info.y+=y;
3240   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3241   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3242   /*
3243     Chop image.
3244   */
3245   chop_image=ChopImage(*image,&chop_info,exception);
3246   XSetCursorState(display,windows,MagickFalse);
3247   if (chop_image == (Image *) NULL)
3248     return(MagickFalse);
3249   *image=DestroyImage(*image);
3250   *image=chop_image;
3251   /*
3252     Update image configuration.
3253   */
3254   XConfigureImageColormap(display,resource_info,windows,*image,exception);
3255   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3256   return(MagickTrue);
3257 }
3258 \f
3259 /*
3260 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3261 %                                                                             %
3262 %                                                                             %
3263 %                                                                             %
3264 +   X C o l o r E d i t I m a g e                                             %
3265 %                                                                             %
3266 %                                                                             %
3267 %                                                                             %
3268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3269 %
3270 %  XColorEditImage() allows the user to interactively change the color of one
3271 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3272 %
3273 %  The format of the XColorEditImage method is:
3274 %
3275 %      MagickBooleanType XColorEditImage(Display *display,
3276 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3277 %          ExceptionInfo *exception)
3278 %
3279 %  A description of each parameter follows:
3280 %
3281 %    o display: Specifies a connection to an X server;  returned from
3282 %      XOpenDisplay.
3283 %
3284 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3285 %
3286 %    o windows: Specifies a pointer to a XWindows structure.
3287 %
3288 %    o image: the image; returned from ReadImage.
3289 %
3290 %    o exception: return any errors or warnings in this structure.
3291 %
3292 */
3293 static MagickBooleanType XColorEditImage(Display *display,
3294   XResourceInfo *resource_info,XWindows *windows,Image **image,
3295   ExceptionInfo *exception)
3296 {
3297   static const char
3298     *ColorEditMenu[] =
3299     {
3300       "Method",
3301       "Pixel Color",
3302       "Border Color",
3303       "Fuzz",
3304       "Undo",
3305       "Help",
3306       "Dismiss",
3307       (char *) NULL
3308     };
3309
3310   static const ModeType
3311     ColorEditCommands[] =
3312     {
3313       ColorEditMethodCommand,
3314       ColorEditColorCommand,
3315       ColorEditBorderCommand,
3316       ColorEditFuzzCommand,
3317       ColorEditUndoCommand,
3318       ColorEditHelpCommand,
3319       ColorEditDismissCommand
3320     };
3321
3322   static PaintMethod
3323     method = PointMethod;
3324
3325   static unsigned int
3326     pen_id = 0;
3327
3328   static XColor
3329     border_color = { 0, 0, 0, 0, 0, 0 };
3330
3331   char
3332     command[MaxTextExtent],
3333     text[MaxTextExtent];
3334
3335   Cursor
3336     cursor;
3337
3338   int
3339     entry,
3340     id,
3341     x,
3342     x_offset,
3343     y,
3344     y_offset;
3345
3346   register Quantum
3347     *q;
3348
3349   register ssize_t
3350     i;
3351
3352   unsigned int
3353     height,
3354     width;
3355
3356   size_t
3357     state;
3358
3359   XColor
3360     color;
3361
3362   XEvent
3363     event;
3364
3365   /*
3366     Map Command widget.
3367   */
3368   (void) CloneString(&windows->command.name,"Color Edit");
3369   windows->command.data=4;
3370   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3371   (void) XMapRaised(display,windows->command.id);
3372   XClientMessage(display,windows->image.id,windows->im_protocols,
3373     windows->im_update_widget,CurrentTime);
3374   /*
3375     Make cursor.
3376   */
3377   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3378     resource_info->background_color,resource_info->foreground_color);
3379   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3380   /*
3381     Track pointer until button 1 is pressed.
3382   */
3383   XQueryPosition(display,windows->image.id,&x,&y);
3384   (void) XSelectInput(display,windows->image.id,
3385     windows->image.attributes.event_mask | PointerMotionMask);
3386   state=DefaultState;
3387   do
3388   {
3389     if (windows->info.mapped != MagickFalse)
3390       {
3391         /*
3392           Display pointer position.
3393         */
3394         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3395           x+windows->image.x,y+windows->image.y);
3396         XInfoWidget(display,windows,text);
3397       }
3398     /*
3399       Wait for next event.
3400     */
3401     XScreenEvent(display,windows,&event,exception);
3402     if (event.xany.window == windows->command.id)
3403       {
3404         /*
3405           Select a command from the Command widget.
3406         */
3407         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3408         if (id < 0)
3409           {
3410             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3411             continue;
3412           }
3413         switch (ColorEditCommands[id])
3414         {
3415           case ColorEditMethodCommand:
3416           {
3417             char
3418               **methods;
3419
3420             /*
3421               Select a method from the pop-up menu.
3422             */
3423             methods=(char **) GetCommandOptions(MagickMethodOptions);
3424             if (methods == (char **) NULL)
3425               break;
3426             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3427               (const char **) methods,command);
3428             if (entry >= 0)
3429               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3430                 MagickFalse,methods[entry]);
3431             methods=DestroyStringList(methods);
3432             break;
3433           }
3434           case ColorEditColorCommand:
3435           {
3436             const char
3437               *ColorMenu[MaxNumberPens];
3438
3439             int
3440               pen_number;
3441
3442             /*
3443               Initialize menu selections.
3444             */
3445             for (i=0; i < (int) (MaxNumberPens-2); i++)
3446               ColorMenu[i]=resource_info->pen_colors[i];
3447             ColorMenu[MaxNumberPens-2]="Browser...";
3448             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3449             /*
3450               Select a pen color from the pop-up menu.
3451             */
3452             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3453               (const char **) ColorMenu,command);
3454             if (pen_number < 0)
3455               break;
3456             if (pen_number == (MaxNumberPens-2))
3457               {
3458                 static char
3459                   color_name[MaxTextExtent] = "gray";
3460
3461                 /*
3462                   Select a pen color from a dialog.
3463                 */
3464                 resource_info->pen_colors[pen_number]=color_name;
3465                 XColorBrowserWidget(display,windows,"Select",color_name);
3466                 if (*color_name == '\0')
3467                   break;
3468               }
3469             /*
3470               Set pen color.
3471             */
3472             (void) XParseColor(display,windows->map_info->colormap,
3473               resource_info->pen_colors[pen_number],&color);
3474             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3475               (unsigned int) MaxColors,&color);
3476             windows->pixel_info->pen_colors[pen_number]=color;
3477             pen_id=(unsigned int) pen_number;
3478             break;
3479           }
3480           case ColorEditBorderCommand:
3481           {
3482             const char
3483               *ColorMenu[MaxNumberPens];
3484
3485             int
3486               pen_number;
3487
3488             /*
3489               Initialize menu selections.
3490             */
3491             for (i=0; i < (int) (MaxNumberPens-2); i++)
3492               ColorMenu[i]=resource_info->pen_colors[i];
3493             ColorMenu[MaxNumberPens-2]="Browser...";
3494             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3495             /*
3496               Select a pen color from the pop-up menu.
3497             */
3498             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3499               (const char **) ColorMenu,command);
3500             if (pen_number < 0)
3501               break;
3502             if (pen_number == (MaxNumberPens-2))
3503               {
3504                 static char
3505                   color_name[MaxTextExtent] = "gray";
3506
3507                 /*
3508                   Select a pen color from a dialog.
3509                 */
3510                 resource_info->pen_colors[pen_number]=color_name;
3511                 XColorBrowserWidget(display,windows,"Select",color_name);
3512                 if (*color_name == '\0')
3513                   break;
3514               }
3515             /*
3516               Set border color.
3517             */
3518             (void) XParseColor(display,windows->map_info->colormap,
3519               resource_info->pen_colors[pen_number],&border_color);
3520             break;
3521           }
3522           case ColorEditFuzzCommand:
3523           {
3524             static char
3525               fuzz[MaxTextExtent];
3526
3527             static const char
3528               *FuzzMenu[] =
3529               {
3530                 "0%",
3531                 "2%",
3532                 "5%",
3533                 "10%",
3534                 "15%",
3535                 "Dialog...",
3536                 (char *) NULL,
3537               };
3538
3539             /*
3540               Select a command from the pop-up menu.
3541             */
3542             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3543               command);
3544             if (entry < 0)
3545               break;
3546             if (entry != 5)
3547               {
3548                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3549                   QuantumRange+1.0);
3550                 break;
3551               }
3552             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3553             (void) XDialogWidget(display,windows,"Ok",
3554               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3555             if (*fuzz == '\0')
3556               break;
3557             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3558             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3559               1.0);
3560             break;
3561           }
3562           case ColorEditUndoCommand:
3563           {
3564             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3565               image,exception);
3566             break;
3567           }
3568           case ColorEditHelpCommand:
3569           default:
3570           {
3571             XTextViewWidget(display,resource_info,windows,MagickFalse,
3572               "Help Viewer - Image Annotation",ImageColorEditHelp);
3573             break;
3574           }
3575           case ColorEditDismissCommand:
3576           {
3577             /*
3578               Prematurely exit.
3579             */
3580             state|=EscapeState;
3581             state|=ExitState;
3582             break;
3583           }
3584         }
3585         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3586         continue;
3587       }
3588     switch (event.type)
3589     {
3590       case ButtonPress:
3591       {
3592         if (event.xbutton.button != Button1)
3593           break;
3594         if ((event.xbutton.window != windows->image.id) &&
3595             (event.xbutton.window != windows->magnify.id))
3596           break;
3597         /*
3598           exit loop.
3599         */
3600         x=event.xbutton.x;
3601         y=event.xbutton.y;
3602         (void) XMagickCommand(display,resource_info,windows,
3603           SaveToUndoBufferCommand,image,exception);
3604         state|=UpdateConfigurationState;
3605         break;
3606       }
3607       case ButtonRelease:
3608       {
3609         if (event.xbutton.button != Button1)
3610           break;
3611         if ((event.xbutton.window != windows->image.id) &&
3612             (event.xbutton.window != windows->magnify.id))
3613           break;
3614         /*
3615           Update colormap information.
3616         */
3617         x=event.xbutton.x;
3618         y=event.xbutton.y;
3619         XConfigureImageColormap(display,resource_info,windows,*image,exception);
3620         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3621         XInfoWidget(display,windows,text);
3622         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3623         state&=(~UpdateConfigurationState);
3624         break;
3625       }
3626       case Expose:
3627         break;
3628       case KeyPress:
3629       {
3630         KeySym
3631           key_symbol;
3632
3633         if (event.xkey.window == windows->magnify.id)
3634           {
3635             Window
3636               window;
3637
3638             window=windows->magnify.id;
3639             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3640           }
3641         if (event.xkey.window != windows->image.id)
3642           break;
3643         /*
3644           Respond to a user key press.
3645         */
3646         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3647           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3648         switch ((int) key_symbol)
3649         {
3650           case XK_Escape:
3651           case XK_F20:
3652           {
3653             /*
3654               Prematurely exit.
3655             */
3656             state|=ExitState;
3657             break;
3658           }
3659           case XK_F1:
3660           case XK_Help:
3661           {
3662             XTextViewWidget(display,resource_info,windows,MagickFalse,
3663               "Help Viewer - Image Annotation",ImageColorEditHelp);
3664             break;
3665           }
3666           default:
3667           {
3668             (void) XBell(display,0);
3669             break;
3670           }
3671         }
3672         break;
3673       }
3674       case MotionNotify:
3675       {
3676         /*
3677           Map and unmap Info widget as cursor crosses its boundaries.
3678         */
3679         x=event.xmotion.x;
3680         y=event.xmotion.y;
3681         if (windows->info.mapped != MagickFalse)
3682           {
3683             if ((x < (int) (windows->info.x+windows->info.width)) &&
3684                 (y < (int) (windows->info.y+windows->info.height)))
3685               (void) XWithdrawWindow(display,windows->info.id,
3686                 windows->info.screen);
3687           }
3688         else
3689           if ((x > (int) (windows->info.x+windows->info.width)) ||
3690               (y > (int) (windows->info.y+windows->info.height)))
3691             (void) XMapWindow(display,windows->info.id);
3692         break;
3693       }
3694       default:
3695         break;
3696     }
3697     if (event.xany.window == windows->magnify.id)
3698       {
3699         x=windows->magnify.x-windows->image.x;
3700         y=windows->magnify.y-windows->image.y;
3701       }
3702     x_offset=x;
3703     y_offset=y;
3704     if ((state & UpdateConfigurationState) != 0)
3705       {
3706         CacheView
3707           *image_view;
3708
3709         int
3710           x,
3711           y;
3712
3713         /*
3714           Pixel edit is relative to image configuration.
3715         */
3716         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3717           MagickTrue);
3718         color=windows->pixel_info->pen_colors[pen_id];
3719         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3720         width=(unsigned int) (*image)->columns;
3721         height=(unsigned int) (*image)->rows;
3722         x=0;
3723         y=0;
3724         if (windows->image.crop_geometry != (char *) NULL)
3725           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3726             &width,&height);
3727         x_offset=(int)
3728           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3729         y_offset=(int)
3730           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3731         if ((x_offset < 0) || (y_offset < 0))
3732           continue;
3733         if ((x_offset >= (int) (*image)->columns) ||
3734             (y_offset >= (int) (*image)->rows))
3735           continue;
3736         image_view=AcquireAuthenticCacheView(*image,exception);
3737         switch (method)
3738         {
3739           case PointMethod:
3740           default:
3741           {
3742             /*
3743               Update color information using point algorithm.
3744             */
3745             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3746               return(MagickFalse);
3747             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3748               (ssize_t) y_offset,1,1,exception);
3749             if (q == (Quantum *) NULL)
3750               break;
3751             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3752             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3753             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3754             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3755             break;
3756           }
3757           case ReplaceMethod:
3758           {
3759             PixelInfo
3760               pixel,
3761               target;
3762
3763             /*
3764               Update color information using replace algorithm.
3765             */
3766             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3767               x_offset,(ssize_t) y_offset,&target,exception);
3768             if ((*image)->storage_class == DirectClass)
3769               {
3770                 for (y=0; y < (int) (*image)->rows; y++)
3771                 {
3772                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3773                     (*image)->columns,1,exception);
3774                   if (q == (Quantum *) NULL)
3775                     break;
3776                   for (x=0; x < (int) (*image)->columns; x++)
3777                   {
3778                     GetPixelInfoPixel(*image,q,&pixel);
3779                     if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3780                       {
3781                         SetPixelRed(*image,ScaleShortToQuantum(
3782                           color.red),q);
3783                         SetPixelGreen(*image,ScaleShortToQuantum(
3784                           color.green),q);
3785                         SetPixelBlue(*image,ScaleShortToQuantum(
3786                           color.blue),q);
3787                       }
3788                     q+=GetPixelChannels(*image);
3789                   }
3790                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3791                     break;
3792                 }
3793               }
3794             else
3795               {
3796                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3797                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3798                     {
3799                       (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3800                         color.red);
3801                       (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3802                         color.green);
3803                       (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3804                         color.blue);
3805                     }
3806                 (void) SyncImage(*image,exception);
3807               }
3808             break;
3809           }
3810           case FloodfillMethod:
3811           case FillToBorderMethod:
3812           {
3813             DrawInfo
3814               *draw_info;
3815
3816             PixelInfo
3817               target;
3818
3819             /*
3820               Update color information using floodfill algorithm.
3821             */
3822             (void) GetOneVirtualPixelInfo(*image,
3823               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3824               y_offset,&target,exception);
3825             if (method == FillToBorderMethod)
3826               {
3827                 target.red=(MagickRealType)
3828                   ScaleShortToQuantum(border_color.red);
3829                 target.green=(MagickRealType)
3830                   ScaleShortToQuantum(border_color.green);
3831                 target.blue=(MagickRealType)
3832                   ScaleShortToQuantum(border_color.blue);
3833               }
3834             draw_info=CloneDrawInfo(resource_info->image_info,
3835               (DrawInfo *) NULL);
3836             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3837               AllCompliance,&draw_info->fill,exception);
3838             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3839               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3840               MagickFalse : MagickTrue,exception);
3841             draw_info=DestroyDrawInfo(draw_info);
3842             break;
3843           }
3844           case ResetMethod:
3845           {
3846             /*
3847               Update color information using reset algorithm.
3848             */
3849             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3850               return(MagickFalse);
3851             for (y=0; y < (int) (*image)->rows; y++)
3852             {
3853               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3854                 (*image)->columns,1,exception);
3855               if (q == (Quantum *) NULL)
3856                 break;
3857               for (x=0; x < (int) (*image)->columns; x++)
3858               {
3859                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3860                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3861                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3862                 q+=GetPixelChannels(*image);
3863               }
3864               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3865                 break;
3866             }
3867             break;
3868           }
3869         }
3870         image_view=DestroyCacheView(image_view);
3871         state&=(~UpdateConfigurationState);
3872       }
3873   } while ((state & ExitState) == 0);
3874   (void) XSelectInput(display,windows->image.id,
3875     windows->image.attributes.event_mask);
3876   XSetCursorState(display,windows,MagickFalse);
3877   (void) XFreeCursor(display,cursor);
3878   return(MagickTrue);
3879 }
3880 \f
3881 /*
3882 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3883 %                                                                             %
3884 %                                                                             %
3885 %                                                                             %
3886 +   X C o m p o s i t e I m a g e                                             %
3887 %                                                                             %
3888 %                                                                             %
3889 %                                                                             %
3890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3891 %
3892 %  XCompositeImage() requests an image name from the user, reads the image and
3893 %  composites it with the X window image at a location the user chooses with
3894 %  the pointer.
3895 %
3896 %  The format of the XCompositeImage method is:
3897 %
3898 %      MagickBooleanType XCompositeImage(Display *display,
3899 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3900 %        ExceptionInfo *exception)
3901 %
3902 %  A description of each parameter follows:
3903 %
3904 %    o display: Specifies a connection to an X server;  returned from
3905 %      XOpenDisplay.
3906 %
3907 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3908 %
3909 %    o windows: Specifies a pointer to a XWindows structure.
3910 %
3911 %    o image: the image; returned from ReadImage.
3912 %
3913 %    o exception: return any errors or warnings in this structure.
3914 %
3915 */
3916 static MagickBooleanType XCompositeImage(Display *display,
3917   XResourceInfo *resource_info,XWindows *windows,Image *image,
3918   ExceptionInfo *exception)
3919 {
3920   static char
3921     displacement_geometry[MaxTextExtent] = "30x30",
3922     filename[MaxTextExtent] = "\0";
3923
3924   static const char
3925     *CompositeMenu[] =
3926     {
3927       "Operators",
3928       "Dissolve",
3929       "Displace",
3930       "Help",
3931       "Dismiss",
3932       (char *) NULL
3933     };
3934
3935   static CompositeOperator
3936     compose = CopyCompositeOp;
3937
3938   static const ModeType
3939     CompositeCommands[] =
3940     {
3941       CompositeOperatorsCommand,
3942       CompositeDissolveCommand,
3943       CompositeDisplaceCommand,
3944       CompositeHelpCommand,
3945       CompositeDismissCommand
3946     };
3947
3948   char
3949     text[MaxTextExtent];
3950
3951   Cursor
3952     cursor;
3953
3954   Image
3955     *composite_image;
3956
3957   int
3958     entry,
3959     id,
3960     x,
3961     y;
3962
3963   MagickRealType
3964     blend,
3965     scale_factor;
3966
3967   RectangleInfo
3968     highlight_info,
3969     composite_info;
3970
3971   unsigned int
3972     height,
3973     width;
3974
3975   size_t
3976     state;
3977
3978   XEvent
3979     event;
3980
3981   /*
3982     Request image file name from user.
3983   */
3984   XFileBrowserWidget(display,windows,"Composite",filename);
3985   if (*filename == '\0')
3986     return(MagickTrue);
3987   /*
3988     Read image.
3989   */
3990   XSetCursorState(display,windows,MagickTrue);
3991   XCheckRefreshWindows(display,windows);
3992   (void) CopyMagickString(resource_info->image_info->filename,filename,
3993     MaxTextExtent);
3994   composite_image=ReadImage(resource_info->image_info,exception);
3995   CatchException(exception);
3996   XSetCursorState(display,windows,MagickFalse);
3997   if (composite_image == (Image *) NULL)
3998     return(MagickFalse);
3999   /*
4000     Map Command widget.
4001   */
4002   (void) CloneString(&windows->command.name,"Composite");
4003   windows->command.data=1;
4004   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4005   (void) XMapRaised(display,windows->command.id);
4006   XClientMessage(display,windows->image.id,windows->im_protocols,
4007     windows->im_update_widget,CurrentTime);
4008   /*
4009     Track pointer until button 1 is pressed.
4010   */
4011   XQueryPosition(display,windows->image.id,&x,&y);
4012   (void) XSelectInput(display,windows->image.id,
4013     windows->image.attributes.event_mask | PointerMotionMask);
4014   composite_info.x=(ssize_t) windows->image.x+x;
4015   composite_info.y=(ssize_t) windows->image.y+y;
4016   composite_info.width=0;
4017   composite_info.height=0;
4018   cursor=XCreateFontCursor(display,XC_ul_angle);
4019   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4020   blend=0.0;
4021   state=DefaultState;
4022   do
4023   {
4024     if (windows->info.mapped != MagickFalse)
4025       {
4026         /*
4027           Display pointer position.
4028         */
4029         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4030           (long) composite_info.x,(long) composite_info.y);
4031         XInfoWidget(display,windows,text);
4032       }
4033     highlight_info=composite_info;
4034     highlight_info.x=composite_info.x-windows->image.x;
4035     highlight_info.y=composite_info.y-windows->image.y;
4036     XHighlightRectangle(display,windows->image.id,
4037       windows->image.highlight_context,&highlight_info);
4038     /*
4039       Wait for next event.
4040     */
4041     XScreenEvent(display,windows,&event,exception);
4042     XHighlightRectangle(display,windows->image.id,
4043       windows->image.highlight_context,&highlight_info);
4044     if (event.xany.window == windows->command.id)
4045       {
4046         /*
4047           Select a command from the Command widget.
4048         */
4049         id=XCommandWidget(display,windows,CompositeMenu,&event);
4050         if (id < 0)
4051           continue;
4052         switch (CompositeCommands[id])
4053         {
4054           case CompositeOperatorsCommand:
4055           {
4056             char
4057               command[MaxTextExtent],
4058               **operators;
4059
4060             /*
4061               Select a command from the pop-up menu.
4062             */
4063             operators=GetCommandOptions(MagickComposeOptions);
4064             if (operators == (char **) NULL)
4065               break;
4066             entry=XMenuWidget(display,windows,CompositeMenu[id],
4067               (const char **) operators,command);
4068             if (entry >= 0)
4069               compose=(CompositeOperator) ParseCommandOption(
4070                 MagickComposeOptions,MagickFalse,operators[entry]);
4071             operators=DestroyStringList(operators);
4072             break;
4073           }
4074           case CompositeDissolveCommand:
4075           {
4076             static char
4077               factor[MaxTextExtent] = "20.0";
4078
4079             /*
4080               Dissolve the two images a given percent.
4081             */
4082             (void) XSetFunction(display,windows->image.highlight_context,
4083               GXcopy);
4084             (void) XDialogWidget(display,windows,"Dissolve",
4085               "Enter the blend factor (0.0 - 99.9%):",factor);
4086             (void) XSetFunction(display,windows->image.highlight_context,
4087               GXinvert);
4088             if (*factor == '\0')
4089               break;
4090             blend=StringToDouble(factor,(char **) NULL);
4091             compose=DissolveCompositeOp;
4092             break;
4093           }
4094           case CompositeDisplaceCommand:
4095           {
4096             /*
4097               Get horizontal and vertical scale displacement geometry.
4098             */
4099             (void) XSetFunction(display,windows->image.highlight_context,
4100               GXcopy);
4101             (void) XDialogWidget(display,windows,"Displace",
4102               "Enter the horizontal and vertical scale:",displacement_geometry);
4103             (void) XSetFunction(display,windows->image.highlight_context,
4104               GXinvert);
4105             if (*displacement_geometry == '\0')
4106               break;
4107             compose=DisplaceCompositeOp;
4108             break;
4109           }
4110           case CompositeHelpCommand:
4111           {
4112             (void) XSetFunction(display,windows->image.highlight_context,
4113               GXcopy);
4114             XTextViewWidget(display,resource_info,windows,MagickFalse,
4115               "Help Viewer - Image Composite",ImageCompositeHelp);
4116             (void) XSetFunction(display,windows->image.highlight_context,
4117               GXinvert);
4118             break;
4119           }
4120           case CompositeDismissCommand:
4121           {
4122             /*
4123               Prematurely exit.
4124             */
4125             state|=EscapeState;
4126             state|=ExitState;
4127             break;
4128           }
4129           default:
4130             break;
4131         }
4132         continue;
4133       }
4134     switch (event.type)
4135     {
4136       case ButtonPress:
4137       {
4138         if (image->debug != MagickFalse)
4139           (void) LogMagickEvent(X11Event,GetMagickModule(),
4140             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4141             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4142         if (event.xbutton.button != Button1)
4143           break;
4144         if (event.xbutton.window != windows->image.id)
4145           break;
4146         /*
4147           Change cursor.
4148         */
4149         composite_info.width=composite_image->columns;
4150         composite_info.height=composite_image->rows;
4151         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4152         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4153         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4154         break;
4155       }
4156       case ButtonRelease:
4157       {
4158         if (image->debug != MagickFalse)
4159           (void) LogMagickEvent(X11Event,GetMagickModule(),
4160             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4161             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4162         if (event.xbutton.button != Button1)
4163           break;
4164         if (event.xbutton.window != windows->image.id)
4165           break;
4166         if ((composite_info.width != 0) && (composite_info.height != 0))
4167           {
4168             /*
4169               User has selected the location of the composite image.
4170             */
4171             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4172             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4173             state|=ExitState;
4174           }
4175         break;
4176       }
4177       case Expose:
4178         break;
4179       case KeyPress:
4180       {
4181         char
4182           command[MaxTextExtent];
4183
4184         KeySym
4185           key_symbol;
4186
4187         int
4188           length;
4189
4190         if (event.xkey.window != windows->image.id)
4191           break;
4192         /*
4193           Respond to a user key press.
4194         */
4195         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4196           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4197         *(command+length)='\0';
4198         if (image->debug != MagickFalse)
4199           (void) LogMagickEvent(X11Event,GetMagickModule(),
4200             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4201         switch ((int) key_symbol)
4202         {
4203           case XK_Escape:
4204           case XK_F20:
4205           {
4206             /*
4207               Prematurely exit.
4208             */
4209             composite_image=DestroyImage(composite_image);
4210             state|=EscapeState;
4211             state|=ExitState;
4212             break;
4213           }
4214           case XK_F1:
4215           case XK_Help:
4216           {
4217             (void) XSetFunction(display,windows->image.highlight_context,
4218               GXcopy);
4219             XTextViewWidget(display,resource_info,windows,MagickFalse,
4220               "Help Viewer - Image Composite",ImageCompositeHelp);
4221             (void) XSetFunction(display,windows->image.highlight_context,
4222               GXinvert);
4223             break;
4224           }
4225           default:
4226           {
4227             (void) XBell(display,0);
4228             break;
4229           }
4230         }
4231         break;
4232       }
4233       case MotionNotify:
4234       {
4235         /*
4236           Map and unmap Info widget as text cursor crosses its boundaries.
4237         */
4238         x=event.xmotion.x;
4239         y=event.xmotion.y;
4240         if (windows->info.mapped != MagickFalse)
4241           {
4242             if ((x < (int) (windows->info.x+windows->info.width)) &&
4243                 (y < (int) (windows->info.y+windows->info.height)))
4244               (void) XWithdrawWindow(display,windows->info.id,
4245                 windows->info.screen);
4246           }
4247         else
4248           if ((x > (int) (windows->info.x+windows->info.width)) ||
4249               (y > (int) (windows->info.y+windows->info.height)))
4250             (void) XMapWindow(display,windows->info.id);
4251         composite_info.x=(ssize_t) windows->image.x+x;
4252         composite_info.y=(ssize_t) windows->image.y+y;
4253         break;
4254       }
4255       default:
4256       {
4257         if (image->debug != MagickFalse)
4258           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4259             event.type);
4260         break;
4261       }
4262     }
4263   } while ((state & ExitState) == 0);
4264   (void) XSelectInput(display,windows->image.id,
4265     windows->image.attributes.event_mask);
4266   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4267   XSetCursorState(display,windows,MagickFalse);
4268   (void) XFreeCursor(display,cursor);
4269   if ((state & EscapeState) != 0)
4270     return(MagickTrue);
4271   /*
4272     Image compositing is relative to image configuration.
4273   */
4274   XSetCursorState(display,windows,MagickTrue);
4275   XCheckRefreshWindows(display,windows);
4276   width=(unsigned int) image->columns;
4277   height=(unsigned int) image->rows;
4278   x=0;
4279   y=0;
4280   if (windows->image.crop_geometry != (char *) NULL)
4281     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4282   scale_factor=(MagickRealType) width/windows->image.ximage->width;
4283   composite_info.x+=x;
4284   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4285   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4286   scale_factor=(MagickRealType) height/windows->image.ximage->height;
4287   composite_info.y+=y;
4288   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4289   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4290   if ((composite_info.width != composite_image->columns) ||
4291       (composite_info.height != composite_image->rows))
4292     {
4293       Image
4294         *resize_image;
4295
4296       /*
4297         Scale composite image.
4298       */
4299       resize_image=ResizeImage(composite_image,composite_info.width,
4300         composite_info.height,composite_image->filter,exception);
4301       composite_image=DestroyImage(composite_image);
4302       if (resize_image == (Image *) NULL)
4303         {
4304           XSetCursorState(display,windows,MagickFalse);
4305           return(MagickFalse);
4306         }
4307       composite_image=resize_image;
4308     }
4309   if (compose == DisplaceCompositeOp)
4310     (void) SetImageArtifact(composite_image,"compose:args",
4311       displacement_geometry);
4312   if (blend != 0.0)
4313     {
4314       CacheView
4315         *image_view;
4316
4317       int
4318         y;
4319
4320       Quantum
4321         opacity;
4322
4323       register int
4324         x;
4325
4326       register Quantum
4327         *q;
4328
4329       /*
4330         Create mattes for blending.
4331       */
4332       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4333       opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4334         ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4335       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4336         return(MagickFalse);
4337       image->matte=MagickTrue;
4338       image_view=AcquireAuthenticCacheView(image,exception);
4339       for (y=0; y < (int) image->rows; y++)
4340       {
4341         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4342           exception);
4343         if (q == (Quantum *) NULL)
4344           break;
4345         for (x=0; x < (int) image->columns; x++)
4346         {
4347           SetPixelAlpha(image,opacity,q);
4348           q+=GetPixelChannels(image);
4349         }
4350         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4351           break;
4352       }
4353       image_view=DestroyCacheView(image_view);
4354     }
4355   /*
4356     Composite image with X Image window.
4357   */
4358   (void) CompositeImage(image,composite_image,compose,MagickTrue,
4359     composite_info.x,composite_info.y,exception);
4360   composite_image=DestroyImage(composite_image);
4361   XSetCursorState(display,windows,MagickFalse);
4362   /*
4363     Update image configuration.
4364   */
4365   XConfigureImageColormap(display,resource_info,windows,image,exception);
4366   (void) XConfigureImage(display,resource_info,windows,image,exception);
4367   return(MagickTrue);
4368 }
4369 \f
4370 /*
4371 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4372 %                                                                             %
4373 %                                                                             %
4374 %                                                                             %
4375 +   X C o n f i g u r e I m a g e                                             %
4376 %                                                                             %
4377 %                                                                             %
4378 %                                                                             %
4379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4380 %
4381 %  XConfigureImage() creates a new X image.  It also notifies the window
4382 %  manager of the new image size and configures the transient widows.
4383 %
4384 %  The format of the XConfigureImage method is:
4385 %
4386 %      MagickBooleanType XConfigureImage(Display *display,
4387 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4388 %        ExceptionInfo *exception)
4389 %
4390 %  A description of each parameter follows:
4391 %
4392 %    o display: Specifies a connection to an X server; returned from
4393 %      XOpenDisplay.
4394 %
4395 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4396 %
4397 %    o windows: Specifies a pointer to a XWindows structure.
4398 %
4399 %    o image: the image.
4400 %
4401 %    o exception: return any errors or warnings in this structure.
4402 %
4403 %    o exception: return any errors or warnings in this structure.
4404 %
4405 */
4406 static MagickBooleanType XConfigureImage(Display *display,
4407   XResourceInfo *resource_info,XWindows *windows,Image *image,
4408   ExceptionInfo *exception)
4409 {
4410   char
4411     geometry[MaxTextExtent];
4412
4413   MagickStatusType
4414     status;
4415
4416   size_t
4417     mask,
4418     height,
4419     width;
4420
4421   ssize_t
4422     x,
4423     y;
4424
4425   XSizeHints
4426     *size_hints;
4427
4428   XWindowChanges
4429     window_changes;
4430
4431   /*
4432     Dismiss if window dimensions are zero.
4433   */
4434   width=(unsigned int) windows->image.window_changes.width;
4435   height=(unsigned int) windows->image.window_changes.height;
4436   if (image->debug != MagickFalse)
4437     (void) LogMagickEvent(X11Event,GetMagickModule(),
4438       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4439       windows->image.ximage->height,(double) width,(double) height);
4440   if ((width*height) == 0)
4441     return(MagickTrue);
4442   x=0;
4443   y=0;
4444   /*
4445     Resize image to fit Image window dimensions.
4446   */
4447   XSetCursorState(display,windows,MagickTrue);
4448   (void) XFlush(display);
4449   if (((int) width != windows->image.ximage->width) ||
4450       ((int) height != windows->image.ximage->height))
4451     image->taint=MagickTrue;
4452   windows->magnify.x=(int)
4453     width*windows->magnify.x/windows->image.ximage->width;
4454   windows->magnify.y=(int)
4455     height*windows->magnify.y/windows->image.ximage->height;
4456   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4457   windows->image.y=(int)
4458     (height*windows->image.y/windows->image.ximage->height);
4459   status=XMakeImage(display,resource_info,&windows->image,image,
4460     (unsigned int) width,(unsigned int) height,exception);
4461   if (status == MagickFalse)
4462     XNoticeWidget(display,windows,"Unable to configure X image:",
4463       windows->image.name);
4464   /*
4465     Notify window manager of the new configuration.
4466   */
4467   if (resource_info->image_geometry != (char *) NULL)
4468     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4469       resource_info->image_geometry);
4470   else
4471     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4472       XDisplayWidth(display,windows->image.screen),
4473       XDisplayHeight(display,windows->image.screen));
4474   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4475   window_changes.width=(int) width;
4476   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4477     window_changes.width=XDisplayWidth(display,windows->image.screen);
4478   window_changes.height=(int) height;
4479   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4480     window_changes.height=XDisplayHeight(display,windows->image.screen);
4481   mask=(size_t) (CWWidth | CWHeight);
4482   if (resource_info->backdrop)
4483     {
4484       mask|=CWX | CWY;
4485       window_changes.x=(int)
4486         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4487       window_changes.y=(int)
4488         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4489     }
4490   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4491     (unsigned int) mask,&window_changes);
4492   (void) XClearWindow(display,windows->image.id);
4493   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4494   /*
4495     Update Magnify window configuration.
4496   */
4497   if (windows->magnify.mapped != MagickFalse)
4498     XMakeMagnifyImage(display,windows,exception);
4499   windows->pan.crop_geometry=windows->image.crop_geometry;
4500   XBestIconSize(display,&windows->pan,image);
4501   while (((windows->pan.width << 1) < MaxIconSize) &&
4502          ((windows->pan.height << 1) < MaxIconSize))
4503   {
4504     windows->pan.width<<=1;
4505     windows->pan.height<<=1;
4506   }
4507   if (windows->pan.geometry != (char *) NULL)
4508     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4509       &windows->pan.width,&windows->pan.height);
4510   window_changes.width=(int) windows->pan.width;
4511   window_changes.height=(int) windows->pan.height;
4512   size_hints=XAllocSizeHints();
4513   if (size_hints != (XSizeHints *) NULL)
4514     {
4515       /*
4516         Set new size hints.
4517       */
4518       size_hints->flags=PSize | PMinSize | PMaxSize;
4519       size_hints->width=window_changes.width;
4520       size_hints->height=window_changes.height;
4521       size_hints->min_width=size_hints->width;
4522       size_hints->min_height=size_hints->height;
4523       size_hints->max_width=size_hints->width;
4524       size_hints->max_height=size_hints->height;
4525       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4526       (void) XFree((void *) size_hints);
4527     }
4528   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4529     (unsigned int) (CWWidth | CWHeight),&window_changes);
4530   /*
4531     Update icon window configuration.
4532   */
4533   windows->icon.crop_geometry=windows->image.crop_geometry;
4534   XBestIconSize(display,&windows->icon,image);
4535   window_changes.width=(int) windows->icon.width;
4536   window_changes.height=(int) windows->icon.height;
4537   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4538     (unsigned int) (CWWidth | CWHeight),&window_changes);
4539   XSetCursorState(display,windows,MagickFalse);
4540   return(status != 0 ? MagickTrue : MagickFalse);
4541 }
4542 \f
4543 /*
4544 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4545 %                                                                             %
4546 %                                                                             %
4547 %                                                                             %
4548 +   X C r o p I m a g e                                                       %
4549 %                                                                             %
4550 %                                                                             %
4551 %                                                                             %
4552 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4553 %
4554 %  XCropImage() allows the user to select a region of the image and crop, copy,
4555 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4556 %  the image with XPasteImage.
4557 %
4558 %  The format of the XCropImage method is:
4559 %
4560 %      MagickBooleanType XCropImage(Display *display,
4561 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4562 %        const ClipboardMode mode,ExceptionInfo *exception)
4563 %
4564 %  A description of each parameter follows:
4565 %
4566 %    o display: Specifies a connection to an X server; returned from
4567 %      XOpenDisplay.
4568 %
4569 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4570 %
4571 %    o windows: Specifies a pointer to a XWindows structure.
4572 %
4573 %    o image: the image; returned from ReadImage.
4574 %
4575 %    o mode: This unsigned value specified whether the image should be
4576 %      cropped, copied, or cut.
4577 %
4578 %    o exception: return any errors or warnings in this structure.
4579 %
4580 */
4581 static MagickBooleanType XCropImage(Display *display,
4582   XResourceInfo *resource_info,XWindows *windows,Image *image,
4583   const ClipboardMode mode,ExceptionInfo *exception)
4584 {
4585   static const char
4586     *CropModeMenu[] =
4587     {
4588       "Help",
4589       "Dismiss",
4590       (char *) NULL
4591     },
4592     *RectifyModeMenu[] =
4593     {
4594       "Crop",
4595       "Help",
4596       "Dismiss",
4597       (char *) NULL
4598     };
4599
4600   static const ModeType
4601     CropCommands[] =
4602     {
4603       CropHelpCommand,
4604       CropDismissCommand
4605     },
4606     RectifyCommands[] =
4607     {
4608       RectifyCopyCommand,
4609       RectifyHelpCommand,
4610       RectifyDismissCommand
4611     };
4612
4613   CacheView
4614     *image_view;
4615
4616   char
4617     command[MaxTextExtent],
4618     text[MaxTextExtent];
4619
4620   Cursor
4621     cursor;
4622
4623   int
4624     id,
4625     x,
4626     y;
4627
4628   KeySym
4629     key_symbol;
4630
4631   Image
4632     *crop_image;
4633
4634   MagickRealType
4635     scale_factor;
4636
4637   RectangleInfo
4638     crop_info,
4639     highlight_info;
4640
4641   register Quantum
4642     *q;
4643
4644   unsigned int
4645     height,
4646     width;
4647
4648   size_t
4649     state;
4650
4651   XEvent
4652     event;
4653
4654   /*
4655     Map Command widget.
4656   */
4657   switch (mode)
4658   {
4659     case CopyMode:
4660     {
4661       (void) CloneString(&windows->command.name,"Copy");
4662       break;
4663     }
4664     case CropMode:
4665     {
4666       (void) CloneString(&windows->command.name,"Crop");
4667       break;
4668     }
4669     case CutMode:
4670     {
4671       (void) CloneString(&windows->command.name,"Cut");
4672       break;
4673     }
4674   }
4675   RectifyModeMenu[0]=windows->command.name;
4676   windows->command.data=0;
4677   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4678   (void) XMapRaised(display,windows->command.id);
4679   XClientMessage(display,windows->image.id,windows->im_protocols,
4680     windows->im_update_widget,CurrentTime);
4681   /*
4682     Track pointer until button 1 is pressed.
4683   */
4684   XQueryPosition(display,windows->image.id,&x,&y);
4685   (void) XSelectInput(display,windows->image.id,
4686     windows->image.attributes.event_mask | PointerMotionMask);
4687   crop_info.x=(ssize_t) windows->image.x+x;
4688   crop_info.y=(ssize_t) windows->image.y+y;
4689   crop_info.width=0;
4690   crop_info.height=0;
4691   cursor=XCreateFontCursor(display,XC_fleur);
4692   state=DefaultState;
4693   do
4694   {
4695     if (windows->info.mapped != MagickFalse)
4696       {
4697         /*
4698           Display pointer position.
4699         */
4700         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4701           (long) crop_info.x,(long) crop_info.y);
4702         XInfoWidget(display,windows,text);
4703       }
4704     /*
4705       Wait for next event.
4706     */
4707     XScreenEvent(display,windows,&event,exception);
4708     if (event.xany.window == windows->command.id)
4709       {
4710         /*
4711           Select a command from the Command widget.
4712         */
4713         id=XCommandWidget(display,windows,CropModeMenu,&event);
4714         if (id < 0)
4715           continue;
4716         switch (CropCommands[id])
4717         {
4718           case CropHelpCommand:
4719           {
4720             switch (mode)
4721             {
4722               case CopyMode:
4723               {
4724                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4725                   "Help Viewer - Image Copy",ImageCopyHelp);
4726                 break;
4727               }
4728               case CropMode:
4729               {
4730                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4731                   "Help Viewer - Image Crop",ImageCropHelp);
4732                 break;
4733               }
4734               case CutMode:
4735               {
4736                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4737                   "Help Viewer - Image Cut",ImageCutHelp);
4738                 break;
4739               }
4740             }
4741             break;
4742           }
4743           case CropDismissCommand:
4744           {
4745             /*
4746               Prematurely exit.
4747             */
4748             state|=EscapeState;
4749             state|=ExitState;
4750             break;
4751           }
4752           default:
4753             break;
4754         }
4755         continue;
4756       }
4757     switch (event.type)
4758     {
4759       case ButtonPress:
4760       {
4761         if (event.xbutton.button != Button1)
4762           break;
4763         if (event.xbutton.window != windows->image.id)
4764           break;
4765         /*
4766           Note first corner of cropping rectangle-- exit loop.
4767         */
4768         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4769         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4770         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4771         state|=ExitState;
4772         break;
4773       }
4774       case ButtonRelease:
4775         break;
4776       case Expose:
4777         break;
4778       case KeyPress:
4779       {
4780         if (event.xkey.window != windows->image.id)
4781           break;
4782         /*
4783           Respond to a user key press.
4784         */
4785         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4786           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4787         switch ((int) key_symbol)
4788         {
4789           case XK_Escape:
4790           case XK_F20:
4791           {
4792             /*
4793               Prematurely exit.
4794             */
4795             state|=EscapeState;
4796             state|=ExitState;
4797             break;
4798           }
4799           case XK_F1:
4800           case XK_Help:
4801           {
4802             switch (mode)
4803             {
4804               case CopyMode:
4805               {
4806                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4807                   "Help Viewer - Image Copy",ImageCopyHelp);
4808                 break;
4809               }
4810               case CropMode:
4811               {
4812                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4813                   "Help Viewer - Image Crop",ImageCropHelp);
4814                 break;
4815               }
4816               case CutMode:
4817               {
4818                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4819                   "Help Viewer - Image Cut",ImageCutHelp);
4820                 break;
4821               }
4822             }
4823             break;
4824           }
4825           default:
4826           {
4827             (void) XBell(display,0);
4828             break;
4829           }
4830         }
4831         break;
4832       }
4833       case MotionNotify:
4834       {
4835         if (event.xmotion.window != windows->image.id)
4836           break;
4837         /*
4838           Map and unmap Info widget as text cursor crosses its boundaries.
4839         */
4840         x=event.xmotion.x;
4841         y=event.xmotion.y;
4842         if (windows->info.mapped != MagickFalse)
4843           {
4844             if ((x < (int) (windows->info.x+windows->info.width)) &&
4845                 (y < (int) (windows->info.y+windows->info.height)))
4846               (void) XWithdrawWindow(display,windows->info.id,
4847                 windows->info.screen);
4848           }
4849         else
4850           if ((x > (int) (windows->info.x+windows->info.width)) ||
4851               (y > (int) (windows->info.y+windows->info.height)))
4852             (void) XMapWindow(display,windows->info.id);
4853         crop_info.x=(ssize_t) windows->image.x+x;
4854         crop_info.y=(ssize_t) windows->image.y+y;
4855         break;
4856       }
4857       default:
4858         break;
4859     }
4860   } while ((state & ExitState) == 0);
4861   (void) XSelectInput(display,windows->image.id,
4862     windows->image.attributes.event_mask);
4863   if ((state & EscapeState) != 0)
4864     {
4865       /*
4866         User want to exit without cropping.
4867       */
4868       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4869       (void) XFreeCursor(display,cursor);
4870       return(MagickTrue);
4871     }
4872   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4873   do
4874   {
4875     /*
4876       Size rectangle as pointer moves until the mouse button is released.
4877     */
4878     x=(int) crop_info.x;
4879     y=(int) crop_info.y;
4880     crop_info.width=0;
4881     crop_info.height=0;
4882     state=DefaultState;
4883     do
4884     {
4885       highlight_info=crop_info;
4886       highlight_info.x=crop_info.x-windows->image.x;
4887       highlight_info.y=crop_info.y-windows->image.y;
4888       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4889         {
4890           /*
4891             Display info and draw cropping rectangle.
4892           */
4893           if (windows->info.mapped == MagickFalse)
4894             (void) XMapWindow(display,windows->info.id);
4895           (void) FormatLocaleString(text,MaxTextExtent,
4896             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4897             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4898           XInfoWidget(display,windows,text);
4899           XHighlightRectangle(display,windows->image.id,
4900             windows->image.highlight_context,&highlight_info);
4901         }
4902       else
4903         if (windows->info.mapped != MagickFalse)
4904           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4905       /*
4906         Wait for next event.
4907       */
4908       XScreenEvent(display,windows,&event,exception);
4909       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4910         XHighlightRectangle(display,windows->image.id,
4911           windows->image.highlight_context,&highlight_info);
4912       switch (event.type)
4913       {
4914         case ButtonPress:
4915         {
4916           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4917           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4918           break;
4919         }
4920         case ButtonRelease:
4921         {
4922           /*
4923             User has committed to cropping rectangle.
4924           */
4925           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4926           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4927           XSetCursorState(display,windows,MagickFalse);
4928           state|=ExitState;
4929           windows->command.data=0;
4930           (void) XCommandWidget(display,windows,RectifyModeMenu,
4931             (XEvent *) NULL);
4932           break;
4933         }
4934         case Expose:
4935           break;
4936         case MotionNotify:
4937         {
4938           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4939           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4940         }
4941         default:
4942           break;
4943       }
4944       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4945           ((state & ExitState) != 0))
4946         {
4947           /*
4948             Check boundary conditions.
4949           */
4950           if (crop_info.x < 0)
4951             crop_info.x=0;
4952           else
4953             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4954               crop_info.x=(ssize_t) windows->image.ximage->width;
4955           if ((int) crop_info.x < x)
4956             crop_info.width=(unsigned int) (x-crop_info.x);
4957           else
4958             {
4959               crop_info.width=(unsigned int) (crop_info.x-x);
4960               crop_info.x=(ssize_t) x;
4961             }
4962           if (crop_info.y < 0)
4963             crop_info.y=0;
4964           else
4965             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4966               crop_info.y=(ssize_t) windows->image.ximage->height;
4967           if ((int) crop_info.y < y)
4968             crop_info.height=(unsigned int) (y-crop_info.y);
4969           else
4970             {
4971               crop_info.height=(unsigned int) (crop_info.y-y);
4972               crop_info.y=(ssize_t) y;
4973             }
4974         }
4975     } while ((state & ExitState) == 0);
4976     /*
4977       Wait for user to grab a corner of the rectangle or press return.
4978     */
4979     state=DefaultState;
4980     (void) XMapWindow(display,windows->info.id);
4981     do
4982     {
4983       if (windows->info.mapped != MagickFalse)
4984         {
4985           /*
4986             Display pointer position.
4987           */
4988           (void) FormatLocaleString(text,MaxTextExtent,
4989             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4990             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4991           XInfoWidget(display,windows,text);
4992         }
4993       highlight_info=crop_info;
4994       highlight_info.x=crop_info.x-windows->image.x;
4995       highlight_info.y=crop_info.y-windows->image.y;
4996       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4997         {
4998           state|=EscapeState;
4999           state|=ExitState;
5000           break;
5001         }
5002       XHighlightRectangle(display,windows->image.id,
5003         windows->image.highlight_context,&highlight_info);
5004       XScreenEvent(display,windows,&event,exception);
5005       if (event.xany.window == windows->command.id)
5006         {
5007           /*
5008             Select a command from the Command widget.
5009           */
5010           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5011           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5012           (void) XSetFunction(display,windows->image.highlight_context,
5013             GXinvert);
5014           XHighlightRectangle(display,windows->image.id,
5015             windows->image.highlight_context,&highlight_info);
5016           if (id >= 0)
5017             switch (RectifyCommands[id])
5018             {
5019               case RectifyCopyCommand:
5020               {
5021                 state|=ExitState;
5022                 break;
5023               }
5024               case RectifyHelpCommand:
5025               {
5026                 (void) XSetFunction(display,windows->image.highlight_context,
5027                   GXcopy);
5028                 switch (mode)
5029                 {
5030                   case CopyMode:
5031                   {
5032                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5033                       "Help Viewer - Image Copy",ImageCopyHelp);
5034                     break;
5035                   }
5036                   case CropMode:
5037                   {
5038                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5039                       "Help Viewer - Image Crop",ImageCropHelp);
5040                     break;
5041                   }
5042                   case CutMode:
5043                   {
5044                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5045                       "Help Viewer - Image Cut",ImageCutHelp);
5046                     break;
5047                   }
5048                 }
5049                 (void) XSetFunction(display,windows->image.highlight_context,
5050                   GXinvert);
5051                 break;
5052               }
5053               case RectifyDismissCommand:
5054               {
5055                 /*
5056                   Prematurely exit.
5057                 */
5058                 state|=EscapeState;
5059                 state|=ExitState;
5060                 break;
5061               }
5062               default:
5063                 break;
5064             }
5065           continue;
5066         }
5067       XHighlightRectangle(display,windows->image.id,
5068         windows->image.highlight_context,&highlight_info);
5069       switch (event.type)
5070       {
5071         case ButtonPress:
5072         {
5073           if (event.xbutton.button != Button1)
5074             break;
5075           if (event.xbutton.window != windows->image.id)
5076             break;
5077           x=windows->image.x+event.xbutton.x;
5078           y=windows->image.y+event.xbutton.y;
5079           if ((x < (int) (crop_info.x+RoiDelta)) &&
5080               (x > (int) (crop_info.x-RoiDelta)) &&
5081               (y < (int) (crop_info.y+RoiDelta)) &&
5082               (y > (int) (crop_info.y-RoiDelta)))
5083             {
5084               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5085               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5086               state|=UpdateConfigurationState;
5087               break;
5088             }
5089           if ((x < (int) (crop_info.x+RoiDelta)) &&
5090               (x > (int) (crop_info.x-RoiDelta)) &&
5091               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5092               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5093             {
5094               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5095               state|=UpdateConfigurationState;
5096               break;
5097             }
5098           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5099               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5100               (y < (int) (crop_info.y+RoiDelta)) &&
5101               (y > (int) (crop_info.y-RoiDelta)))
5102             {
5103               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5104               state|=UpdateConfigurationState;
5105               break;
5106             }
5107           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5108               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5109               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5110               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5111             {
5112               state|=UpdateConfigurationState;
5113               break;
5114             }
5115         }
5116         case ButtonRelease:
5117         {
5118           if (event.xbutton.window == windows->pan.id)
5119             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5120                 (highlight_info.y != crop_info.y-windows->image.y))
5121               XHighlightRectangle(display,windows->image.id,
5122                 windows->image.highlight_context,&highlight_info);
5123           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5124             event.xbutton.time);
5125           break;
5126         }
5127         case Expose:
5128         {
5129           if (event.xexpose.window == windows->image.id)
5130             if (event.xexpose.count == 0)
5131               {
5132                 event.xexpose.x=(int) highlight_info.x;
5133                 event.xexpose.y=(int) highlight_info.y;
5134                 event.xexpose.width=(int) highlight_info.width;
5135                 event.xexpose.height=(int) highlight_info.height;
5136                 XRefreshWindow(display,&windows->image,&event);
5137               }
5138           if (event.xexpose.window == windows->info.id)
5139             if (event.xexpose.count == 0)
5140               XInfoWidget(display,windows,text);
5141           break;
5142         }
5143         case KeyPress:
5144         {
5145           if (event.xkey.window != windows->image.id)
5146             break;
5147           /*
5148             Respond to a user key press.
5149           */
5150           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5151             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5152           switch ((int) key_symbol)
5153           {
5154             case XK_Escape:
5155             case XK_F20:
5156               state|=EscapeState;
5157             case XK_Return:
5158             {
5159               state|=ExitState;
5160               break;
5161             }
5162             case XK_Home:
5163             case XK_KP_Home:
5164             {
5165               crop_info.x=(ssize_t) (windows->image.width/2L-
5166                 crop_info.width/2L);
5167               crop_info.y=(ssize_t) (windows->image.height/2L-
5168                 crop_info.height/2L);
5169               break;
5170             }
5171             case XK_Left:
5172             case XK_KP_Left:
5173             {
5174               crop_info.x--;
5175               break;
5176             }
5177             case XK_Up:
5178             case XK_KP_Up:
5179             case XK_Next:
5180             {
5181               crop_info.y--;
5182               break;
5183             }
5184             case XK_Right:
5185             case XK_KP_Right:
5186             {
5187               crop_info.x++;
5188               break;
5189             }
5190             case XK_Prior:
5191             case XK_Down:
5192             case XK_KP_Down:
5193             {
5194               crop_info.y++;
5195               break;
5196             }
5197             case XK_F1:
5198             case XK_Help:
5199             {
5200               (void) XSetFunction(display,windows->image.highlight_context,
5201                 GXcopy);
5202               switch (mode)
5203               {
5204                 case CopyMode:
5205                 {
5206                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5207                     "Help Viewer - Image Copy",ImageCopyHelp);
5208                   break;
5209                 }
5210                 case CropMode:
5211                 {
5212                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5213                     "Help Viewer - Image Cropg",ImageCropHelp);
5214                   break;
5215                 }
5216                 case CutMode:
5217                 {
5218                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5219                     "Help Viewer - Image Cutg",ImageCutHelp);
5220                   break;
5221                 }
5222               }
5223               (void) XSetFunction(display,windows->image.highlight_context,
5224                 GXinvert);
5225               break;
5226             }
5227             default:
5228             {
5229               (void) XBell(display,0);
5230               break;
5231             }
5232           }
5233           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5234             event.xkey.time);
5235           break;
5236         }
5237         case KeyRelease:
5238           break;
5239         case MotionNotify:
5240         {
5241           if (event.xmotion.window != windows->image.id)
5242             break;
5243           /*
5244             Map and unmap Info widget as text cursor crosses its boundaries.
5245           */
5246           x=event.xmotion.x;
5247           y=event.xmotion.y;
5248           if (windows->info.mapped != MagickFalse)
5249             {
5250               if ((x < (int) (windows->info.x+windows->info.width)) &&
5251                   (y < (int) (windows->info.y+windows->info.height)))
5252                 (void) XWithdrawWindow(display,windows->info.id,
5253                   windows->info.screen);
5254             }
5255           else
5256             if ((x > (int) (windows->info.x+windows->info.width)) ||
5257                 (y > (int) (windows->info.y+windows->info.height)))
5258               (void) XMapWindow(display,windows->info.id);
5259           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5260           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5261           break;
5262         }
5263         case SelectionRequest:
5264         {
5265           XSelectionEvent
5266             notify;
5267
5268           XSelectionRequestEvent
5269             *request;
5270
5271           /*
5272             Set primary selection.
5273           */
5274           (void) FormatLocaleString(text,MaxTextExtent,
5275             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5276             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5277           request=(&(event.xselectionrequest));
5278           (void) XChangeProperty(request->display,request->requestor,
5279             request->property,request->target,8,PropModeReplace,
5280             (unsigned char *) text,(int) strlen(text));
5281           notify.type=SelectionNotify;
5282           notify.display=request->display;
5283           notify.requestor=request->requestor;
5284           notify.selection=request->selection;
5285           notify.target=request->target;
5286           notify.time=request->time;
5287           if (request->property == None)
5288             notify.property=request->target;
5289           else
5290             notify.property=request->property;
5291           (void) XSendEvent(request->display,request->requestor,False,0,
5292             (XEvent *) &notify);
5293         }
5294         default:
5295           break;
5296       }
5297       if ((state & UpdateConfigurationState) != 0)
5298         {
5299           (void) XPutBackEvent(display,&event);
5300           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5301           break;
5302         }
5303     } while ((state & ExitState) == 0);
5304   } while ((state & ExitState) == 0);
5305   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5306   XSetCursorState(display,windows,MagickFalse);
5307   if ((state & EscapeState) != 0)
5308     return(MagickTrue);
5309   if (mode == CropMode)
5310     if (((int) crop_info.width != windows->image.ximage->width) ||
5311         ((int) crop_info.height != windows->image.ximage->height))
5312       {
5313         /*
5314           Reconfigure Image window as defined by cropping rectangle.
5315         */
5316         XSetCropGeometry(display,windows,&crop_info,image);
5317         windows->image.window_changes.width=(int) crop_info.width;
5318         windows->image.window_changes.height=(int) crop_info.height;
5319         (void) XConfigureImage(display,resource_info,windows,image,exception);
5320         return(MagickTrue);
5321       }
5322   /*
5323     Copy image before applying image transforms.
5324   */
5325   XSetCursorState(display,windows,MagickTrue);
5326   XCheckRefreshWindows(display,windows);
5327   width=(unsigned int) image->columns;
5328   height=(unsigned int) image->rows;
5329   x=0;
5330   y=0;
5331   if (windows->image.crop_geometry != (char *) NULL)
5332     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5333   scale_factor=(MagickRealType) width/windows->image.ximage->width;
5334   crop_info.x+=x;
5335   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5336   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5337   scale_factor=(MagickRealType) height/windows->image.ximage->height;
5338   crop_info.y+=y;
5339   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5340   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5341   crop_image=CropImage(image,&crop_info,exception);
5342   XSetCursorState(display,windows,MagickFalse);
5343   if (crop_image == (Image *) NULL)
5344     return(MagickFalse);
5345   if (resource_info->copy_image != (Image *) NULL)
5346     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5347   resource_info->copy_image=crop_image;
5348   if (mode == CopyMode)
5349     {
5350       (void) XConfigureImage(display,resource_info,windows,image,exception);
5351       return(MagickTrue);
5352     }
5353   /*
5354     Cut image.
5355   */
5356   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5357     return(MagickFalse);
5358   image->matte=MagickTrue;
5359   image_view=AcquireAuthenticCacheView(image,exception);
5360   for (y=0; y < (int) crop_info.height; y++)
5361   {
5362     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5363       crop_info.width,1,exception);
5364     if (q == (Quantum *) NULL)
5365       break;
5366     for (x=0; x < (int) crop_info.width; x++)
5367     {
5368       SetPixelAlpha(image,TransparentAlpha,q);
5369       q+=GetPixelChannels(image);
5370     }
5371     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5372       break;
5373   }
5374   image_view=DestroyCacheView(image_view);
5375   /*
5376     Update image configuration.
5377   */
5378   XConfigureImageColormap(display,resource_info,windows,image,exception);
5379   (void) XConfigureImage(display,resource_info,windows,image,exception);
5380   return(MagickTrue);
5381 }
5382 \f
5383 /*
5384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5385 %                                                                             %
5386 %                                                                             %
5387 %                                                                             %
5388 +   X D r a w I m a g e                                                       %
5389 %                                                                             %
5390 %                                                                             %
5391 %                                                                             %
5392 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5393 %
5394 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5395 %  the image.
5396 %
5397 %  The format of the XDrawEditImage method is:
5398 %
5399 %      MagickBooleanType XDrawEditImage(Display *display,
5400 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5401 %        ExceptionInfo *exception)
5402 %
5403 %  A description of each parameter follows:
5404 %
5405 %    o display: Specifies a connection to an X server; returned from
5406 %      XOpenDisplay.
5407 %
5408 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5409 %
5410 %    o windows: Specifies a pointer to a XWindows structure.
5411 %
5412 %    o image: the image.
5413 %
5414 %    o exception: return any errors or warnings in this structure.
5415 %
5416 */
5417 static MagickBooleanType XDrawEditImage(Display *display,
5418   XResourceInfo *resource_info,XWindows *windows,Image **image,
5419   ExceptionInfo *exception)
5420 {
5421   static const char
5422     *DrawMenu[] =
5423     {
5424       "Element",
5425       "Color",
5426       "Stipple",
5427       "Width",
5428       "Undo",
5429       "Help",
5430       "Dismiss",
5431       (char *) NULL
5432     };
5433
5434   static ElementType
5435     element = PointElement;
5436
5437   static const ModeType
5438     DrawCommands[] =
5439     {
5440       DrawElementCommand,
5441       DrawColorCommand,
5442       DrawStippleCommand,
5443       DrawWidthCommand,
5444       DrawUndoCommand,
5445       DrawHelpCommand,
5446       DrawDismissCommand
5447     };
5448
5449   static Pixmap
5450     stipple = (Pixmap) NULL;
5451
5452   static unsigned int
5453     pen_id = 0,
5454     line_width = 1;
5455
5456   char
5457     command[MaxTextExtent],
5458     text[MaxTextExtent];
5459
5460   Cursor
5461     cursor;
5462
5463   int
5464     entry,
5465     id,
5466     number_coordinates,
5467     x,
5468     y;
5469
5470   MagickRealType
5471     degrees;
5472
5473   MagickStatusType
5474     status;
5475
5476   RectangleInfo
5477     rectangle_info;
5478
5479   register int
5480     i;
5481
5482   unsigned int
5483     distance,
5484     height,
5485     max_coordinates,
5486     width;
5487
5488   size_t
5489     state;
5490
5491   Window
5492     root_window;
5493
5494   XDrawInfo
5495     draw_info;
5496
5497   XEvent
5498     event;
5499
5500   XPoint
5501     *coordinate_info;
5502
5503   XSegment
5504     line_info;
5505
5506   /*
5507     Allocate polygon info.
5508   */
5509   max_coordinates=2048;
5510   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5511     sizeof(*coordinate_info));
5512   if (coordinate_info == (XPoint *) NULL)
5513     {
5514       (void) ThrowMagickException(exception,GetMagickModule(),
5515         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5516       return(MagickFalse);
5517     }
5518   /*
5519     Map Command widget.
5520   */
5521   (void) CloneString(&windows->command.name,"Draw");
5522   windows->command.data=4;
5523   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5524   (void) XMapRaised(display,windows->command.id);
5525   XClientMessage(display,windows->image.id,windows->im_protocols,
5526     windows->im_update_widget,CurrentTime);
5527   /*
5528     Wait for first button press.
5529   */
5530   root_window=XRootWindow(display,XDefaultScreen(display));
5531   draw_info.stencil=OpaqueStencil;
5532   status=MagickTrue;
5533   cursor=XCreateFontCursor(display,XC_tcross);
5534   for ( ; ; )
5535   {
5536     XQueryPosition(display,windows->image.id,&x,&y);
5537     (void) XSelectInput(display,windows->image.id,
5538       windows->image.attributes.event_mask | PointerMotionMask);
5539     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5540     state=DefaultState;
5541     do
5542     {
5543       if (windows->info.mapped != MagickFalse)
5544         {
5545           /*
5546             Display pointer position.
5547           */
5548           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5549             x+windows->image.x,y+windows->image.y);
5550           XInfoWidget(display,windows,text);
5551         }
5552       /*
5553         Wait for next event.
5554       */
5555       XScreenEvent(display,windows,&event,exception);
5556       if (event.xany.window == windows->command.id)
5557         {
5558           /*
5559             Select a command from the Command widget.
5560           */
5561           id=XCommandWidget(display,windows,DrawMenu,&event);
5562           if (id < 0)
5563             continue;
5564           switch (DrawCommands[id])
5565           {
5566             case DrawElementCommand:
5567             {
5568               static const char
5569                 *Elements[] =
5570                 {
5571                   "point",
5572                   "line",
5573                   "rectangle",
5574                   "fill rectangle",
5575                   "circle",
5576                   "fill circle",
5577                   "ellipse",
5578                   "fill ellipse",
5579                   "polygon",
5580                   "fill polygon",
5581                   (char *) NULL,
5582                 };
5583
5584               /*
5585                 Select a command from the pop-up menu.
5586               */
5587               element=(ElementType) (XMenuWidget(display,windows,
5588                 DrawMenu[id],Elements,command)+1);
5589               break;
5590             }
5591             case DrawColorCommand:
5592             {
5593               const char
5594                 *ColorMenu[MaxNumberPens+1];
5595
5596               int
5597                 pen_number;
5598
5599               MagickBooleanType
5600                 transparent;
5601
5602               XColor
5603                 color;
5604
5605               /*
5606                 Initialize menu selections.
5607               */
5608               for (i=0; i < (int) (MaxNumberPens-2); i++)
5609                 ColorMenu[i]=resource_info->pen_colors[i];
5610               ColorMenu[MaxNumberPens-2]="transparent";
5611               ColorMenu[MaxNumberPens-1]="Browser...";
5612               ColorMenu[MaxNumberPens]=(char *) NULL;
5613               /*
5614                 Select a pen color from the pop-up menu.
5615               */
5616               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5617                 (const char **) ColorMenu,command);
5618               if (pen_number < 0)
5619                 break;
5620               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5621                 MagickFalse;
5622               if (transparent != MagickFalse)
5623                 {
5624                   draw_info.stencil=TransparentStencil;
5625                   break;
5626                 }
5627               if (pen_number == (MaxNumberPens-1))
5628                 {
5629                   static char
5630                     color_name[MaxTextExtent] = "gray";
5631
5632                   /*
5633                     Select a pen color from a dialog.
5634                   */
5635                   resource_info->pen_colors[pen_number]=color_name;
5636                   XColorBrowserWidget(display,windows,"Select",color_name);
5637                   if (*color_name == '\0')
5638                     break;
5639                 }
5640               /*
5641                 Set pen color.
5642               */
5643               (void) XParseColor(display,windows->map_info->colormap,
5644                 resource_info->pen_colors[pen_number],&color);
5645               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5646                 (unsigned int) MaxColors,&color);
5647               windows->pixel_info->pen_colors[pen_number]=color;
5648               pen_id=(unsigned int) pen_number;
5649               draw_info.stencil=OpaqueStencil;
5650               break;
5651             }
5652             case DrawStippleCommand:
5653             {
5654               Image
5655                 *stipple_image;
5656
5657               ImageInfo
5658                 *image_info;
5659
5660               int
5661                 status;
5662
5663               static char
5664                 filename[MaxTextExtent] = "\0";
5665
5666               static const char
5667                 *StipplesMenu[] =
5668                 {
5669                   "Brick",
5670                   "Diagonal",
5671                   "Scales",
5672                   "Vertical",
5673                   "Wavy",
5674                   "Translucent",
5675                   "Opaque",
5676                   (char *) NULL,
5677                   (char *) NULL,
5678                 };
5679
5680               /*
5681                 Select a command from the pop-up menu.
5682               */
5683               StipplesMenu[7]="Open...";
5684               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5685                 command);
5686               if (entry < 0)
5687                 break;
5688               if (stipple != (Pixmap) NULL)
5689                 (void) XFreePixmap(display,stipple);
5690               stipple=(Pixmap) NULL;
5691               if (entry != 7)
5692                 {
5693                   switch (entry)
5694                   {
5695                     case 0:
5696                     {
5697                       stipple=XCreateBitmapFromData(display,root_window,
5698                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5699                       break;
5700                     }
5701                     case 1:
5702                     {
5703                       stipple=XCreateBitmapFromData(display,root_window,
5704                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5705                       break;
5706                     }
5707                     case 2:
5708                     {
5709                       stipple=XCreateBitmapFromData(display,root_window,
5710                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5711                       break;
5712                     }
5713                     case 3:
5714                     {
5715                       stipple=XCreateBitmapFromData(display,root_window,
5716                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5717                       break;
5718                     }
5719                     case 4:
5720                     {
5721                       stipple=XCreateBitmapFromData(display,root_window,
5722                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5723                       break;
5724                     }
5725                     case 5:
5726                     {
5727                       stipple=XCreateBitmapFromData(display,root_window,
5728                         (char *) HighlightBitmap,HighlightWidth,
5729                         HighlightHeight);
5730                       break;
5731                     }
5732                     case 6:
5733                     default:
5734                     {
5735                       stipple=XCreateBitmapFromData(display,root_window,
5736                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5737                       break;
5738                     }
5739                   }
5740                   break;
5741                 }
5742               XFileBrowserWidget(display,windows,"Stipple",filename);
5743               if (*filename == '\0')
5744                 break;
5745               /*
5746                 Read image.
5747               */
5748               XSetCursorState(display,windows,MagickTrue);
5749               XCheckRefreshWindows(display,windows);
5750               image_info=AcquireImageInfo();
5751               (void) CopyMagickString(image_info->filename,filename,
5752                 MaxTextExtent);
5753               stipple_image=ReadImage(image_info,exception);
5754               CatchException(exception);
5755               XSetCursorState(display,windows,MagickFalse);
5756               if (stipple_image == (Image *) NULL)
5757                 break;
5758               (void) AcquireUniqueFileResource(filename);
5759               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5760                 "xbm:%s",filename);
5761               (void) WriteImage(image_info,stipple_image,exception);
5762               stipple_image=DestroyImage(stipple_image);
5763               image_info=DestroyImageInfo(image_info);
5764               status=XReadBitmapFile(display,root_window,filename,&width,
5765                 &height,&stipple,&x,&y);
5766               (void) RelinquishUniqueFileResource(filename);
5767               if ((status != BitmapSuccess) != 0)
5768                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5769                   filename);
5770               break;
5771             }
5772             case DrawWidthCommand:
5773             {
5774               static char
5775                 width[MaxTextExtent] = "0";
5776
5777               static const char
5778                 *WidthsMenu[] =
5779                 {
5780                   "1",
5781                   "2",
5782                   "4",
5783                   "8",
5784                   "16",
5785                   "Dialog...",
5786                   (char *) NULL,
5787                 };
5788
5789               /*
5790                 Select a command from the pop-up menu.
5791               */
5792               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5793                 command);
5794               if (entry < 0)
5795                 break;
5796               if (entry != 5)
5797                 {
5798                   line_width=(unsigned int) StringToUnsignedLong(
5799                     WidthsMenu[entry]);
5800                   break;
5801                 }
5802               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5803                 width);
5804               if (*width == '\0')
5805                 break;
5806               line_width=(unsigned int) StringToUnsignedLong(width);
5807               break;
5808             }
5809             case DrawUndoCommand:
5810             {
5811               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5812                 image,exception);
5813               break;
5814             }
5815             case DrawHelpCommand:
5816             {
5817               XTextViewWidget(display,resource_info,windows,MagickFalse,
5818                 "Help Viewer - Image Rotation",ImageDrawHelp);
5819               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5820               break;
5821             }
5822             case DrawDismissCommand:
5823             {
5824               /*
5825                 Prematurely exit.
5826               */
5827               state|=EscapeState;
5828               state|=ExitState;
5829               break;
5830             }
5831             default:
5832               break;
5833           }
5834           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5835           continue;
5836         }
5837       switch (event.type)
5838       {
5839         case ButtonPress:
5840         {
5841           if (event.xbutton.button != Button1)
5842             break;
5843           if (event.xbutton.window != windows->image.id)
5844             break;
5845           /*
5846             exit loop.
5847           */
5848           x=event.xbutton.x;
5849           y=event.xbutton.y;
5850           state|=ExitState;
5851           break;
5852         }
5853         case ButtonRelease:
5854           break;
5855         case Expose:
5856           break;
5857         case KeyPress:
5858         {
5859           KeySym
5860             key_symbol;
5861
5862           if (event.xkey.window != windows->image.id)
5863             break;
5864           /*
5865             Respond to a user key press.
5866           */
5867           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5868             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5869           switch ((int) key_symbol)
5870           {
5871             case XK_Escape:
5872             case XK_F20:
5873             {
5874               /*
5875                 Prematurely exit.
5876               */
5877               state|=EscapeState;
5878               state|=ExitState;
5879               break;
5880             }
5881             case XK_F1:
5882             case XK_Help:
5883             {
5884               XTextViewWidget(display,resource_info,windows,MagickFalse,
5885                 "Help Viewer - Image Rotation",ImageDrawHelp);
5886               break;
5887             }
5888             default:
5889             {
5890               (void) XBell(display,0);
5891               break;
5892             }
5893           }
5894           break;
5895         }
5896         case MotionNotify:
5897         {
5898           /*
5899             Map and unmap Info widget as text cursor crosses its boundaries.
5900           */
5901           x=event.xmotion.x;
5902           y=event.xmotion.y;
5903           if (windows->info.mapped != MagickFalse)
5904             {
5905               if ((x < (int) (windows->info.x+windows->info.width)) &&
5906                   (y < (int) (windows->info.y+windows->info.height)))
5907                 (void) XWithdrawWindow(display,windows->info.id,
5908                   windows->info.screen);
5909             }
5910           else
5911             if ((x > (int) (windows->info.x+windows->info.width)) ||
5912                 (y > (int) (windows->info.y+windows->info.height)))
5913               (void) XMapWindow(display,windows->info.id);
5914           break;
5915         }
5916       }
5917     } while ((state & ExitState) == 0);
5918     (void) XSelectInput(display,windows->image.id,
5919       windows->image.attributes.event_mask);
5920     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5921     if ((state & EscapeState) != 0)
5922       break;
5923     /*
5924       Draw element as pointer moves until the button is released.
5925     */
5926     distance=0;
5927     degrees=0.0;
5928     line_info.x1=x;
5929     line_info.y1=y;
5930     line_info.x2=x;
5931     line_info.y2=y;
5932     rectangle_info.x=(ssize_t) x;
5933     rectangle_info.y=(ssize_t) y;
5934     rectangle_info.width=0;
5935     rectangle_info.height=0;
5936     number_coordinates=1;
5937     coordinate_info->x=x;
5938     coordinate_info->y=y;
5939     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5940     state=DefaultState;
5941     do
5942     {
5943       switch (element)
5944       {
5945         case PointElement:
5946         default:
5947         {
5948           if (number_coordinates > 1)
5949             {
5950               (void) XDrawLines(display,windows->image.id,
5951                 windows->image.highlight_context,coordinate_info,
5952                 number_coordinates,CoordModeOrigin);
5953               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5954                 coordinate_info[number_coordinates-1].x,
5955                 coordinate_info[number_coordinates-1].y);
5956               XInfoWidget(display,windows,text);
5957             }
5958           break;
5959         }
5960         case LineElement:
5961         {
5962           if (distance > 9)
5963             {
5964               /*
5965                 Display angle of the line.
5966               */
5967               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5968                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5969               (void) FormatLocaleString(text,MaxTextExtent," %g",
5970                 (double) degrees);
5971               XInfoWidget(display,windows,text);
5972               XHighlightLine(display,windows->image.id,
5973                 windows->image.highlight_context,&line_info);
5974             }
5975           else
5976             if (windows->info.mapped != MagickFalse)
5977               (void) XWithdrawWindow(display,windows->info.id,
5978                 windows->info.screen);
5979           break;
5980         }
5981         case RectangleElement:
5982         case FillRectangleElement:
5983         {
5984           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5985             {
5986               /*
5987                 Display info and draw drawing rectangle.
5988               */
5989               (void) FormatLocaleString(text,MaxTextExtent,
5990                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5991                 (double) rectangle_info.height,(double) rectangle_info.x,
5992                 (double) rectangle_info.y);
5993               XInfoWidget(display,windows,text);
5994               XHighlightRectangle(display,windows->image.id,
5995                 windows->image.highlight_context,&rectangle_info);
5996             }
5997           else
5998             if (windows->info.mapped != MagickFalse)
5999               (void) XWithdrawWindow(display,windows->info.id,
6000                 windows->info.screen);
6001           break;
6002         }
6003         case CircleElement:
6004         case FillCircleElement:
6005         case EllipseElement:
6006         case FillEllipseElement:
6007         {
6008           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6009             {
6010               /*
6011                 Display info and draw drawing rectangle.
6012               */
6013               (void) FormatLocaleString(text,MaxTextExtent,
6014                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6015                 (double) rectangle_info.height,(double) rectangle_info.x,
6016                 (double) rectangle_info.y);
6017               XInfoWidget(display,windows,text);
6018               XHighlightEllipse(display,windows->image.id,
6019                 windows->image.highlight_context,&rectangle_info);
6020             }
6021           else
6022             if (windows->info.mapped != MagickFalse)
6023               (void) XWithdrawWindow(display,windows->info.id,
6024                 windows->info.screen);
6025           break;
6026         }
6027         case PolygonElement:
6028         case FillPolygonElement:
6029         {
6030           if (number_coordinates > 1)
6031             (void) XDrawLines(display,windows->image.id,
6032               windows->image.highlight_context,coordinate_info,
6033               number_coordinates,CoordModeOrigin);
6034           if (distance > 9)
6035             {
6036               /*
6037                 Display angle of the line.
6038               */
6039               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6040                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6041               (void) FormatLocaleString(text,MaxTextExtent," %g",
6042                 (double) degrees);
6043               XInfoWidget(display,windows,text);
6044               XHighlightLine(display,windows->image.id,
6045                 windows->image.highlight_context,&line_info);
6046             }
6047           else
6048             if (windows->info.mapped != MagickFalse)
6049               (void) XWithdrawWindow(display,windows->info.id,
6050                 windows->info.screen);
6051           break;
6052         }
6053       }
6054       /*
6055         Wait for next event.
6056       */
6057       XScreenEvent(display,windows,&event,exception);
6058       switch (element)
6059       {
6060         case PointElement:
6061         default:
6062         {
6063           if (number_coordinates > 1)
6064             (void) XDrawLines(display,windows->image.id,
6065               windows->image.highlight_context,coordinate_info,
6066               number_coordinates,CoordModeOrigin);
6067           break;
6068         }
6069         case LineElement:
6070         {
6071           if (distance > 9)
6072             XHighlightLine(display,windows->image.id,
6073               windows->image.highlight_context,&line_info);
6074           break;
6075         }
6076         case RectangleElement:
6077         case FillRectangleElement:
6078         {
6079           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6080             XHighlightRectangle(display,windows->image.id,
6081               windows->image.highlight_context,&rectangle_info);
6082           break;
6083         }
6084         case CircleElement:
6085         case FillCircleElement:
6086         case EllipseElement:
6087         case FillEllipseElement:
6088         {
6089           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6090             XHighlightEllipse(display,windows->image.id,
6091               windows->image.highlight_context,&rectangle_info);
6092           break;
6093         }
6094         case PolygonElement:
6095         case FillPolygonElement:
6096         {
6097           if (number_coordinates > 1)
6098             (void) XDrawLines(display,windows->image.id,
6099               windows->image.highlight_context,coordinate_info,
6100               number_coordinates,CoordModeOrigin);
6101           if (distance > 9)
6102             XHighlightLine(display,windows->image.id,
6103               windows->image.highlight_context,&line_info);
6104           break;
6105         }
6106       }
6107       switch (event.type)
6108       {
6109         case ButtonPress:
6110           break;
6111         case ButtonRelease:
6112         {
6113           /*
6114             User has committed to element.
6115           */
6116           line_info.x2=event.xbutton.x;
6117           line_info.y2=event.xbutton.y;
6118           rectangle_info.x=(ssize_t) event.xbutton.x;
6119           rectangle_info.y=(ssize_t) event.xbutton.y;
6120           coordinate_info[number_coordinates].x=event.xbutton.x;
6121           coordinate_info[number_coordinates].y=event.xbutton.y;
6122           if (((element != PolygonElement) &&
6123                (element != FillPolygonElement)) || (distance <= 9))
6124             {
6125               state|=ExitState;
6126               break;
6127             }
6128           number_coordinates++;
6129           if (number_coordinates < (int) max_coordinates)
6130             {
6131               line_info.x1=event.xbutton.x;
6132               line_info.y1=event.xbutton.y;
6133               break;
6134             }
6135           max_coordinates<<=1;
6136           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6137             max_coordinates,sizeof(*coordinate_info));
6138           if (coordinate_info == (XPoint *) NULL)
6139             (void) ThrowMagickException(exception,GetMagickModule(),
6140               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6141           break;
6142         }
6143         case Expose:
6144           break;
6145         case MotionNotify:
6146         {
6147           if (event.xmotion.window != windows->image.id)
6148             break;
6149           if (element != PointElement)
6150             {
6151               line_info.x2=event.xmotion.x;
6152               line_info.y2=event.xmotion.y;
6153               rectangle_info.x=(ssize_t) event.xmotion.x;
6154               rectangle_info.y=(ssize_t) event.xmotion.y;
6155               break;
6156             }
6157           coordinate_info[number_coordinates].x=event.xbutton.x;
6158           coordinate_info[number_coordinates].y=event.xbutton.y;
6159           number_coordinates++;
6160           if (number_coordinates < (int) max_coordinates)
6161             break;
6162           max_coordinates<<=1;
6163           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6164             max_coordinates,sizeof(*coordinate_info));
6165           if (coordinate_info == (XPoint *) NULL)
6166             (void) ThrowMagickException(exception,GetMagickModule(),
6167               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6168           break;
6169         }
6170         default:
6171           break;
6172       }
6173       /*
6174         Check boundary conditions.
6175       */
6176       if (line_info.x2 < 0)
6177         line_info.x2=0;
6178       else
6179         if (line_info.x2 > (int) windows->image.width)
6180           line_info.x2=(short) windows->image.width;
6181       if (line_info.y2 < 0)
6182         line_info.y2=0;
6183       else
6184         if (line_info.y2 > (int) windows->image.height)
6185           line_info.y2=(short) windows->image.height;
6186       distance=(unsigned int)
6187         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6188          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6189       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6190           ((state & ExitState) != 0))
6191         {
6192           if (rectangle_info.x < 0)
6193             rectangle_info.x=0;
6194           else
6195             if (rectangle_info.x > (ssize_t) windows->image.width)
6196               rectangle_info.x=(ssize_t) windows->image.width;
6197           if ((int) rectangle_info.x < x)
6198             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6199           else
6200             {
6201               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6202               rectangle_info.x=(ssize_t) x;
6203             }
6204           if (rectangle_info.y < 0)
6205             rectangle_info.y=0;
6206           else
6207             if (rectangle_info.y > (ssize_t) windows->image.height)
6208               rectangle_info.y=(ssize_t) windows->image.height;
6209           if ((int) rectangle_info.y < y)
6210             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6211           else
6212             {
6213               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6214               rectangle_info.y=(ssize_t) y;
6215             }
6216         }
6217     } while ((state & ExitState) == 0);
6218     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6219     if ((element == PointElement) || (element == PolygonElement) ||
6220         (element == FillPolygonElement))
6221       {
6222         /*
6223           Determine polygon bounding box.
6224         */
6225         rectangle_info.x=(ssize_t) coordinate_info->x;
6226         rectangle_info.y=(ssize_t) coordinate_info->y;
6227         x=coordinate_info->x;
6228         y=coordinate_info->y;
6229         for (i=1; i < number_coordinates; i++)
6230         {
6231           if (coordinate_info[i].x > x)
6232             x=coordinate_info[i].x;
6233           if (coordinate_info[i].y > y)
6234             y=coordinate_info[i].y;
6235           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6236             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6237           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6238             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6239         }
6240         rectangle_info.width=(size_t) (x-rectangle_info.x);
6241         rectangle_info.height=(size_t) (y-rectangle_info.y);
6242         for (i=0; i < number_coordinates; i++)
6243         {
6244           coordinate_info[i].x-=rectangle_info.x;
6245           coordinate_info[i].y-=rectangle_info.y;
6246         }
6247       }
6248     else
6249       if (distance <= 9)
6250         continue;
6251       else
6252         if ((element == RectangleElement) ||
6253             (element == CircleElement) || (element == EllipseElement))
6254           {
6255             rectangle_info.width--;
6256             rectangle_info.height--;
6257           }
6258     /*
6259       Drawing is relative to image configuration.
6260     */
6261     draw_info.x=(int) rectangle_info.x;
6262     draw_info.y=(int) rectangle_info.y;
6263     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6264       image,exception);
6265     width=(unsigned int) (*image)->columns;
6266     height=(unsigned int) (*image)->rows;
6267     x=0;
6268     y=0;
6269     if (windows->image.crop_geometry != (char *) NULL)
6270       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6271     draw_info.x+=windows->image.x-(line_width/2);
6272     if (draw_info.x < 0)
6273       draw_info.x=0;
6274     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6275     draw_info.y+=windows->image.y-(line_width/2);
6276     if (draw_info.y < 0)
6277       draw_info.y=0;
6278     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6279     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6280     if (draw_info.width > (unsigned int) (*image)->columns)
6281       draw_info.width=(unsigned int) (*image)->columns;
6282     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6283     if (draw_info.height > (unsigned int) (*image)->rows)
6284       draw_info.height=(unsigned int) (*image)->rows;
6285     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6286       width*draw_info.width/windows->image.ximage->width,
6287       height*draw_info.height/windows->image.ximage->height,
6288       draw_info.x+x,draw_info.y+y);
6289     /*
6290       Initialize drawing attributes.
6291     */
6292     draw_info.degrees=0.0;
6293     draw_info.element=element;
6294     draw_info.stipple=stipple;
6295     draw_info.line_width=line_width;
6296     draw_info.line_info=line_info;
6297     if (line_info.x1 > (int) (line_width/2))
6298       draw_info.line_info.x1=(short) line_width/2;
6299     if (line_info.y1 > (int) (line_width/2))
6300       draw_info.line_info.y1=(short) line_width/2;
6301     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6302     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6303     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6304       {
6305         draw_info.line_info.x2=(-draw_info.line_info.x2);
6306         draw_info.line_info.y2=(-draw_info.line_info.y2);
6307       }
6308     if (draw_info.line_info.x2 < 0)
6309       {
6310         draw_info.line_info.x2=(-draw_info.line_info.x2);
6311         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6312       }
6313     if (draw_info.line_info.y2 < 0)
6314       {
6315         draw_info.line_info.y2=(-draw_info.line_info.y2);
6316         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6317       }
6318     draw_info.rectangle_info=rectangle_info;
6319     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6320       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6321     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6322       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6323     draw_info.number_coordinates=(unsigned int) number_coordinates;
6324     draw_info.coordinate_info=coordinate_info;
6325     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6326     /*
6327       Draw element on image.
6328     */
6329     XSetCursorState(display,windows,MagickTrue);
6330     XCheckRefreshWindows(display,windows);
6331     status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6332     XSetCursorState(display,windows,MagickFalse);
6333     /*
6334       Update image colormap and return to image drawing.
6335     */
6336     XConfigureImageColormap(display,resource_info,windows,*image,exception);
6337     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6338   }
6339   XSetCursorState(display,windows,MagickFalse);
6340   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6341   return(status != 0 ? MagickTrue : MagickFalse);
6342 }
6343 \f
6344 /*
6345 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6346 %                                                                             %
6347 %                                                                             %
6348 %                                                                             %
6349 +   X D r a w P a n R e c t a n g l e                                         %
6350 %                                                                             %
6351 %                                                                             %
6352 %                                                                             %
6353 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6354 %
6355 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6356 %  displays a zoom image and the rectangle shows which portion of the image is
6357 %  displayed in the Image window.
6358 %
6359 %  The format of the XDrawPanRectangle method is:
6360 %
6361 %      XDrawPanRectangle(Display *display,XWindows *windows)
6362 %
6363 %  A description of each parameter follows:
6364 %
6365 %    o display: Specifies a connection to an X server;  returned from
6366 %      XOpenDisplay.
6367 %
6368 %    o windows: Specifies a pointer to a XWindows structure.
6369 %
6370 */
6371 static void XDrawPanRectangle(Display *display,XWindows *windows)
6372 {
6373   MagickRealType
6374     scale_factor;
6375
6376   RectangleInfo
6377     highlight_info;
6378
6379   /*
6380     Determine dimensions of the panning rectangle.
6381   */
6382   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6383   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6384   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6385   scale_factor=(MagickRealType)
6386     windows->pan.height/windows->image.ximage->height;
6387   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6388   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6389   /*
6390     Display the panning rectangle.
6391   */
6392   (void) XClearWindow(display,windows->pan.id);
6393   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6394     &highlight_info);
6395 }
6396 \f
6397 /*
6398 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6399 %                                                                             %
6400 %                                                                             %
6401 %                                                                             %
6402 +   X I m a g e C a c h e                                                     %
6403 %                                                                             %
6404 %                                                                             %
6405 %                                                                             %
6406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6407 %
6408 %  XImageCache() handles the creation, manipulation, and destruction of the
6409 %  image cache (undo and redo buffers).
6410 %
6411 %  The format of the XImageCache method is:
6412 %
6413 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6414 %        XWindows *windows,const CommandType command,Image **image,
6415 %        ExceptionInfo *exception)
6416 %
6417 %  A description of each parameter follows:
6418 %
6419 %    o display: Specifies a connection to an X server; returned from
6420 %      XOpenDisplay.
6421 %
6422 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6423 %
6424 %    o windows: Specifies a pointer to a XWindows structure.
6425 %
6426 %    o command: Specifies a command to perform.
6427 %
6428 %    o image: the image;  XImageCache may transform the image and return a new
6429 %      image pointer.
6430 %
6431 %    o exception: return any errors or warnings in this structure.
6432 %
6433 */
6434 static void XImageCache(Display *display,XResourceInfo *resource_info,
6435   XWindows *windows,const CommandType command,Image **image,
6436   ExceptionInfo *exception)
6437 {
6438   Image
6439     *cache_image;
6440
6441   static Image
6442     *redo_image = (Image *) NULL,
6443     *undo_image = (Image *) NULL;
6444
6445   switch (command)
6446   {
6447     case FreeBuffersCommand:
6448     {
6449       /*
6450         Free memory from the undo and redo cache.
6451       */
6452       while (undo_image != (Image *) NULL)
6453       {
6454         cache_image=undo_image;
6455         undo_image=GetPreviousImageInList(undo_image);
6456         cache_image->list=DestroyImage(cache_image->list);
6457         cache_image=DestroyImage(cache_image);
6458       }
6459       undo_image=NewImageList();
6460       if (redo_image != (Image *) NULL)
6461         redo_image=DestroyImage(redo_image);
6462       redo_image=NewImageList();
6463       return;
6464     }
6465     case UndoCommand:
6466     {
6467       char
6468         image_geometry[MaxTextExtent];
6469
6470       /*
6471         Undo the last image transformation.
6472       */
6473       if (undo_image == (Image *) NULL)
6474         {
6475           (void) XBell(display,0);
6476           return;
6477         }
6478       cache_image=undo_image;
6479       undo_image=GetPreviousImageInList(undo_image);
6480       windows->image.window_changes.width=(int) cache_image->columns;
6481       windows->image.window_changes.height=(int) cache_image->rows;
6482       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6483         windows->image.ximage->width,windows->image.ximage->height);
6484       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6485         exception);
6486       if (windows->image.crop_geometry != (char *) NULL)
6487         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6488           windows->image.crop_geometry);
6489       windows->image.crop_geometry=cache_image->geometry;
6490       if (redo_image != (Image *) NULL)
6491         redo_image=DestroyImage(redo_image);
6492       redo_image=(*image);
6493       *image=cache_image->list;
6494       cache_image=DestroyImage(cache_image);
6495       if (windows->image.orphan != MagickFalse)
6496         return;
6497       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6498       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6499       return;
6500     }
6501     case CutCommand:
6502     case PasteCommand:
6503     case ApplyCommand:
6504     case HalfSizeCommand:
6505     case OriginalSizeCommand:
6506     case DoubleSizeCommand:
6507     case ResizeCommand:
6508     case TrimCommand:
6509     case CropCommand:
6510     case ChopCommand:
6511     case FlipCommand:
6512     case FlopCommand:
6513     case RotateRightCommand:
6514     case RotateLeftCommand:
6515     case RotateCommand:
6516     case ShearCommand:
6517     case RollCommand:
6518     case NegateCommand:
6519     case ContrastStretchCommand:
6520     case SigmoidalContrastCommand:
6521     case NormalizeCommand:
6522     case EqualizeCommand:
6523     case HueCommand:
6524     case SaturationCommand:
6525     case BrightnessCommand:
6526     case GammaCommand:
6527     case SpiffCommand:
6528     case DullCommand:
6529     case GrayscaleCommand:
6530     case MapCommand:
6531     case QuantizeCommand:
6532     case DespeckleCommand:
6533     case EmbossCommand:
6534     case ReduceNoiseCommand:
6535     case AddNoiseCommand:
6536     case SharpenCommand:
6537     case BlurCommand:
6538     case ThresholdCommand:
6539     case EdgeDetectCommand:
6540     case SpreadCommand:
6541     case ShadeCommand:
6542     case RaiseCommand:
6543     case SegmentCommand:
6544     case SolarizeCommand:
6545     case SepiaToneCommand:
6546     case SwirlCommand:
6547     case ImplodeCommand:
6548     case VignetteCommand:
6549     case WaveCommand:
6550     case OilPaintCommand:
6551     case CharcoalDrawCommand:
6552     case AnnotateCommand:
6553     case AddBorderCommand:
6554     case AddFrameCommand:
6555     case CompositeCommand:
6556     case CommentCommand:
6557     case LaunchCommand:
6558     case RegionofInterestCommand:
6559     case SaveToUndoBufferCommand:
6560     case RedoCommand:
6561     {
6562       Image
6563         *previous_image;
6564
6565       ssize_t
6566         bytes;
6567
6568       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6569       if (undo_image != (Image *) NULL)
6570         {
6571           /*
6572             Ensure the undo cache has enough memory available.
6573           */
6574           previous_image=undo_image;
6575           while (previous_image != (Image *) NULL)
6576           {
6577             bytes+=previous_image->list->columns*previous_image->list->rows*
6578               sizeof(PixelInfo);
6579             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6580               {
6581                 previous_image=GetPreviousImageInList(previous_image);
6582                 continue;
6583               }
6584             bytes-=previous_image->list->columns*previous_image->list->rows*
6585               sizeof(PixelInfo);
6586             if (previous_image == undo_image)
6587               undo_image=NewImageList();
6588             else
6589               previous_image->next->previous=NewImageList();
6590             break;
6591           }
6592           while (previous_image != (Image *) NULL)
6593           {
6594             /*
6595               Delete any excess memory from undo cache.
6596             */
6597             cache_image=previous_image;
6598             previous_image=GetPreviousImageInList(previous_image);
6599             cache_image->list=DestroyImage(cache_image->list);
6600             cache_image=DestroyImage(cache_image);
6601           }
6602         }
6603       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6604         break;
6605       /*
6606         Save image before transformations are applied.
6607       */
6608       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6609       if (cache_image == (Image *) NULL)
6610         break;
6611       XSetCursorState(display,windows,MagickTrue);
6612       XCheckRefreshWindows(display,windows);
6613       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6614       XSetCursorState(display,windows,MagickFalse);
6615       if (cache_image->list == (Image *) NULL)
6616         {
6617           cache_image=DestroyImage(cache_image);
6618           break;
6619         }
6620       cache_image->columns=(size_t) windows->image.ximage->width;
6621       cache_image->rows=(size_t) windows->image.ximage->height;
6622       cache_image->geometry=windows->image.crop_geometry;
6623       if (windows->image.crop_geometry != (char *) NULL)
6624         {
6625           cache_image->geometry=AcquireString((char *) NULL);
6626           (void) CopyMagickString(cache_image->geometry,
6627             windows->image.crop_geometry,MaxTextExtent);
6628         }
6629       if (undo_image == (Image *) NULL)
6630         {
6631           undo_image=cache_image;
6632           break;
6633         }
6634       undo_image->next=cache_image;
6635       undo_image->next->previous=undo_image;
6636       undo_image=undo_image->next;
6637       break;
6638     }
6639     default:
6640       break;
6641   }
6642   if (command == RedoCommand)
6643     {
6644       /*
6645         Redo the last image transformation.
6646       */
6647       if (redo_image == (Image *) NULL)
6648         {
6649           (void) XBell(display,0);
6650           return;
6651         }
6652       windows->image.window_changes.width=(int) redo_image->columns;
6653       windows->image.window_changes.height=(int) redo_image->rows;
6654       if (windows->image.crop_geometry != (char *) NULL)
6655         windows->image.crop_geometry=(char *)
6656           RelinquishMagickMemory(windows->image.crop_geometry);
6657       windows->image.crop_geometry=redo_image->geometry;
6658       *image=DestroyImage(*image);
6659       *image=redo_image;
6660       redo_image=NewImageList();
6661       if (windows->image.orphan != MagickFalse)
6662         return;
6663       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6664       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6665       return;
6666     }
6667   if (command != InfoCommand)
6668     return;
6669   /*
6670     Display image info.
6671   */
6672   XSetCursorState(display,windows,MagickTrue);
6673   XCheckRefreshWindows(display,windows);
6674   XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6675   XSetCursorState(display,windows,MagickFalse);
6676 }
6677 \f
6678 /*
6679 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6680 %                                                                             %
6681 %                                                                             %
6682 %                                                                             %
6683 +   X I m a g e W i n d o w C o m m a n d                                     %
6684 %                                                                             %
6685 %                                                                             %
6686 %                                                                             %
6687 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6688 %
6689 %  XImageWindowCommand() makes a transform to the image or Image window as
6690 %  specified by a user menu button or keyboard command.
6691 %
6692 %  The format of the XImageWindowCommand method is:
6693 %
6694 %      CommandType XImageWindowCommand(Display *display,
6695 %        XResourceInfo *resource_info,XWindows *windows,
6696 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6697 %        ExceptionInfo *exception)
6698 %
6699 %  A description of each parameter follows:
6700 %
6701 %    o nexus:  Method XImageWindowCommand returns an image when the
6702 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6703 %      image is returned.
6704 %
6705 %    o display: Specifies a connection to an X server; returned from
6706 %      XOpenDisplay.
6707 %
6708 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6709 %
6710 %    o windows: Specifies a pointer to a XWindows structure.
6711 %
6712 %    o state: key mask.
6713 %
6714 %    o key_symbol: Specifies a command to perform.
6715 %
6716 %    o image: the image;  XImageWIndowCommand may transform the image and
6717 %      return a new image pointer.
6718 %
6719 %    o exception: return any errors or warnings in this structure.
6720 %
6721 */
6722 static CommandType XImageWindowCommand(Display *display,
6723   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6724   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6725 {
6726   static char
6727     delta[MaxTextExtent] = "";
6728
6729   static const char
6730     Digits[] = "01234567890";
6731
6732   static KeySym
6733     last_symbol = XK_0;
6734
6735   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6736     {
6737       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6738         {
6739           *delta='\0';
6740           resource_info->quantum=1;
6741         }
6742       last_symbol=key_symbol;
6743       delta[strlen(delta)+1]='\0';
6744       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6745       resource_info->quantum=StringToLong(delta);
6746       return(NullCommand);
6747     }
6748   last_symbol=key_symbol;
6749   if (resource_info->immutable)
6750     {
6751       /*
6752         Virtual image window has a restricted command set.
6753       */
6754       switch (key_symbol)
6755       {
6756         case XK_question:
6757           return(InfoCommand);
6758         case XK_p:
6759         case XK_Print:
6760           return(PrintCommand);
6761         case XK_space:
6762           return(NextCommand);
6763         case XK_q:
6764         case XK_Escape:
6765           return(QuitCommand);
6766         default:
6767           break;
6768       }
6769       return(NullCommand);
6770     }
6771   switch ((int) key_symbol)
6772   {
6773     case XK_o:
6774     {
6775       if ((state & ControlMask) == 0)
6776         break;
6777       return(OpenCommand);
6778     }
6779     case XK_space:
6780       return(NextCommand);
6781     case XK_BackSpace:
6782       return(FormerCommand);
6783     case XK_s:
6784     {
6785       if ((state & Mod1Mask) != 0)
6786         return(SwirlCommand);
6787       if ((state & ControlMask) == 0)
6788         return(ShearCommand);
6789       return(SaveCommand);
6790     }
6791     case XK_p:
6792     case XK_Print:
6793     {
6794       if ((state & Mod1Mask) != 0)
6795         return(OilPaintCommand);
6796       if ((state & Mod4Mask) != 0)
6797         return(ColorCommand);
6798       if ((state & ControlMask) == 0)
6799         return(NullCommand);
6800       return(PrintCommand);
6801     }
6802     case XK_d:
6803     {
6804       if ((state & Mod4Mask) != 0)
6805         return(DrawCommand);
6806       if ((state & ControlMask) == 0)
6807         return(NullCommand);
6808       return(DeleteCommand);
6809     }
6810     case XK_Select:
6811     {
6812       if ((state & ControlMask) == 0)
6813         return(NullCommand);
6814       return(SelectCommand);
6815     }
6816     case XK_n:
6817     {
6818       if ((state & ControlMask) == 0)
6819         return(NullCommand);
6820       return(NewCommand);
6821     }
6822     case XK_q:
6823     case XK_Escape:
6824       return(QuitCommand);
6825     case XK_z:
6826     case XK_Undo:
6827     {
6828       if ((state & ControlMask) == 0)
6829         return(NullCommand);
6830       return(UndoCommand);
6831     }
6832     case XK_r:
6833     case XK_Redo:
6834     {
6835       if ((state & ControlMask) == 0)
6836         return(RollCommand);
6837       return(RedoCommand);
6838     }
6839     case XK_x:
6840     {
6841       if ((state & ControlMask) == 0)
6842         return(NullCommand);
6843       return(CutCommand);
6844     }
6845     case XK_c:
6846     {
6847       if ((state & Mod1Mask) != 0)
6848         return(CharcoalDrawCommand);
6849       if ((state & ControlMask) == 0)
6850         return(CropCommand);
6851       return(CopyCommand);
6852     }
6853     case XK_v:
6854     case XK_Insert:
6855     {
6856       if ((state & Mod4Mask) != 0)
6857         return(CompositeCommand);
6858       if ((state & ControlMask) == 0)
6859         return(FlipCommand);
6860       return(PasteCommand);
6861     }
6862     case XK_less:
6863       return(HalfSizeCommand);
6864     case XK_minus:
6865       return(OriginalSizeCommand);
6866     case XK_greater:
6867       return(DoubleSizeCommand);
6868     case XK_percent:
6869       return(ResizeCommand);
6870     case XK_at:
6871       return(RefreshCommand);
6872     case XK_bracketleft:
6873       return(ChopCommand);
6874     case XK_h:
6875       return(FlopCommand);
6876     case XK_slash:
6877       return(RotateRightCommand);
6878     case XK_backslash:
6879       return(RotateLeftCommand);
6880     case XK_asterisk:
6881       return(RotateCommand);
6882     case XK_t:
6883       return(TrimCommand);
6884     case XK_H:
6885       return(HueCommand);
6886     case XK_S:
6887       return(SaturationCommand);
6888     case XK_L:
6889       return(BrightnessCommand);
6890     case XK_G:
6891       return(GammaCommand);
6892     case XK_C:
6893       return(SpiffCommand);
6894     case XK_Z:
6895       return(DullCommand);
6896     case XK_N:
6897       return(NormalizeCommand);
6898     case XK_equal:
6899       return(EqualizeCommand);
6900     case XK_asciitilde:
6901       return(NegateCommand);
6902     case XK_period:
6903       return(GrayscaleCommand);
6904     case XK_numbersign:
6905       return(QuantizeCommand);
6906     case XK_F2:
6907       return(DespeckleCommand);
6908     case XK_F3:
6909       return(EmbossCommand);
6910     case XK_F4:
6911       return(ReduceNoiseCommand);
6912     case XK_F5:
6913       return(AddNoiseCommand);
6914     case XK_F6:
6915       return(SharpenCommand);
6916     case XK_F7:
6917       return(BlurCommand);
6918     case XK_F8:
6919       return(ThresholdCommand);
6920     case XK_F9:
6921       return(EdgeDetectCommand);
6922     case XK_F10:
6923       return(SpreadCommand);
6924     case XK_F11:
6925       return(ShadeCommand);
6926     case XK_F12:
6927       return(RaiseCommand);
6928     case XK_F13:
6929       return(SegmentCommand);
6930     case XK_i:
6931     {
6932       if ((state & Mod1Mask) == 0)
6933         return(NullCommand);
6934       return(ImplodeCommand);
6935     }
6936     case XK_w:
6937     {
6938       if ((state & Mod1Mask) == 0)
6939         return(NullCommand);
6940       return(WaveCommand);
6941     }
6942     case XK_m:
6943     {
6944       if ((state & Mod4Mask) == 0)
6945         return(NullCommand);
6946       return(MatteCommand);
6947     }
6948     case XK_b:
6949     {
6950       if ((state & Mod4Mask) == 0)
6951         return(NullCommand);
6952       return(AddBorderCommand);
6953     }
6954     case XK_f:
6955     {
6956       if ((state & Mod4Mask) == 0)
6957         return(NullCommand);
6958       return(AddFrameCommand);
6959     }
6960     case XK_exclam:
6961     {
6962       if ((state & Mod4Mask) == 0)
6963         return(NullCommand);
6964       return(CommentCommand);
6965     }
6966     case XK_a:
6967     {
6968       if ((state & Mod1Mask) != 0)
6969         return(ApplyCommand);
6970       if ((state & Mod4Mask) != 0)
6971         return(AnnotateCommand);
6972       if ((state & ControlMask) == 0)
6973         return(NullCommand);
6974       return(RegionofInterestCommand);
6975     }
6976     case XK_question:
6977       return(InfoCommand);
6978     case XK_plus:
6979       return(ZoomCommand);
6980     case XK_P:
6981     {
6982       if ((state & ShiftMask) == 0)
6983         return(NullCommand);
6984       return(ShowPreviewCommand);
6985     }
6986     case XK_Execute:
6987       return(LaunchCommand);
6988     case XK_F1:
6989       return(HelpCommand);
6990     case XK_Find:
6991       return(BrowseDocumentationCommand);
6992     case XK_Menu:
6993     {
6994       (void) XMapRaised(display,windows->command.id);
6995       return(NullCommand);
6996     }
6997     case XK_Next:
6998     case XK_Prior:
6999     case XK_Home:
7000     case XK_KP_Home:
7001     {
7002       XTranslateImage(display,windows,*image,key_symbol);
7003       return(NullCommand);
7004     }
7005     case XK_Up:
7006     case XK_KP_Up:
7007     case XK_Down:
7008     case XK_KP_Down:
7009     case XK_Left:
7010     case XK_KP_Left:
7011     case XK_Right:
7012     case XK_KP_Right:
7013     {
7014       if ((state & Mod1Mask) != 0)
7015         {
7016           RectangleInfo
7017             crop_info;
7018
7019           /*
7020             Trim one pixel from edge of image.
7021           */
7022           crop_info.x=0;
7023           crop_info.y=0;
7024           crop_info.width=(size_t) windows->image.ximage->width;
7025           crop_info.height=(size_t) windows->image.ximage->height;
7026           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7027             {
7028               if (resource_info->quantum >= (int) crop_info.height)
7029                 resource_info->quantum=(int) crop_info.height-1;
7030               crop_info.height-=resource_info->quantum;
7031             }
7032           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7033             {
7034               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7035                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7036               crop_info.y+=resource_info->quantum;
7037               crop_info.height-=resource_info->quantum;
7038             }
7039           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7040             {
7041               if (resource_info->quantum >= (int) crop_info.width)
7042                 resource_info->quantum=(int) crop_info.width-1;
7043               crop_info.width-=resource_info->quantum;
7044             }
7045           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7046             {
7047               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7048                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7049               crop_info.x+=resource_info->quantum;
7050               crop_info.width-=resource_info->quantum;
7051             }
7052           if ((int) (windows->image.x+windows->image.width) >
7053               (int) crop_info.width)
7054             windows->image.x=(int) (crop_info.width-windows->image.width);
7055           if ((int) (windows->image.y+windows->image.height) >
7056               (int) crop_info.height)
7057             windows->image.y=(int) (crop_info.height-windows->image.height);
7058           XSetCropGeometry(display,windows,&crop_info,*image);
7059           windows->image.window_changes.width=(int) crop_info.width;
7060           windows->image.window_changes.height=(int) crop_info.height;
7061           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7062           (void) XConfigureImage(display,resource_info,windows,*image,
7063             exception);
7064           return(NullCommand);
7065         }
7066       XTranslateImage(display,windows,*image,key_symbol);
7067       return(NullCommand);
7068     }
7069     default:
7070       return(NullCommand);
7071   }
7072   return(NullCommand);
7073 }
7074 \f
7075 /*
7076 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7077 %                                                                             %
7078 %                                                                             %
7079 %                                                                             %
7080 +   X M a g i c k C o m m a n d                                               %
7081 %                                                                             %
7082 %                                                                             %
7083 %                                                                             %
7084 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7085 %
7086 %  XMagickCommand() makes a transform to the image or Image window as
7087 %  specified by a user menu button or keyboard command.
7088 %
7089 %  The format of the XMagickCommand method is:
7090 %
7091 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7092 %        XWindows *windows,const CommandType command,Image **image,
7093 %        ExceptionInfo *exception)
7094 %
7095 %  A description of each parameter follows:
7096 %
7097 %    o display: Specifies a connection to an X server; returned from
7098 %      XOpenDisplay.
7099 %
7100 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7101 %
7102 %    o windows: Specifies a pointer to a XWindows structure.
7103 %
7104 %    o command: Specifies a command to perform.
7105 %
7106 %    o image: the image;  XMagickCommand may transform the image and return a
7107 %      new image pointer.
7108 %
7109 %    o exception: return any errors or warnings in this structure.
7110 %
7111 */
7112 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7113   XWindows *windows,const CommandType command,Image **image,
7114   ExceptionInfo *exception)
7115 {
7116   char
7117     filename[MaxTextExtent],
7118     geometry[MaxTextExtent],
7119     modulate_factors[MaxTextExtent];
7120
7121   GeometryInfo
7122     geometry_info;
7123
7124   Image
7125     *nexus;
7126
7127   ImageInfo
7128     *image_info;
7129
7130   int
7131     x,
7132     y;
7133
7134   MagickStatusType
7135     flags,
7136     status;
7137
7138   QuantizeInfo
7139     quantize_info;
7140
7141   RectangleInfo
7142     page_geometry;
7143
7144   register int
7145     i;
7146
7147   static char
7148     color[MaxTextExtent] = "gray";
7149
7150   unsigned int
7151     height,
7152     width;
7153
7154   /*
7155     Process user command.
7156   */
7157   XCheckRefreshWindows(display,windows);
7158   XImageCache(display,resource_info,windows,command,image,exception);
7159   nexus=NewImageList();
7160   windows->image.window_changes.width=windows->image.ximage->width;
7161   windows->image.window_changes.height=windows->image.ximage->height;
7162   image_info=CloneImageInfo(resource_info->image_info);
7163   SetGeometryInfo(&geometry_info);
7164   GetQuantizeInfo(&quantize_info);
7165   switch (command)
7166   {
7167     case OpenCommand:
7168     {
7169       /*
7170         Load image.
7171       */
7172       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7173       break;
7174     }
7175     case NextCommand:
7176     {
7177       /*
7178         Display next image.
7179       */
7180       for (i=0; i < resource_info->quantum; i++)
7181         XClientMessage(display,windows->image.id,windows->im_protocols,
7182           windows->im_next_image,CurrentTime);
7183       break;
7184     }
7185     case FormerCommand:
7186     {
7187       /*
7188         Display former image.
7189       */
7190       for (i=0; i < resource_info->quantum; i++)
7191         XClientMessage(display,windows->image.id,windows->im_protocols,
7192           windows->im_former_image,CurrentTime);
7193       break;
7194     }
7195     case SelectCommand:
7196     {
7197       int
7198         status;
7199
7200       /*
7201         Select image.
7202       */
7203       status=chdir(resource_info->home_directory);
7204       if (status == -1)
7205         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7206           "UnableToOpenFile","%s",resource_info->home_directory);
7207       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7208       break;
7209     }
7210     case SaveCommand:
7211     {
7212       /*
7213         Save image.
7214       */
7215       status=XSaveImage(display,resource_info,windows,*image,exception);
7216       if (status == MagickFalse)
7217         {
7218           char
7219             message[MaxTextExtent];
7220
7221           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7222             exception->reason != (char *) NULL ? exception->reason : "",
7223             exception->description != (char *) NULL ? exception->description :
7224             "");
7225           XNoticeWidget(display,windows,"Unable to save file:",message);
7226           break;
7227         }
7228       break;
7229     }
7230     case PrintCommand:
7231     {
7232       /*
7233         Print image.
7234       */
7235       status=XPrintImage(display,resource_info,windows,*image,exception);
7236       if (status == MagickFalse)
7237         {
7238           char
7239             message[MaxTextExtent];
7240
7241           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7242             exception->reason != (char *) NULL ? exception->reason : "",
7243             exception->description != (char *) NULL ? exception->description :
7244             "");
7245           XNoticeWidget(display,windows,"Unable to print file:",message);
7246           break;
7247         }
7248       break;
7249     }
7250     case DeleteCommand:
7251     {
7252       static char
7253         filename[MaxTextExtent] = "\0";
7254
7255       /*
7256         Delete image file.
7257       */
7258       XFileBrowserWidget(display,windows,"Delete",filename);
7259       if (*filename == '\0')
7260         break;
7261       status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
7262       if (status != MagickFalse)
7263         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7264       break;
7265     }
7266     case NewCommand:
7267     {
7268       int
7269         status;
7270
7271       static char
7272         color[MaxTextExtent] = "gray",
7273         geometry[MaxTextExtent] = "640x480";
7274
7275       static const char
7276         *format = "gradient";
7277
7278       /*
7279         Query user for canvas geometry.
7280       */
7281       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7282         geometry);
7283       if (*geometry == '\0')
7284         break;
7285       if (status == 0)
7286         format="xc";
7287       XColorBrowserWidget(display,windows,"Select",color);
7288       if (*color == '\0')
7289         break;
7290       /*
7291         Create canvas.
7292       */
7293       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7294         "%s:%s",format,color);
7295       (void) CloneString(&image_info->size,geometry);
7296       nexus=ReadImage(image_info,exception);
7297       CatchException(exception);
7298       XClientMessage(display,windows->image.id,windows->im_protocols,
7299         windows->im_next_image,CurrentTime);
7300       break;
7301     }
7302     case VisualDirectoryCommand:
7303     {
7304       /*
7305         Visual Image directory.
7306       */
7307       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7308       break;
7309     }
7310     case QuitCommand:
7311     {
7312       /*
7313         exit program.
7314       */
7315       if (resource_info->confirm_exit == MagickFalse)
7316         XClientMessage(display,windows->image.id,windows->im_protocols,
7317           windows->im_exit,CurrentTime);
7318       else
7319         {
7320           int
7321             status;
7322
7323           /*
7324             Confirm program exit.
7325           */
7326           status=XConfirmWidget(display,windows,"Do you really want to exit",
7327             resource_info->client_name);
7328           if (status > 0)
7329             XClientMessage(display,windows->image.id,windows->im_protocols,
7330               windows->im_exit,CurrentTime);
7331         }
7332       break;
7333     }
7334     case CutCommand:
7335     {
7336       /*
7337         Cut image.
7338       */
7339       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7340       break;
7341     }
7342     case CopyCommand:
7343     {
7344       /*
7345         Copy image.
7346       */
7347       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7348         exception);
7349       break;
7350     }
7351     case PasteCommand:
7352     {
7353       /*
7354         Paste image.
7355       */
7356       status=XPasteImage(display,resource_info,windows,*image,exception);
7357       if (status == MagickFalse)
7358         {
7359           XNoticeWidget(display,windows,"Unable to paste X image",
7360             (*image)->filename);
7361           break;
7362         }
7363       break;
7364     }
7365     case HalfSizeCommand:
7366     {
7367       /*
7368         Half image size.
7369       */
7370       windows->image.window_changes.width=windows->image.ximage->width/2;
7371       windows->image.window_changes.height=windows->image.ximage->height/2;
7372       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7373       break;
7374     }
7375     case OriginalSizeCommand:
7376     {
7377       /*
7378         Original image size.
7379       */
7380       windows->image.window_changes.width=(int) (*image)->columns;
7381       windows->image.window_changes.height=(int) (*image)->rows;
7382       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7383       break;
7384     }
7385     case DoubleSizeCommand:
7386     {
7387       /*
7388         Double the image size.
7389       */
7390       windows->image.window_changes.width=windows->image.ximage->width << 1;
7391       windows->image.window_changes.height=windows->image.ximage->height << 1;
7392       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7393       break;
7394     }
7395     case ResizeCommand:
7396     {
7397       int
7398         status;
7399
7400       size_t
7401         height,
7402         width;
7403
7404       ssize_t
7405         x,
7406         y;
7407
7408       /*
7409         Resize image.
7410       */
7411       width=(size_t) windows->image.ximage->width;
7412       height=(size_t) windows->image.ximage->height;
7413       x=0;
7414       y=0;
7415       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7416         (double) width,(double) height);
7417       status=XDialogWidget(display,windows,"Resize",
7418         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7419       if (*geometry == '\0')
7420         break;
7421       if (status == 0)
7422         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7423       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7424       windows->image.window_changes.width=(int) width;
7425       windows->image.window_changes.height=(int) height;
7426       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7427       break;
7428     }
7429     case ApplyCommand:
7430     {
7431       char
7432         image_geometry[MaxTextExtent];
7433
7434       if ((windows->image.crop_geometry == (char *) NULL) &&
7435           ((int) (*image)->columns == windows->image.ximage->width) &&
7436           ((int) (*image)->rows == windows->image.ximage->height))
7437         break;
7438       /*
7439         Apply size transforms to image.
7440       */
7441       XSetCursorState(display,windows,MagickTrue);
7442       XCheckRefreshWindows(display,windows);
7443       /*
7444         Crop and/or scale displayed image.
7445       */
7446       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7447         windows->image.ximage->width,windows->image.ximage->height);
7448       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7449         exception);
7450       if (windows->image.crop_geometry != (char *) NULL)
7451         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7452           windows->image.crop_geometry);
7453       windows->image.x=0;
7454       windows->image.y=0;
7455       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7456       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7457       break;
7458     }
7459     case RefreshCommand:
7460     {
7461       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7462       break;
7463     }
7464     case RestoreCommand:
7465     {
7466       /*
7467         Restore Image window to its original size.
7468       */
7469       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7470           (windows->image.height == (unsigned int) (*image)->rows) &&
7471           (windows->image.crop_geometry == (char *) NULL))
7472         {
7473           (void) XBell(display,0);
7474           break;
7475         }
7476       windows->image.window_changes.width=(int) (*image)->columns;
7477       windows->image.window_changes.height=(int) (*image)->rows;
7478       if (windows->image.crop_geometry != (char *) NULL)
7479         {
7480           windows->image.crop_geometry=(char *)
7481             RelinquishMagickMemory(windows->image.crop_geometry);
7482           windows->image.crop_geometry=(char *) NULL;
7483           windows->image.x=0;
7484           windows->image.y=0;
7485         }
7486       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7487       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7488       break;
7489     }
7490     case CropCommand:
7491     {
7492       /*
7493         Crop image.
7494       */
7495       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7496         exception);
7497       break;
7498     }
7499     case ChopCommand:
7500     {
7501       /*
7502         Chop image.
7503       */
7504       status=XChopImage(display,resource_info,windows,image,exception);
7505       if (status == MagickFalse)
7506         {
7507           XNoticeWidget(display,windows,"Unable to cut X image",
7508             (*image)->filename);
7509           break;
7510         }
7511       break;
7512     }
7513     case FlopCommand:
7514     {
7515       Image
7516         *flop_image;
7517
7518       /*
7519         Flop image scanlines.
7520       */
7521       XSetCursorState(display,windows,MagickTrue);
7522       XCheckRefreshWindows(display,windows);
7523       flop_image=FlopImage(*image,exception);
7524       if (flop_image != (Image *) NULL)
7525         {
7526           *image=DestroyImage(*image);
7527           *image=flop_image;
7528         }
7529       CatchException(exception);
7530       XSetCursorState(display,windows,MagickFalse);
7531       if (windows->image.crop_geometry != (char *) NULL)
7532         {
7533           /*
7534             Flop crop geometry.
7535           */
7536           width=(unsigned int) (*image)->columns;
7537           height=(unsigned int) (*image)->rows;
7538           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7539             &width,&height);
7540           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7541             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7542         }
7543       if (windows->image.orphan != MagickFalse)
7544         break;
7545       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7546       break;
7547     }
7548     case FlipCommand:
7549     {
7550       Image
7551         *flip_image;
7552
7553       /*
7554         Flip image scanlines.
7555       */
7556       XSetCursorState(display,windows,MagickTrue);
7557       XCheckRefreshWindows(display,windows);
7558       flip_image=FlipImage(*image,exception);
7559       if (flip_image != (Image *) NULL)
7560         {
7561           *image=DestroyImage(*image);
7562           *image=flip_image;
7563         }
7564       CatchException(exception);
7565       XSetCursorState(display,windows,MagickFalse);
7566       if (windows->image.crop_geometry != (char *) NULL)
7567         {
7568           /*
7569             Flip crop geometry.
7570           */
7571           width=(unsigned int) (*image)->columns;
7572           height=(unsigned int) (*image)->rows;
7573           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7574             &width,&height);
7575           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7576             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7577         }
7578       if (windows->image.orphan != MagickFalse)
7579         break;
7580       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7581       break;
7582     }
7583     case RotateRightCommand:
7584     {
7585       /*
7586         Rotate image 90 degrees clockwise.
7587       */
7588       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7589       if (status == MagickFalse)
7590         {
7591           XNoticeWidget(display,windows,"Unable to rotate X image",
7592             (*image)->filename);
7593           break;
7594         }
7595       break;
7596     }
7597     case RotateLeftCommand:
7598     {
7599       /*
7600         Rotate image 90 degrees counter-clockwise.
7601       */
7602       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7603       if (status == MagickFalse)
7604         {
7605           XNoticeWidget(display,windows,"Unable to rotate X image",
7606             (*image)->filename);
7607           break;
7608         }
7609       break;
7610     }
7611     case RotateCommand:
7612     {
7613       /*
7614         Rotate image.
7615       */
7616       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7617       if (status == MagickFalse)
7618         {
7619           XNoticeWidget(display,windows,"Unable to rotate X image",
7620             (*image)->filename);
7621           break;
7622         }
7623       break;
7624     }
7625     case ShearCommand:
7626     {
7627       Image
7628         *shear_image;
7629
7630       static char
7631         geometry[MaxTextExtent] = "45.0x45.0";
7632
7633       /*
7634         Query user for shear color and geometry.
7635       */
7636       XColorBrowserWidget(display,windows,"Select",color);
7637       if (*color == '\0')
7638         break;
7639       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7640         geometry);
7641       if (*geometry == '\0')
7642         break;
7643       /*
7644         Shear image.
7645       */
7646       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7647         exception);
7648       XSetCursorState(display,windows,MagickTrue);
7649       XCheckRefreshWindows(display,windows);
7650       (void) QueryColorCompliance(color,AllCompliance,
7651         &(*image)->background_color,exception);
7652       flags=ParseGeometry(geometry,&geometry_info);
7653       if ((flags & SigmaValue) == 0)
7654         geometry_info.sigma=geometry_info.rho;
7655       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7656         exception);
7657       if (shear_image != (Image *) NULL)
7658         {
7659           *image=DestroyImage(*image);
7660           *image=shear_image;
7661         }
7662       CatchException(exception);
7663       XSetCursorState(display,windows,MagickFalse);
7664       if (windows->image.orphan != MagickFalse)
7665         break;
7666       windows->image.window_changes.width=(int) (*image)->columns;
7667       windows->image.window_changes.height=(int) (*image)->rows;
7668       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7669       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7670       break;
7671     }
7672     case RollCommand:
7673     {
7674       Image
7675         *roll_image;
7676
7677       static char
7678         geometry[MaxTextExtent] = "+2+2";
7679
7680       /*
7681         Query user for the roll geometry.
7682       */
7683       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7684         geometry);
7685       if (*geometry == '\0')
7686         break;
7687       /*
7688         Roll image.
7689       */
7690       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7691         exception);
7692       XSetCursorState(display,windows,MagickTrue);
7693       XCheckRefreshWindows(display,windows);
7694       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7695         exception);
7696       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7697         exception);
7698       if (roll_image != (Image *) NULL)
7699         {
7700           *image=DestroyImage(*image);
7701           *image=roll_image;
7702         }
7703       CatchException(exception);
7704       XSetCursorState(display,windows,MagickFalse);
7705       if (windows->image.orphan != MagickFalse)
7706         break;
7707       windows->image.window_changes.width=(int) (*image)->columns;
7708       windows->image.window_changes.height=(int) (*image)->rows;
7709       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7710       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7711       break;
7712     }
7713     case TrimCommand:
7714     {
7715       static char
7716         fuzz[MaxTextExtent];
7717
7718       /*
7719         Query user for the fuzz factor.
7720       */
7721       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7722         (*image)->fuzz/(QuantumRange+1.0));
7723       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7724       if (*fuzz == '\0')
7725         break;
7726       (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7727       /*
7728         Trim image.
7729       */
7730       status=XTrimImage(display,resource_info,windows,*image,exception);
7731       if (status == MagickFalse)
7732         {
7733           XNoticeWidget(display,windows,"Unable to trim X image",
7734             (*image)->filename);
7735           break;
7736         }
7737       break;
7738     }
7739     case HueCommand:
7740     {
7741       static char
7742         hue_percent[MaxTextExtent] = "110";
7743
7744       /*
7745         Query user for percent hue change.
7746       */
7747       (void) XDialogWidget(display,windows,"Apply",
7748         "Enter percent change in image hue (0-200):",hue_percent);
7749       if (*hue_percent == '\0')
7750         break;
7751       /*
7752         Vary the image hue.
7753       */
7754       XSetCursorState(display,windows,MagickTrue);
7755       XCheckRefreshWindows(display,windows);
7756       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7757       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7758         MaxTextExtent);
7759       (void) ModulateImage(*image,modulate_factors,exception);
7760       XSetCursorState(display,windows,MagickFalse);
7761       if (windows->image.orphan != MagickFalse)
7762         break;
7763       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7764       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7765       break;
7766     }
7767     case SaturationCommand:
7768     {
7769       static char
7770         saturation_percent[MaxTextExtent] = "110";
7771
7772       /*
7773         Query user for percent saturation change.
7774       */
7775       (void) XDialogWidget(display,windows,"Apply",
7776         "Enter percent change in color saturation (0-200):",saturation_percent);
7777       if (*saturation_percent == '\0')
7778         break;
7779       /*
7780         Vary color saturation.
7781       */
7782       XSetCursorState(display,windows,MagickTrue);
7783       XCheckRefreshWindows(display,windows);
7784       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7785       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7786         MaxTextExtent);
7787       (void) ModulateImage(*image,modulate_factors,exception);
7788       XSetCursorState(display,windows,MagickFalse);
7789       if (windows->image.orphan != MagickFalse)
7790         break;
7791       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7792       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7793       break;
7794     }
7795     case BrightnessCommand:
7796     {
7797       static char
7798         brightness_percent[MaxTextExtent] = "110";
7799
7800       /*
7801         Query user for percent brightness change.
7802       */
7803       (void) XDialogWidget(display,windows,"Apply",
7804         "Enter percent change in color brightness (0-200):",brightness_percent);
7805       if (*brightness_percent == '\0')
7806         break;
7807       /*
7808         Vary the color brightness.
7809       */
7810       XSetCursorState(display,windows,MagickTrue);
7811       XCheckRefreshWindows(display,windows);
7812       (void) CopyMagickString(modulate_factors,brightness_percent,
7813         MaxTextExtent);
7814       (void) ModulateImage(*image,modulate_factors,exception);
7815       XSetCursorState(display,windows,MagickFalse);
7816       if (windows->image.orphan != MagickFalse)
7817         break;
7818       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7819       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7820       break;
7821     }
7822     case GammaCommand:
7823     {
7824       static char
7825         factor[MaxTextExtent] = "1.6";
7826
7827       /*
7828         Query user for gamma value.
7829       */
7830       (void) XDialogWidget(display,windows,"Gamma",
7831         "Enter gamma value (e.g. 1.2):",factor);
7832       if (*factor == '\0')
7833         break;
7834       /*
7835         Gamma correct image.
7836       */
7837       XSetCursorState(display,windows,MagickTrue);
7838       XCheckRefreshWindows(display,windows);
7839       (void) GammaImage(*image,atof(factor),exception);
7840       XSetCursorState(display,windows,MagickFalse);
7841       if (windows->image.orphan != MagickFalse)
7842         break;
7843       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7844       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7845       break;
7846     }
7847     case SpiffCommand:
7848     {
7849       /*
7850         Sharpen the image contrast.
7851       */
7852       XSetCursorState(display,windows,MagickTrue);
7853       XCheckRefreshWindows(display,windows);
7854       (void) ContrastImage(*image,MagickTrue,exception);
7855       XSetCursorState(display,windows,MagickFalse);
7856       if (windows->image.orphan != MagickFalse)
7857         break;
7858       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7859       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7860       break;
7861     }
7862     case DullCommand:
7863     {
7864       /*
7865         Dull the image contrast.
7866       */
7867       XSetCursorState(display,windows,MagickTrue);
7868       XCheckRefreshWindows(display,windows);
7869       (void) ContrastImage(*image,MagickFalse,exception);
7870       XSetCursorState(display,windows,MagickFalse);
7871       if (windows->image.orphan != MagickFalse)
7872         break;
7873       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7874       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7875       break;
7876     }
7877     case ContrastStretchCommand:
7878     {
7879       double
7880         black_point,
7881         white_point;
7882
7883       static char
7884         levels[MaxTextExtent] = "1%";
7885
7886       /*
7887         Query user for gamma value.
7888       */
7889       (void) XDialogWidget(display,windows,"Contrast Stretch",
7890         "Enter black and white points:",levels);
7891       if (*levels == '\0')
7892         break;
7893       /*
7894         Contrast stretch image.
7895       */
7896       XSetCursorState(display,windows,MagickTrue);
7897       XCheckRefreshWindows(display,windows);
7898       flags=ParseGeometry(levels,&geometry_info);
7899       black_point=geometry_info.rho;
7900       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7901       if ((flags & PercentValue) != 0)
7902         {
7903           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7904           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7905         }
7906       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7907       (void) ContrastStretchImage(*image,black_point,white_point,
7908         exception);
7909       XSetCursorState(display,windows,MagickFalse);
7910       if (windows->image.orphan != MagickFalse)
7911         break;
7912       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7913       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7914       break;
7915     }
7916     case SigmoidalContrastCommand:
7917     {
7918       GeometryInfo
7919         geometry_info;
7920
7921       MagickStatusType
7922         flags;
7923
7924       static char
7925         levels[MaxTextExtent] = "3x50%";
7926
7927       /*
7928         Query user for gamma value.
7929       */
7930       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7931         "Enter contrast and midpoint:",levels);
7932       if (*levels == '\0')
7933         break;
7934       /*
7935         Contrast stretch image.
7936       */
7937       XSetCursorState(display,windows,MagickTrue);
7938       XCheckRefreshWindows(display,windows);
7939       flags=ParseGeometry(levels,&geometry_info);
7940       if ((flags & SigmaValue) == 0)
7941         geometry_info.sigma=1.0*QuantumRange/2.0;
7942       if ((flags & PercentValue) != 0)
7943         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7944       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7945         geometry_info.sigma,exception);
7946       XSetCursorState(display,windows,MagickFalse);
7947       if (windows->image.orphan != MagickFalse)
7948         break;
7949       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7950       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7951       break;
7952     }
7953     case NormalizeCommand:
7954     {
7955       /*
7956         Perform histogram normalization on the image.
7957       */
7958       XSetCursorState(display,windows,MagickTrue);
7959       XCheckRefreshWindows(display,windows);
7960       (void) NormalizeImage(*image,exception);
7961       XSetCursorState(display,windows,MagickFalse);
7962       if (windows->image.orphan != MagickFalse)
7963         break;
7964       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7965       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7966       break;
7967     }
7968     case EqualizeCommand:
7969     {
7970       /*
7971         Perform histogram equalization on the image.
7972       */
7973       XSetCursorState(display,windows,MagickTrue);
7974       XCheckRefreshWindows(display,windows);
7975       (void) EqualizeImage(*image,exception);
7976       XSetCursorState(display,windows,MagickFalse);
7977       if (windows->image.orphan != MagickFalse)
7978         break;
7979       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7980       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7981       break;
7982     }
7983     case NegateCommand:
7984     {
7985       /*
7986         Negate colors in image.
7987       */
7988       XSetCursorState(display,windows,MagickTrue);
7989       XCheckRefreshWindows(display,windows);
7990       (void) NegateImage(*image,MagickFalse,exception);
7991       XSetCursorState(display,windows,MagickFalse);
7992       if (windows->image.orphan != MagickFalse)
7993         break;
7994       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7995       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7996       break;
7997     }
7998     case GrayscaleCommand:
7999     {
8000       /*
8001         Convert image to grayscale.
8002       */
8003       XSetCursorState(display,windows,MagickTrue);
8004       XCheckRefreshWindows(display,windows);
8005       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
8006         GrayscaleType : GrayscaleMatteType,exception);
8007       XSetCursorState(display,windows,MagickFalse);
8008       if (windows->image.orphan != MagickFalse)
8009         break;
8010       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8011       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8012       break;
8013     }
8014     case MapCommand:
8015     {
8016       Image
8017         *affinity_image;
8018
8019       static char
8020         filename[MaxTextExtent] = "\0";
8021
8022       /*
8023         Request image file name from user.
8024       */
8025       XFileBrowserWidget(display,windows,"Map",filename);
8026       if (*filename == '\0')
8027         break;
8028       /*
8029         Map image.
8030       */
8031       XSetCursorState(display,windows,MagickTrue);
8032       XCheckRefreshWindows(display,windows);
8033       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8034       affinity_image=ReadImage(image_info,exception);
8035       if (affinity_image != (Image *) NULL)
8036         {
8037           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8038           affinity_image=DestroyImage(affinity_image);
8039         }
8040       CatchException(exception);
8041       XSetCursorState(display,windows,MagickFalse);
8042       if (windows->image.orphan != MagickFalse)
8043         break;
8044       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8045       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8046       break;
8047     }
8048     case QuantizeCommand:
8049     {
8050       int
8051         status;
8052
8053       static char
8054         colors[MaxTextExtent] = "256";
8055
8056       /*
8057         Query user for maximum number of colors.
8058       */
8059       status=XDialogWidget(display,windows,"Quantize",
8060         "Maximum number of colors:",colors);
8061       if (*colors == '\0')
8062         break;
8063       /*
8064         Color reduce the image.
8065       */
8066       XSetCursorState(display,windows,MagickTrue);
8067       XCheckRefreshWindows(display,windows);
8068       quantize_info.number_colors=StringToUnsignedLong(colors);
8069       quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8070       (void) QuantizeImage(&quantize_info,*image,exception);
8071       XSetCursorState(display,windows,MagickFalse);
8072       if (windows->image.orphan != MagickFalse)
8073         break;
8074       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8075       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8076       break;
8077     }
8078     case DespeckleCommand:
8079     {
8080       Image
8081         *despeckle_image;
8082
8083       /*
8084         Despeckle image.
8085       */
8086       XSetCursorState(display,windows,MagickTrue);
8087       XCheckRefreshWindows(display,windows);
8088       despeckle_image=DespeckleImage(*image,exception);
8089       if (despeckle_image != (Image *) NULL)
8090         {
8091           *image=DestroyImage(*image);
8092           *image=despeckle_image;
8093         }
8094       CatchException(exception);
8095       XSetCursorState(display,windows,MagickFalse);
8096       if (windows->image.orphan != MagickFalse)
8097         break;
8098       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8099       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8100       break;
8101     }
8102     case EmbossCommand:
8103     {
8104       Image
8105         *emboss_image;
8106
8107       static char
8108         radius[MaxTextExtent] = "0.0x1.0";
8109
8110       /*
8111         Query user for emboss radius.
8112       */
8113       (void) XDialogWidget(display,windows,"Emboss",
8114         "Enter the emboss radius and standard deviation:",radius);
8115       if (*radius == '\0')
8116         break;
8117       /*
8118         Reduce noise in the image.
8119       */
8120       XSetCursorState(display,windows,MagickTrue);
8121       XCheckRefreshWindows(display,windows);
8122       flags=ParseGeometry(radius,&geometry_info);
8123       if ((flags & SigmaValue) == 0)
8124         geometry_info.sigma=1.0;
8125       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8126         exception);
8127       if (emboss_image != (Image *) NULL)
8128         {
8129           *image=DestroyImage(*image);
8130           *image=emboss_image;
8131         }
8132       CatchException(exception);
8133       XSetCursorState(display,windows,MagickFalse);
8134       if (windows->image.orphan != MagickFalse)
8135         break;
8136       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8137       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8138       break;
8139     }
8140     case ReduceNoiseCommand:
8141     {
8142       Image
8143         *noise_image;
8144
8145       static char
8146         radius[MaxTextExtent] = "0";
8147
8148       /*
8149         Query user for noise radius.
8150       */
8151       (void) XDialogWidget(display,windows,"Reduce Noise",
8152         "Enter the noise radius:",radius);
8153       if (*radius == '\0')
8154         break;
8155       /*
8156         Reduce noise in the image.
8157       */
8158       XSetCursorState(display,windows,MagickTrue);
8159       XCheckRefreshWindows(display,windows);
8160       flags=ParseGeometry(radius,&geometry_info);
8161       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8162         geometry_info.rho,(size_t) geometry_info.rho,exception);
8163       if (noise_image != (Image *) NULL)
8164         {
8165           *image=DestroyImage(*image);
8166           *image=noise_image;
8167         }
8168       CatchException(exception);
8169       XSetCursorState(display,windows,MagickFalse);
8170       if (windows->image.orphan != MagickFalse)
8171         break;
8172       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8173       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8174       break;
8175     }
8176     case AddNoiseCommand:
8177     {
8178       char
8179         **noises;
8180
8181       Image
8182         *noise_image;
8183
8184       static char
8185         noise_type[MaxTextExtent] = "Gaussian";
8186
8187       /*
8188         Add noise to the image.
8189       */
8190       noises=GetCommandOptions(MagickNoiseOptions);
8191       if (noises == (char **) NULL)
8192         break;
8193       XListBrowserWidget(display,windows,&windows->widget,
8194         (const char **) noises,"Add Noise",
8195         "Select a type of noise to add to your image:",noise_type);
8196       noises=DestroyStringList(noises);
8197       if (*noise_type == '\0')
8198         break;
8199       XSetCursorState(display,windows,MagickTrue);
8200       XCheckRefreshWindows(display,windows);
8201       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8202         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8203       if (noise_image != (Image *) NULL)
8204         {
8205           *image=DestroyImage(*image);
8206           *image=noise_image;
8207         }
8208       CatchException(exception);
8209       XSetCursorState(display,windows,MagickFalse);
8210       if (windows->image.orphan != MagickFalse)
8211         break;
8212       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8213       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8214       break;
8215     }
8216     case SharpenCommand:
8217     {
8218       Image
8219         *sharp_image;
8220
8221       static char
8222         radius[MaxTextExtent] = "0.0x1.0";
8223
8224       /*
8225         Query user for sharpen radius.
8226       */
8227       (void) XDialogWidget(display,windows,"Sharpen",
8228         "Enter the sharpen radius and standard deviation:",radius);
8229       if (*radius == '\0')
8230         break;
8231       /*
8232         Sharpen image scanlines.
8233       */
8234       XSetCursorState(display,windows,MagickTrue);
8235       XCheckRefreshWindows(display,windows);
8236       flags=ParseGeometry(radius,&geometry_info);
8237       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8238         exception);
8239       if (sharp_image != (Image *) NULL)
8240         {
8241           *image=DestroyImage(*image);
8242           *image=sharp_image;
8243         }
8244       CatchException(exception);
8245       XSetCursorState(display,windows,MagickFalse);
8246       if (windows->image.orphan != MagickFalse)
8247         break;
8248       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8249       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8250       break;
8251     }
8252     case BlurCommand:
8253     {
8254       Image
8255         *blur_image;
8256
8257       static char
8258         radius[MaxTextExtent] = "0.0x1.0";
8259
8260       /*
8261         Query user for blur radius.
8262       */
8263       (void) XDialogWidget(display,windows,"Blur",
8264         "Enter the blur radius and standard deviation:",radius);
8265       if (*radius == '\0')
8266         break;
8267       /*
8268         Blur an image.
8269       */
8270       XSetCursorState(display,windows,MagickTrue);
8271       XCheckRefreshWindows(display,windows);
8272       flags=ParseGeometry(radius,&geometry_info);
8273       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8274         exception);
8275       if (blur_image != (Image *) NULL)
8276         {
8277           *image=DestroyImage(*image);
8278           *image=blur_image;
8279         }
8280       CatchException(exception);
8281       XSetCursorState(display,windows,MagickFalse);
8282       if (windows->image.orphan != MagickFalse)
8283         break;
8284       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8285       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8286       break;
8287     }
8288     case ThresholdCommand:
8289     {
8290       double
8291         threshold;
8292
8293       static char
8294         factor[MaxTextExtent] = "128";
8295
8296       /*
8297         Query user for threshold value.
8298       */
8299       (void) XDialogWidget(display,windows,"Threshold",
8300         "Enter threshold value:",factor);
8301       if (*factor == '\0')
8302         break;
8303       /*
8304         Gamma correct image.
8305       */
8306       XSetCursorState(display,windows,MagickTrue);
8307       XCheckRefreshWindows(display,windows);
8308       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8309       (void) BilevelImage(*image,threshold,exception);
8310       XSetCursorState(display,windows,MagickFalse);
8311       if (windows->image.orphan != MagickFalse)
8312         break;
8313       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8314       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8315       break;
8316     }
8317     case EdgeDetectCommand:
8318     {
8319       Image
8320         *edge_image;
8321
8322       static char
8323         radius[MaxTextExtent] = "0";
8324
8325       /*
8326         Query user for edge factor.
8327       */
8328       (void) XDialogWidget(display,windows,"Detect Edges",
8329         "Enter the edge detect radius:",radius);
8330       if (*radius == '\0')
8331         break;
8332       /*
8333         Detect edge in image.
8334       */
8335       XSetCursorState(display,windows,MagickTrue);
8336       XCheckRefreshWindows(display,windows);
8337       flags=ParseGeometry(radius,&geometry_info);
8338       edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8339         exception);
8340       if (edge_image != (Image *) NULL)
8341         {
8342           *image=DestroyImage(*image);
8343           *image=edge_image;
8344         }
8345       CatchException(exception);
8346       XSetCursorState(display,windows,MagickFalse);
8347       if (windows->image.orphan != MagickFalse)
8348         break;
8349       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8350       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8351       break;
8352     }
8353     case SpreadCommand:
8354     {
8355       Image
8356         *spread_image;
8357
8358       static char
8359         amount[MaxTextExtent] = "2";
8360
8361       /*
8362         Query user for spread amount.
8363       */
8364       (void) XDialogWidget(display,windows,"Spread",
8365         "Enter the displacement amount:",amount);
8366       if (*amount == '\0')
8367         break;
8368       /*
8369         Displace image pixels by a random amount.
8370       */
8371       XSetCursorState(display,windows,MagickTrue);
8372       XCheckRefreshWindows(display,windows);
8373       flags=ParseGeometry(amount,&geometry_info);
8374       spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8375         exception);
8376       if (spread_image != (Image *) NULL)
8377         {
8378           *image=DestroyImage(*image);
8379           *image=spread_image;
8380         }
8381       CatchException(exception);
8382       XSetCursorState(display,windows,MagickFalse);
8383       if (windows->image.orphan != MagickFalse)
8384         break;
8385       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8386       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8387       break;
8388     }
8389     case ShadeCommand:
8390     {
8391       Image
8392         *shade_image;
8393
8394       int
8395         status;
8396
8397       static char
8398         geometry[MaxTextExtent] = "30x30";
8399
8400       /*
8401         Query user for the shade geometry.
8402       */
8403       status=XDialogWidget(display,windows,"Shade",
8404         "Enter the azimuth and elevation of the light source:",geometry);
8405       if (*geometry == '\0')
8406         break;
8407       /*
8408         Shade image pixels.
8409       */
8410       XSetCursorState(display,windows,MagickTrue);
8411       XCheckRefreshWindows(display,windows);
8412       flags=ParseGeometry(geometry,&geometry_info);
8413       if ((flags & SigmaValue) == 0)
8414         geometry_info.sigma=1.0;
8415       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8416         geometry_info.rho,geometry_info.sigma,exception);
8417       if (shade_image != (Image *) NULL)
8418         {
8419           *image=DestroyImage(*image);
8420           *image=shade_image;
8421         }
8422       CatchException(exception);
8423       XSetCursorState(display,windows,MagickFalse);
8424       if (windows->image.orphan != MagickFalse)
8425         break;
8426       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8427       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8428       break;
8429     }
8430     case RaiseCommand:
8431     {
8432       static char
8433         bevel_width[MaxTextExtent] = "10";
8434
8435       /*
8436         Query user for bevel width.
8437       */
8438       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8439       if (*bevel_width == '\0')
8440         break;
8441       /*
8442         Raise an image.
8443       */
8444       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8445         exception);
8446       XSetCursorState(display,windows,MagickTrue);
8447       XCheckRefreshWindows(display,windows);
8448       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8449         exception);
8450       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8451       XSetCursorState(display,windows,MagickFalse);
8452       if (windows->image.orphan != MagickFalse)
8453         break;
8454       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8455       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8456       break;
8457     }
8458     case SegmentCommand:
8459     {
8460       static char
8461         threshold[MaxTextExtent] = "1.0x1.5";
8462
8463       /*
8464         Query user for smoothing threshold.
8465       */
8466       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8467         threshold);
8468       if (*threshold == '\0')
8469         break;
8470       /*
8471         Segment an image.
8472       */
8473       XSetCursorState(display,windows,MagickTrue);
8474       XCheckRefreshWindows(display,windows);
8475       flags=ParseGeometry(threshold,&geometry_info);
8476       if ((flags & SigmaValue) == 0)
8477         geometry_info.sigma=1.0;
8478       (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8479         geometry_info.sigma,exception);
8480       XSetCursorState(display,windows,MagickFalse);
8481       if (windows->image.orphan != MagickFalse)
8482         break;
8483       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8484       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8485       break;
8486     }
8487     case SepiaToneCommand:
8488     {
8489       double
8490         threshold;
8491
8492       Image
8493         *sepia_image;
8494
8495       static char
8496         factor[MaxTextExtent] = "80%";
8497
8498       /*
8499         Query user for sepia-tone factor.
8500       */
8501       (void) XDialogWidget(display,windows,"Sepia Tone",
8502         "Enter the sepia tone factor (0 - 99.9%):",factor);
8503       if (*factor == '\0')
8504         break;
8505       /*
8506         Sepia tone image pixels.
8507       */
8508       XSetCursorState(display,windows,MagickTrue);
8509       XCheckRefreshWindows(display,windows);
8510       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8511       sepia_image=SepiaToneImage(*image,threshold,exception);
8512       if (sepia_image != (Image *) NULL)
8513         {
8514           *image=DestroyImage(*image);
8515           *image=sepia_image;
8516         }
8517       CatchException(exception);
8518       XSetCursorState(display,windows,MagickFalse);
8519       if (windows->image.orphan != MagickFalse)
8520         break;
8521       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8522       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8523       break;
8524     }
8525     case SolarizeCommand:
8526     {
8527       double
8528         threshold;
8529
8530       static char
8531         factor[MaxTextExtent] = "60%";
8532
8533       /*
8534         Query user for solarize factor.
8535       */
8536       (void) XDialogWidget(display,windows,"Solarize",
8537         "Enter the solarize factor (0 - 99.9%):",factor);
8538       if (*factor == '\0')
8539         break;
8540       /*
8541         Solarize image pixels.
8542       */
8543       XSetCursorState(display,windows,MagickTrue);
8544       XCheckRefreshWindows(display,windows);
8545       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8546       (void) SolarizeImage(*image,threshold,exception);
8547       XSetCursorState(display,windows,MagickFalse);
8548       if (windows->image.orphan != MagickFalse)
8549         break;
8550       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8551       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8552       break;
8553     }
8554     case SwirlCommand:
8555     {
8556       Image
8557         *swirl_image;
8558
8559       static char
8560         degrees[MaxTextExtent] = "60";
8561
8562       /*
8563         Query user for swirl angle.
8564       */
8565       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8566         degrees);
8567       if (*degrees == '\0')
8568         break;
8569       /*
8570         Swirl image pixels about the center.
8571       */
8572       XSetCursorState(display,windows,MagickTrue);
8573       XCheckRefreshWindows(display,windows);
8574       flags=ParseGeometry(degrees,&geometry_info);
8575       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8576         exception);
8577       if (swirl_image != (Image *) NULL)
8578         {
8579           *image=DestroyImage(*image);
8580           *image=swirl_image;
8581         }
8582       CatchException(exception);
8583       XSetCursorState(display,windows,MagickFalse);
8584       if (windows->image.orphan != MagickFalse)
8585         break;
8586       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8587       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8588       break;
8589     }
8590     case ImplodeCommand:
8591     {
8592       Image
8593         *implode_image;
8594
8595       static char
8596         factor[MaxTextExtent] = "0.3";
8597
8598       /*
8599         Query user for implode factor.
8600       */
8601       (void) XDialogWidget(display,windows,"Implode",
8602         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8603       if (*factor == '\0')
8604         break;
8605       /*
8606         Implode image pixels about the center.
8607       */
8608       XSetCursorState(display,windows,MagickTrue);
8609       XCheckRefreshWindows(display,windows);
8610       flags=ParseGeometry(factor,&geometry_info);
8611       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8612         exception);
8613       if (implode_image != (Image *) NULL)
8614         {
8615           *image=DestroyImage(*image);
8616           *image=implode_image;
8617         }
8618       CatchException(exception);
8619       XSetCursorState(display,windows,MagickFalse);
8620       if (windows->image.orphan != MagickFalse)
8621         break;
8622       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8623       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8624       break;
8625     }
8626     case VignetteCommand:
8627     {
8628       Image
8629         *vignette_image;
8630
8631       static char
8632         geometry[MaxTextExtent] = "0x20";
8633
8634       /*
8635         Query user for the vignette geometry.
8636       */
8637       (void) XDialogWidget(display,windows,"Vignette",
8638         "Enter the radius, sigma, and x and y offsets:",geometry);
8639       if (*geometry == '\0')
8640         break;
8641       /*
8642         Soften the edges of the image in vignette style
8643       */
8644       XSetCursorState(display,windows,MagickTrue);
8645       XCheckRefreshWindows(display,windows);
8646       flags=ParseGeometry(geometry,&geometry_info);
8647       if ((flags & SigmaValue) == 0)
8648         geometry_info.sigma=1.0;
8649       if ((flags & XiValue) == 0)
8650         geometry_info.xi=0.1*(*image)->columns;
8651       if ((flags & PsiValue) == 0)
8652         geometry_info.psi=0.1*(*image)->rows;
8653       vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8654         ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8655         exception);
8656       if (vignette_image != (Image *) NULL)
8657         {
8658           *image=DestroyImage(*image);
8659           *image=vignette_image;
8660         }
8661       CatchException(exception);
8662       XSetCursorState(display,windows,MagickFalse);
8663       if (windows->image.orphan != MagickFalse)
8664         break;
8665       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8666       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8667       break;
8668     }
8669     case WaveCommand:
8670     {
8671       Image
8672         *wave_image;
8673
8674       static char
8675         geometry[MaxTextExtent] = "25x150";
8676
8677       /*
8678         Query user for the wave geometry.
8679       */
8680       (void) XDialogWidget(display,windows,"Wave",
8681         "Enter the amplitude and length of the wave:",geometry);
8682       if (*geometry == '\0')
8683         break;
8684       /*
8685         Alter an image along a sine wave.
8686       */
8687       XSetCursorState(display,windows,MagickTrue);
8688       XCheckRefreshWindows(display,windows);
8689       flags=ParseGeometry(geometry,&geometry_info);
8690       if ((flags & SigmaValue) == 0)
8691         geometry_info.sigma=1.0;
8692       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8693         (*image)->interpolate,exception);
8694       if (wave_image != (Image *) NULL)
8695         {
8696           *image=DestroyImage(*image);
8697           *image=wave_image;
8698         }
8699       CatchException(exception);
8700       XSetCursorState(display,windows,MagickFalse);
8701       if (windows->image.orphan != MagickFalse)
8702         break;
8703       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8704       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8705       break;
8706     }
8707     case OilPaintCommand:
8708     {
8709       Image
8710         *paint_image;
8711
8712       static char
8713         radius[MaxTextExtent] = "0";
8714
8715       /*
8716         Query user for circular neighborhood radius.
8717       */
8718       (void) XDialogWidget(display,windows,"Oil Paint",
8719         "Enter the mask radius:",radius);
8720       if (*radius == '\0')
8721         break;
8722       /*
8723         OilPaint image scanlines.
8724       */
8725       XSetCursorState(display,windows,MagickTrue);
8726       XCheckRefreshWindows(display,windows);
8727       flags=ParseGeometry(radius,&geometry_info);
8728       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8729         exception);
8730       if (paint_image != (Image *) NULL)
8731         {
8732           *image=DestroyImage(*image);
8733           *image=paint_image;
8734         }
8735       CatchException(exception);
8736       XSetCursorState(display,windows,MagickFalse);
8737       if (windows->image.orphan != MagickFalse)
8738         break;
8739       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8740       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8741       break;
8742     }
8743     case CharcoalDrawCommand:
8744     {
8745       Image
8746         *charcoal_image;
8747
8748       static char
8749         radius[MaxTextExtent] = "0x1";
8750
8751       /*
8752         Query user for charcoal radius.
8753       */
8754       (void) XDialogWidget(display,windows,"Charcoal Draw",
8755         "Enter the charcoal radius and sigma:",radius);
8756       if (*radius == '\0')
8757         break;
8758       /*
8759         Charcoal the image.
8760       */
8761       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8762         exception);
8763       XSetCursorState(display,windows,MagickTrue);
8764       XCheckRefreshWindows(display,windows);
8765       flags=ParseGeometry(radius,&geometry_info);
8766       if ((flags & SigmaValue) == 0)
8767         geometry_info.sigma=geometry_info.rho;
8768       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8769         exception);
8770       if (charcoal_image != (Image *) NULL)
8771         {
8772           *image=DestroyImage(*image);
8773           *image=charcoal_image;
8774         }
8775       CatchException(exception);
8776       XSetCursorState(display,windows,MagickFalse);
8777       if (windows->image.orphan != MagickFalse)
8778         break;
8779       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8780       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8781       break;
8782     }
8783     case AnnotateCommand:
8784     {
8785       /*
8786         Annotate the image with text.
8787       */
8788       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8789       if (status == MagickFalse)
8790         {
8791           XNoticeWidget(display,windows,"Unable to annotate X image",
8792             (*image)->filename);
8793           break;
8794         }
8795       break;
8796     }
8797     case DrawCommand:
8798     {
8799       /*
8800         Draw image.
8801       */
8802       status=XDrawEditImage(display,resource_info,windows,image,exception);
8803       if (status == MagickFalse)
8804         {
8805           XNoticeWidget(display,windows,"Unable to draw on the X image",
8806             (*image)->filename);
8807           break;
8808         }
8809       break;
8810     }
8811     case ColorCommand:
8812     {
8813       /*
8814         Color edit.
8815       */
8816       status=XColorEditImage(display,resource_info,windows,image,exception);
8817       if (status == MagickFalse)
8818         {
8819           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8820             (*image)->filename);
8821           break;
8822         }
8823       break;
8824     }
8825     case MatteCommand:
8826     {
8827       /*
8828         Matte edit.
8829       */
8830       status=XMatteEditImage(display,resource_info,windows,image,exception);
8831       if (status == MagickFalse)
8832         {
8833           XNoticeWidget(display,windows,"Unable to matte edit X image",
8834             (*image)->filename);
8835           break;
8836         }
8837       break;
8838     }
8839     case CompositeCommand:
8840     {
8841       /*
8842         Composite image.
8843       */
8844       status=XCompositeImage(display,resource_info,windows,*image,
8845         exception);
8846       if (status == MagickFalse)
8847         {
8848           XNoticeWidget(display,windows,"Unable to composite X image",
8849             (*image)->filename);
8850           break;
8851         }
8852       break;
8853     }
8854     case AddBorderCommand:
8855     {
8856       Image
8857         *border_image;
8858
8859       static char
8860         geometry[MaxTextExtent] = "6x6";
8861
8862       /*
8863         Query user for border color and geometry.
8864       */
8865       XColorBrowserWidget(display,windows,"Select",color);
8866       if (*color == '\0')
8867         break;
8868       (void) XDialogWidget(display,windows,"Add Border",
8869         "Enter border geometry:",geometry);
8870       if (*geometry == '\0')
8871         break;
8872       /*
8873         Add a border to the image.
8874       */
8875       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8876         exception);
8877       XSetCursorState(display,windows,MagickTrue);
8878       XCheckRefreshWindows(display,windows);
8879       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8880         exception);
8881       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8882         exception);
8883       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8884         exception);
8885       if (border_image != (Image *) NULL)
8886         {
8887           *image=DestroyImage(*image);
8888           *image=border_image;
8889         }
8890       CatchException(exception);
8891       XSetCursorState(display,windows,MagickFalse);
8892       if (windows->image.orphan != MagickFalse)
8893         break;
8894       windows->image.window_changes.width=(int) (*image)->columns;
8895       windows->image.window_changes.height=(int) (*image)->rows;
8896       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8897       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8898       break;
8899     }
8900     case AddFrameCommand:
8901     {
8902       FrameInfo
8903         frame_info;
8904
8905       Image
8906         *frame_image;
8907
8908       static char
8909         geometry[MaxTextExtent] = "6x6";
8910
8911       /*
8912         Query user for frame color and geometry.
8913       */
8914       XColorBrowserWidget(display,windows,"Select",color);
8915       if (*color == '\0')
8916         break;
8917       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8918         geometry);
8919       if (*geometry == '\0')
8920         break;
8921       /*
8922         Surround image with an ornamental border.
8923       */
8924       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8925         exception);
8926       XSetCursorState(display,windows,MagickTrue);
8927       XCheckRefreshWindows(display,windows);
8928       (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8929         exception);
8930       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8931         exception);
8932       frame_info.width=page_geometry.width;
8933       frame_info.height=page_geometry.height;
8934       frame_info.outer_bevel=page_geometry.x;
8935       frame_info.inner_bevel=page_geometry.y;
8936       frame_info.x=(ssize_t) frame_info.width;
8937       frame_info.y=(ssize_t) frame_info.height;
8938       frame_info.width=(*image)->columns+2*frame_info.width;
8939       frame_info.height=(*image)->rows+2*frame_info.height;
8940       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8941       if (frame_image != (Image *) NULL)
8942         {
8943           *image=DestroyImage(*image);
8944           *image=frame_image;
8945         }
8946       CatchException(exception);
8947       XSetCursorState(display,windows,MagickFalse);
8948       if (windows->image.orphan != MagickFalse)
8949         break;
8950       windows->image.window_changes.width=(int) (*image)->columns;
8951       windows->image.window_changes.height=(int) (*image)->rows;
8952       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8953       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8954       break;
8955     }
8956     case CommentCommand:
8957     {
8958       const char
8959         *value;
8960
8961       FILE
8962         *file;
8963
8964       int
8965         unique_file;
8966
8967       /*
8968         Edit image comment.
8969       */
8970       unique_file=AcquireUniqueFileResource(image_info->filename);
8971       if (unique_file == -1)
8972         XNoticeWidget(display,windows,"Unable to edit image comment",
8973           image_info->filename);
8974       value=GetImageProperty(*image,"comment",exception);
8975       if (value == (char *) NULL)
8976         unique_file=close(unique_file)-1;
8977       else
8978         {
8979           register const char
8980             *p;
8981
8982           file=fdopen(unique_file,"w");
8983           if (file == (FILE *) NULL)
8984             {
8985               XNoticeWidget(display,windows,"Unable to edit image comment",
8986                 image_info->filename);
8987               break;
8988             }
8989           for (p=value; *p != '\0'; p++)
8990             (void) fputc((int) *p,file);
8991           (void) fputc('\n',file);
8992           (void) fclose(file);
8993         }
8994       XSetCursorState(display,windows,MagickTrue);
8995       XCheckRefreshWindows(display,windows);
8996       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8997         exception);
8998       if (status == MagickFalse)
8999         XNoticeWidget(display,windows,"Unable to edit image comment",
9000           (char *) NULL);
9001       else
9002         {
9003           char
9004             *comment;
9005
9006           comment=FileToString(image_info->filename,~0UL,exception);
9007           if (comment != (char *) NULL)
9008             {
9009               (void) SetImageProperty(*image,"comment",comment,exception);
9010               (*image)->taint=MagickTrue;
9011             }
9012         }
9013       (void) RelinquishUniqueFileResource(image_info->filename);
9014       XSetCursorState(display,windows,MagickFalse);
9015       break;
9016     }
9017     case LaunchCommand:
9018     {
9019       /*
9020         Launch program.
9021       */
9022       XSetCursorState(display,windows,MagickTrue);
9023       XCheckRefreshWindows(display,windows);
9024       (void) AcquireUniqueFilename(filename);
9025       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9026         filename);
9027       status=WriteImage(image_info,*image,exception);
9028       if (status == MagickFalse)
9029         XNoticeWidget(display,windows,"Unable to launch image editor",
9030           (char *) NULL);
9031       else
9032         {
9033           nexus=ReadImage(resource_info->image_info,exception);
9034           CatchException(exception);
9035           XClientMessage(display,windows->image.id,windows->im_protocols,
9036             windows->im_next_image,CurrentTime);
9037         }
9038       (void) RelinquishUniqueFileResource(filename);
9039       XSetCursorState(display,windows,MagickFalse);
9040       break;
9041     }
9042     case RegionofInterestCommand:
9043     {
9044       /*
9045         Apply an image processing technique to a region of interest.
9046       */
9047       (void) XROIImage(display,resource_info,windows,image,exception);
9048       break;
9049     }
9050     case InfoCommand:
9051       break;
9052     case ZoomCommand:
9053     {
9054       /*
9055         Zoom image.
9056       */
9057       if (windows->magnify.mapped != MagickFalse)
9058         (void) XRaiseWindow(display,windows->magnify.id);
9059       else
9060         {
9061           /*
9062             Make magnify image.
9063           */
9064           XSetCursorState(display,windows,MagickTrue);
9065           (void) XMapRaised(display,windows->magnify.id);
9066           XSetCursorState(display,windows,MagickFalse);
9067         }
9068       break;
9069     }
9070     case ShowPreviewCommand:
9071     {
9072       char
9073         **previews;
9074
9075       Image
9076         *preview_image;
9077
9078       static char
9079         preview_type[MaxTextExtent] = "Gamma";
9080
9081       /*
9082         Select preview type from menu.
9083       */
9084       previews=GetCommandOptions(MagickPreviewOptions);
9085       if (previews == (char **) NULL)
9086         break;
9087       XListBrowserWidget(display,windows,&windows->widget,
9088         (const char **) previews,"Preview",
9089         "Select an enhancement, effect, or F/X:",preview_type);
9090       previews=DestroyStringList(previews);
9091       if (*preview_type == '\0')
9092         break;
9093       /*
9094         Show image preview.
9095       */
9096       XSetCursorState(display,windows,MagickTrue);
9097       XCheckRefreshWindows(display,windows);
9098       image_info->preview_type=(PreviewType)
9099         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9100       image_info->group=(ssize_t) windows->image.id;
9101       (void) DeleteImageProperty(*image,"label");
9102       (void) SetImageProperty(*image,"label","Preview",exception);
9103       (void) AcquireUniqueFilename(filename);
9104       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9105         filename);
9106       status=WriteImage(image_info,*image,exception);
9107       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9108       preview_image=ReadImage(image_info,exception);
9109       (void) RelinquishUniqueFileResource(filename);
9110       if (preview_image == (Image *) NULL)
9111         break;
9112       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9113         filename);
9114       status=WriteImage(image_info,preview_image,exception);
9115       preview_image=DestroyImage(preview_image);
9116       if (status == MagickFalse)
9117         XNoticeWidget(display,windows,"Unable to show image preview",
9118           (*image)->filename);
9119       XDelay(display,1500);
9120       XSetCursorState(display,windows,MagickFalse);
9121       break;
9122     }
9123     case ShowHistogramCommand:
9124     {
9125       Image
9126         *histogram_image;
9127
9128       /*
9129         Show image histogram.
9130       */
9131       XSetCursorState(display,windows,MagickTrue);
9132       XCheckRefreshWindows(display,windows);
9133       image_info->group=(ssize_t) windows->image.id;
9134       (void) DeleteImageProperty(*image,"label");
9135       (void) SetImageProperty(*image,"label","Histogram",exception);
9136       (void) AcquireUniqueFilename(filename);
9137       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9138         filename);
9139       status=WriteImage(image_info,*image,exception);
9140       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9141       histogram_image=ReadImage(image_info,exception);
9142       (void) RelinquishUniqueFileResource(filename);
9143       if (histogram_image == (Image *) NULL)
9144         break;
9145       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9146         "show:%s",filename);
9147       status=WriteImage(image_info,histogram_image,exception);
9148       histogram_image=DestroyImage(histogram_image);
9149       if (status == MagickFalse)
9150         XNoticeWidget(display,windows,"Unable to show histogram",
9151           (*image)->filename);
9152       XDelay(display,1500);
9153       XSetCursorState(display,windows,MagickFalse);
9154       break;
9155     }
9156     case ShowMatteCommand:
9157     {
9158       Image
9159         *matte_image;
9160
9161       if ((*image)->matte == MagickFalse)
9162         {
9163           XNoticeWidget(display,windows,
9164             "Image does not have any matte information",(*image)->filename);
9165           break;
9166         }
9167       /*
9168         Show image matte.
9169       */
9170       XSetCursorState(display,windows,MagickTrue);
9171       XCheckRefreshWindows(display,windows);
9172       image_info->group=(ssize_t) windows->image.id;
9173       (void) DeleteImageProperty(*image,"label");
9174       (void) SetImageProperty(*image,"label","Matte",exception);
9175       (void) AcquireUniqueFilename(filename);
9176       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9177         filename);
9178       status=WriteImage(image_info,*image,exception);
9179       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9180       matte_image=ReadImage(image_info,exception);
9181       (void) RelinquishUniqueFileResource(filename);
9182       if (matte_image == (Image *) NULL)
9183         break;
9184       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9185         filename);
9186       status=WriteImage(image_info,matte_image,exception);
9187       matte_image=DestroyImage(matte_image);
9188       if (status == MagickFalse)
9189         XNoticeWidget(display,windows,"Unable to show matte",
9190           (*image)->filename);
9191       XDelay(display,1500);
9192       XSetCursorState(display,windows,MagickFalse);
9193       break;
9194     }
9195     case BackgroundCommand:
9196     {
9197       /*
9198         Background image.
9199       */
9200       status=XBackgroundImage(display,resource_info,windows,image,exception);
9201       if (status == MagickFalse)
9202         break;
9203       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9204       if (nexus != (Image *) NULL)
9205         XClientMessage(display,windows->image.id,windows->im_protocols,
9206           windows->im_next_image,CurrentTime);
9207       break;
9208     }
9209     case SlideShowCommand:
9210     {
9211       static char
9212         delay[MaxTextExtent] = "5";
9213
9214       /*
9215         Display next image after pausing.
9216       */
9217       (void) XDialogWidget(display,windows,"Slide Show",
9218         "Pause how many 1/100ths of a second between images:",delay);
9219       if (*delay == '\0')
9220         break;
9221       resource_info->delay=StringToUnsignedLong(delay);
9222       XClientMessage(display,windows->image.id,windows->im_protocols,
9223         windows->im_next_image,CurrentTime);
9224       break;
9225     }
9226     case PreferencesCommand:
9227     {
9228       /*
9229         Set user preferences.
9230       */
9231       status=XPreferencesWidget(display,resource_info,windows);
9232       if (status == MagickFalse)
9233         break;
9234       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9235       if (nexus != (Image *) NULL)
9236         XClientMessage(display,windows->image.id,windows->im_protocols,
9237           windows->im_next_image,CurrentTime);
9238       break;
9239     }
9240     case HelpCommand:
9241     {
9242       /*
9243         User requested help.
9244       */
9245       XTextViewWidget(display,resource_info,windows,MagickFalse,
9246         "Help Viewer - Display",DisplayHelp);
9247       break;
9248     }
9249     case BrowseDocumentationCommand:
9250     {
9251       Atom
9252         mozilla_atom;
9253
9254       Window
9255         mozilla_window,
9256         root_window;
9257
9258       /*
9259         Browse the ImageMagick documentation.
9260       */
9261       root_window=XRootWindow(display,XDefaultScreen(display));
9262       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9263       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9264       if (mozilla_window != (Window) NULL)
9265         {
9266           char
9267             command[MaxTextExtent],
9268             *url;
9269
9270           /*
9271             Display documentation using Netscape remote control.
9272           */
9273           url=GetMagickHomeURL();
9274           (void) FormatLocaleString(command,MaxTextExtent,
9275             "openurl(%s,new-tab)",url);
9276           url=DestroyString(url);
9277           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9278           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9279             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9280           XSetCursorState(display,windows,MagickFalse);
9281           break;
9282         }
9283       XSetCursorState(display,windows,MagickTrue);
9284       XCheckRefreshWindows(display,windows);
9285       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9286         exception);
9287       if (status == MagickFalse)
9288         XNoticeWidget(display,windows,"Unable to browse documentation",
9289           (char *) NULL);
9290       XDelay(display,1500);
9291       XSetCursorState(display,windows,MagickFalse);
9292       break;
9293     }
9294     case VersionCommand:
9295     {
9296       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9297         GetMagickCopyright());
9298       break;
9299     }
9300     case SaveToUndoBufferCommand:
9301       break;
9302     default:
9303     {
9304       (void) XBell(display,0);
9305       break;
9306     }
9307   }
9308   image_info=DestroyImageInfo(image_info);
9309   return(nexus);
9310 }
9311 \f
9312 /*
9313 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9314 %                                                                             %
9315 %                                                                             %
9316 %                                                                             %
9317 +   X M a g n i f y I m a g e                                                 %
9318 %                                                                             %
9319 %                                                                             %
9320 %                                                                             %
9321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9322 %
9323 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9324 %  The magnified portion is displayed in a separate window.
9325 %
9326 %  The format of the XMagnifyImage method is:
9327 %
9328 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9329 %        ExceptionInfo *exception)
9330 %
9331 %  A description of each parameter follows:
9332 %
9333 %    o display: Specifies a connection to an X server;  returned from
9334 %      XOpenDisplay.
9335 %
9336 %    o windows: Specifies a pointer to a XWindows structure.
9337 %
9338 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9339 %      the entire image is refreshed.
9340 %
9341 %    o exception: return any errors or warnings in this structure.
9342 %
9343 */
9344 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9345   ExceptionInfo *exception)
9346 {
9347   char
9348     text[MaxTextExtent];
9349
9350   register int
9351     x,
9352     y;
9353
9354   size_t
9355     state;
9356
9357   /*
9358     Update magnified image until the mouse button is released.
9359   */
9360   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9361   state=DefaultState;
9362   x=event->xbutton.x;
9363   y=event->xbutton.y;
9364   windows->magnify.x=(int) windows->image.x+x;
9365   windows->magnify.y=(int) windows->image.y+y;
9366   do
9367   {
9368     /*
9369       Map and unmap Info widget as text cursor crosses its boundaries.
9370     */
9371     if (windows->info.mapped != MagickFalse)
9372       {
9373         if ((x < (int) (windows->info.x+windows->info.width)) &&
9374             (y < (int) (windows->info.y+windows->info.height)))
9375           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9376       }
9377     else
9378       if ((x > (int) (windows->info.x+windows->info.width)) ||
9379           (y > (int) (windows->info.y+windows->info.height)))
9380         (void) XMapWindow(display,windows->info.id);
9381     if (windows->info.mapped != MagickFalse)
9382       {
9383         /*
9384           Display pointer position.
9385         */
9386         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9387           windows->magnify.x,windows->magnify.y);
9388         XInfoWidget(display,windows,text);
9389       }
9390     /*
9391       Wait for next event.
9392     */
9393     XScreenEvent(display,windows,event,exception);
9394     switch (event->type)
9395     {
9396       case ButtonPress:
9397         break;
9398       case ButtonRelease:
9399       {
9400         /*
9401           User has finished magnifying image.
9402         */
9403         x=event->xbutton.x;
9404         y=event->xbutton.y;
9405         state|=ExitState;
9406         break;
9407       }
9408       case Expose:
9409         break;
9410       case MotionNotify:
9411       {
9412         x=event->xmotion.x;
9413         y=event->xmotion.y;
9414         break;
9415       }
9416       default:
9417         break;
9418     }
9419     /*
9420       Check boundary conditions.
9421     */
9422     if (x < 0)
9423       x=0;
9424     else
9425       if (x >= (int) windows->image.width)
9426         x=(int) windows->image.width-1;
9427     if (y < 0)
9428       y=0;
9429     else
9430      if (y >= (int) windows->image.height)
9431        y=(int) windows->image.height-1;
9432   } while ((state & ExitState) == 0);
9433   /*
9434     Display magnified image.
9435   */
9436   XSetCursorState(display,windows,MagickFalse);
9437 }
9438 \f
9439 /*
9440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9441 %                                                                             %
9442 %                                                                             %
9443 %                                                                             %
9444 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9445 %                                                                             %
9446 %                                                                             %
9447 %                                                                             %
9448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9449 %
9450 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9451 %  pixel as specified by the key symbol.
9452 %
9453 %  The format of the XMagnifyWindowCommand method is:
9454 %
9455 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9456 %        const MagickStatusType state,const KeySym key_symbol,
9457 %        ExceptionInfo *exception)
9458 %
9459 %  A description of each parameter follows:
9460 %
9461 %    o display: Specifies a connection to an X server; returned from
9462 %      XOpenDisplay.
9463 %
9464 %    o windows: Specifies a pointer to a XWindows structure.
9465 %
9466 %    o state: key mask.
9467 %
9468 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9469 %      to trim.
9470 %
9471 %    o exception: return any errors or warnings in this structure.
9472 %
9473 */
9474 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9475   const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9476 {
9477   unsigned int
9478     quantum;
9479
9480   /*
9481     User specified a magnify factor or position.
9482   */
9483   quantum=1;
9484   if ((state & Mod1Mask) != 0)
9485     quantum=10;
9486   switch ((int) key_symbol)
9487   {
9488     case QuitCommand:
9489     {
9490       (void) XWithdrawWindow(display,windows->magnify.id,
9491         windows->magnify.screen);
9492       break;
9493     }
9494     case XK_Home:
9495     case XK_KP_Home:
9496     {
9497       windows->magnify.x=(int) windows->image.width/2;
9498       windows->magnify.y=(int) windows->image.height/2;
9499       break;
9500     }
9501     case XK_Left:
9502     case XK_KP_Left:
9503     {
9504       if (windows->magnify.x > 0)
9505         windows->magnify.x-=quantum;
9506       break;
9507     }
9508     case XK_Up:
9509     case XK_KP_Up:
9510     {
9511       if (windows->magnify.y > 0)
9512         windows->magnify.y-=quantum;
9513       break;
9514     }
9515     case XK_Right:
9516     case XK_KP_Right:
9517     {
9518       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9519         windows->magnify.x+=quantum;
9520       break;
9521     }
9522     case XK_Down:
9523     case XK_KP_Down:
9524     {
9525       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9526         windows->magnify.y+=quantum;
9527       break;
9528     }
9529     case XK_0:
9530     case XK_1:
9531     case XK_2:
9532     case XK_3:
9533     case XK_4:
9534     case XK_5:
9535     case XK_6:
9536     case XK_7:
9537     case XK_8:
9538     case XK_9:
9539     {
9540       windows->magnify.data=(key_symbol-XK_0);
9541       break;
9542     }
9543     case XK_KP_0:
9544     case XK_KP_1:
9545     case XK_KP_2:
9546     case XK_KP_3:
9547     case XK_KP_4:
9548     case XK_KP_5:
9549     case XK_KP_6:
9550     case XK_KP_7:
9551     case XK_KP_8:
9552     case XK_KP_9:
9553     {
9554       windows->magnify.data=(key_symbol-XK_KP_0);
9555       break;
9556     }
9557     default:
9558       break;
9559   }
9560   XMakeMagnifyImage(display,windows,exception);
9561 }
9562 \f
9563 /*
9564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9565 %                                                                             %
9566 %                                                                             %
9567 %                                                                             %
9568 +   X M a k e P a n I m a g e                                                 %
9569 %                                                                             %
9570 %                                                                             %
9571 %                                                                             %
9572 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9573 %
9574 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9575 %  icon window.
9576 %
9577 %  The format of the XMakePanImage method is:
9578 %
9579 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9580 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9581 %
9582 %  A description of each parameter follows:
9583 %
9584 %    o display: Specifies a connection to an X server;  returned from
9585 %      XOpenDisplay.
9586 %
9587 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9588 %
9589 %    o windows: Specifies a pointer to a XWindows structure.
9590 %
9591 %    o image: the image.
9592 %
9593 %    o exception: return any errors or warnings in this structure.
9594 %
9595 */
9596 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9597   XWindows *windows,Image *image,ExceptionInfo *exception)
9598 {
9599   MagickStatusType
9600     status;
9601
9602   /*
9603     Create and display image for panning icon.
9604   */
9605   XSetCursorState(display,windows,MagickTrue);
9606   XCheckRefreshWindows(display,windows);
9607   windows->pan.x=(int) windows->image.x;
9608   windows->pan.y=(int) windows->image.y;
9609   status=XMakeImage(display,resource_info,&windows->pan,image,
9610     windows->pan.width,windows->pan.height,exception);
9611   if (status == MagickFalse)
9612     ThrowXWindowFatalException(ResourceLimitError,
9613      "MemoryAllocationFailed",image->filename);
9614   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9615     windows->pan.pixmap);
9616   (void) XClearWindow(display,windows->pan.id);
9617   XDrawPanRectangle(display,windows);
9618   XSetCursorState(display,windows,MagickFalse);
9619 }
9620 \f
9621 /*
9622 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9623 %                                                                             %
9624 %                                                                             %
9625 %                                                                             %
9626 +   X M a t t a E d i t I m a g e                                             %
9627 %                                                                             %
9628 %                                                                             %
9629 %                                                                             %
9630 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9631 %
9632 %  XMatteEditImage() allows the user to interactively change the Matte channel
9633 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9634 %  before the matte information is stored.
9635 %
9636 %  The format of the XMatteEditImage method is:
9637 %
9638 %      MagickBooleanType XMatteEditImage(Display *display,
9639 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9640 %        ExceptionInfo *exception)
9641 %
9642 %  A description of each parameter follows:
9643 %
9644 %    o display: Specifies a connection to an X server;  returned from
9645 %      XOpenDisplay.
9646 %
9647 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9648 %
9649 %    o windows: Specifies a pointer to a XWindows structure.
9650 %
9651 %    o image: the image; returned from ReadImage.
9652 %
9653 %    o exception: return any errors or warnings in this structure.
9654 %
9655 */
9656 static MagickBooleanType XMatteEditImage(Display *display,
9657   XResourceInfo *resource_info,XWindows *windows,Image **image,
9658   ExceptionInfo *exception)
9659 {
9660   static char
9661     matte[MaxTextExtent] = "0";
9662
9663   static const char
9664     *MatteEditMenu[] =
9665     {
9666       "Method",
9667       "Border Color",
9668       "Fuzz",
9669       "Matte Value",
9670       "Undo",
9671       "Help",
9672       "Dismiss",
9673       (char *) NULL
9674     };
9675
9676   static const ModeType
9677     MatteEditCommands[] =
9678     {
9679       MatteEditMethod,
9680       MatteEditBorderCommand,
9681       MatteEditFuzzCommand,
9682       MatteEditValueCommand,
9683       MatteEditUndoCommand,
9684       MatteEditHelpCommand,
9685       MatteEditDismissCommand
9686     };
9687
9688   static PaintMethod
9689     method = PointMethod;
9690
9691   static XColor
9692     border_color = { 0, 0, 0, 0, 0, 0 };
9693
9694   char
9695     command[MaxTextExtent],
9696     text[MaxTextExtent];
9697
9698   Cursor
9699     cursor;
9700
9701   int
9702     entry,
9703     id,
9704     x,
9705     x_offset,
9706     y,
9707     y_offset;
9708
9709   register int
9710     i;
9711
9712   register Quantum
9713     *q;
9714
9715   unsigned int
9716     height,
9717     width;
9718
9719   size_t
9720     state;
9721
9722   XEvent
9723     event;
9724
9725   /*
9726     Map Command widget.
9727   */
9728   (void) CloneString(&windows->command.name,"Matte Edit");
9729   windows->command.data=4;
9730   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9731   (void) XMapRaised(display,windows->command.id);
9732   XClientMessage(display,windows->image.id,windows->im_protocols,
9733     windows->im_update_widget,CurrentTime);
9734   /*
9735     Make cursor.
9736   */
9737   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9738     resource_info->background_color,resource_info->foreground_color);
9739   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9740   /*
9741     Track pointer until button 1 is pressed.
9742   */
9743   XQueryPosition(display,windows->image.id,&x,&y);
9744   (void) XSelectInput(display,windows->image.id,
9745     windows->image.attributes.event_mask | PointerMotionMask);
9746   state=DefaultState;
9747   do
9748   {
9749     if (windows->info.mapped != MagickFalse)
9750       {
9751         /*
9752           Display pointer position.
9753         */
9754         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9755           x+windows->image.x,y+windows->image.y);
9756         XInfoWidget(display,windows,text);
9757       }
9758     /*
9759       Wait for next event.
9760     */
9761     XScreenEvent(display,windows,&event,exception);
9762     if (event.xany.window == windows->command.id)
9763       {
9764         /*
9765           Select a command from the Command widget.
9766         */
9767         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9768         if (id < 0)
9769           {
9770             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9771             continue;
9772           }
9773         switch (MatteEditCommands[id])
9774         {
9775           case MatteEditMethod:
9776           {
9777             char
9778               **methods;
9779
9780             /*
9781               Select a method from the pop-up menu.
9782             */
9783             methods=GetCommandOptions(MagickMethodOptions);
9784             if (methods == (char **) NULL)
9785               break;
9786             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9787               (const char **) methods,command);
9788             if (entry >= 0)
9789               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9790                 MagickFalse,methods[entry]);
9791             methods=DestroyStringList(methods);
9792             break;
9793           }
9794           case MatteEditBorderCommand:
9795           {
9796             const char
9797               *ColorMenu[MaxNumberPens];
9798
9799             int
9800               pen_number;
9801
9802             /*
9803               Initialize menu selections.
9804             */
9805             for (i=0; i < (int) (MaxNumberPens-2); i++)
9806               ColorMenu[i]=resource_info->pen_colors[i];
9807             ColorMenu[MaxNumberPens-2]="Browser...";
9808             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9809             /*
9810               Select a pen color from the pop-up menu.
9811             */
9812             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9813               (const char **) ColorMenu,command);
9814             if (pen_number < 0)
9815               break;
9816             if (pen_number == (MaxNumberPens-2))
9817               {
9818                 static char
9819                   color_name[MaxTextExtent] = "gray";
9820
9821                 /*
9822                   Select a pen color from a dialog.
9823                 */
9824                 resource_info->pen_colors[pen_number]=color_name;
9825                 XColorBrowserWidget(display,windows,"Select",color_name);
9826                 if (*color_name == '\0')
9827                   break;
9828               }
9829             /*
9830               Set border color.
9831             */
9832             (void) XParseColor(display,windows->map_info->colormap,
9833               resource_info->pen_colors[pen_number],&border_color);
9834             break;
9835           }
9836           case MatteEditFuzzCommand:
9837           {
9838             static char
9839               fuzz[MaxTextExtent];
9840
9841             static const char
9842               *FuzzMenu[] =
9843               {
9844                 "0%",
9845                 "2%",
9846                 "5%",
9847                 "10%",
9848                 "15%",
9849                 "Dialog...",
9850                 (char *) NULL,
9851               };
9852
9853             /*
9854               Select a command from the pop-up menu.
9855             */
9856             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9857               command);
9858             if (entry < 0)
9859               break;
9860             if (entry != 5)
9861               {
9862                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9863                   QuantumRange+1.0);
9864                 break;
9865               }
9866             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9867             (void) XDialogWidget(display,windows,"Ok",
9868               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9869             if (*fuzz == '\0')
9870               break;
9871             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9872             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9873               1.0);
9874             break;
9875           }
9876           case MatteEditValueCommand:
9877           {
9878             static char
9879               message[MaxTextExtent];
9880
9881             static const char
9882               *MatteMenu[] =
9883               {
9884                 "Opaque",
9885                 "Transparent",
9886                 "Dialog...",
9887                 (char *) NULL,
9888               };
9889
9890             /*
9891               Select a command from the pop-up menu.
9892             */
9893             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9894               command);
9895             if (entry < 0)
9896               break;
9897             if (entry != 2)
9898               {
9899                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9900                   OpaqueAlpha);
9901                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9902                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9903                     (Quantum) TransparentAlpha);
9904                 break;
9905               }
9906             (void) FormatLocaleString(message,MaxTextExtent,
9907               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9908               QuantumRange);
9909             (void) XDialogWidget(display,windows,"Matte",message,matte);
9910             if (*matte == '\0')
9911               break;
9912             break;
9913           }
9914           case MatteEditUndoCommand:
9915           {
9916             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9917               image,exception);
9918             break;
9919           }
9920           case MatteEditHelpCommand:
9921           {
9922             XTextViewWidget(display,resource_info,windows,MagickFalse,
9923               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9924             break;
9925           }
9926           case MatteEditDismissCommand:
9927           {
9928             /*
9929               Prematurely exit.
9930             */
9931             state|=EscapeState;
9932             state|=ExitState;
9933             break;
9934           }
9935           default:
9936             break;
9937         }
9938         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9939         continue;
9940       }
9941     switch (event.type)
9942     {
9943       case ButtonPress:
9944       {
9945         if (event.xbutton.button != Button1)
9946           break;
9947         if ((event.xbutton.window != windows->image.id) &&
9948             (event.xbutton.window != windows->magnify.id))
9949           break;
9950         /*
9951           Update matte data.
9952         */
9953         x=event.xbutton.x;
9954         y=event.xbutton.y;
9955         (void) XMagickCommand(display,resource_info,windows,
9956           SaveToUndoBufferCommand,image,exception);
9957         state|=UpdateConfigurationState;
9958         break;
9959       }
9960       case ButtonRelease:
9961       {
9962         if (event.xbutton.button != Button1)
9963           break;
9964         if ((event.xbutton.window != windows->image.id) &&
9965             (event.xbutton.window != windows->magnify.id))
9966           break;
9967         /*
9968           Update colormap information.
9969         */
9970         x=event.xbutton.x;
9971         y=event.xbutton.y;
9972         XConfigureImageColormap(display,resource_info,windows,*image,exception);
9973         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9974         XInfoWidget(display,windows,text);
9975         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9976         state&=(~UpdateConfigurationState);
9977         break;
9978       }
9979       case Expose:
9980         break;
9981       case KeyPress:
9982       {
9983         char
9984           command[MaxTextExtent];
9985
9986         KeySym
9987           key_symbol;
9988
9989         if (event.xkey.window == windows->magnify.id)
9990           {
9991             Window
9992               window;
9993
9994             window=windows->magnify.id;
9995             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9996           }
9997         if (event.xkey.window != windows->image.id)
9998           break;
9999         /*
10000           Respond to a user key press.
10001         */
10002         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10003           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10004         switch ((int) key_symbol)
10005         {
10006           case XK_Escape:
10007           case XK_F20:
10008           {
10009             /*
10010               Prematurely exit.
10011             */
10012             state|=ExitState;
10013             break;
10014           }
10015           case XK_F1:
10016           case XK_Help:
10017           {
10018             XTextViewWidget(display,resource_info,windows,MagickFalse,
10019               "Help Viewer - Matte Edit",ImageMatteEditHelp);
10020             break;
10021           }
10022           default:
10023           {
10024             (void) XBell(display,0);
10025             break;
10026           }
10027         }
10028         break;
10029       }
10030       case MotionNotify:
10031       {
10032         /*
10033           Map and unmap Info widget as cursor crosses its boundaries.
10034         */
10035         x=event.xmotion.x;
10036         y=event.xmotion.y;
10037         if (windows->info.mapped != MagickFalse)
10038           {
10039             if ((x < (int) (windows->info.x+windows->info.width)) &&
10040                 (y < (int) (windows->info.y+windows->info.height)))
10041               (void) XWithdrawWindow(display,windows->info.id,
10042                 windows->info.screen);
10043           }
10044         else
10045           if ((x > (int) (windows->info.x+windows->info.width)) ||
10046               (y > (int) (windows->info.y+windows->info.height)))
10047             (void) XMapWindow(display,windows->info.id);
10048         break;
10049       }
10050       default:
10051         break;
10052     }
10053     if (event.xany.window == windows->magnify.id)
10054       {
10055         x=windows->magnify.x-windows->image.x;
10056         y=windows->magnify.y-windows->image.y;
10057       }
10058     x_offset=x;
10059     y_offset=y;
10060     if ((state & UpdateConfigurationState) != 0)
10061       {
10062         CacheView
10063           *image_view;
10064
10065         int
10066           x,
10067           y;
10068
10069         /*
10070           Matte edit is relative to image configuration.
10071         */
10072         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10073           MagickTrue);
10074         XPutPixel(windows->image.ximage,x_offset,y_offset,
10075           windows->pixel_info->background_color.pixel);
10076         width=(unsigned int) (*image)->columns;
10077         height=(unsigned int) (*image)->rows;
10078         x=0;
10079         y=0;
10080         if (windows->image.crop_geometry != (char *) NULL)
10081           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10082             &height);
10083         x_offset=(int) (width*(windows->image.x+x_offset)/
10084           windows->image.ximage->width+x);
10085         y_offset=(int) (height*(windows->image.y+y_offset)/
10086           windows->image.ximage->height+y);
10087         if ((x_offset < 0) || (y_offset < 0))
10088           continue;
10089         if ((x_offset >= (int) (*image)->columns) ||
10090             (y_offset >= (int) (*image)->rows))
10091           continue;
10092         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10093           return(MagickFalse);
10094         if ((*image)->matte == MagickFalse)
10095           (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10096         image_view=AcquireAuthenticCacheView(*image,exception);
10097         switch (method)
10098         {
10099           case PointMethod:
10100           default:
10101           {
10102             /*
10103               Update matte information using point algorithm.
10104             */
10105             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10106               (ssize_t) y_offset,1,1,exception);
10107             if (q == (Quantum *) NULL)
10108               break;
10109             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10110             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10111             break;
10112           }
10113           case ReplaceMethod:
10114           {
10115             PixelInfo
10116               pixel,
10117               target;
10118
10119             /*
10120               Update matte information using replace algorithm.
10121             */
10122             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10123               x_offset,(ssize_t) y_offset,&target,exception);
10124             for (y=0; y < (int) (*image)->rows; y++)
10125             {
10126               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10127                 (*image)->columns,1,exception);
10128               if (q == (Quantum *) NULL)
10129                 break;
10130               for (x=0; x < (int) (*image)->columns; x++)
10131               {
10132                 GetPixelInfoPixel(*image,q,&pixel);
10133                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10134                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10135                 q+=GetPixelChannels(*image);
10136               }
10137               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10138                 break;
10139             }
10140             break;
10141           }
10142           case FloodfillMethod:
10143           case FillToBorderMethod:
10144           {
10145             ChannelType
10146               channel_mask;
10147
10148             DrawInfo
10149               *draw_info;
10150
10151             PixelInfo
10152               target;
10153
10154             /*
10155               Update matte information using floodfill algorithm.
10156             */
10157             (void) GetOneVirtualPixelInfo(*image,
10158               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10159               y_offset,&target,exception);
10160             if (method == FillToBorderMethod)
10161               {
10162                 target.red=(MagickRealType) ScaleShortToQuantum(
10163                   border_color.red);
10164                 target.green=(MagickRealType) ScaleShortToQuantum(
10165                   border_color.green);
10166                 target.blue=(MagickRealType) ScaleShortToQuantum(
10167                   border_color.blue);
10168               }
10169             draw_info=CloneDrawInfo(resource_info->image_info,
10170               (DrawInfo *) NULL);
10171             draw_info->fill.alpha=(MagickRealType) ClampToQuantum(
10172               StringToDouble(matte,(char **) NULL));
10173             channel_mask=SetPixelChannelMask(*image,AlphaChannel); 
10174             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10175               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10176               MagickFalse : MagickTrue,exception);
10177             (void) SetPixelChannelMapMask(*image,channel_mask);
10178             draw_info=DestroyDrawInfo(draw_info);
10179             break;
10180           }
10181           case ResetMethod:
10182           {
10183             /*
10184               Update matte information using reset algorithm.
10185             */
10186             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10187               return(MagickFalse);
10188             for (y=0; y < (int) (*image)->rows; y++)
10189             {
10190               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10191                 (*image)->columns,1,exception);
10192               if (q == (Quantum *) NULL)
10193                 break;
10194               for (x=0; x < (int) (*image)->columns; x++)
10195               {
10196                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10197                 q+=GetPixelChannels(*image);
10198               }
10199               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10200                 break;
10201             }
10202             if (StringToLong(matte) == (long) OpaqueAlpha)
10203               (*image)->matte=MagickFalse;
10204             break;
10205           }
10206         }
10207         image_view=DestroyCacheView(image_view);
10208         state&=(~UpdateConfigurationState);
10209       }
10210   } while ((state & ExitState) == 0);
10211   (void) XSelectInput(display,windows->image.id,
10212     windows->image.attributes.event_mask);
10213   XSetCursorState(display,windows,MagickFalse);
10214   (void) XFreeCursor(display,cursor);
10215   return(MagickTrue);
10216 }
10217 \f
10218 /*
10219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10220 %                                                                             %
10221 %                                                                             %
10222 %                                                                             %
10223 +   X O p e n I m a g e                                                       %
10224 %                                                                             %
10225 %                                                                             %
10226 %                                                                             %
10227 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10228 %
10229 %  XOpenImage() loads an image from a file.
10230 %
10231 %  The format of the XOpenImage method is:
10232 %
10233 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10234 %       XWindows *windows,const unsigned int command)
10235 %
10236 %  A description of each parameter follows:
10237 %
10238 %    o display: Specifies a connection to an X server; returned from
10239 %      XOpenDisplay.
10240 %
10241 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10242 %
10243 %    o windows: Specifies a pointer to a XWindows structure.
10244 %
10245 %    o command: A value other than zero indicates that the file is selected
10246 %      from the command line argument list.
10247 %
10248 */
10249 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10250   XWindows *windows,const MagickBooleanType command)
10251 {
10252   const MagickInfo
10253     *magick_info;
10254
10255   ExceptionInfo
10256     *exception;
10257
10258   Image
10259     *nexus;
10260
10261   ImageInfo
10262     *image_info;
10263
10264   static char
10265     filename[MaxTextExtent] = "\0";
10266
10267   /*
10268     Request file name from user.
10269   */
10270   if (command == MagickFalse)
10271     XFileBrowserWidget(display,windows,"Open",filename);
10272   else
10273     {
10274       char
10275         **filelist,
10276         **files;
10277
10278       int
10279         count,
10280         status;
10281
10282       register int
10283         i,
10284         j;
10285
10286       /*
10287         Select next image from the command line.
10288       */
10289       status=XGetCommand(display,windows->image.id,&files,&count);
10290       if (status == 0)
10291         {
10292           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10293           return((Image *) NULL);
10294         }
10295       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10296       if (filelist == (char **) NULL)
10297         {
10298           ThrowXWindowFatalException(ResourceLimitError,
10299             "MemoryAllocationFailed","...");
10300           (void) XFreeStringList(files);
10301           return((Image *) NULL);
10302         }
10303       j=0;
10304       for (i=1; i < count; i++)
10305         if (*files[i] != '-')
10306           filelist[j++]=files[i];
10307       filelist[j]=(char *) NULL;
10308       XListBrowserWidget(display,windows,&windows->widget,
10309         (const char **) filelist,"Load","Select Image to Load:",filename);
10310       filelist=(char **) RelinquishMagickMemory(filelist);
10311       (void) XFreeStringList(files);
10312     }
10313   if (*filename == '\0')
10314     return((Image *) NULL);
10315   image_info=CloneImageInfo(resource_info->image_info);
10316   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10317     (void *) NULL);
10318   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10319   exception=AcquireExceptionInfo();
10320   (void) SetImageInfo(image_info,0,exception);
10321   if (LocaleCompare(image_info->magick,"X") == 0)
10322     {
10323       char
10324         seconds[MaxTextExtent];
10325
10326       /*
10327         User may want to delay the X server screen grab.
10328       */
10329       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10330       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10331         seconds);
10332       if (*seconds == '\0')
10333         return((Image *) NULL);
10334       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10335     }
10336   magick_info=GetMagickInfo(image_info->magick,exception);
10337   if ((magick_info != (const MagickInfo *) NULL) &&
10338       (magick_info->raw != MagickFalse))
10339     {
10340       char
10341         geometry[MaxTextExtent];
10342
10343       /*
10344         Request image size from the user.
10345       */
10346       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10347       if (image_info->size != (char *) NULL)
10348         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10349       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10350         geometry);
10351       (void) CloneString(&image_info->size,geometry);
10352     }
10353   /*
10354     Load the image.
10355   */
10356   XSetCursorState(display,windows,MagickTrue);
10357   XCheckRefreshWindows(display,windows);
10358   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10359   nexus=ReadImage(image_info,exception);
10360   CatchException(exception);
10361   XSetCursorState(display,windows,MagickFalse);
10362   if (nexus != (Image *) NULL)
10363     XClientMessage(display,windows->image.id,windows->im_protocols,
10364       windows->im_next_image,CurrentTime);
10365   else
10366     {
10367       char
10368         *text,
10369         **textlist;
10370
10371       /*
10372         Unknown image format.
10373       */
10374       text=FileToString(filename,~0,exception);
10375       if (text == (char *) NULL)
10376         return((Image *) NULL);
10377       textlist=StringToList(text);
10378       if (textlist != (char **) NULL)
10379         {
10380           char
10381             title[MaxTextExtent];
10382
10383           register int
10384             i;
10385
10386           (void) FormatLocaleString(title,MaxTextExtent,
10387             "Unknown format: %s",filename);
10388           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10389             (const char **) textlist);
10390           for (i=0; textlist[i] != (char *) NULL; i++)
10391             textlist[i]=DestroyString(textlist[i]);
10392           textlist=(char **) RelinquishMagickMemory(textlist);
10393         }
10394       text=DestroyString(text);
10395     }
10396   exception=DestroyExceptionInfo(exception);
10397   image_info=DestroyImageInfo(image_info);
10398   return(nexus);
10399 }
10400 \f
10401 /*
10402 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10403 %                                                                             %
10404 %                                                                             %
10405 %                                                                             %
10406 +   X P a n I m a g e                                                         %
10407 %                                                                             %
10408 %                                                                             %
10409 %                                                                             %
10410 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10411 %
10412 %  XPanImage() pans the image until the mouse button is released.
10413 %
10414 %  The format of the XPanImage method is:
10415 %
10416 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10417 %        ExceptionInfo *exception)
10418 %
10419 %  A description of each parameter follows:
10420 %
10421 %    o display: Specifies a connection to an X server;  returned from
10422 %      XOpenDisplay.
10423 %
10424 %    o windows: Specifies a pointer to a XWindows structure.
10425 %
10426 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10427 %      the entire image is refreshed.
10428 %
10429 %    o exception: return any errors or warnings in this structure.
10430 %
10431 */
10432 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10433   ExceptionInfo *exception)
10434 {
10435   char
10436     text[MaxTextExtent];
10437
10438   Cursor
10439     cursor;
10440
10441   MagickRealType
10442     x_factor,
10443     y_factor;
10444
10445   RectangleInfo
10446     pan_info;
10447
10448   size_t
10449     state;
10450
10451   /*
10452     Define cursor.
10453   */
10454   if ((windows->image.ximage->width > (int) windows->image.width) &&
10455       (windows->image.ximage->height > (int) windows->image.height))
10456     cursor=XCreateFontCursor(display,XC_fleur);
10457   else
10458     if (windows->image.ximage->width > (int) windows->image.width)
10459       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10460     else
10461       if (windows->image.ximage->height > (int) windows->image.height)
10462         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10463       else
10464         cursor=XCreateFontCursor(display,XC_arrow);
10465   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10466   /*
10467     Pan image as pointer moves until the mouse button is released.
10468   */
10469   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10470   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10471   pan_info.width=windows->pan.width*windows->image.width/
10472     windows->image.ximage->width;
10473   pan_info.height=windows->pan.height*windows->image.height/
10474     windows->image.ximage->height;
10475   pan_info.x=0;
10476   pan_info.y=0;
10477   state=UpdateConfigurationState;
10478   do
10479   {
10480     switch (event->type)
10481     {
10482       case ButtonPress:
10483       {
10484         /*
10485           User choose an initial pan location.
10486         */
10487         pan_info.x=(ssize_t) event->xbutton.x;
10488         pan_info.y=(ssize_t) event->xbutton.y;
10489         state|=UpdateConfigurationState;
10490         break;
10491       }
10492       case ButtonRelease:
10493       {
10494         /*
10495           User has finished panning the image.
10496         */
10497         pan_info.x=(ssize_t) event->xbutton.x;
10498         pan_info.y=(ssize_t) event->xbutton.y;
10499         state|=UpdateConfigurationState | ExitState;
10500         break;
10501       }
10502       case MotionNotify:
10503       {
10504         pan_info.x=(ssize_t) event->xmotion.x;
10505         pan_info.y=(ssize_t) event->xmotion.y;
10506         state|=UpdateConfigurationState;
10507       }
10508       default:
10509         break;
10510     }
10511     if ((state & UpdateConfigurationState) != 0)
10512       {
10513         /*
10514           Check boundary conditions.
10515         */
10516         if (pan_info.x < (ssize_t) (pan_info.width/2))
10517           pan_info.x=0;
10518         else
10519           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10520         if (pan_info.x < 0)
10521           pan_info.x=0;
10522         else
10523           if ((int) (pan_info.x+windows->image.width) >
10524               windows->image.ximage->width)
10525             pan_info.x=(ssize_t)
10526               (windows->image.ximage->width-windows->image.width);
10527         if (pan_info.y < (ssize_t) (pan_info.height/2))
10528           pan_info.y=0;
10529         else
10530           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10531         if (pan_info.y < 0)
10532           pan_info.y=0;
10533         else
10534           if ((int) (pan_info.y+windows->image.height) >
10535               windows->image.ximage->height)
10536             pan_info.y=(ssize_t)
10537               (windows->image.ximage->height-windows->image.height);
10538         if ((windows->image.x != (int) pan_info.x) ||
10539             (windows->image.y != (int) pan_info.y))
10540           {
10541             /*
10542               Display image pan offset.
10543             */
10544             windows->image.x=(int) pan_info.x;
10545             windows->image.y=(int) pan_info.y;
10546             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10547               windows->image.width,windows->image.height,windows->image.x,
10548               windows->image.y);
10549             XInfoWidget(display,windows,text);
10550             /*
10551               Refresh Image window.
10552             */
10553             XDrawPanRectangle(display,windows);
10554             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10555           }
10556         state&=(~UpdateConfigurationState);
10557       }
10558     /*
10559       Wait for next event.
10560     */
10561     if ((state & ExitState) == 0)
10562       XScreenEvent(display,windows,event,exception);
10563   } while ((state & ExitState) == 0);
10564   /*
10565     Restore cursor.
10566   */
10567   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10568   (void) XFreeCursor(display,cursor);
10569   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10570 }
10571 \f
10572 /*
10573 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10574 %                                                                             %
10575 %                                                                             %
10576 %                                                                             %
10577 +   X P a s t e I m a g e                                                     %
10578 %                                                                             %
10579 %                                                                             %
10580 %                                                                             %
10581 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10582 %
10583 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10584 %  window image at a location the user chooses with the pointer.
10585 %
10586 %  The format of the XPasteImage method is:
10587 %
10588 %      MagickBooleanType XPasteImage(Display *display,
10589 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10590 %        ExceptionInfo *exception)
10591 %
10592 %  A description of each parameter follows:
10593 %
10594 %    o display: Specifies a connection to an X server;  returned from
10595 %      XOpenDisplay.
10596 %
10597 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10598 %
10599 %    o windows: Specifies a pointer to a XWindows structure.
10600 %
10601 %    o image: the image; returned from ReadImage.
10602 %
10603 %    o exception: return any errors or warnings in this structure.
10604 %
10605 */
10606 static MagickBooleanType XPasteImage(Display *display,
10607   XResourceInfo *resource_info,XWindows *windows,Image *image,
10608   ExceptionInfo *exception)
10609 {
10610   static const char
10611     *PasteMenu[] =
10612     {
10613       "Operator",
10614       "Help",
10615       "Dismiss",
10616       (char *) NULL
10617     };
10618
10619   static const ModeType
10620     PasteCommands[] =
10621     {
10622       PasteOperatorsCommand,
10623       PasteHelpCommand,
10624       PasteDismissCommand
10625     };
10626
10627   static CompositeOperator
10628     compose = CopyCompositeOp;
10629
10630   char
10631     text[MaxTextExtent];
10632
10633   Cursor
10634     cursor;
10635
10636   Image
10637     *paste_image;
10638
10639   int
10640     entry,
10641     id,
10642     x,
10643     y;
10644
10645   MagickRealType
10646     scale_factor;
10647
10648   RectangleInfo
10649     highlight_info,
10650     paste_info;
10651
10652   unsigned int
10653     height,
10654     width;
10655
10656   size_t
10657     state;
10658
10659   XEvent
10660     event;
10661
10662   /*
10663     Copy image.
10664   */
10665   if (resource_info->copy_image == (Image *) NULL)
10666     return(MagickFalse);
10667   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10668   /*
10669     Map Command widget.
10670   */
10671   (void) CloneString(&windows->command.name,"Paste");
10672   windows->command.data=1;
10673   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10674   (void) XMapRaised(display,windows->command.id);
10675   XClientMessage(display,windows->image.id,windows->im_protocols,
10676     windows->im_update_widget,CurrentTime);
10677   /*
10678     Track pointer until button 1 is pressed.
10679   */
10680   XSetCursorState(display,windows,MagickFalse);
10681   XQueryPosition(display,windows->image.id,&x,&y);
10682   (void) XSelectInput(display,windows->image.id,
10683     windows->image.attributes.event_mask | PointerMotionMask);
10684   paste_info.x=(ssize_t) windows->image.x+x;
10685   paste_info.y=(ssize_t) windows->image.y+y;
10686   paste_info.width=0;
10687   paste_info.height=0;
10688   cursor=XCreateFontCursor(display,XC_ul_angle);
10689   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10690   state=DefaultState;
10691   do
10692   {
10693     if (windows->info.mapped != MagickFalse)
10694       {
10695         /*
10696           Display pointer position.
10697         */
10698         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10699           (long) paste_info.x,(long) paste_info.y);
10700         XInfoWidget(display,windows,text);
10701       }
10702     highlight_info=paste_info;
10703     highlight_info.x=paste_info.x-windows->image.x;
10704     highlight_info.y=paste_info.y-windows->image.y;
10705     XHighlightRectangle(display,windows->image.id,
10706       windows->image.highlight_context,&highlight_info);
10707     /*
10708       Wait for next event.
10709     */
10710     XScreenEvent(display,windows,&event,exception);
10711     XHighlightRectangle(display,windows->image.id,
10712       windows->image.highlight_context,&highlight_info);
10713     if (event.xany.window == windows->command.id)
10714       {
10715         /*
10716           Select a command from the Command widget.
10717         */
10718         id=XCommandWidget(display,windows,PasteMenu,&event);
10719         if (id < 0)
10720           continue;
10721         switch (PasteCommands[id])
10722         {
10723           case PasteOperatorsCommand:
10724           {
10725             char
10726               command[MaxTextExtent],
10727               **operators;
10728
10729             /*
10730               Select a command from the pop-up menu.
10731             */
10732             operators=GetCommandOptions(MagickComposeOptions);
10733             if (operators == (char **) NULL)
10734               break;
10735             entry=XMenuWidget(display,windows,PasteMenu[id],
10736               (const char **) operators,command);
10737             if (entry >= 0)
10738               compose=(CompositeOperator) ParseCommandOption(
10739                 MagickComposeOptions,MagickFalse,operators[entry]);
10740             operators=DestroyStringList(operators);
10741             break;
10742           }
10743           case PasteHelpCommand:
10744           {
10745             XTextViewWidget(display,resource_info,windows,MagickFalse,
10746               "Help Viewer - Image Composite",ImagePasteHelp);
10747             break;
10748           }
10749           case PasteDismissCommand:
10750           {
10751             /*
10752               Prematurely exit.
10753             */
10754             state|=EscapeState;
10755             state|=ExitState;
10756             break;
10757           }
10758           default:
10759             break;
10760         }
10761         continue;
10762       }
10763     switch (event.type)
10764     {
10765       case ButtonPress:
10766       {
10767         if (image->debug != MagickFalse)
10768           (void) LogMagickEvent(X11Event,GetMagickModule(),
10769             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10770             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10771         if (event.xbutton.button != Button1)
10772           break;
10773         if (event.xbutton.window != windows->image.id)
10774           break;
10775         /*
10776           Paste rectangle is relative to image configuration.
10777         */
10778         width=(unsigned int) image->columns;
10779         height=(unsigned int) image->rows;
10780         x=0;
10781         y=0;
10782         if (windows->image.crop_geometry != (char *) NULL)
10783           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10784             &width,&height);
10785         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10786         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10787         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10788         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10789         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10790         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10791         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10792         break;
10793       }
10794       case ButtonRelease:
10795       {
10796         if (image->debug != MagickFalse)
10797           (void) LogMagickEvent(X11Event,GetMagickModule(),
10798             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10799             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10800         if (event.xbutton.button != Button1)
10801           break;
10802         if (event.xbutton.window != windows->image.id)
10803           break;
10804         if ((paste_info.width != 0) && (paste_info.height != 0))
10805           {
10806             /*
10807               User has selected the location of the paste image.
10808             */
10809             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10810             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10811             state|=ExitState;
10812           }
10813         break;
10814       }
10815       case Expose:
10816         break;
10817       case KeyPress:
10818       {
10819         char
10820           command[MaxTextExtent];
10821
10822         KeySym
10823           key_symbol;
10824
10825         int
10826           length;
10827
10828         if (event.xkey.window != windows->image.id)
10829           break;
10830         /*
10831           Respond to a user key press.
10832         */
10833         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10834           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10835         *(command+length)='\0';
10836         if (image->debug != MagickFalse)
10837           (void) LogMagickEvent(X11Event,GetMagickModule(),
10838             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10839         switch ((int) key_symbol)
10840         {
10841           case XK_Escape:
10842           case XK_F20:
10843           {
10844             /*
10845               Prematurely exit.
10846             */
10847             paste_image=DestroyImage(paste_image);
10848             state|=EscapeState;
10849             state|=ExitState;
10850             break;
10851           }
10852           case XK_F1:
10853           case XK_Help:
10854           {
10855             (void) XSetFunction(display,windows->image.highlight_context,
10856               GXcopy);
10857             XTextViewWidget(display,resource_info,windows,MagickFalse,
10858               "Help Viewer - Image Composite",ImagePasteHelp);
10859             (void) XSetFunction(display,windows->image.highlight_context,
10860               GXinvert);
10861             break;
10862           }
10863           default:
10864           {
10865             (void) XBell(display,0);
10866             break;
10867           }
10868         }
10869         break;
10870       }
10871       case MotionNotify:
10872       {
10873         /*
10874           Map and unmap Info widget as text cursor crosses its boundaries.
10875         */
10876         x=event.xmotion.x;
10877         y=event.xmotion.y;
10878         if (windows->info.mapped != MagickFalse)
10879           {
10880             if ((x < (int) (windows->info.x+windows->info.width)) &&
10881                 (y < (int) (windows->info.y+windows->info.height)))
10882               (void) XWithdrawWindow(display,windows->info.id,
10883                 windows->info.screen);
10884           }
10885         else
10886           if ((x > (int) (windows->info.x+windows->info.width)) ||
10887               (y > (int) (windows->info.y+windows->info.height)))
10888             (void) XMapWindow(display,windows->info.id);
10889         paste_info.x=(ssize_t) windows->image.x+x;
10890         paste_info.y=(ssize_t) windows->image.y+y;
10891         break;
10892       }
10893       default:
10894       {
10895         if (image->debug != MagickFalse)
10896           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10897             event.type);
10898         break;
10899       }
10900     }
10901   } while ((state & ExitState) == 0);
10902   (void) XSelectInput(display,windows->image.id,
10903     windows->image.attributes.event_mask);
10904   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10905   XSetCursorState(display,windows,MagickFalse);
10906   (void) XFreeCursor(display,cursor);
10907   if ((state & EscapeState) != 0)
10908     return(MagickTrue);
10909   /*
10910     Image pasting is relative to image configuration.
10911   */
10912   XSetCursorState(display,windows,MagickTrue);
10913   XCheckRefreshWindows(display,windows);
10914   width=(unsigned int) image->columns;
10915   height=(unsigned int) image->rows;
10916   x=0;
10917   y=0;
10918   if (windows->image.crop_geometry != (char *) NULL)
10919     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10920   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10921   paste_info.x+=x;
10922   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10923   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10924   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10925   paste_info.y+=y;
10926   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10927   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10928   /*
10929     Paste image with X Image window.
10930   */
10931   (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10932     paste_info.y,exception);
10933   paste_image=DestroyImage(paste_image);
10934   XSetCursorState(display,windows,MagickFalse);
10935   /*
10936     Update image colormap.
10937   */
10938   XConfigureImageColormap(display,resource_info,windows,image,exception);
10939   (void) XConfigureImage(display,resource_info,windows,image,exception);
10940   return(MagickTrue);
10941 }
10942 \f
10943 /*
10944 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10945 %                                                                             %
10946 %                                                                             %
10947 %                                                                             %
10948 +   X P r i n t I m a g e                                                     %
10949 %                                                                             %
10950 %                                                                             %
10951 %                                                                             %
10952 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10953 %
10954 %  XPrintImage() prints an image to a Postscript printer.
10955 %
10956 %  The format of the XPrintImage method is:
10957 %
10958 %      MagickBooleanType XPrintImage(Display *display,
10959 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10960 %        ExceptionInfo *exception)
10961 %
10962 %  A description of each parameter follows:
10963 %
10964 %    o display: Specifies a connection to an X server; returned from
10965 %      XOpenDisplay.
10966 %
10967 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10968 %
10969 %    o windows: Specifies a pointer to a XWindows structure.
10970 %
10971 %    o image: the image.
10972 %
10973 %    o exception: return any errors or warnings in this structure.
10974 %
10975 */
10976 static MagickBooleanType XPrintImage(Display *display,
10977   XResourceInfo *resource_info,XWindows *windows,Image *image,
10978   ExceptionInfo *exception)
10979 {
10980   char
10981     filename[MaxTextExtent],
10982     geometry[MaxTextExtent];
10983
10984   Image
10985     *print_image;
10986
10987   ImageInfo
10988     *image_info;
10989
10990   MagickStatusType
10991     status;
10992
10993   /*
10994     Request Postscript page geometry from user.
10995   */
10996   image_info=CloneImageInfo(resource_info->image_info);
10997   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10998   if (image_info->page != (char *) NULL)
10999     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11000   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11001     "Select Postscript Page Geometry:",geometry);
11002   if (*geometry == '\0')
11003     return(MagickTrue);
11004   image_info->page=GetPageGeometry(geometry);
11005   /*
11006     Apply image transforms.
11007   */
11008   XSetCursorState(display,windows,MagickTrue);
11009   XCheckRefreshWindows(display,windows);
11010   print_image=CloneImage(image,0,0,MagickTrue,exception);
11011   if (print_image == (Image *) NULL)
11012     return(MagickFalse);
11013   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11014     windows->image.ximage->width,windows->image.ximage->height);
11015   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11016     exception);
11017   /*
11018     Print image.
11019   */
11020   (void) AcquireUniqueFilename(filename);
11021   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11022     filename);
11023   status=WriteImage(image_info,print_image,exception);
11024   (void) RelinquishUniqueFileResource(filename);
11025   print_image=DestroyImage(print_image);
11026   image_info=DestroyImageInfo(image_info);
11027   XSetCursorState(display,windows,MagickFalse);
11028   return(status != 0 ? MagickTrue : MagickFalse);
11029 }
11030 \f
11031 /*
11032 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11033 %                                                                             %
11034 %                                                                             %
11035 %                                                                             %
11036 +   X R O I I m a g e                                                         %
11037 %                                                                             %
11038 %                                                                             %
11039 %                                                                             %
11040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11041 %
11042 %  XROIImage() applies an image processing technique to a region of interest.
11043 %
11044 %  The format of the XROIImage method is:
11045 %
11046 %      MagickBooleanType XROIImage(Display *display,
11047 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11048 %        ExceptionInfo *exception)
11049 %
11050 %  A description of each parameter follows:
11051 %
11052 %    o display: Specifies a connection to an X server; returned from
11053 %      XOpenDisplay.
11054 %
11055 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11056 %
11057 %    o windows: Specifies a pointer to a XWindows structure.
11058 %
11059 %    o image: the image; returned from ReadImage.
11060 %
11061 %    o exception: return any errors or warnings in this structure.
11062 %
11063 */
11064 static MagickBooleanType XROIImage(Display *display,
11065   XResourceInfo *resource_info,XWindows *windows,Image **image,
11066   ExceptionInfo *exception)
11067 {
11068 #define ApplyMenus  7
11069
11070   static const char
11071     *ROIMenu[] =
11072     {
11073       "Help",
11074       "Dismiss",
11075       (char *) NULL
11076     },
11077     *ApplyMenu[] =
11078     {
11079       "File",
11080       "Edit",
11081       "Transform",
11082       "Enhance",
11083       "Effects",
11084       "F/X",
11085       "Miscellany",
11086       "Help",
11087       "Dismiss",
11088       (char *) NULL
11089     },
11090     *FileMenu[] =
11091     {
11092       "Save...",
11093       "Print...",
11094       (char *) NULL
11095     },
11096     *EditMenu[] =
11097     {
11098       "Undo",
11099       "Redo",
11100       (char *) NULL
11101     },
11102     *TransformMenu[] =
11103     {
11104       "Flop",
11105       "Flip",
11106       "Rotate Right",
11107       "Rotate Left",
11108       (char *) NULL
11109     },
11110     *EnhanceMenu[] =
11111     {
11112       "Hue...",
11113       "Saturation...",
11114       "Brightness...",
11115       "Gamma...",
11116       "Spiff",
11117       "Dull",
11118       "Contrast Stretch...",
11119       "Sigmoidal Contrast...",
11120       "Normalize",
11121       "Equalize",
11122       "Negate",
11123       "Grayscale",
11124       "Map...",
11125       "Quantize...",
11126       (char *) NULL
11127     },
11128     *EffectsMenu[] =
11129     {
11130       "Despeckle",
11131       "Emboss",
11132       "Reduce Noise",
11133       "Add Noise",
11134       "Sharpen...",
11135       "Blur...",
11136       "Threshold...",
11137       "Edge Detect...",
11138       "Spread...",
11139       "Shade...",
11140       "Raise...",
11141       "Segment...",
11142       (char *) NULL
11143     },
11144     *FXMenu[] =
11145     {
11146       "Solarize...",
11147       "Sepia Tone...",
11148       "Swirl...",
11149       "Implode...",
11150       "Vignette...",
11151       "Wave...",
11152       "Oil Paint...",
11153       "Charcoal Draw...",
11154       (char *) NULL
11155     },
11156     *MiscellanyMenu[] =
11157     {
11158       "Image Info",
11159       "Zoom Image",
11160       "Show Preview...",
11161       "Show Histogram",
11162       "Show Matte",
11163       (char *) NULL
11164     };
11165
11166   static const char
11167     **Menus[ApplyMenus] =
11168     {
11169       FileMenu,
11170       EditMenu,
11171       TransformMenu,
11172       EnhanceMenu,
11173       EffectsMenu,
11174       FXMenu,
11175       MiscellanyMenu
11176     };
11177
11178   static const CommandType
11179     ApplyCommands[] =
11180     {
11181       NullCommand,
11182       NullCommand,
11183       NullCommand,
11184       NullCommand,
11185       NullCommand,
11186       NullCommand,
11187       NullCommand,
11188       HelpCommand,
11189       QuitCommand
11190     },
11191     FileCommands[] =
11192     {
11193       SaveCommand,
11194       PrintCommand
11195     },
11196     EditCommands[] =
11197     {
11198       UndoCommand,
11199       RedoCommand
11200     },
11201     TransformCommands[] =
11202     {
11203       FlopCommand,
11204       FlipCommand,
11205       RotateRightCommand,
11206       RotateLeftCommand
11207     },
11208     EnhanceCommands[] =
11209     {
11210       HueCommand,
11211       SaturationCommand,
11212       BrightnessCommand,
11213       GammaCommand,
11214       SpiffCommand,
11215       DullCommand,
11216       ContrastStretchCommand,
11217       SigmoidalContrastCommand,
11218       NormalizeCommand,
11219       EqualizeCommand,
11220       NegateCommand,
11221       GrayscaleCommand,
11222       MapCommand,
11223       QuantizeCommand
11224     },
11225     EffectsCommands[] =
11226     {
11227       DespeckleCommand,
11228       EmbossCommand,
11229       ReduceNoiseCommand,
11230       AddNoiseCommand,
11231       SharpenCommand,
11232       BlurCommand,
11233       EdgeDetectCommand,
11234       SpreadCommand,
11235       ShadeCommand,
11236       RaiseCommand,
11237       SegmentCommand
11238     },
11239     FXCommands[] =
11240     {
11241       SolarizeCommand,
11242       SepiaToneCommand,
11243       SwirlCommand,
11244       ImplodeCommand,
11245       VignetteCommand,
11246       WaveCommand,
11247       OilPaintCommand,
11248       CharcoalDrawCommand
11249     },
11250     MiscellanyCommands[] =
11251     {
11252       InfoCommand,
11253       ZoomCommand,
11254       ShowPreviewCommand,
11255       ShowHistogramCommand,
11256       ShowMatteCommand
11257     },
11258     ROICommands[] =
11259     {
11260       ROIHelpCommand,
11261       ROIDismissCommand
11262     };
11263
11264   static const CommandType
11265     *Commands[ApplyMenus] =
11266     {
11267       FileCommands,
11268       EditCommands,
11269       TransformCommands,
11270       EnhanceCommands,
11271       EffectsCommands,
11272       FXCommands,
11273       MiscellanyCommands
11274     };
11275
11276   char
11277     command[MaxTextExtent],
11278     text[MaxTextExtent];
11279
11280   CommandType
11281     command_type;
11282
11283   Cursor
11284     cursor;
11285
11286   Image
11287     *roi_image;
11288
11289   int
11290     entry,
11291     id,
11292     x,
11293     y;
11294
11295   MagickRealType
11296     scale_factor;
11297
11298   MagickProgressMonitor
11299     progress_monitor;
11300
11301   RectangleInfo
11302     crop_info,
11303     highlight_info,
11304     roi_info;
11305
11306   unsigned int
11307     height,
11308     width;
11309
11310   size_t
11311     state;
11312
11313   XEvent
11314     event;
11315
11316   /*
11317     Map Command widget.
11318   */
11319   (void) CloneString(&windows->command.name,"ROI");
11320   windows->command.data=0;
11321   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11322   (void) XMapRaised(display,windows->command.id);
11323   XClientMessage(display,windows->image.id,windows->im_protocols,
11324     windows->im_update_widget,CurrentTime);
11325   /*
11326     Track pointer until button 1 is pressed.
11327   */
11328   XQueryPosition(display,windows->image.id,&x,&y);
11329   (void) XSelectInput(display,windows->image.id,
11330     windows->image.attributes.event_mask | PointerMotionMask);
11331   roi_info.x=(ssize_t) windows->image.x+x;
11332   roi_info.y=(ssize_t) windows->image.y+y;
11333   roi_info.width=0;
11334   roi_info.height=0;
11335   cursor=XCreateFontCursor(display,XC_fleur);
11336   state=DefaultState;
11337   do
11338   {
11339     if (windows->info.mapped != MagickFalse)
11340       {
11341         /*
11342           Display pointer position.
11343         */
11344         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11345           (long) roi_info.x,(long) roi_info.y);
11346         XInfoWidget(display,windows,text);
11347       }
11348     /*
11349       Wait for next event.
11350     */
11351     XScreenEvent(display,windows,&event,exception);
11352     if (event.xany.window == windows->command.id)
11353       {
11354         /*
11355           Select a command from the Command widget.
11356         */
11357         id=XCommandWidget(display,windows,ROIMenu,&event);
11358         if (id < 0)
11359           continue;
11360         switch (ROICommands[id])
11361         {
11362           case ROIHelpCommand:
11363           {
11364             XTextViewWidget(display,resource_info,windows,MagickFalse,
11365               "Help Viewer - Region of Interest",ImageROIHelp);
11366             break;
11367           }
11368           case ROIDismissCommand:
11369           {
11370             /*
11371               Prematurely exit.
11372             */
11373             state|=EscapeState;
11374             state|=ExitState;
11375             break;
11376           }
11377           default:
11378             break;
11379         }
11380         continue;
11381       }
11382     switch (event.type)
11383     {
11384       case ButtonPress:
11385       {
11386         if (event.xbutton.button != Button1)
11387           break;
11388         if (event.xbutton.window != windows->image.id)
11389           break;
11390         /*
11391           Note first corner of region of interest rectangle-- exit loop.
11392         */
11393         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11394         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11395         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11396         state|=ExitState;
11397         break;
11398       }
11399       case ButtonRelease:
11400         break;
11401       case Expose:
11402         break;
11403       case KeyPress:
11404       {
11405         KeySym
11406           key_symbol;
11407
11408         if (event.xkey.window != windows->image.id)
11409           break;
11410         /*
11411           Respond to a user key press.
11412         */
11413         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11414           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11415         switch ((int) key_symbol)
11416         {
11417           case XK_Escape:
11418           case XK_F20:
11419           {
11420             /*
11421               Prematurely exit.
11422             */
11423             state|=EscapeState;
11424             state|=ExitState;
11425             break;
11426           }
11427           case XK_F1:
11428           case XK_Help:
11429           {
11430             XTextViewWidget(display,resource_info,windows,MagickFalse,
11431               "Help Viewer - Region of Interest",ImageROIHelp);
11432             break;
11433           }
11434           default:
11435           {
11436             (void) XBell(display,0);
11437             break;
11438           }
11439         }
11440         break;
11441       }
11442       case MotionNotify:
11443       {
11444         /*
11445           Map and unmap Info widget as text cursor crosses its boundaries.
11446         */
11447         x=event.xmotion.x;
11448         y=event.xmotion.y;
11449         if (windows->info.mapped != MagickFalse)
11450           {
11451             if ((x < (int) (windows->info.x+windows->info.width)) &&
11452                 (y < (int) (windows->info.y+windows->info.height)))
11453               (void) XWithdrawWindow(display,windows->info.id,
11454                 windows->info.screen);
11455           }
11456         else
11457           if ((x > (int) (windows->info.x+windows->info.width)) ||
11458               (y > (int) (windows->info.y+windows->info.height)))
11459             (void) XMapWindow(display,windows->info.id);
11460         roi_info.x=(ssize_t) windows->image.x+x;
11461         roi_info.y=(ssize_t) windows->image.y+y;
11462         break;
11463       }
11464       default:
11465         break;
11466     }
11467   } while ((state & ExitState) == 0);
11468   (void) XSelectInput(display,windows->image.id,
11469     windows->image.attributes.event_mask);
11470   if ((state & EscapeState) != 0)
11471     {
11472       /*
11473         User want to exit without region of interest.
11474       */
11475       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11476       (void) XFreeCursor(display,cursor);
11477       return(MagickTrue);
11478     }
11479   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11480   do
11481   {
11482     /*
11483       Size rectangle as pointer moves until the mouse button is released.
11484     */
11485     x=(int) roi_info.x;
11486     y=(int) roi_info.y;
11487     roi_info.width=0;
11488     roi_info.height=0;
11489     state=DefaultState;
11490     do
11491     {
11492       highlight_info=roi_info;
11493       highlight_info.x=roi_info.x-windows->image.x;
11494       highlight_info.y=roi_info.y-windows->image.y;
11495       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11496         {
11497           /*
11498             Display info and draw region of interest rectangle.
11499           */
11500           if (windows->info.mapped == MagickFalse)
11501             (void) XMapWindow(display,windows->info.id);
11502           (void) FormatLocaleString(text,MaxTextExtent,
11503             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11504             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11505           XInfoWidget(display,windows,text);
11506           XHighlightRectangle(display,windows->image.id,
11507             windows->image.highlight_context,&highlight_info);
11508         }
11509       else
11510         if (windows->info.mapped != MagickFalse)
11511           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11512       /*
11513         Wait for next event.
11514       */
11515       XScreenEvent(display,windows,&event,exception);
11516       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11517         XHighlightRectangle(display,windows->image.id,
11518           windows->image.highlight_context,&highlight_info);
11519       switch (event.type)
11520       {
11521         case ButtonPress:
11522         {
11523           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11524           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11525           break;
11526         }
11527         case ButtonRelease:
11528         {
11529           /*
11530             User has committed to region of interest rectangle.
11531           */
11532           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11533           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11534           XSetCursorState(display,windows,MagickFalse);
11535           state|=ExitState;
11536           if (LocaleCompare(windows->command.name,"Apply") == 0)
11537             break;
11538           (void) CloneString(&windows->command.name,"Apply");
11539           windows->command.data=ApplyMenus;
11540           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11541           break;
11542         }
11543         case Expose:
11544           break;
11545         case MotionNotify:
11546         {
11547           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11548           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11549         }
11550         default:
11551           break;
11552       }
11553       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11554           ((state & ExitState) != 0))
11555         {
11556           /*
11557             Check boundary conditions.
11558           */
11559           if (roi_info.x < 0)
11560             roi_info.x=0;
11561           else
11562             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11563               roi_info.x=(ssize_t) windows->image.ximage->width;
11564           if ((int) roi_info.x < x)
11565             roi_info.width=(unsigned int) (x-roi_info.x);
11566           else
11567             {
11568               roi_info.width=(unsigned int) (roi_info.x-x);
11569               roi_info.x=(ssize_t) x;
11570             }
11571           if (roi_info.y < 0)
11572             roi_info.y=0;
11573           else
11574             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11575               roi_info.y=(ssize_t) windows->image.ximage->height;
11576           if ((int) roi_info.y < y)
11577             roi_info.height=(unsigned int) (y-roi_info.y);
11578           else
11579             {
11580               roi_info.height=(unsigned int) (roi_info.y-y);
11581               roi_info.y=(ssize_t) y;
11582             }
11583         }
11584     } while ((state & ExitState) == 0);
11585     /*
11586       Wait for user to grab a corner of the rectangle or press return.
11587     */
11588     state=DefaultState;
11589     command_type=NullCommand;
11590     (void) XMapWindow(display,windows->info.id);
11591     do
11592     {
11593       if (windows->info.mapped != MagickFalse)
11594         {
11595           /*
11596             Display pointer position.
11597           */
11598           (void) FormatLocaleString(text,MaxTextExtent,
11599             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11600             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11601           XInfoWidget(display,windows,text);
11602         }
11603       highlight_info=roi_info;
11604       highlight_info.x=roi_info.x-windows->image.x;
11605       highlight_info.y=roi_info.y-windows->image.y;
11606       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11607         {
11608           state|=EscapeState;
11609           state|=ExitState;
11610           break;
11611         }
11612       if ((state & UpdateRegionState) != 0)
11613         {
11614           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11615           switch (command_type)
11616           {
11617             case UndoCommand:
11618             case RedoCommand:
11619             {
11620               (void) XMagickCommand(display,resource_info,windows,command_type,
11621                 image,exception);
11622               break;
11623             }
11624             default:
11625             {
11626               /*
11627                 Region of interest is relative to image configuration.
11628               */
11629               progress_monitor=SetImageProgressMonitor(*image,
11630                 (MagickProgressMonitor) NULL,(*image)->client_data);
11631               crop_info=roi_info;
11632               width=(unsigned int) (*image)->columns;
11633               height=(unsigned int) (*image)->rows;
11634               x=0;
11635               y=0;
11636               if (windows->image.crop_geometry != (char *) NULL)
11637                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11638                   &width,&height);
11639               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11640               crop_info.x+=x;
11641               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11642               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11643               scale_factor=(MagickRealType)
11644                 height/windows->image.ximage->height;
11645               crop_info.y+=y;
11646               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11647               crop_info.height=(unsigned int)
11648                 (scale_factor*crop_info.height+0.5);
11649               roi_image=CropImage(*image,&crop_info,exception);
11650               (void) SetImageProgressMonitor(*image,progress_monitor,
11651                 (*image)->client_data);
11652               if (roi_image == (Image *) NULL)
11653                 continue;
11654               /*
11655                 Apply image processing technique to the region of interest.
11656               */
11657               windows->image.orphan=MagickTrue;
11658               (void) XMagickCommand(display,resource_info,windows,command_type,
11659                 &roi_image,exception);
11660               progress_monitor=SetImageProgressMonitor(*image,
11661                 (MagickProgressMonitor) NULL,(*image)->client_data);
11662               (void) XMagickCommand(display,resource_info,windows,
11663                 SaveToUndoBufferCommand,image,exception);
11664               windows->image.orphan=MagickFalse;
11665               (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11666                 MagickTrue,crop_info.x,crop_info.y,exception);
11667               roi_image=DestroyImage(roi_image);
11668               (void) SetImageProgressMonitor(*image,progress_monitor,
11669                 (*image)->client_data);
11670               break;
11671             }
11672           }
11673           if (command_type != InfoCommand)
11674             {
11675               XConfigureImageColormap(display,resource_info,windows,*image,
11676                 exception);
11677               (void) XConfigureImage(display,resource_info,windows,*image,
11678                 exception);
11679             }
11680           XCheckRefreshWindows(display,windows);
11681           XInfoWidget(display,windows,text);
11682           (void) XSetFunction(display,windows->image.highlight_context,
11683             GXinvert);
11684           state&=(~UpdateRegionState);
11685         }
11686       XHighlightRectangle(display,windows->image.id,
11687         windows->image.highlight_context,&highlight_info);
11688       XScreenEvent(display,windows,&event,exception);
11689       if (event.xany.window == windows->command.id)
11690         {
11691           /*
11692             Select a command from the Command widget.
11693           */
11694           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11695           command_type=NullCommand;
11696           id=XCommandWidget(display,windows,ApplyMenu,&event);
11697           if (id >= 0)
11698             {
11699               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11700               command_type=ApplyCommands[id];
11701               if (id < ApplyMenus)
11702                 {
11703                   /*
11704                     Select a command from a pop-up menu.
11705                   */
11706                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11707                     (const char **) Menus[id],command);
11708                   if (entry >= 0)
11709                     {
11710                       (void) CopyMagickString(command,Menus[id][entry],
11711                         MaxTextExtent);
11712                       command_type=Commands[id][entry];
11713                     }
11714                 }
11715             }
11716           (void) XSetFunction(display,windows->image.highlight_context,
11717             GXinvert);
11718           XHighlightRectangle(display,windows->image.id,
11719             windows->image.highlight_context,&highlight_info);
11720           if (command_type == HelpCommand)
11721             {
11722               (void) XSetFunction(display,windows->image.highlight_context,
11723                 GXcopy);
11724               XTextViewWidget(display,resource_info,windows,MagickFalse,
11725                 "Help Viewer - Region of Interest",ImageROIHelp);
11726               (void) XSetFunction(display,windows->image.highlight_context,
11727                 GXinvert);
11728               continue;
11729             }
11730           if (command_type == QuitCommand)
11731             {
11732               /*
11733                 exit.
11734               */
11735               state|=EscapeState;
11736               state|=ExitState;
11737               continue;
11738             }
11739           if (command_type != NullCommand)
11740             state|=UpdateRegionState;
11741           continue;
11742         }
11743       XHighlightRectangle(display,windows->image.id,
11744         windows->image.highlight_context,&highlight_info);
11745       switch (event.type)
11746       {
11747         case ButtonPress:
11748         {
11749           x=windows->image.x;
11750           y=windows->image.y;
11751           if (event.xbutton.button != Button1)
11752             break;
11753           if (event.xbutton.window != windows->image.id)
11754             break;
11755           x=windows->image.x+event.xbutton.x;
11756           y=windows->image.y+event.xbutton.y;
11757           if ((x < (int) (roi_info.x+RoiDelta)) &&
11758               (x > (int) (roi_info.x-RoiDelta)) &&
11759               (y < (int) (roi_info.y+RoiDelta)) &&
11760               (y > (int) (roi_info.y-RoiDelta)))
11761             {
11762               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11763               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11764               state|=UpdateConfigurationState;
11765               break;
11766             }
11767           if ((x < (int) (roi_info.x+RoiDelta)) &&
11768               (x > (int) (roi_info.x-RoiDelta)) &&
11769               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11770               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11771             {
11772               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11773               state|=UpdateConfigurationState;
11774               break;
11775             }
11776           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11777               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11778               (y < (int) (roi_info.y+RoiDelta)) &&
11779               (y > (int) (roi_info.y-RoiDelta)))
11780             {
11781               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11782               state|=UpdateConfigurationState;
11783               break;
11784             }
11785           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11786               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11787               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11788               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11789             {
11790               state|=UpdateConfigurationState;
11791               break;
11792             }
11793         }
11794         case ButtonRelease:
11795         {
11796           if (event.xbutton.window == windows->pan.id)
11797             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11798                 (highlight_info.y != crop_info.y-windows->image.y))
11799               XHighlightRectangle(display,windows->image.id,
11800                 windows->image.highlight_context,&highlight_info);
11801           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11802             event.xbutton.time);
11803           break;
11804         }
11805         case Expose:
11806         {
11807           if (event.xexpose.window == windows->image.id)
11808             if (event.xexpose.count == 0)
11809               {
11810                 event.xexpose.x=(int) highlight_info.x;
11811                 event.xexpose.y=(int) highlight_info.y;
11812                 event.xexpose.width=(int) highlight_info.width;
11813                 event.xexpose.height=(int) highlight_info.height;
11814                 XRefreshWindow(display,&windows->image,&event);
11815               }
11816           if (event.xexpose.window == windows->info.id)
11817             if (event.xexpose.count == 0)
11818               XInfoWidget(display,windows,text);
11819           break;
11820         }
11821         case KeyPress:
11822         {
11823           KeySym
11824             key_symbol;
11825
11826           if (event.xkey.window != windows->image.id)
11827             break;
11828           /*
11829             Respond to a user key press.
11830           */
11831           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11832             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11833           switch ((int) key_symbol)
11834           {
11835             case XK_Shift_L:
11836             case XK_Shift_R:
11837               break;
11838             case XK_Escape:
11839             case XK_F20:
11840               state|=EscapeState;
11841             case XK_Return:
11842             {
11843               state|=ExitState;
11844               break;
11845             }
11846             case XK_Home:
11847             case XK_KP_Home:
11848             {
11849               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11850               roi_info.y=(ssize_t) (windows->image.height/2L-
11851                 roi_info.height/2L);
11852               break;
11853             }
11854             case XK_Left:
11855             case XK_KP_Left:
11856             {
11857               roi_info.x--;
11858               break;
11859             }
11860             case XK_Up:
11861             case XK_KP_Up:
11862             case XK_Next:
11863             {
11864               roi_info.y--;
11865               break;
11866             }
11867             case XK_Right:
11868             case XK_KP_Right:
11869             {
11870               roi_info.x++;
11871               break;
11872             }
11873             case XK_Prior:
11874             case XK_Down:
11875             case XK_KP_Down:
11876             {
11877               roi_info.y++;
11878               break;
11879             }
11880             case XK_F1:
11881             case XK_Help:
11882             {
11883               (void) XSetFunction(display,windows->image.highlight_context,
11884                 GXcopy);
11885               XTextViewWidget(display,resource_info,windows,MagickFalse,
11886                 "Help Viewer - Region of Interest",ImageROIHelp);
11887               (void) XSetFunction(display,windows->image.highlight_context,
11888                 GXinvert);
11889               break;
11890             }
11891             default:
11892             {
11893               command_type=XImageWindowCommand(display,resource_info,windows,
11894                 event.xkey.state,key_symbol,image,exception);
11895               if (command_type != NullCommand)
11896                 state|=UpdateRegionState;
11897               break;
11898             }
11899           }
11900           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11901             event.xkey.time);
11902           break;
11903         }
11904         case KeyRelease:
11905           break;
11906         case MotionNotify:
11907         {
11908           if (event.xbutton.window != windows->image.id)
11909             break;
11910           /*
11911             Map and unmap Info widget as text cursor crosses its boundaries.
11912           */
11913           x=event.xmotion.x;
11914           y=event.xmotion.y;
11915           if (windows->info.mapped != MagickFalse)
11916             {
11917               if ((x < (int) (windows->info.x+windows->info.width)) &&
11918                   (y < (int) (windows->info.y+windows->info.height)))
11919                 (void) XWithdrawWindow(display,windows->info.id,
11920                   windows->info.screen);
11921             }
11922           else
11923             if ((x > (int) (windows->info.x+windows->info.width)) ||
11924                 (y > (int) (windows->info.y+windows->info.height)))
11925               (void) XMapWindow(display,windows->info.id);
11926           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11927           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11928           break;
11929         }
11930         case SelectionRequest:
11931         {
11932           XSelectionEvent
11933             notify;
11934
11935           XSelectionRequestEvent
11936             *request;
11937
11938           /*
11939             Set primary selection.
11940           */
11941           (void) FormatLocaleString(text,MaxTextExtent,
11942             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11943             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11944           request=(&(event.xselectionrequest));
11945           (void) XChangeProperty(request->display,request->requestor,
11946             request->property,request->target,8,PropModeReplace,
11947             (unsigned char *) text,(int) strlen(text));
11948           notify.type=SelectionNotify;
11949           notify.display=request->display;
11950           notify.requestor=request->requestor;
11951           notify.selection=request->selection;
11952           notify.target=request->target;
11953           notify.time=request->time;
11954           if (request->property == None)
11955             notify.property=request->target;
11956           else
11957             notify.property=request->property;
11958           (void) XSendEvent(request->display,request->requestor,False,0,
11959             (XEvent *) &notify);
11960         }
11961         default:
11962           break;
11963       }
11964       if ((state & UpdateConfigurationState) != 0)
11965         {
11966           (void) XPutBackEvent(display,&event);
11967           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11968           break;
11969         }
11970     } while ((state & ExitState) == 0);
11971   } while ((state & ExitState) == 0);
11972   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11973   XSetCursorState(display,windows,MagickFalse);
11974   if ((state & EscapeState) != 0)
11975     return(MagickTrue);
11976   return(MagickTrue);
11977 }
11978 \f
11979 /*
11980 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11981 %                                                                             %
11982 %                                                                             %
11983 %                                                                             %
11984 +   X R o t a t e I m a g e                                                   %
11985 %                                                                             %
11986 %                                                                             %
11987 %                                                                             %
11988 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11989 %
11990 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11991 %  rotation angle is computed from the slope of a line drawn by the user.
11992 %
11993 %  The format of the XRotateImage method is:
11994 %
11995 %      MagickBooleanType XRotateImage(Display *display,
11996 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11997 %        Image **image,ExceptionInfo *exception)
11998 %
11999 %  A description of each parameter follows:
12000 %
12001 %    o display: Specifies a connection to an X server; returned from
12002 %      XOpenDisplay.
12003 %
12004 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12005 %
12006 %    o windows: Specifies a pointer to a XWindows structure.
12007 %
12008 %    o degrees: Specifies the number of degrees to rotate the image.
12009 %
12010 %    o image: the image.
12011 %
12012 %    o exception: return any errors or warnings in this structure.
12013 %
12014 */
12015 static MagickBooleanType XRotateImage(Display *display,
12016   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12017   ExceptionInfo *exception)
12018 {
12019   static const char
12020     *RotateMenu[] =
12021     {
12022       "Pixel Color",
12023       "Direction",
12024       "Help",
12025       "Dismiss",
12026       (char *) NULL
12027     };
12028
12029   static ModeType
12030     direction = HorizontalRotateCommand;
12031
12032   static const ModeType
12033     DirectionCommands[] =
12034     {
12035       HorizontalRotateCommand,
12036       VerticalRotateCommand
12037     },
12038     RotateCommands[] =
12039     {
12040       RotateColorCommand,
12041       RotateDirectionCommand,
12042       RotateHelpCommand,
12043       RotateDismissCommand
12044     };
12045
12046   static unsigned int
12047     pen_id = 0;
12048
12049   char
12050     command[MaxTextExtent],
12051     text[MaxTextExtent];
12052
12053   Image
12054     *rotate_image;
12055
12056   int
12057     id,
12058     x,
12059     y;
12060
12061   MagickRealType
12062     normalized_degrees;
12063
12064   register int
12065     i;
12066
12067   unsigned int
12068     height,
12069     rotations,
12070     width;
12071
12072   if (degrees == 0.0)
12073     {
12074       unsigned int
12075         distance;
12076
12077       size_t
12078         state;
12079
12080       XEvent
12081         event;
12082
12083       XSegment
12084         rotate_info;
12085
12086       /*
12087         Map Command widget.
12088       */
12089       (void) CloneString(&windows->command.name,"Rotate");
12090       windows->command.data=2;
12091       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12092       (void) XMapRaised(display,windows->command.id);
12093       XClientMessage(display,windows->image.id,windows->im_protocols,
12094         windows->im_update_widget,CurrentTime);
12095       /*
12096         Wait for first button press.
12097       */
12098       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12099       XQueryPosition(display,windows->image.id,&x,&y);
12100       rotate_info.x1=x;
12101       rotate_info.y1=y;
12102       rotate_info.x2=x;
12103       rotate_info.y2=y;
12104       state=DefaultState;
12105       do
12106       {
12107         XHighlightLine(display,windows->image.id,
12108           windows->image.highlight_context,&rotate_info);
12109         /*
12110           Wait for next event.
12111         */
12112         XScreenEvent(display,windows,&event,exception);
12113         XHighlightLine(display,windows->image.id,
12114           windows->image.highlight_context,&rotate_info);
12115         if (event.xany.window == windows->command.id)
12116           {
12117             /*
12118               Select a command from the Command widget.
12119             */
12120             id=XCommandWidget(display,windows,RotateMenu,&event);
12121             if (id < 0)
12122               continue;
12123             (void) XSetFunction(display,windows->image.highlight_context,
12124               GXcopy);
12125             switch (RotateCommands[id])
12126             {
12127               case RotateColorCommand:
12128               {
12129                 const char
12130                   *ColorMenu[MaxNumberPens];
12131
12132                 int
12133                   pen_number;
12134
12135                 XColor
12136                   color;
12137
12138                 /*
12139                   Initialize menu selections.
12140                 */
12141                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12142                   ColorMenu[i]=resource_info->pen_colors[i];
12143                 ColorMenu[MaxNumberPens-2]="Browser...";
12144                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12145                 /*
12146                   Select a pen color from the pop-up menu.
12147                 */
12148                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12149                   (const char **) ColorMenu,command);
12150                 if (pen_number < 0)
12151                   break;
12152                 if (pen_number == (MaxNumberPens-2))
12153                   {
12154                     static char
12155                       color_name[MaxTextExtent] = "gray";
12156
12157                     /*
12158                       Select a pen color from a dialog.
12159                     */
12160                     resource_info->pen_colors[pen_number]=color_name;
12161                     XColorBrowserWidget(display,windows,"Select",color_name);
12162                     if (*color_name == '\0')
12163                       break;
12164                   }
12165                 /*
12166                   Set pen color.
12167                 */
12168                 (void) XParseColor(display,windows->map_info->colormap,
12169                   resource_info->pen_colors[pen_number],&color);
12170                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12171                   (unsigned int) MaxColors,&color);
12172                 windows->pixel_info->pen_colors[pen_number]=color;
12173                 pen_id=(unsigned int) pen_number;
12174                 break;
12175               }
12176               case RotateDirectionCommand:
12177               {
12178                 static const char
12179                   *Directions[] =
12180                   {
12181                     "horizontal",
12182                     "vertical",
12183                     (char *) NULL,
12184                   };
12185
12186                 /*
12187                   Select a command from the pop-up menu.
12188                 */
12189                 id=XMenuWidget(display,windows,RotateMenu[id],
12190                   Directions,command);
12191                 if (id >= 0)
12192                   direction=DirectionCommands[id];
12193                 break;
12194               }
12195               case RotateHelpCommand:
12196               {
12197                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12198                   "Help Viewer - Image Rotation",ImageRotateHelp);
12199                 break;
12200               }
12201               case RotateDismissCommand:
12202               {
12203                 /*
12204                   Prematurely exit.
12205                 */
12206                 state|=EscapeState;
12207                 state|=ExitState;
12208                 break;
12209               }
12210               default:
12211                 break;
12212             }
12213             (void) XSetFunction(display,windows->image.highlight_context,
12214               GXinvert);
12215             continue;
12216           }
12217         switch (event.type)
12218         {
12219           case ButtonPress:
12220           {
12221             if (event.xbutton.button != Button1)
12222               break;
12223             if (event.xbutton.window != windows->image.id)
12224               break;
12225             /*
12226               exit loop.
12227             */
12228             (void) XSetFunction(display,windows->image.highlight_context,
12229               GXcopy);
12230             rotate_info.x1=event.xbutton.x;
12231             rotate_info.y1=event.xbutton.y;
12232             state|=ExitState;
12233             break;
12234           }
12235           case ButtonRelease:
12236             break;
12237           case Expose:
12238             break;
12239           case KeyPress:
12240           {
12241             char
12242               command[MaxTextExtent];
12243
12244             KeySym
12245               key_symbol;
12246
12247             if (event.xkey.window != windows->image.id)
12248               break;
12249             /*
12250               Respond to a user key press.
12251             */
12252             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12253               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12254             switch ((int) key_symbol)
12255             {
12256               case XK_Escape:
12257               case XK_F20:
12258               {
12259                 /*
12260                   Prematurely exit.
12261                 */
12262                 state|=EscapeState;
12263                 state|=ExitState;
12264                 break;
12265               }
12266               case XK_F1:
12267               case XK_Help:
12268               {
12269                 (void) XSetFunction(display,windows->image.highlight_context,
12270                   GXcopy);
12271                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12272                   "Help Viewer - Image Rotation",ImageRotateHelp);
12273                 (void) XSetFunction(display,windows->image.highlight_context,
12274                   GXinvert);
12275                 break;
12276               }
12277               default:
12278               {
12279                 (void) XBell(display,0);
12280                 break;
12281               }
12282             }
12283             break;
12284           }
12285           case MotionNotify:
12286           {
12287             rotate_info.x1=event.xmotion.x;
12288             rotate_info.y1=event.xmotion.y;
12289           }
12290         }
12291         rotate_info.x2=rotate_info.x1;
12292         rotate_info.y2=rotate_info.y1;
12293         if (direction == HorizontalRotateCommand)
12294           rotate_info.x2+=32;
12295         else
12296           rotate_info.y2-=32;
12297       } while ((state & ExitState) == 0);
12298       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12299       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12300       if ((state & EscapeState) != 0)
12301         return(MagickTrue);
12302       /*
12303         Draw line as pointer moves until the mouse button is released.
12304       */
12305       distance=0;
12306       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12307       state=DefaultState;
12308       do
12309       {
12310         if (distance > 9)
12311           {
12312             /*
12313               Display info and draw rotation line.
12314             */
12315             if (windows->info.mapped == MagickFalse)
12316               (void) XMapWindow(display,windows->info.id);
12317             (void) FormatLocaleString(text,MaxTextExtent," %g",
12318               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12319             XInfoWidget(display,windows,text);
12320             XHighlightLine(display,windows->image.id,
12321               windows->image.highlight_context,&rotate_info);
12322           }
12323         else
12324           if (windows->info.mapped != MagickFalse)
12325             (void) XWithdrawWindow(display,windows->info.id,
12326               windows->info.screen);
12327         /*
12328           Wait for next event.
12329         */
12330         XScreenEvent(display,windows,&event,exception);
12331         if (distance > 9)
12332           XHighlightLine(display,windows->image.id,
12333             windows->image.highlight_context,&rotate_info);
12334         switch (event.type)
12335         {
12336           case ButtonPress:
12337             break;
12338           case ButtonRelease:
12339           {
12340             /*
12341               User has committed to rotation line.
12342             */
12343             rotate_info.x2=event.xbutton.x;
12344             rotate_info.y2=event.xbutton.y;
12345             state|=ExitState;
12346             break;
12347           }
12348           case Expose:
12349             break;
12350           case MotionNotify:
12351           {
12352             rotate_info.x2=event.xmotion.x;
12353             rotate_info.y2=event.xmotion.y;
12354           }
12355           default:
12356             break;
12357         }
12358         /*
12359           Check boundary conditions.
12360         */
12361         if (rotate_info.x2 < 0)
12362           rotate_info.x2=0;
12363         else
12364           if (rotate_info.x2 > (int) windows->image.width)
12365             rotate_info.x2=(short) windows->image.width;
12366         if (rotate_info.y2 < 0)
12367           rotate_info.y2=0;
12368         else
12369           if (rotate_info.y2 > (int) windows->image.height)
12370             rotate_info.y2=(short) windows->image.height;
12371         /*
12372           Compute rotation angle from the slope of the line.
12373         */
12374         degrees=0.0;
12375         distance=(unsigned int)
12376           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12377           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12378         if (distance > 9)
12379           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12380             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12381       } while ((state & ExitState) == 0);
12382       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12383       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12384       if (distance <= 9)
12385         return(MagickTrue);
12386     }
12387   if (direction == VerticalRotateCommand)
12388     degrees-=90.0;
12389   if (degrees == 0.0)
12390     return(MagickTrue);
12391   /*
12392     Rotate image.
12393   */
12394   normalized_degrees=degrees;
12395   while (normalized_degrees < -45.0)
12396     normalized_degrees+=360.0;
12397   for (rotations=0; normalized_degrees > 45.0; rotations++)
12398     normalized_degrees-=90.0;
12399   if (normalized_degrees != 0.0)
12400     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12401       exception);
12402   XSetCursorState(display,windows,MagickTrue);
12403   XCheckRefreshWindows(display,windows);
12404   (*image)->background_color.red=(double) ScaleShortToQuantum(
12405     windows->pixel_info->pen_colors[pen_id].red);
12406   (*image)->background_color.green=(double) ScaleShortToQuantum(
12407     windows->pixel_info->pen_colors[pen_id].green);
12408   (*image)->background_color.blue=(double) ScaleShortToQuantum(
12409     windows->pixel_info->pen_colors[pen_id].blue);
12410   rotate_image=RotateImage(*image,degrees,exception);
12411   XSetCursorState(display,windows,MagickFalse);
12412   if (rotate_image == (Image *) NULL)
12413     return(MagickFalse);
12414   *image=DestroyImage(*image);
12415   *image=rotate_image;
12416   if (windows->image.crop_geometry != (char *) NULL)
12417     {
12418       /*
12419         Rotate crop geometry.
12420       */
12421       width=(unsigned int) (*image)->columns;
12422       height=(unsigned int) (*image)->rows;
12423       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12424       switch (rotations % 4)
12425       {
12426         default:
12427         case 0:
12428           break;
12429         case 1:
12430         {
12431           /*
12432             Rotate 90 degrees.
12433           */
12434           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12435             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12436             (int) height-y,x);
12437           break;
12438         }
12439         case 2:
12440         {
12441           /*
12442             Rotate 180 degrees.
12443           */
12444           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12445             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12446           break;
12447         }
12448         case 3:
12449         {
12450           /*
12451             Rotate 270 degrees.
12452           */
12453           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12454             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12455           break;
12456         }
12457       }
12458     }
12459   if (windows->image.orphan != MagickFalse)
12460     return(MagickTrue);
12461   if (normalized_degrees != 0.0)
12462     {
12463       /*
12464         Update image colormap.
12465       */
12466       windows->image.window_changes.width=(int) (*image)->columns;
12467       windows->image.window_changes.height=(int) (*image)->rows;
12468       if (windows->image.crop_geometry != (char *) NULL)
12469         {
12470           /*
12471             Obtain dimensions of image from crop geometry.
12472           */
12473           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12474             &width,&height);
12475           windows->image.window_changes.width=(int) width;
12476           windows->image.window_changes.height=(int) height;
12477         }
12478       XConfigureImageColormap(display,resource_info,windows,*image,exception);
12479     }
12480   else
12481     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12482       {
12483         windows->image.window_changes.width=windows->image.ximage->height;
12484         windows->image.window_changes.height=windows->image.ximage->width;
12485       }
12486   /*
12487     Update image configuration.
12488   */
12489   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12490   return(MagickTrue);
12491 }
12492 \f
12493 /*
12494 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12495 %                                                                             %
12496 %                                                                             %
12497 %                                                                             %
12498 +   X S a v e I m a g e                                                       %
12499 %                                                                             %
12500 %                                                                             %
12501 %                                                                             %
12502 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12503 %
12504 %  XSaveImage() saves an image to a file.
12505 %
12506 %  The format of the XSaveImage method is:
12507 %
12508 %      MagickBooleanType XSaveImage(Display *display,
12509 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12510 %        ExceptionInfo *exception)
12511 %
12512 %  A description of each parameter follows:
12513 %
12514 %    o display: Specifies a connection to an X server; returned from
12515 %      XOpenDisplay.
12516 %
12517 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12518 %
12519 %    o windows: Specifies a pointer to a XWindows structure.
12520 %
12521 %    o image: the image.
12522 %
12523 %    o exception: return any errors or warnings in this structure.
12524 %
12525 */
12526 static MagickBooleanType XSaveImage(Display *display,
12527   XResourceInfo *resource_info,XWindows *windows,Image *image,
12528   ExceptionInfo *exception)
12529 {
12530   char
12531     filename[MaxTextExtent],
12532     geometry[MaxTextExtent];
12533
12534   Image
12535     *save_image;
12536
12537   ImageInfo
12538     *image_info;
12539
12540   MagickStatusType
12541     status;
12542
12543   /*
12544     Request file name from user.
12545   */
12546   if (resource_info->write_filename != (char *) NULL)
12547     (void) CopyMagickString(filename,resource_info->write_filename,
12548       MaxTextExtent);
12549   else
12550     {
12551       char
12552         path[MaxTextExtent];
12553
12554       int
12555         status;
12556
12557       GetPathComponent(image->filename,HeadPath,path);
12558       GetPathComponent(image->filename,TailPath,filename);
12559       if (*path != '\0')
12560         {
12561           status=chdir(path);
12562           if (status == -1)
12563             (void) ThrowMagickException(exception,GetMagickModule(),
12564               FileOpenError,"UnableToOpenFile","%s",path);
12565         }
12566     }
12567   XFileBrowserWidget(display,windows,"Save",filename);
12568   if (*filename == '\0')
12569     return(MagickTrue);
12570   if (IsPathAccessible(filename) != MagickFalse)
12571     {
12572       int
12573         status;
12574
12575       /*
12576         File exists-- seek user's permission before overwriting.
12577       */
12578       status=XConfirmWidget(display,windows,"Overwrite",filename);
12579       if (status <= 0)
12580         return(MagickTrue);
12581     }
12582   image_info=CloneImageInfo(resource_info->image_info);
12583   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12584   (void) SetImageInfo(image_info,1,exception);
12585   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12586       (LocaleCompare(image_info->magick,"JPG") == 0))
12587     {
12588       char
12589         quality[MaxTextExtent];
12590
12591       int
12592         status;
12593
12594       /*
12595         Request JPEG quality from user.
12596       */
12597       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12598         image->quality);
12599       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12600         quality);
12601       if (*quality == '\0')
12602         return(MagickTrue);
12603       image->quality=StringToUnsignedLong(quality);
12604       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12605     }
12606   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12607       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12608       (LocaleCompare(image_info->magick,"PS") == 0) ||
12609       (LocaleCompare(image_info->magick,"PS2") == 0))
12610     {
12611       char
12612         geometry[MaxTextExtent];
12613
12614       /*
12615         Request page geometry from user.
12616       */
12617       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12618       if (LocaleCompare(image_info->magick,"PDF") == 0)
12619         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12620       if (image_info->page != (char *) NULL)
12621         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12622       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12623         "Select page geometry:",geometry);
12624       if (*geometry != '\0')
12625         image_info->page=GetPageGeometry(geometry);
12626     }
12627   /*
12628     Apply image transforms.
12629   */
12630   XSetCursorState(display,windows,MagickTrue);
12631   XCheckRefreshWindows(display,windows);
12632   save_image=CloneImage(image,0,0,MagickTrue,exception);
12633   if (save_image == (Image *) NULL)
12634     return(MagickFalse);
12635   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12636     windows->image.ximage->width,windows->image.ximage->height);
12637   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12638     exception);
12639   /*
12640     Write image.
12641   */
12642   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12643   status=WriteImage(image_info,save_image,exception);
12644   if (status != MagickFalse)
12645     image->taint=MagickFalse;
12646   save_image=DestroyImage(save_image);
12647   image_info=DestroyImageInfo(image_info);
12648   XSetCursorState(display,windows,MagickFalse);
12649   return(status != 0 ? MagickTrue : MagickFalse);
12650 }
12651 \f
12652 /*
12653 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12654 %                                                                             %
12655 %                                                                             %
12656 %                                                                             %
12657 +   X S c r e e n E v e n t                                                   %
12658 %                                                                             %
12659 %                                                                             %
12660 %                                                                             %
12661 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12662 %
12663 %  XScreenEvent() handles global events associated with the Pan and Magnify
12664 %  windows.
12665 %
12666 %  The format of the XScreenEvent function is:
12667 %
12668 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12669 %        ExceptionInfo *exception)
12670 %
12671 %  A description of each parameter follows:
12672 %
12673 %    o display: Specifies a pointer to the Display structure;  returned from
12674 %      XOpenDisplay.
12675 %
12676 %    o windows: Specifies a pointer to a XWindows structure.
12677 %
12678 %    o event: Specifies a pointer to a X11 XEvent structure.
12679 %
12680 %    o exception: return any errors or warnings in this structure.
12681 %
12682 */
12683
12684 #if defined(__cplusplus) || defined(c_plusplus)
12685 extern "C" {
12686 #endif
12687
12688 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12689 {
12690   register XWindows
12691     *windows;
12692
12693   windows=(XWindows *) data;
12694   if ((event->type == ClientMessage) &&
12695       (event->xclient.window == windows->image.id))
12696     return(MagickFalse);
12697   return(MagickTrue);
12698 }
12699
12700 #if defined(__cplusplus) || defined(c_plusplus)
12701 }
12702 #endif
12703
12704 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12705   ExceptionInfo *exception)
12706 {
12707   register int
12708     x,
12709     y;
12710
12711   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12712   if (event->xany.window == windows->command.id)
12713     return;
12714   switch (event->type)
12715   {
12716     case ButtonPress:
12717     case ButtonRelease:
12718     {
12719       if ((event->xbutton.button == Button3) &&
12720           (event->xbutton.state & Mod1Mask))
12721         {
12722           /*
12723             Convert Alt-Button3 to Button2.
12724           */
12725           event->xbutton.button=Button2;
12726           event->xbutton.state&=(~Mod1Mask);
12727         }
12728       if (event->xbutton.window == windows->backdrop.id)
12729         {
12730           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12731             event->xbutton.time);
12732           break;
12733         }
12734       if (event->xbutton.window == windows->pan.id)
12735         {
12736           XPanImage(display,windows,event,exception);
12737           break;
12738         }
12739       if (event->xbutton.window == windows->image.id)
12740         if (event->xbutton.button == Button2)
12741           {
12742             /*
12743               Update magnified image.
12744             */
12745             x=event->xbutton.x;
12746             y=event->xbutton.y;
12747             if (x < 0)
12748               x=0;
12749             else
12750               if (x >= (int) windows->image.width)
12751                 x=(int) (windows->image.width-1);
12752             windows->magnify.x=(int) windows->image.x+x;
12753             if (y < 0)
12754               y=0;
12755             else
12756              if (y >= (int) windows->image.height)
12757                y=(int) (windows->image.height-1);
12758             windows->magnify.y=windows->image.y+y;
12759             if (windows->magnify.mapped == MagickFalse)
12760               (void) XMapRaised(display,windows->magnify.id);
12761             XMakeMagnifyImage(display,windows,exception);
12762             if (event->type == ButtonRelease)
12763               (void) XWithdrawWindow(display,windows->info.id,
12764                 windows->info.screen);
12765             break;
12766           }
12767       break;
12768     }
12769     case ClientMessage:
12770     {
12771       /*
12772         If client window delete message, exit.
12773       */
12774       if (event->xclient.message_type != windows->wm_protocols)
12775         break;
12776       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12777         break;
12778       if (event->xclient.window == windows->magnify.id)
12779         {
12780           (void) XWithdrawWindow(display,windows->magnify.id,
12781             windows->magnify.screen);
12782           break;
12783         }
12784       break;
12785     }
12786     case ConfigureNotify:
12787     {
12788       if (event->xconfigure.window == windows->magnify.id)
12789         {
12790           unsigned int
12791             magnify;
12792
12793           /*
12794             Magnify window has a new configuration.
12795           */
12796           windows->magnify.width=(unsigned int) event->xconfigure.width;
12797           windows->magnify.height=(unsigned int) event->xconfigure.height;
12798           if (windows->magnify.mapped == MagickFalse)
12799             break;
12800           magnify=1;
12801           while ((int) magnify <= event->xconfigure.width)
12802             magnify<<=1;
12803           while ((int) magnify <= event->xconfigure.height)
12804             magnify<<=1;
12805           magnify>>=1;
12806           if (((int) magnify != event->xconfigure.width) ||
12807               ((int) magnify != event->xconfigure.height))
12808             {
12809               XWindowChanges
12810                 window_changes;
12811
12812               window_changes.width=(int) magnify;
12813               window_changes.height=(int) magnify;
12814               (void) XReconfigureWMWindow(display,windows->magnify.id,
12815                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12816                 &window_changes);
12817               break;
12818             }
12819           XMakeMagnifyImage(display,windows,exception);
12820           break;
12821         }
12822       break;
12823     }
12824     case Expose:
12825     {
12826       if (event->xexpose.window == windows->image.id)
12827         {
12828           XRefreshWindow(display,&windows->image,event);
12829           break;
12830         }
12831       if (event->xexpose.window == windows->pan.id)
12832         if (event->xexpose.count == 0)
12833           {
12834             XDrawPanRectangle(display,windows);
12835             break;
12836           }
12837       if (event->xexpose.window == windows->magnify.id)
12838         if (event->xexpose.count == 0)
12839           {
12840             XMakeMagnifyImage(display,windows,exception);
12841             break;
12842           }
12843       break;
12844     }
12845     case KeyPress:
12846     {
12847       char
12848         command[MaxTextExtent];
12849
12850       KeySym
12851         key_symbol;
12852
12853       if (event->xkey.window != windows->magnify.id)
12854         break;
12855       /*
12856         Respond to a user key press.
12857       */
12858       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12859         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12860       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12861         exception);
12862       break;
12863     }
12864     case MapNotify:
12865     {
12866       if (event->xmap.window == windows->magnify.id)
12867         {
12868           windows->magnify.mapped=MagickTrue;
12869           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12870           break;
12871         }
12872       if (event->xmap.window == windows->info.id)
12873         {
12874           windows->info.mapped=MagickTrue;
12875           break;
12876         }
12877       break;
12878     }
12879     case MotionNotify:
12880     {
12881       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12882       if (event->xmotion.window == windows->image.id)
12883         if (windows->magnify.mapped != MagickFalse)
12884           {
12885             /*
12886               Update magnified image.
12887             */
12888             x=event->xmotion.x;
12889             y=event->xmotion.y;
12890             if (x < 0)
12891               x=0;
12892             else
12893               if (x >= (int) windows->image.width)
12894                 x=(int) (windows->image.width-1);
12895             windows->magnify.x=(int) windows->image.x+x;
12896             if (y < 0)
12897               y=0;
12898             else
12899              if (y >= (int) windows->image.height)
12900                y=(int) (windows->image.height-1);
12901             windows->magnify.y=windows->image.y+y;
12902             XMakeMagnifyImage(display,windows,exception);
12903           }
12904       break;
12905     }
12906     case UnmapNotify:
12907     {
12908       if (event->xunmap.window == windows->magnify.id)
12909         {
12910           windows->magnify.mapped=MagickFalse;
12911           break;
12912         }
12913       if (event->xunmap.window == windows->info.id)
12914         {
12915           windows->info.mapped=MagickFalse;
12916           break;
12917         }
12918       break;
12919     }
12920     default:
12921       break;
12922   }
12923 }
12924 \f
12925 /*
12926 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12927 %                                                                             %
12928 %                                                                             %
12929 %                                                                             %
12930 +   X S e t C r o p G e o m e t r y                                           %
12931 %                                                                             %
12932 %                                                                             %
12933 %                                                                             %
12934 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12935 %
12936 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12937 %  and translates it to a cropping geometry relative to the image.
12938 %
12939 %  The format of the XSetCropGeometry method is:
12940 %
12941 %      void XSetCropGeometry(Display *display,XWindows *windows,
12942 %        RectangleInfo *crop_info,Image *image)
12943 %
12944 %  A description of each parameter follows:
12945 %
12946 %    o display: Specifies a connection to an X server; returned from
12947 %      XOpenDisplay.
12948 %
12949 %    o windows: Specifies a pointer to a XWindows structure.
12950 %
12951 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12952 %      Image window to crop.
12953 %
12954 %    o image: the image.
12955 %
12956 */
12957 static void XSetCropGeometry(Display *display,XWindows *windows,
12958   RectangleInfo *crop_info,Image *image)
12959 {
12960   char
12961     text[MaxTextExtent];
12962
12963   int
12964     x,
12965     y;
12966
12967   MagickRealType
12968     scale_factor;
12969
12970   unsigned int
12971     height,
12972     width;
12973
12974   if (windows->info.mapped != MagickFalse)
12975     {
12976       /*
12977         Display info on cropping rectangle.
12978       */
12979       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12980         (double) crop_info->width,(double) crop_info->height,(double)
12981         crop_info->x,(double) crop_info->y);
12982       XInfoWidget(display,windows,text);
12983     }
12984   /*
12985     Cropping geometry is relative to any previous crop geometry.
12986   */
12987   x=0;
12988   y=0;
12989   width=(unsigned int) image->columns;
12990   height=(unsigned int) image->rows;
12991   if (windows->image.crop_geometry != (char *) NULL)
12992     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12993   else
12994     windows->image.crop_geometry=AcquireString((char *) NULL);
12995   /*
12996     Define the crop geometry string from the cropping rectangle.
12997   */
12998   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12999   if (crop_info->x > 0)
13000     x+=(int) (scale_factor*crop_info->x+0.5);
13001   width=(unsigned int) (scale_factor*crop_info->width+0.5);
13002   if (width == 0)
13003     width=1;
13004   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13005   if (crop_info->y > 0)
13006     y+=(int) (scale_factor*crop_info->y+0.5);
13007   height=(unsigned int) (scale_factor*crop_info->height+0.5);
13008   if (height == 0)
13009     height=1;
13010   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13011     "%ux%u%+d%+d",width,height,x,y);
13012 }
13013 \f
13014 /*
13015 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13016 %                                                                             %
13017 %                                                                             %
13018 %                                                                             %
13019 +   X T i l e I m a g e                                                       %
13020 %                                                                             %
13021 %                                                                             %
13022 %                                                                             %
13023 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13024 %
13025 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13026 %  The load or delete command is chosen from a menu.
13027 %
13028 %  The format of the XTileImage method is:
13029 %
13030 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13031 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13032 %
13033 %  A description of each parameter follows:
13034 %
13035 %    o tile_image:  XTileImage reads or deletes the tile image
13036 %      and returns it.  A null image is returned if an error occurs.
13037 %
13038 %    o display: Specifies a connection to an X server;  returned from
13039 %      XOpenDisplay.
13040 %
13041 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13042 %
13043 %    o windows: Specifies a pointer to a XWindows structure.
13044 %
13045 %    o image: the image; returned from ReadImage.
13046 %
13047 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13048 %      the entire image is refreshed.
13049 %
13050 %    o exception: return any errors or warnings in this structure.
13051 %
13052 */
13053 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13054   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13055 {
13056   static const char
13057     *VerbMenu[] =
13058     {
13059       "Load",
13060       "Next",
13061       "Former",
13062       "Delete",
13063       "Update",
13064       (char *) NULL,
13065     };
13066
13067   static const ModeType
13068     TileCommands[] =
13069     {
13070       TileLoadCommand,
13071       TileNextCommand,
13072       TileFormerCommand,
13073       TileDeleteCommand,
13074       TileUpdateCommand
13075     };
13076
13077   char
13078     command[MaxTextExtent],
13079     filename[MaxTextExtent];
13080
13081   Image
13082     *tile_image;
13083
13084   int
13085     id,
13086     status,
13087     tile,
13088     x,
13089     y;
13090
13091   MagickRealType
13092     scale_factor;
13093
13094   register char
13095     *p,
13096     *q;
13097
13098   register int
13099     i;
13100
13101   unsigned int
13102     height,
13103     width;
13104
13105   /*
13106     Tile image is relative to montage image configuration.
13107   */
13108   x=0;
13109   y=0;
13110   width=(unsigned int) image->columns;
13111   height=(unsigned int) image->rows;
13112   if (windows->image.crop_geometry != (char *) NULL)
13113     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13114   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13115   event->xbutton.x+=windows->image.x;
13116   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13117   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13118   event->xbutton.y+=windows->image.y;
13119   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13120   /*
13121     Determine size and location of each tile in the visual image directory.
13122   */
13123   width=(unsigned int) image->columns;
13124   height=(unsigned int) image->rows;
13125   x=0;
13126   y=0;
13127   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13128   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13129     (event->xbutton.x-x)/width;
13130   if (tile < 0)
13131     {
13132       /*
13133         Button press is outside any tile.
13134       */
13135       (void) XBell(display,0);
13136       return((Image *) NULL);
13137     }
13138   /*
13139     Determine file name from the tile directory.
13140   */
13141   p=image->directory;
13142   for (i=tile; (i != 0) && (*p != '\0'); )
13143   {
13144     if (*p == '\n')
13145       i--;
13146     p++;
13147   }
13148   if (*p == '\0')
13149     {
13150       /*
13151         Button press is outside any tile.
13152       */
13153       (void) XBell(display,0);
13154       return((Image *) NULL);
13155     }
13156   /*
13157     Select a command from the pop-up menu.
13158   */
13159   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13160   if (id < 0)
13161     return((Image *) NULL);
13162   q=p;
13163   while ((*q != '\n') && (*q != '\0'))
13164     q++;
13165   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13166   /*
13167     Perform command for the selected tile.
13168   */
13169   XSetCursorState(display,windows,MagickTrue);
13170   XCheckRefreshWindows(display,windows);
13171   tile_image=NewImageList();
13172   switch (TileCommands[id])
13173   {
13174     case TileLoadCommand:
13175     {
13176       /*
13177         Load tile image.
13178       */
13179       XCheckRefreshWindows(display,windows);
13180       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13181         MaxTextExtent);
13182       (void) CopyMagickString(resource_info->image_info->filename,filename,
13183         MaxTextExtent);
13184       tile_image=ReadImage(resource_info->image_info,exception);
13185       CatchException(exception);
13186       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13187       break;
13188     }
13189     case TileNextCommand:
13190     {
13191       /*
13192         Display next image.
13193       */
13194       XClientMessage(display,windows->image.id,windows->im_protocols,
13195         windows->im_next_image,CurrentTime);
13196       break;
13197     }
13198     case TileFormerCommand:
13199     {
13200       /*
13201         Display former image.
13202       */
13203       XClientMessage(display,windows->image.id,windows->im_protocols,
13204         windows->im_former_image,CurrentTime);
13205       break;
13206     }
13207     case TileDeleteCommand:
13208     {
13209       /*
13210         Delete tile image.
13211       */
13212       if (IsPathAccessible(filename) == MagickFalse)
13213         {
13214           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13215           break;
13216         }
13217       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13218       if (status <= 0)
13219         break;
13220       status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
13221       if (status != MagickFalse)
13222         {
13223           XNoticeWidget(display,windows,"Unable to delete image file:",
13224             filename);
13225           break;
13226         }
13227     }
13228     case TileUpdateCommand:
13229     {
13230       int
13231         x_offset,
13232         y_offset;
13233
13234       PixelInfo
13235         pixel;
13236
13237       register int
13238         j;
13239
13240       register Quantum
13241         *s;
13242
13243       /*
13244         Ensure all the images exist.
13245       */
13246       tile=0;
13247       GetPixelInfo(image,&pixel);
13248       for (p=image->directory; *p != '\0'; p++)
13249       {
13250         CacheView
13251           *image_view;
13252
13253         q=p;
13254         while ((*q != '\n') && (*q != '\0'))
13255           q++;
13256         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13257         p=q;
13258         if (IsPathAccessible(filename) != MagickFalse)
13259           {
13260             tile++;
13261             continue;
13262           }
13263         /*
13264           Overwrite tile with background color.
13265         */
13266         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13267         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13268         image_view=AcquireAuthenticCacheView(image,exception);
13269         (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13270         for (i=0; i < (int) height; i++)
13271         {
13272           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13273             y_offset+i,width,1,exception);
13274           if (s == (Quantum *) NULL)
13275             break;
13276           for (j=0; j < (int) width; j++)
13277           {
13278             SetPixelInfoPixel(image,&pixel,s);
13279             s+=GetPixelChannels(image);
13280           }
13281           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13282             break;
13283         }
13284         image_view=DestroyCacheView(image_view);
13285         tile++;
13286       }
13287       windows->image.window_changes.width=(int) image->columns;
13288       windows->image.window_changes.height=(int) image->rows;
13289       XConfigureImageColormap(display,resource_info,windows,image,exception);
13290       (void) XConfigureImage(display,resource_info,windows,image,exception);
13291       break;
13292     }
13293     default:
13294       break;
13295   }
13296   XSetCursorState(display,windows,MagickFalse);
13297   return(tile_image);
13298 }
13299 \f
13300 /*
13301 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13302 %                                                                             %
13303 %                                                                             %
13304 %                                                                             %
13305 +   X T r a n s l a t e I m a g e                                             %
13306 %                                                                             %
13307 %                                                                             %
13308 %                                                                             %
13309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13310 %
13311 %  XTranslateImage() translates the image within an Image window by one pixel
13312 %  as specified by the key symbol.  If the image has a `montage string the
13313 %  translation is respect to the width and height contained within the string.
13314 %
13315 %  The format of the XTranslateImage method is:
13316 %
13317 %      void XTranslateImage(Display *display,XWindows *windows,
13318 %        Image *image,const KeySym key_symbol)
13319 %
13320 %  A description of each parameter follows:
13321 %
13322 %    o display: Specifies a connection to an X server; returned from
13323 %      XOpenDisplay.
13324 %
13325 %    o windows: Specifies a pointer to a XWindows structure.
13326 %
13327 %    o image: the image.
13328 %
13329 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13330 %      to trim.
13331 %
13332 */
13333 static void XTranslateImage(Display *display,XWindows *windows,
13334   Image *image,const KeySym key_symbol)
13335 {
13336   char
13337     text[MaxTextExtent];
13338
13339   int
13340     x,
13341     y;
13342
13343   unsigned int
13344     x_offset,
13345     y_offset;
13346
13347   /*
13348     User specified a pan position offset.
13349   */
13350   x_offset=windows->image.width;
13351   y_offset=windows->image.height;
13352   if (image->montage != (char *) NULL)
13353     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13354   switch ((int) key_symbol)
13355   {
13356     case XK_Home:
13357     case XK_KP_Home:
13358     {
13359       windows->image.x=(int) windows->image.width/2;
13360       windows->image.y=(int) windows->image.height/2;
13361       break;
13362     }
13363     case XK_Left:
13364     case XK_KP_Left:
13365     {
13366       windows->image.x-=x_offset;
13367       break;
13368     }
13369     case XK_Next:
13370     case XK_Up:
13371     case XK_KP_Up:
13372     {
13373       windows->image.y-=y_offset;
13374       break;
13375     }
13376     case XK_Right:
13377     case XK_KP_Right:
13378     {
13379       windows->image.x+=x_offset;
13380       break;
13381     }
13382     case XK_Prior:
13383     case XK_Down:
13384     case XK_KP_Down:
13385     {
13386       windows->image.y+=y_offset;
13387       break;
13388     }
13389     default:
13390       return;
13391   }
13392   /*
13393     Check boundary conditions.
13394   */
13395   if (windows->image.x < 0)
13396     windows->image.x=0;
13397   else
13398     if ((int) (windows->image.x+windows->image.width) >
13399         windows->image.ximage->width)
13400       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13401   if (windows->image.y < 0)
13402     windows->image.y=0;
13403   else
13404     if ((int) (windows->image.y+windows->image.height) >
13405         windows->image.ximage->height)
13406       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13407   /*
13408     Refresh Image window.
13409   */
13410   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13411     windows->image.width,windows->image.height,windows->image.x,
13412     windows->image.y);
13413   XInfoWidget(display,windows,text);
13414   XCheckRefreshWindows(display,windows);
13415   XDrawPanRectangle(display,windows);
13416   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13417   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13418 }
13419 \f
13420 /*
13421 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13422 %                                                                             %
13423 %                                                                             %
13424 %                                                                             %
13425 +   X T r i m I m a g e                                                       %
13426 %                                                                             %
13427 %                                                                             %
13428 %                                                                             %
13429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13430 %
13431 %  XTrimImage() trims the edges from the Image window.
13432 %
13433 %  The format of the XTrimImage method is:
13434 %
13435 %      MagickBooleanType XTrimImage(Display *display,
13436 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13437 %        ExceptionInfo *exception)
13438 %
13439 %  A description of each parameter follows:
13440 %
13441 %    o display: Specifies a connection to an X server; returned from
13442 %      XOpenDisplay.
13443 %
13444 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13445 %
13446 %    o windows: Specifies a pointer to a XWindows structure.
13447 %
13448 %    o image: the image.
13449 %
13450 %    o exception: return any errors or warnings in this structure.
13451 %
13452 */
13453 static MagickBooleanType XTrimImage(Display *display,
13454   XResourceInfo *resource_info,XWindows *windows,Image *image,
13455   ExceptionInfo *exception)
13456 {
13457   RectangleInfo
13458     trim_info;
13459
13460   register int
13461     x,
13462     y;
13463
13464   size_t
13465     background,
13466     pixel;
13467
13468   /*
13469     Trim edges from image.
13470   */
13471   XSetCursorState(display,windows,MagickTrue);
13472   XCheckRefreshWindows(display,windows);
13473   /*
13474     Crop the left edge.
13475   */
13476   background=XGetPixel(windows->image.ximage,0,0);
13477   trim_info.width=(size_t) windows->image.ximage->width;
13478   for (x=0; x < windows->image.ximage->width; x++)
13479   {
13480     for (y=0; y < windows->image.ximage->height; y++)
13481     {
13482       pixel=XGetPixel(windows->image.ximage,x,y);
13483       if (pixel != background)
13484         break;
13485     }
13486     if (y < windows->image.ximage->height)
13487       break;
13488   }
13489   trim_info.x=(ssize_t) x;
13490   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13491     {
13492       XSetCursorState(display,windows,MagickFalse);
13493       return(MagickFalse);
13494     }
13495   /*
13496     Crop the right edge.
13497   */
13498   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13499   for (x=windows->image.ximage->width-1; x != 0; x--)
13500   {
13501     for (y=0; y < windows->image.ximage->height; y++)
13502     {
13503       pixel=XGetPixel(windows->image.ximage,x,y);
13504       if (pixel != background)
13505         break;
13506     }
13507     if (y < windows->image.ximage->height)
13508       break;
13509   }
13510   trim_info.width=(size_t) (x-trim_info.x+1);
13511   /*
13512     Crop the top edge.
13513   */
13514   background=XGetPixel(windows->image.ximage,0,0);
13515   trim_info.height=(size_t) windows->image.ximage->height;
13516   for (y=0; y < windows->image.ximage->height; y++)
13517   {
13518     for (x=0; x < windows->image.ximage->width; x++)
13519     {
13520       pixel=XGetPixel(windows->image.ximage,x,y);
13521       if (pixel != background)
13522         break;
13523     }
13524     if (x < windows->image.ximage->width)
13525       break;
13526   }
13527   trim_info.y=(ssize_t) y;
13528   /*
13529     Crop the bottom edge.
13530   */
13531   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13532   for (y=windows->image.ximage->height-1; y != 0; y--)
13533   {
13534     for (x=0; x < windows->image.ximage->width; x++)
13535     {
13536       pixel=XGetPixel(windows->image.ximage,x,y);
13537       if (pixel != background)
13538         break;
13539     }
13540     if (x < windows->image.ximage->width)
13541       break;
13542   }
13543   trim_info.height=(size_t) y-trim_info.y+1;
13544   if (((unsigned int) trim_info.width != windows->image.width) ||
13545       ((unsigned int) trim_info.height != windows->image.height))
13546     {
13547       /*
13548         Reconfigure Image window as defined by the trimming rectangle.
13549       */
13550       XSetCropGeometry(display,windows,&trim_info,image);
13551       windows->image.window_changes.width=(int) trim_info.width;
13552       windows->image.window_changes.height=(int) trim_info.height;
13553       (void) XConfigureImage(display,resource_info,windows,image,exception);
13554     }
13555   XSetCursorState(display,windows,MagickFalse);
13556   return(MagickTrue);
13557 }
13558 \f
13559 /*
13560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13561 %                                                                             %
13562 %                                                                             %
13563 %                                                                             %
13564 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13565 %                                                                             %
13566 %                                                                             %
13567 %                                                                             %
13568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13569 %
13570 %  XVisualDirectoryImage() creates a Visual Image Directory.
13571 %
13572 %  The format of the XVisualDirectoryImage method is:
13573 %
13574 %      Image *XVisualDirectoryImage(Display *display,
13575 %        XResourceInfo *resource_info,XWindows *windows,
13576 %        ExceptionInfo *exception)
13577 %
13578 %  A description of each parameter follows:
13579 %
13580 %    o display: Specifies a connection to an X server; returned from
13581 %      XOpenDisplay.
13582 %
13583 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13584 %
13585 %    o windows: Specifies a pointer to a XWindows structure.
13586 %
13587 %    o exception: return any errors or warnings in this structure.
13588 %
13589 */
13590 static Image *XVisualDirectoryImage(Display *display,
13591   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13592 {
13593 #define TileImageTag  "Scale/Image"
13594 #define XClientName  "montage"
13595
13596   char
13597     **filelist;
13598
13599   Image
13600     *images,
13601     *montage_image,
13602     *next_image,
13603     *thumbnail_image;
13604
13605   ImageInfo
13606     *read_info;
13607
13608   int
13609     number_files;
13610
13611   MagickBooleanType
13612     backdrop;
13613
13614   MagickStatusType
13615     status;
13616
13617   MontageInfo
13618     *montage_info;
13619
13620   RectangleInfo
13621     geometry;
13622
13623   register int
13624     i;
13625
13626   static char
13627     filename[MaxTextExtent] = "\0",
13628     filenames[MaxTextExtent] = "*";
13629
13630   XResourceInfo
13631     background_resources;
13632
13633   /*
13634     Request file name from user.
13635   */
13636   XFileBrowserWidget(display,windows,"Directory",filenames);
13637   if (*filenames == '\0')
13638     return((Image *) NULL);
13639   /*
13640     Expand the filenames.
13641   */
13642   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13643   if (filelist == (char **) NULL)
13644     {
13645       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13646         filenames);
13647       return((Image *) NULL);
13648     }
13649   number_files=1;
13650   filelist[0]=filenames;
13651   status=ExpandFilenames(&number_files,&filelist);
13652   if ((status == MagickFalse) || (number_files == 0))
13653     {
13654       if (number_files == 0)
13655         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13656       else
13657         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13658           filenames);
13659       return((Image *) NULL);
13660     }
13661   /*
13662     Set image background resources.
13663   */
13664   background_resources=(*resource_info);
13665   background_resources.window_id=AcquireString("");
13666   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13667     "0x%lx",windows->image.id);
13668   background_resources.backdrop=MagickTrue;
13669   /*
13670     Read each image and convert them to a tile.
13671   */
13672   backdrop=(windows->visual_info->klass == TrueColor) ||
13673     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13674   read_info=CloneImageInfo(resource_info->image_info);
13675   (void) SetImageOption(read_info,"jpeg:size","120x120");
13676   (void) CloneString(&read_info->size,DefaultTileGeometry);
13677   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13678     (void *) NULL);
13679   images=NewImageList();
13680   XSetCursorState(display,windows,MagickTrue);
13681   XCheckRefreshWindows(display,windows);
13682   for (i=0; i < (int) number_files; i++)
13683   {
13684     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13685     filelist[i]=DestroyString(filelist[i]);
13686     *read_info->magick='\0';
13687     next_image=ReadImage(read_info,exception);
13688     CatchException(exception);
13689     if (next_image != (Image *) NULL)
13690       {
13691         (void) DeleteImageProperty(next_image,"label");
13692         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13693           read_info,next_image,DefaultTileLabel,exception),exception);
13694         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13695           exception);
13696         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13697           geometry.height,exception);
13698         if (thumbnail_image != (Image *) NULL)
13699           {
13700             next_image=DestroyImage(next_image);
13701             next_image=thumbnail_image;
13702           }
13703         if (backdrop)
13704           {
13705             (void) XDisplayBackgroundImage(display,&background_resources,
13706               next_image,exception);
13707             XSetCursorState(display,windows,MagickTrue);
13708           }
13709         AppendImageToList(&images,next_image);
13710         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13711           {
13712             MagickBooleanType
13713               proceed;
13714
13715             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13716               (MagickSizeType) number_files);
13717             if (proceed == MagickFalse)
13718               break;
13719           }
13720       }
13721   }
13722   filelist=(char **) RelinquishMagickMemory(filelist);
13723   if (images == (Image *) NULL)
13724     {
13725       read_info=DestroyImageInfo(read_info);
13726       XSetCursorState(display,windows,MagickFalse);
13727       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13728       return((Image *) NULL);
13729     }
13730   /*
13731     Create the Visual Image Directory.
13732   */
13733   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13734   montage_info->pointsize=10;
13735   if (resource_info->font != (char *) NULL)
13736     (void) CloneString(&montage_info->font,resource_info->font);
13737   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13738   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13739     images),exception);
13740   images=DestroyImageList(images);
13741   montage_info=DestroyMontageInfo(montage_info);
13742   read_info=DestroyImageInfo(read_info);
13743   XSetCursorState(display,windows,MagickFalse);
13744   if (montage_image == (Image *) NULL)
13745     return(montage_image);
13746   XClientMessage(display,windows->image.id,windows->im_protocols,
13747     windows->im_next_image,CurrentTime);
13748   return(montage_image);
13749 }
13750 \f
13751 /*
13752 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13753 %                                                                             %
13754 %                                                                             %
13755 %                                                                             %
13756 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13757 %                                                                             %
13758 %                                                                             %
13759 %                                                                             %
13760 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13761 %
13762 %  XDisplayBackgroundImage() displays an image in the background of a window.
13763 %
13764 %  The format of the XDisplayBackgroundImage method is:
13765 %
13766 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13767 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13768 %
13769 %  A description of each parameter follows:
13770 %
13771 %    o display: Specifies a connection to an X server;  returned from
13772 %      XOpenDisplay.
13773 %
13774 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13775 %
13776 %    o image: the image.
13777 %
13778 %    o exception: return any errors or warnings in this structure.
13779 %
13780 */
13781 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13782   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13783 {
13784   char
13785     geometry[MaxTextExtent],
13786     visual_type[MaxTextExtent];
13787
13788   int
13789     height,
13790     status,
13791     width;
13792
13793   RectangleInfo
13794     geometry_info;
13795
13796   static XPixelInfo
13797     pixel;
13798
13799   static XStandardColormap
13800     *map_info;
13801
13802   static XVisualInfo
13803     *visual_info = (XVisualInfo *) NULL;
13804
13805   static XWindowInfo
13806     window_info;
13807
13808   size_t
13809     delay;
13810
13811   Window
13812     root_window;
13813
13814   XGCValues
13815     context_values;
13816
13817   XResourceInfo
13818     resources;
13819
13820   XWindowAttributes
13821     window_attributes;
13822
13823   /*
13824     Determine target window.
13825   */
13826   assert(image != (Image *) NULL);
13827   assert(image->signature == MagickSignature);
13828   if (image->debug != MagickFalse)
13829     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13830   resources=(*resource_info);
13831   window_info.id=(Window) NULL;
13832   root_window=XRootWindow(display,XDefaultScreen(display));
13833   if (LocaleCompare(resources.window_id,"root") == 0)
13834     window_info.id=root_window;
13835   else
13836     {
13837       if (isdigit((unsigned char) *resources.window_id) != 0)
13838         window_info.id=XWindowByID(display,root_window,
13839           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13840       if (window_info.id == (Window) NULL)
13841         window_info.id=XWindowByName(display,root_window,resources.window_id);
13842     }
13843   if (window_info.id == (Window) NULL)
13844     {
13845       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13846         resources.window_id);
13847       return(MagickFalse);
13848     }
13849   /*
13850     Determine window visual id.
13851   */
13852   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13853   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13854   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13855   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13856   if (status != 0)
13857     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13858       XVisualIDFromVisual(window_attributes.visual));
13859   if (visual_info == (XVisualInfo *) NULL)
13860     {
13861       /*
13862         Allocate standard colormap.
13863       */
13864       map_info=XAllocStandardColormap();
13865       if (map_info == (XStandardColormap *) NULL)
13866         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13867           image->filename);
13868       map_info->colormap=(Colormap) NULL;
13869       pixel.pixels=(unsigned long *) NULL;
13870       /*
13871         Initialize visual info.
13872       */
13873       resources.map_type=(char *) NULL;
13874       resources.visual_type=visual_type;
13875       visual_info=XBestVisualInfo(display,map_info,&resources);
13876       if (visual_info == (XVisualInfo *) NULL)
13877         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13878           resources.visual_type);
13879       /*
13880         Initialize window info.
13881       */
13882       window_info.ximage=(XImage *) NULL;
13883       window_info.matte_image=(XImage *) NULL;
13884       window_info.pixmap=(Pixmap) NULL;
13885       window_info.matte_pixmap=(Pixmap) NULL;
13886     }
13887   /*
13888     Free previous root colors.
13889   */
13890   if (window_info.id == root_window)
13891     (void) XDestroyWindowColors(display,root_window);
13892   /*
13893     Initialize Standard Colormap.
13894   */
13895   resources.colormap=SharedColormap;
13896   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13897     exception);
13898   /*
13899     Graphic context superclass.
13900   */
13901   context_values.background=pixel.background_color.pixel;
13902   context_values.foreground=pixel.foreground_color.pixel;
13903   pixel.annotate_context=XCreateGC(display,window_info.id,
13904     (size_t) (GCBackground | GCForeground),&context_values);
13905   if (pixel.annotate_context == (GC) NULL)
13906     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13907       image->filename);
13908   /*
13909     Initialize Image window attributes.
13910   */
13911   window_info.name=AcquireString("\0");
13912   window_info.icon_name=AcquireString("\0");
13913   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13914     &resources,&window_info);
13915   /*
13916     Create the X image.
13917   */
13918   window_info.width=(unsigned int) image->columns;
13919   window_info.height=(unsigned int) image->rows;
13920   if ((image->columns != window_info.width) ||
13921       (image->rows != window_info.height))
13922     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13923       image->filename);
13924   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13925     window_attributes.width,window_attributes.height);
13926   geometry_info.width=window_info.width;
13927   geometry_info.height=window_info.height;
13928   geometry_info.x=(ssize_t) window_info.x;
13929   geometry_info.y=(ssize_t) window_info.y;
13930   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13931     &geometry_info.width,&geometry_info.height);
13932   window_info.width=(unsigned int) geometry_info.width;
13933   window_info.height=(unsigned int) geometry_info.height;
13934   window_info.x=(int) geometry_info.x;
13935   window_info.y=(int) geometry_info.y;
13936   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13937     window_info.height,exception);
13938   if (status == MagickFalse)
13939     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13940       image->filename);
13941   window_info.x=0;
13942   window_info.y=0;
13943   if (image->debug != MagickFalse)
13944     {
13945       (void) LogMagickEvent(X11Event,GetMagickModule(),
13946         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13947         (double) image->columns,(double) image->rows);
13948       if (image->colors != 0)
13949         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13950           image->colors);
13951       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13952     }
13953   /*
13954     Adjust image dimensions as specified by backdrop or geometry options.
13955   */
13956   width=(int) window_info.width;
13957   height=(int) window_info.height;
13958   if (resources.backdrop != MagickFalse)
13959     {
13960       /*
13961         Center image on window.
13962       */
13963       window_info.x=(window_attributes.width/2)-
13964         (window_info.ximage->width/2);
13965       window_info.y=(window_attributes.height/2)-
13966         (window_info.ximage->height/2);
13967       width=window_attributes.width;
13968       height=window_attributes.height;
13969     }
13970   if ((resources.image_geometry != (char *) NULL) &&
13971       (*resources.image_geometry != '\0'))
13972     {
13973       char
13974         default_geometry[MaxTextExtent];
13975
13976       int
13977         flags,
13978         gravity;
13979
13980       XSizeHints
13981         *size_hints;
13982
13983       /*
13984         User specified geometry.
13985       */
13986       size_hints=XAllocSizeHints();
13987       if (size_hints == (XSizeHints *) NULL)
13988         ThrowXWindowFatalException(ResourceLimitFatalError,
13989           "MemoryAllocationFailed",image->filename);
13990       size_hints->flags=0L;
13991       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13992         width,height);
13993       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13994         default_geometry,window_info.border_width,size_hints,&window_info.x,
13995         &window_info.y,&width,&height,&gravity);
13996       if (flags & (XValue | YValue))
13997         {
13998           width=window_attributes.width;
13999           height=window_attributes.height;
14000         }
14001       (void) XFree((void *) size_hints);
14002     }
14003   /*
14004     Create the X pixmap.
14005   */
14006   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14007     (unsigned int) height,window_info.depth);
14008   if (window_info.pixmap == (Pixmap) NULL)
14009     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14010       image->filename);
14011   /*
14012     Display pixmap on the window.
14013   */
14014   if (((unsigned int) width > window_info.width) ||
14015       ((unsigned int) height > window_info.height))
14016     (void) XFillRectangle(display,window_info.pixmap,
14017       window_info.annotate_context,0,0,(unsigned int) width,
14018       (unsigned int) height);
14019   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14020     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14021     window_info.width,(unsigned int) window_info.height);
14022   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14023   (void) XClearWindow(display,window_info.id);
14024   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14025   XDelay(display,delay == 0UL ? 10UL : delay);
14026   (void) XSync(display,MagickFalse);
14027   return(window_info.id == root_window ? MagickTrue : MagickFalse);
14028 }
14029 \f
14030 /*
14031 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14032 %                                                                             %
14033 %                                                                             %
14034 %                                                                             %
14035 +   X D i s p l a y I m a g e                                                 %
14036 %                                                                             %
14037 %                                                                             %
14038 %                                                                             %
14039 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14040 %
14041 %  XDisplayImage() displays an image via X11.  A new image is created and
14042 %  returned if the user interactively transforms the displayed image.
14043 %
14044 %  The format of the XDisplayImage method is:
14045 %
14046 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14047 %        char **argv,int argc,Image **image,size_t *state,
14048 %        ExceptionInfo *exception)
14049 %
14050 %  A description of each parameter follows:
14051 %
14052 %    o nexus:  Method XDisplayImage returns an image when the
14053 %      user chooses 'Open Image' from the command menu or picks a tile
14054 %      from the image directory.  Otherwise a null image is returned.
14055 %
14056 %    o display: Specifies a connection to an X server;  returned from
14057 %      XOpenDisplay.
14058 %
14059 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14060 %
14061 %    o argv: Specifies the application's argument list.
14062 %
14063 %    o argc: Specifies the number of arguments.
14064 %
14065 %    o image: Specifies an address to an address of an Image structure;
14066 %
14067 %    o exception: return any errors or warnings in this structure.
14068 %
14069 */
14070 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14071   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14072 {
14073 #define MagnifySize  256  /* must be a power of 2 */
14074 #define MagickMenus  10
14075 #define MagickTitle  "Commands"
14076
14077   static const char
14078     *CommandMenu[] =
14079     {
14080       "File",
14081       "Edit",
14082       "View",
14083       "Transform",
14084       "Enhance",
14085       "Effects",
14086       "F/X",
14087       "Image Edit",
14088       "Miscellany",
14089       "Help",
14090       (char *) NULL
14091     },
14092     *FileMenu[] =
14093     {
14094       "Open...",
14095       "Next",
14096       "Former",
14097       "Select...",
14098       "Save...",
14099       "Print...",
14100       "Delete...",
14101       "New...",
14102       "Visual Directory...",
14103       "Quit",
14104       (char *) NULL
14105     },
14106     *EditMenu[] =
14107     {
14108       "Undo",
14109       "Redo",
14110       "Cut",
14111       "Copy",
14112       "Paste",
14113       (char *) NULL
14114     },
14115     *ViewMenu[] =
14116     {
14117       "Half Size",
14118       "Original Size",
14119       "Double Size",
14120       "Resize...",
14121       "Apply",
14122       "Refresh",
14123       "Restore",
14124       (char *) NULL
14125     },
14126     *TransformMenu[] =
14127     {
14128       "Crop",
14129       "Chop",
14130       "Flop",
14131       "Flip",
14132       "Rotate Right",
14133       "Rotate Left",
14134       "Rotate...",
14135       "Shear...",
14136       "Roll...",
14137       "Trim Edges",
14138       (char *) NULL
14139     },
14140     *EnhanceMenu[] =
14141     {
14142       "Hue...",
14143       "Saturation...",
14144       "Brightness...",
14145       "Gamma...",
14146       "Spiff",
14147       "Dull",
14148       "Contrast Stretch...",
14149       "Sigmoidal Contrast...",
14150       "Normalize",
14151       "Equalize",
14152       "Negate",
14153       "Grayscale",
14154       "Map...",
14155       "Quantize...",
14156       (char *) NULL
14157     },
14158     *EffectsMenu[] =
14159     {
14160       "Despeckle",
14161       "Emboss",
14162       "Reduce Noise",
14163       "Add Noise...",
14164       "Sharpen...",
14165       "Blur...",
14166       "Threshold...",
14167       "Edge Detect...",
14168       "Spread...",
14169       "Shade...",
14170       "Raise...",
14171       "Segment...",
14172       (char *) NULL
14173     },
14174     *FXMenu[] =
14175     {
14176       "Solarize...",
14177       "Sepia Tone...",
14178       "Swirl...",
14179       "Implode...",
14180       "Vignette...",
14181       "Wave...",
14182       "Oil Paint...",
14183       "Charcoal Draw...",
14184       (char *) NULL
14185     },
14186     *ImageEditMenu[] =
14187     {
14188       "Annotate...",
14189       "Draw...",
14190       "Color...",
14191       "Matte...",
14192       "Composite...",
14193       "Add Border...",
14194       "Add Frame...",
14195       "Comment...",
14196       "Launch...",
14197       "Region of Interest...",
14198       (char *) NULL
14199     },
14200     *MiscellanyMenu[] =
14201     {
14202       "Image Info",
14203       "Zoom Image",
14204       "Show Preview...",
14205       "Show Histogram",
14206       "Show Matte",
14207       "Background...",
14208       "Slide Show...",
14209       "Preferences...",
14210       (char *) NULL
14211     },
14212     *HelpMenu[] =
14213     {
14214       "Overview",
14215       "Browse Documentation",
14216       "About Display",
14217       (char *) NULL
14218     },
14219     *ShortCutsMenu[] =
14220     {
14221       "Next",
14222       "Former",
14223       "Open...",
14224       "Save...",
14225       "Print...",
14226       "Undo",
14227       "Restore",
14228       "Image Info",
14229       "Quit",
14230       (char *) NULL
14231     },
14232     *VirtualMenu[] =
14233     {
14234       "Image Info",
14235       "Print",
14236       "Next",
14237       "Quit",
14238       (char *) NULL
14239     };
14240
14241   static const char
14242     **Menus[MagickMenus] =
14243     {
14244       FileMenu,
14245       EditMenu,
14246       ViewMenu,
14247       TransformMenu,
14248       EnhanceMenu,
14249       EffectsMenu,
14250       FXMenu,
14251       ImageEditMenu,
14252       MiscellanyMenu,
14253       HelpMenu
14254     };
14255
14256   static CommandType
14257     CommandMenus[] =
14258     {
14259       NullCommand,
14260       NullCommand,
14261       NullCommand,
14262       NullCommand,
14263       NullCommand,
14264       NullCommand,
14265       NullCommand,
14266       NullCommand,
14267       NullCommand,
14268       NullCommand,
14269     },
14270     FileCommands[] =
14271     {
14272       OpenCommand,
14273       NextCommand,
14274       FormerCommand,
14275       SelectCommand,
14276       SaveCommand,
14277       PrintCommand,
14278       DeleteCommand,
14279       NewCommand,
14280       VisualDirectoryCommand,
14281       QuitCommand
14282     },
14283     EditCommands[] =
14284     {
14285       UndoCommand,
14286       RedoCommand,
14287       CutCommand,
14288       CopyCommand,
14289       PasteCommand
14290     },
14291     ViewCommands[] =
14292     {
14293       HalfSizeCommand,
14294       OriginalSizeCommand,
14295       DoubleSizeCommand,
14296       ResizeCommand,
14297       ApplyCommand,
14298       RefreshCommand,
14299       RestoreCommand
14300     },
14301     TransformCommands[] =
14302     {
14303       CropCommand,
14304       ChopCommand,
14305       FlopCommand,
14306       FlipCommand,
14307       RotateRightCommand,
14308       RotateLeftCommand,
14309       RotateCommand,
14310       ShearCommand,
14311       RollCommand,
14312       TrimCommand
14313     },
14314     EnhanceCommands[] =
14315     {
14316       HueCommand,
14317       SaturationCommand,
14318       BrightnessCommand,
14319       GammaCommand,
14320       SpiffCommand,
14321       DullCommand,
14322       ContrastStretchCommand,
14323       SigmoidalContrastCommand,
14324       NormalizeCommand,
14325       EqualizeCommand,
14326       NegateCommand,
14327       GrayscaleCommand,
14328       MapCommand,
14329       QuantizeCommand
14330     },
14331     EffectsCommands[] =
14332     {
14333       DespeckleCommand,
14334       EmbossCommand,
14335       ReduceNoiseCommand,
14336       AddNoiseCommand,
14337       SharpenCommand,
14338       BlurCommand,
14339       ThresholdCommand,
14340       EdgeDetectCommand,
14341       SpreadCommand,
14342       ShadeCommand,
14343       RaiseCommand,
14344       SegmentCommand
14345     },
14346     FXCommands[] =
14347     {
14348       SolarizeCommand,
14349       SepiaToneCommand,
14350       SwirlCommand,
14351       ImplodeCommand,
14352       VignetteCommand,
14353       WaveCommand,
14354       OilPaintCommand,
14355       CharcoalDrawCommand
14356     },
14357     ImageEditCommands[] =
14358     {
14359       AnnotateCommand,
14360       DrawCommand,
14361       ColorCommand,
14362       MatteCommand,
14363       CompositeCommand,
14364       AddBorderCommand,
14365       AddFrameCommand,
14366       CommentCommand,
14367       LaunchCommand,
14368       RegionofInterestCommand
14369     },
14370     MiscellanyCommands[] =
14371     {
14372       InfoCommand,
14373       ZoomCommand,
14374       ShowPreviewCommand,
14375       ShowHistogramCommand,
14376       ShowMatteCommand,
14377       BackgroundCommand,
14378       SlideShowCommand,
14379       PreferencesCommand
14380     },
14381     HelpCommands[] =
14382     {
14383       HelpCommand,
14384       BrowseDocumentationCommand,
14385       VersionCommand
14386     },
14387     ShortCutsCommands[] =
14388     {
14389       NextCommand,
14390       FormerCommand,
14391       OpenCommand,
14392       SaveCommand,
14393       PrintCommand,
14394       UndoCommand,
14395       RestoreCommand,
14396       InfoCommand,
14397       QuitCommand
14398     },
14399     VirtualCommands[] =
14400     {
14401       InfoCommand,
14402       PrintCommand,
14403       NextCommand,
14404       QuitCommand
14405     };
14406
14407   static CommandType
14408     *Commands[MagickMenus] =
14409     {
14410       FileCommands,
14411       EditCommands,
14412       ViewCommands,
14413       TransformCommands,
14414       EnhanceCommands,
14415       EffectsCommands,
14416       FXCommands,
14417       ImageEditCommands,
14418       MiscellanyCommands,
14419       HelpCommands
14420     };
14421
14422   char
14423     command[MaxTextExtent],
14424     *directory,
14425     geometry[MaxTextExtent],
14426     resource_name[MaxTextExtent];
14427
14428   CommandType
14429     command_type;
14430
14431   Image
14432     *display_image,
14433     *nexus;
14434
14435   int
14436     entry,
14437     id;
14438
14439   KeySym
14440     key_symbol;
14441
14442   MagickStatusType
14443     context_mask,
14444     status;
14445
14446   RectangleInfo
14447     geometry_info;
14448
14449   register int
14450     i;
14451
14452   static char
14453     working_directory[MaxTextExtent];
14454
14455   static XPoint
14456     vid_info;
14457
14458   static XWindowInfo
14459     *magick_windows[MaxXWindows];
14460
14461   static unsigned int
14462     number_windows;
14463
14464   struct stat
14465     attributes;
14466
14467   time_t
14468     timer,
14469     timestamp,
14470     update_time;
14471
14472   unsigned int
14473     height,
14474     width;
14475
14476   size_t
14477     delay;
14478
14479   WarningHandler
14480     warning_handler;
14481
14482   Window
14483     root_window;
14484
14485   XClassHint
14486     *class_hints;
14487
14488   XEvent
14489     event;
14490
14491   XFontStruct
14492     *font_info;
14493
14494   XGCValues
14495     context_values;
14496
14497   XPixelInfo
14498     *icon_pixel,
14499     *pixel;
14500
14501   XResourceInfo
14502     *icon_resources;
14503
14504   XStandardColormap
14505     *icon_map,
14506     *map_info;
14507
14508   XVisualInfo
14509     *icon_visual,
14510     *visual_info;
14511
14512   XWindowChanges
14513     window_changes;
14514
14515   XWindows
14516     *windows;
14517
14518   XWMHints
14519     *manager_hints;
14520
14521   assert(image != (Image **) NULL);
14522   assert((*image)->signature == MagickSignature);
14523   if ((*image)->debug != MagickFalse)
14524     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14525   display_image=(*image);
14526   warning_handler=(WarningHandler) NULL;
14527   windows=XSetWindows((XWindows *) ~0);
14528   if (windows != (XWindows *) NULL)
14529     {
14530       int
14531         status;
14532
14533       status=chdir(working_directory);
14534       if (status == -1)
14535         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14536           "UnableToOpenFile","%s",working_directory);
14537       warning_handler=resource_info->display_warnings ?
14538         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14539       warning_handler=resource_info->display_warnings ?
14540         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14541     }
14542   else
14543     {
14544       /*
14545         Allocate windows structure.
14546       */
14547       resource_info->colors=display_image->colors;
14548       windows=XSetWindows(XInitializeWindows(display,resource_info));
14549       if (windows == (XWindows *) NULL)
14550         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14551           (*image)->filename);
14552       /*
14553         Initialize window id's.
14554       */
14555       number_windows=0;
14556       magick_windows[number_windows++]=(&windows->icon);
14557       magick_windows[number_windows++]=(&windows->backdrop);
14558       magick_windows[number_windows++]=(&windows->image);
14559       magick_windows[number_windows++]=(&windows->info);
14560       magick_windows[number_windows++]=(&windows->command);
14561       magick_windows[number_windows++]=(&windows->widget);
14562       magick_windows[number_windows++]=(&windows->popup);
14563       magick_windows[number_windows++]=(&windows->magnify);
14564       magick_windows[number_windows++]=(&windows->pan);
14565       for (i=0; i < (int) number_windows; i++)
14566         magick_windows[i]->id=(Window) NULL;
14567       vid_info.x=0;
14568       vid_info.y=0;
14569     }
14570   /*
14571     Initialize font info.
14572   */
14573   if (windows->font_info != (XFontStruct *) NULL)
14574     (void) XFreeFont(display,windows->font_info);
14575   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14576   if (windows->font_info == (XFontStruct *) NULL)
14577     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14578       resource_info->font);
14579   /*
14580     Initialize Standard Colormap.
14581   */
14582   map_info=windows->map_info;
14583   icon_map=windows->icon_map;
14584   visual_info=windows->visual_info;
14585   icon_visual=windows->icon_visual;
14586   pixel=windows->pixel_info;
14587   icon_pixel=windows->icon_pixel;
14588   font_info=windows->font_info;
14589   icon_resources=windows->icon_resources;
14590   class_hints=windows->class_hints;
14591   manager_hints=windows->manager_hints;
14592   root_window=XRootWindow(display,visual_info->screen);
14593   nexus=NewImageList();
14594   if (display_image->debug != MagickFalse)
14595     {
14596       (void) LogMagickEvent(X11Event,GetMagickModule(),
14597         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14598         (double) display_image->scene,(double) display_image->columns,
14599         (double) display_image->rows);
14600       if (display_image->colors != 0)
14601         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14602           display_image->colors);
14603       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14604         display_image->magick);
14605     }
14606   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14607     map_info,pixel,exception);
14608   display_image->taint=MagickFalse;
14609   /*
14610     Initialize graphic context.
14611   */
14612   windows->context.id=(Window) NULL;
14613   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14614     resource_info,&windows->context);
14615   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14616   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14617   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14618   manager_hints->flags=InputHint | StateHint;
14619   manager_hints->input=MagickFalse;
14620   manager_hints->initial_state=WithdrawnState;
14621   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14622     &windows->context);
14623   if (display_image->debug != MagickFalse)
14624     (void) LogMagickEvent(X11Event,GetMagickModule(),
14625       "Window id: 0x%lx (context)",windows->context.id);
14626   context_values.background=pixel->background_color.pixel;
14627   context_values.font=font_info->fid;
14628   context_values.foreground=pixel->foreground_color.pixel;
14629   context_values.graphics_exposures=MagickFalse;
14630   context_mask=(MagickStatusType)
14631     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14632   if (pixel->annotate_context != (GC) NULL)
14633     (void) XFreeGC(display,pixel->annotate_context);
14634   pixel->annotate_context=XCreateGC(display,windows->context.id,
14635     context_mask,&context_values);
14636   if (pixel->annotate_context == (GC) NULL)
14637     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14638       display_image->filename);
14639   context_values.background=pixel->depth_color.pixel;
14640   if (pixel->widget_context != (GC) NULL)
14641     (void) XFreeGC(display,pixel->widget_context);
14642   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14643     &context_values);
14644   if (pixel->widget_context == (GC) NULL)
14645     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14646       display_image->filename);
14647   context_values.background=pixel->foreground_color.pixel;
14648   context_values.foreground=pixel->background_color.pixel;
14649   context_values.plane_mask=context_values.background ^
14650     context_values.foreground;
14651   if (pixel->highlight_context != (GC) NULL)
14652     (void) XFreeGC(display,pixel->highlight_context);
14653   pixel->highlight_context=XCreateGC(display,windows->context.id,
14654     (size_t) (context_mask | GCPlaneMask),&context_values);
14655   if (pixel->highlight_context == (GC) NULL)
14656     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14657       display_image->filename);
14658   (void) XDestroyWindow(display,windows->context.id);
14659   /*
14660     Initialize icon window.
14661   */
14662   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14663     icon_resources,&windows->icon);
14664   windows->icon.geometry=resource_info->icon_geometry;
14665   XBestIconSize(display,&windows->icon,display_image);
14666   windows->icon.attributes.colormap=XDefaultColormap(display,
14667     icon_visual->screen);
14668   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14669   manager_hints->flags=InputHint | StateHint;
14670   manager_hints->input=MagickFalse;
14671   manager_hints->initial_state=IconicState;
14672   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14673     &windows->icon);
14674   if (display_image->debug != MagickFalse)
14675     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14676       windows->icon.id);
14677   /*
14678     Initialize graphic context for icon window.
14679   */
14680   if (icon_pixel->annotate_context != (GC) NULL)
14681     (void) XFreeGC(display,icon_pixel->annotate_context);
14682   context_values.background=icon_pixel->background_color.pixel;
14683   context_values.foreground=icon_pixel->foreground_color.pixel;
14684   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14685     (size_t) (GCBackground | GCForeground),&context_values);
14686   if (icon_pixel->annotate_context == (GC) NULL)
14687     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14688       display_image->filename);
14689   windows->icon.annotate_context=icon_pixel->annotate_context;
14690   /*
14691     Initialize Image window.
14692   */
14693   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14694     &windows->image);
14695   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14696   if (resource_info->use_shared_memory == MagickFalse)
14697     windows->image.shared_memory=MagickFalse;
14698   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14699     {
14700       char
14701         *title;
14702
14703       title=InterpretImageProperties(resource_info->image_info,display_image,
14704         resource_info->title,exception);
14705       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14706       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14707       title=DestroyString(title);
14708     }
14709   else
14710     {
14711       char
14712         filename[MaxTextExtent];
14713
14714       /*
14715         Window name is the base of the filename.
14716       */
14717       GetPathComponent(display_image->magick_filename,TailPath,filename);
14718       if (display_image->scene == 0)
14719         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14720           "%s: %s",MagickPackageName,filename);
14721       else
14722         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14723           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14724           (double) display_image->scene,(double) GetImageListLength(
14725           display_image));
14726       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14727     }
14728   if (resource_info->immutable)
14729     windows->image.immutable=MagickTrue;
14730   windows->image.use_pixmap=resource_info->use_pixmap;
14731   windows->image.geometry=resource_info->image_geometry;
14732   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14733     XDisplayWidth(display,visual_info->screen),
14734     XDisplayHeight(display,visual_info->screen));
14735   geometry_info.width=display_image->columns;
14736   geometry_info.height=display_image->rows;
14737   geometry_info.x=0;
14738   geometry_info.y=0;
14739   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14740     &geometry_info.width,&geometry_info.height);
14741   windows->image.width=(unsigned int) geometry_info.width;
14742   windows->image.height=(unsigned int) geometry_info.height;
14743   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14744     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14745     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14746     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14747   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14748     resource_info,&windows->backdrop);
14749   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14750     {
14751       /*
14752         Initialize backdrop window.
14753       */
14754       windows->backdrop.x=0;
14755       windows->backdrop.y=0;
14756       (void) CloneString(&windows->backdrop.name,"Backdrop");
14757       windows->backdrop.flags=(size_t) (USSize | USPosition);
14758       windows->backdrop.width=(unsigned int)
14759         XDisplayWidth(display,visual_info->screen);
14760       windows->backdrop.height=(unsigned int)
14761         XDisplayHeight(display,visual_info->screen);
14762       windows->backdrop.border_width=0;
14763       windows->backdrop.immutable=MagickTrue;
14764       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14765         ButtonReleaseMask;
14766       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14767         StructureNotifyMask;
14768       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14769       manager_hints->icon_window=windows->icon.id;
14770       manager_hints->input=MagickTrue;
14771       manager_hints->initial_state=resource_info->iconic ? IconicState :
14772         NormalState;
14773       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14774         &windows->backdrop);
14775       if (display_image->debug != MagickFalse)
14776         (void) LogMagickEvent(X11Event,GetMagickModule(),
14777           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14778       (void) XMapWindow(display,windows->backdrop.id);
14779       (void) XClearWindow(display,windows->backdrop.id);
14780       if (windows->image.id != (Window) NULL)
14781         {
14782           (void) XDestroyWindow(display,windows->image.id);
14783           windows->image.id=(Window) NULL;
14784         }
14785       /*
14786         Position image in the center the backdrop.
14787       */
14788       windows->image.flags|=USPosition;
14789       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14790         (windows->image.width/2);
14791       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14792         (windows->image.height/2);
14793     }
14794   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14795   manager_hints->icon_window=windows->icon.id;
14796   manager_hints->input=MagickTrue;
14797   manager_hints->initial_state=resource_info->iconic ? IconicState :
14798     NormalState;
14799   if (windows->group_leader.id != (Window) NULL)
14800     {
14801       /*
14802         Follow the leader.
14803       */
14804       manager_hints->flags|=WindowGroupHint;
14805       manager_hints->window_group=windows->group_leader.id;
14806       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14807       if (display_image->debug != MagickFalse)
14808         (void) LogMagickEvent(X11Event,GetMagickModule(),
14809           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14810     }
14811   XMakeWindow(display,
14812     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14813     argv,argc,class_hints,manager_hints,&windows->image);
14814   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14815     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14816   if (windows->group_leader.id != (Window) NULL)
14817     (void) XSetTransientForHint(display,windows->image.id,
14818       windows->group_leader.id);
14819   if (display_image->debug != MagickFalse)
14820     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14821       windows->image.id);
14822   /*
14823     Initialize Info widget.
14824   */
14825   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14826     &windows->info);
14827   (void) CloneString(&windows->info.name,"Info");
14828   (void) CloneString(&windows->info.icon_name,"Info");
14829   windows->info.border_width=1;
14830   windows->info.x=2;
14831   windows->info.y=2;
14832   windows->info.flags|=PPosition;
14833   windows->info.attributes.win_gravity=UnmapGravity;
14834   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14835     StructureNotifyMask;
14836   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14837   manager_hints->input=MagickFalse;
14838   manager_hints->initial_state=NormalState;
14839   manager_hints->window_group=windows->image.id;
14840   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14841     &windows->info);
14842   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14843     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14844   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14845     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14846   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14847   if (windows->image.mapped != MagickFalse)
14848     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14849   if (display_image->debug != MagickFalse)
14850     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14851       windows->info.id);
14852   /*
14853     Initialize Command widget.
14854   */
14855   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14856     resource_info,&windows->command);
14857   windows->command.data=MagickMenus;
14858   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14859   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14860     resource_info->client_name);
14861   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14862     resource_name,"geometry",(char *) NULL);
14863   (void) CloneString(&windows->command.name,MagickTitle);
14864   windows->command.border_width=0;
14865   windows->command.flags|=PPosition;
14866   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14867     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14868     OwnerGrabButtonMask | StructureNotifyMask;
14869   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14870   manager_hints->input=MagickTrue;
14871   manager_hints->initial_state=NormalState;
14872   manager_hints->window_group=windows->image.id;
14873   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14874     &windows->command);
14875   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14876     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14877     HighlightHeight);
14878   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14879     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14880   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14881   if (windows->command.mapped != MagickFalse)
14882     (void) XMapRaised(display,windows->command.id);
14883   if (display_image->debug != MagickFalse)
14884     (void) LogMagickEvent(X11Event,GetMagickModule(),
14885       "Window id: 0x%lx (command)",windows->command.id);
14886   /*
14887     Initialize Widget window.
14888   */
14889   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14890     resource_info,&windows->widget);
14891   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14892     resource_info->client_name);
14893   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14894     resource_name,"geometry",(char *) NULL);
14895   windows->widget.border_width=0;
14896   windows->widget.flags|=PPosition;
14897   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14898     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14899     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14900     StructureNotifyMask;
14901   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14902   manager_hints->input=MagickTrue;
14903   manager_hints->initial_state=NormalState;
14904   manager_hints->window_group=windows->image.id;
14905   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14906     &windows->widget);
14907   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14908     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14909   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14910     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14911   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14912   if (display_image->debug != MagickFalse)
14913     (void) LogMagickEvent(X11Event,GetMagickModule(),
14914       "Window id: 0x%lx (widget)",windows->widget.id);
14915   /*
14916     Initialize popup window.
14917   */
14918   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14919     resource_info,&windows->popup);
14920   windows->popup.border_width=0;
14921   windows->popup.flags|=PPosition;
14922   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14923     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14924     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14925   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14926   manager_hints->input=MagickTrue;
14927   manager_hints->initial_state=NormalState;
14928   manager_hints->window_group=windows->image.id;
14929   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14930     &windows->popup);
14931   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14932     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14933   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14934     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14935   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14936   if (display_image->debug != MagickFalse)
14937     (void) LogMagickEvent(X11Event,GetMagickModule(),
14938       "Window id: 0x%lx (pop up)",windows->popup.id);
14939   /*
14940     Initialize Magnify window and cursor.
14941   */
14942   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14943     resource_info,&windows->magnify);
14944   if (resource_info->use_shared_memory == MagickFalse)
14945     windows->magnify.shared_memory=MagickFalse;
14946   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14947     resource_info->client_name);
14948   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14949     resource_name,"geometry",(char *) NULL);
14950   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14951     resource_info->magnify);
14952   if (windows->magnify.cursor != (Cursor) NULL)
14953     (void) XFreeCursor(display,windows->magnify.cursor);
14954   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14955     map_info->colormap,resource_info->background_color,
14956     resource_info->foreground_color);
14957   if (windows->magnify.cursor == (Cursor) NULL)
14958     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14959       display_image->filename);
14960   windows->magnify.width=MagnifySize;
14961   windows->magnify.height=MagnifySize;
14962   windows->magnify.flags|=PPosition;
14963   windows->magnify.min_width=MagnifySize;
14964   windows->magnify.min_height=MagnifySize;
14965   windows->magnify.width_inc=MagnifySize;
14966   windows->magnify.height_inc=MagnifySize;
14967   windows->magnify.data=resource_info->magnify;
14968   windows->magnify.attributes.cursor=windows->magnify.cursor;
14969   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14970     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14971     StructureNotifyMask;
14972   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14973   manager_hints->input=MagickTrue;
14974   manager_hints->initial_state=NormalState;
14975   manager_hints->window_group=windows->image.id;
14976   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14977     &windows->magnify);
14978   if (display_image->debug != MagickFalse)
14979     (void) LogMagickEvent(X11Event,GetMagickModule(),
14980       "Window id: 0x%lx (magnify)",windows->magnify.id);
14981   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14982   /*
14983     Initialize panning window.
14984   */
14985   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14986     resource_info,&windows->pan);
14987   (void) CloneString(&windows->pan.name,"Pan Icon");
14988   windows->pan.width=windows->icon.width;
14989   windows->pan.height=windows->icon.height;
14990   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14991     resource_info->client_name);
14992   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14993     resource_name,"geometry",(char *) NULL);
14994   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14995     &windows->pan.width,&windows->pan.height);
14996   windows->pan.flags|=PPosition;
14997   windows->pan.immutable=MagickTrue;
14998   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14999     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15000     StructureNotifyMask;
15001   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15002   manager_hints->input=MagickFalse;
15003   manager_hints->initial_state=NormalState;
15004   manager_hints->window_group=windows->image.id;
15005   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15006     &windows->pan);
15007   if (display_image->debug != MagickFalse)
15008     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15009       windows->pan.id);
15010   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15011   if (windows->info.mapped != MagickFalse)
15012     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15013   if ((windows->image.mapped == MagickFalse) ||
15014       (windows->backdrop.id != (Window) NULL))
15015     (void) XMapWindow(display,windows->image.id);
15016   /*
15017     Set our progress monitor and warning handlers.
15018   */
15019   if (warning_handler == (WarningHandler) NULL)
15020     {
15021       warning_handler=resource_info->display_warnings ?
15022         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15023       warning_handler=resource_info->display_warnings ?
15024         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15025     }
15026   /*
15027     Initialize Image and Magnify X images.
15028   */
15029   windows->image.x=0;
15030   windows->image.y=0;
15031   windows->magnify.shape=MagickFalse;
15032   width=(unsigned int) display_image->columns;
15033   height=(unsigned int) display_image->rows;
15034   if ((display_image->columns != width) || (display_image->rows != height))
15035     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15036       display_image->filename);
15037   status=XMakeImage(display,resource_info,&windows->image,display_image,
15038     width,height,exception);
15039   if (status == MagickFalse)
15040     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15041       display_image->filename);
15042   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15043     windows->magnify.width,windows->magnify.height,exception);
15044   if (status == MagickFalse)
15045     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15046       display_image->filename);
15047   if (windows->magnify.mapped != MagickFalse)
15048     (void) XMapRaised(display,windows->magnify.id);
15049   if (windows->pan.mapped != MagickFalse)
15050     (void) XMapRaised(display,windows->pan.id);
15051   windows->image.window_changes.width=(int) display_image->columns;
15052   windows->image.window_changes.height=(int) display_image->rows;
15053   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15054   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15055   (void) XSync(display,MagickFalse);
15056   /*
15057     Respond to events.
15058   */
15059   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15060   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15061   update_time=0;
15062   if (resource_info->update != MagickFalse)
15063     {
15064       MagickBooleanType
15065         status;
15066
15067       /*
15068         Determine when file data was last modified.
15069       */
15070       status=GetPathAttributes(display_image->filename,&attributes);
15071       if (status != MagickFalse)
15072         update_time=attributes.st_mtime;
15073     }
15074   *state&=(~FormerImageState);
15075   *state&=(~MontageImageState);
15076   *state&=(~NextImageState);
15077   do
15078   {
15079     /*
15080       Handle a window event.
15081     */
15082     if (windows->image.mapped != MagickFalse)
15083       if ((display_image->delay != 0) || (resource_info->update != 0))
15084         {
15085           if (timer < time((time_t *) NULL))
15086             {
15087               if (resource_info->update == MagickFalse)
15088                 *state|=NextImageState | ExitState;
15089               else
15090                 {
15091                   MagickBooleanType
15092                     status;
15093
15094                   /*
15095                     Determine if image file was modified.
15096                   */
15097                   status=GetPathAttributes(display_image->filename,&attributes);
15098                   if (status != MagickFalse)
15099                     if (update_time != attributes.st_mtime)
15100                       {
15101                         /*
15102                           Redisplay image.
15103                         */
15104                         (void) FormatLocaleString(
15105                           resource_info->image_info->filename,MaxTextExtent,
15106                           "%s:%s",display_image->magick,
15107                           display_image->filename);
15108                         nexus=ReadImage(resource_info->image_info,exception);
15109                         if (nexus != (Image *) NULL)
15110                           {
15111                             nexus=DestroyImage(nexus);
15112                             *state|=NextImageState | ExitState;
15113                           }
15114                       }
15115                   delay=display_image->delay/MagickMax(
15116                     display_image->ticks_per_second,1L);
15117                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15118                 }
15119             }
15120           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15121             {
15122               /*
15123                 Do not block if delay > 0.
15124               */
15125               XDelay(display,SuspendTime << 2);
15126               continue;
15127             }
15128         }
15129     timestamp=time((time_t *) NULL);
15130     (void) XNextEvent(display,&event);
15131     if (windows->image.stasis == MagickFalse)
15132       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15133         MagickTrue : MagickFalse;
15134     if (windows->magnify.stasis == MagickFalse)
15135       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15136         MagickTrue : MagickFalse;
15137     if (event.xany.window == windows->command.id)
15138       {
15139         /*
15140           Select a command from the Command widget.
15141         */
15142         id=XCommandWidget(display,windows,CommandMenu,&event);
15143         if (id < 0)
15144           continue;
15145         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15146         command_type=CommandMenus[id];
15147         if (id < MagickMenus)
15148           {
15149             /*
15150               Select a command from a pop-up menu.
15151             */
15152             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15153               command);
15154             if (entry < 0)
15155               continue;
15156             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15157             command_type=Commands[id][entry];
15158           }
15159         if (command_type != NullCommand)
15160           nexus=XMagickCommand(display,resource_info,windows,command_type,
15161             &display_image,exception);
15162         continue;
15163       }
15164     switch (event.type)
15165     {
15166       case ButtonPress:
15167       {
15168         if (display_image->debug != MagickFalse)
15169           (void) LogMagickEvent(X11Event,GetMagickModule(),
15170             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15171             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15172         if ((event.xbutton.button == Button3) &&
15173             (event.xbutton.state & Mod1Mask))
15174           {
15175             /*
15176               Convert Alt-Button3 to Button2.
15177             */
15178             event.xbutton.button=Button2;
15179             event.xbutton.state&=(~Mod1Mask);
15180           }
15181         if (event.xbutton.window == windows->backdrop.id)
15182           {
15183             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15184               event.xbutton.time);
15185             break;
15186           }
15187         if (event.xbutton.window == windows->image.id)
15188           {
15189             switch (event.xbutton.button)
15190             {
15191               case Button1:
15192               {
15193                 if (resource_info->immutable)
15194                   {
15195                     /*
15196                       Select a command from the Virtual menu.
15197                     */
15198                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15199                       command);
15200                     if (entry >= 0)
15201                       nexus=XMagickCommand(display,resource_info,windows,
15202                         VirtualCommands[entry],&display_image,exception);
15203                     break;
15204                   }
15205                 /*
15206                   Map/unmap Command widget.
15207                 */
15208                 if (windows->command.mapped != MagickFalse)
15209                   (void) XWithdrawWindow(display,windows->command.id,
15210                     windows->command.screen);
15211                 else
15212                   {
15213                     (void) XCommandWidget(display,windows,CommandMenu,
15214                       (XEvent *) NULL);
15215                     (void) XMapRaised(display,windows->command.id);
15216                   }
15217                 break;
15218               }
15219               case Button2:
15220               {
15221                 /*
15222                   User pressed the image magnify button.
15223                 */
15224                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15225                   &display_image,exception);
15226                 XMagnifyImage(display,windows,&event,exception);
15227                 break;
15228               }
15229               case Button3:
15230               {
15231                 if (resource_info->immutable)
15232                   {
15233                     /*
15234                       Select a command from the Virtual menu.
15235                     */
15236                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15237                       command);
15238                     if (entry >= 0)
15239                       nexus=XMagickCommand(display,resource_info,windows,
15240                         VirtualCommands[entry],&display_image,exception);
15241                     break;
15242                   }
15243                 if (display_image->montage != (char *) NULL)
15244                   {
15245                     /*
15246                       Open or delete a tile from a visual image directory.
15247                     */
15248                     nexus=XTileImage(display,resource_info,windows,
15249                       display_image,&event,exception);
15250                     if (nexus != (Image *) NULL)
15251                       *state|=MontageImageState | NextImageState | ExitState;
15252                     vid_info.x=(short int) windows->image.x;
15253                     vid_info.y=(short int) windows->image.y;
15254                     break;
15255                   }
15256                 /*
15257                   Select a command from the Short Cuts menu.
15258                 */
15259                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15260                   command);
15261                 if (entry >= 0)
15262                   nexus=XMagickCommand(display,resource_info,windows,
15263                     ShortCutsCommands[entry],&display_image,exception);
15264                 break;
15265               }
15266               case Button4:
15267               {
15268                 /*
15269                   Wheel up.
15270                 */
15271                 XTranslateImage(display,windows,*image,XK_Up);
15272                 break;
15273               }
15274               case Button5:
15275               {
15276                 /*
15277                   Wheel down.
15278                 */
15279                 XTranslateImage(display,windows,*image,XK_Down);
15280                 break;
15281               }
15282               default:
15283                 break;
15284             }
15285             break;
15286           }
15287         if (event.xbutton.window == windows->magnify.id)
15288           {
15289             int
15290               factor;
15291
15292             static const char
15293               *MagnifyMenu[] =
15294               {
15295                 "2",
15296                 "4",
15297                 "5",
15298                 "6",
15299                 "7",
15300                 "8",
15301                 "9",
15302                 "3",
15303                 (char *) NULL,
15304               };
15305
15306             static KeySym
15307               MagnifyCommands[] =
15308               {
15309                 XK_2,
15310                 XK_4,
15311                 XK_5,
15312                 XK_6,
15313                 XK_7,
15314                 XK_8,
15315                 XK_9,
15316                 XK_3
15317               };
15318
15319             /*
15320               Select a magnify factor from the pop-up menu.
15321             */
15322             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15323             if (factor >= 0)
15324               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15325                 exception);
15326             break;
15327           }
15328         if (event.xbutton.window == windows->pan.id)
15329           {
15330             switch (event.xbutton.button)
15331             {
15332               case Button4:
15333               {
15334                 /*
15335                   Wheel up.
15336                 */
15337                 XTranslateImage(display,windows,*image,XK_Up);
15338                 break;
15339               }
15340               case Button5:
15341               {
15342                 /*
15343                   Wheel down.
15344                 */
15345                 XTranslateImage(display,windows,*image,XK_Down);
15346                 break;
15347               }
15348               default:
15349               {
15350                 XPanImage(display,windows,&event,exception);
15351                 break;
15352               }
15353             }
15354             break;
15355           }
15356         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15357           1L);
15358         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15359         break;
15360       }
15361       case ButtonRelease:
15362       {
15363         if (display_image->debug != MagickFalse)
15364           (void) LogMagickEvent(X11Event,GetMagickModule(),
15365             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15366             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15367         break;
15368       }
15369       case ClientMessage:
15370       {
15371         if (display_image->debug != MagickFalse)
15372           (void) LogMagickEvent(X11Event,GetMagickModule(),
15373             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15374             event.xclient.message_type,event.xclient.format,(unsigned long)
15375             event.xclient.data.l[0]);
15376         if (event.xclient.message_type == windows->im_protocols)
15377           {
15378             if (*event.xclient.data.l == (long) windows->im_update_widget)
15379               {
15380                 (void) CloneString(&windows->command.name,MagickTitle);
15381                 windows->command.data=MagickMenus;
15382                 (void) XCommandWidget(display,windows,CommandMenu,
15383                   (XEvent *) NULL);
15384                 break;
15385               }
15386             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15387               {
15388                 /*
15389                   Update graphic context and window colormap.
15390                 */
15391                 for (i=0; i < (int) number_windows; i++)
15392                 {
15393                   if (magick_windows[i]->id == windows->icon.id)
15394                     continue;
15395                   context_values.background=pixel->background_color.pixel;
15396                   context_values.foreground=pixel->foreground_color.pixel;
15397                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15398                     context_mask,&context_values);
15399                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15400                     context_mask,&context_values);
15401                   context_values.background=pixel->foreground_color.pixel;
15402                   context_values.foreground=pixel->background_color.pixel;
15403                   context_values.plane_mask=context_values.background ^
15404                     context_values.foreground;
15405                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15406                     (size_t) (context_mask | GCPlaneMask),
15407                     &context_values);
15408                   magick_windows[i]->attributes.background_pixel=
15409                     pixel->background_color.pixel;
15410                   magick_windows[i]->attributes.border_pixel=
15411                     pixel->border_color.pixel;
15412                   magick_windows[i]->attributes.colormap=map_info->colormap;
15413                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15414                     (unsigned long) magick_windows[i]->mask,
15415                     &magick_windows[i]->attributes);
15416                 }
15417                 if (windows->pan.mapped != MagickFalse)
15418                   {
15419                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15420                       windows->pan.pixmap);
15421                     (void) XClearWindow(display,windows->pan.id);
15422                     XDrawPanRectangle(display,windows);
15423                   }
15424                 if (windows->backdrop.id != (Window) NULL)
15425                   (void) XInstallColormap(display,map_info->colormap);
15426                 break;
15427               }
15428             if (*event.xclient.data.l == (long) windows->im_former_image)
15429               {
15430                 *state|=FormerImageState | ExitState;
15431                 break;
15432               }
15433             if (*event.xclient.data.l == (long) windows->im_next_image)
15434               {
15435                 *state|=NextImageState | ExitState;
15436                 break;
15437               }
15438             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15439               {
15440                 *state|=RetainColorsState;
15441                 break;
15442               }
15443             if (*event.xclient.data.l == (long) windows->im_exit)
15444               {
15445                 *state|=ExitState;
15446                 break;
15447               }
15448             break;
15449           }
15450         if (event.xclient.message_type == windows->dnd_protocols)
15451           {
15452             Atom
15453               selection,
15454               type;
15455
15456             int
15457               format,
15458               status;
15459
15460             unsigned char
15461               *data;
15462
15463             unsigned long
15464               after,
15465               length;
15466
15467             /*
15468               Display image named by the Drag-and-Drop selection.
15469             */
15470             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15471               break;
15472             selection=XInternAtom(display,"DndSelection",MagickFalse);
15473             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15474               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15475               &length,&after,&data);
15476             if ((status != Success) || (length == 0))
15477               break;
15478             if (*event.xclient.data.l == 2)
15479               {
15480                 /*
15481                   Offix DND.
15482                 */
15483                 (void) CopyMagickString(resource_info->image_info->filename,
15484                   (char *) data,MaxTextExtent);
15485               }
15486             else
15487               {
15488                 /*
15489                   XDND.
15490                 */
15491                 if (strncmp((char *) data, "file:", 5) != 0)
15492                   {
15493                     (void) XFree((void *) data);
15494                     break;
15495                   }
15496                 (void) CopyMagickString(resource_info->image_info->filename,
15497                   ((char *) data)+5,MaxTextExtent);
15498               }
15499             nexus=ReadImage(resource_info->image_info,exception);
15500             CatchException(exception);
15501             if (nexus != (Image *) NULL)
15502               *state|=NextImageState | ExitState;
15503             (void) XFree((void *) data);
15504             break;
15505           }
15506         /*
15507           If client window delete message, exit.
15508         */
15509         if (event.xclient.message_type != windows->wm_protocols)
15510           break;
15511         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15512           break;
15513         (void) XWithdrawWindow(display,event.xclient.window,
15514           visual_info->screen);
15515         if (event.xclient.window == windows->image.id)
15516           {
15517             *state|=ExitState;
15518             break;
15519           }
15520         if (event.xclient.window == windows->pan.id)
15521           {
15522             /*
15523               Restore original image size when pan window is deleted.
15524             */
15525             windows->image.window_changes.width=windows->image.ximage->width;
15526             windows->image.window_changes.height=windows->image.ximage->height;
15527             (void) XConfigureImage(display,resource_info,windows,
15528               display_image,exception);
15529           }
15530         break;
15531       }
15532       case ConfigureNotify:
15533       {
15534         if (display_image->debug != MagickFalse)
15535           (void) LogMagickEvent(X11Event,GetMagickModule(),
15536             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15537             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15538             event.xconfigure.y,event.xconfigure.send_event);
15539         if (event.xconfigure.window == windows->image.id)
15540           {
15541             /*
15542               Image window has a new configuration.
15543             */
15544             if (event.xconfigure.send_event != 0)
15545               {
15546                 XWindowChanges
15547                   window_changes;
15548
15549                 /*
15550                   Position the transient windows relative of the Image window.
15551                 */
15552                 if (windows->command.geometry == (char *) NULL)
15553                   if (windows->command.mapped == MagickFalse)
15554                     {
15555                       windows->command.x=event.xconfigure.x-
15556                         windows->command.width-25;
15557                       windows->command.y=event.xconfigure.y;
15558                       XConstrainWindowPosition(display,&windows->command);
15559                       window_changes.x=windows->command.x;
15560                       window_changes.y=windows->command.y;
15561                       (void) XReconfigureWMWindow(display,windows->command.id,
15562                         windows->command.screen,(unsigned int) (CWX | CWY),
15563                         &window_changes);
15564                     }
15565                 if (windows->widget.geometry == (char *) NULL)
15566                   if (windows->widget.mapped == MagickFalse)
15567                     {
15568                       windows->widget.x=event.xconfigure.x+
15569                         event.xconfigure.width/10;
15570                       windows->widget.y=event.xconfigure.y+
15571                         event.xconfigure.height/10;
15572                       XConstrainWindowPosition(display,&windows->widget);
15573                       window_changes.x=windows->widget.x;
15574                       window_changes.y=windows->widget.y;
15575                       (void) XReconfigureWMWindow(display,windows->widget.id,
15576                         windows->widget.screen,(unsigned int) (CWX | CWY),
15577                         &window_changes);
15578                     }
15579                 if (windows->magnify.geometry == (char *) NULL)
15580                   if (windows->magnify.mapped == MagickFalse)
15581                     {
15582                       windows->magnify.x=event.xconfigure.x+
15583                         event.xconfigure.width+25;
15584                       windows->magnify.y=event.xconfigure.y;
15585                       XConstrainWindowPosition(display,&windows->magnify);
15586                       window_changes.x=windows->magnify.x;
15587                       window_changes.y=windows->magnify.y;
15588                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15589                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15590                         &window_changes);
15591                     }
15592                 if (windows->pan.geometry == (char *) NULL)
15593                   if (windows->pan.mapped == MagickFalse)
15594                     {
15595                       windows->pan.x=event.xconfigure.x+
15596                         event.xconfigure.width+25;
15597                       windows->pan.y=event.xconfigure.y+
15598                         windows->magnify.height+50;
15599                       XConstrainWindowPosition(display,&windows->pan);
15600                       window_changes.x=windows->pan.x;
15601                       window_changes.y=windows->pan.y;
15602                       (void) XReconfigureWMWindow(display,windows->pan.id,
15603                         windows->pan.screen,(unsigned int) (CWX | CWY),
15604                         &window_changes);
15605                     }
15606               }
15607             if ((event.xconfigure.width == (int) windows->image.width) &&
15608                 (event.xconfigure.height == (int) windows->image.height))
15609               break;
15610             windows->image.width=(unsigned int) event.xconfigure.width;
15611             windows->image.height=(unsigned int) event.xconfigure.height;
15612             windows->image.x=0;
15613             windows->image.y=0;
15614             if (display_image->montage != (char *) NULL)
15615               {
15616                 windows->image.x=vid_info.x;
15617                 windows->image.y=vid_info.y;
15618               }
15619             if ((windows->image.mapped != MagickFalse) &&
15620                 (windows->image.stasis != MagickFalse))
15621               {
15622                 /*
15623                   Update image window configuration.
15624                 */
15625                 windows->image.window_changes.width=event.xconfigure.width;
15626                 windows->image.window_changes.height=event.xconfigure.height;
15627                 (void) XConfigureImage(display,resource_info,windows,
15628                   display_image,exception);
15629               }
15630             /*
15631               Update pan window configuration.
15632             */
15633             if ((event.xconfigure.width < windows->image.ximage->width) ||
15634                 (event.xconfigure.height < windows->image.ximage->height))
15635               {
15636                 (void) XMapRaised(display,windows->pan.id);
15637                 XDrawPanRectangle(display,windows);
15638               }
15639             else
15640               if (windows->pan.mapped != MagickFalse)
15641                 (void) XWithdrawWindow(display,windows->pan.id,
15642                   windows->pan.screen);
15643             break;
15644           }
15645         if (event.xconfigure.window == windows->magnify.id)
15646           {
15647             unsigned int
15648               magnify;
15649
15650             /*
15651               Magnify window has a new configuration.
15652             */
15653             windows->magnify.width=(unsigned int) event.xconfigure.width;
15654             windows->magnify.height=(unsigned int) event.xconfigure.height;
15655             if (windows->magnify.mapped == MagickFalse)
15656               break;
15657             magnify=1;
15658             while ((int) magnify <= event.xconfigure.width)
15659               magnify<<=1;
15660             while ((int) magnify <= event.xconfigure.height)
15661               magnify<<=1;
15662             magnify>>=1;
15663             if (((int) magnify != event.xconfigure.width) ||
15664                 ((int) magnify != event.xconfigure.height))
15665               {
15666                 window_changes.width=(int) magnify;
15667                 window_changes.height=(int) magnify;
15668                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15669                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15670                   &window_changes);
15671                 break;
15672               }
15673             if ((windows->magnify.mapped != MagickFalse) &&
15674                 (windows->magnify.stasis != MagickFalse))
15675               {
15676                 status=XMakeImage(display,resource_info,&windows->magnify,
15677                   display_image,windows->magnify.width,windows->magnify.height,
15678                   exception);
15679                 XMakeMagnifyImage(display,windows,exception);
15680               }
15681             break;
15682           }
15683         if ((windows->magnify.mapped != MagickFalse) &&
15684             (event.xconfigure.window == windows->pan.id))
15685           {
15686             /*
15687               Pan icon window has a new configuration.
15688             */
15689             if (event.xconfigure.send_event != 0)
15690               {
15691                 windows->pan.x=event.xconfigure.x;
15692                 windows->pan.y=event.xconfigure.y;
15693               }
15694             windows->pan.width=(unsigned int) event.xconfigure.width;
15695             windows->pan.height=(unsigned int) event.xconfigure.height;
15696             break;
15697           }
15698         if (event.xconfigure.window == windows->icon.id)
15699           {
15700             /*
15701               Icon window has a new configuration.
15702             */
15703             windows->icon.width=(unsigned int) event.xconfigure.width;
15704             windows->icon.height=(unsigned int) event.xconfigure.height;
15705             break;
15706           }
15707         break;
15708       }
15709       case DestroyNotify:
15710       {
15711         /*
15712           Group leader has exited.
15713         */
15714         if (display_image->debug != MagickFalse)
15715           (void) LogMagickEvent(X11Event,GetMagickModule(),
15716             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15717         if (event.xdestroywindow.window == windows->group_leader.id)
15718           {
15719             *state|=ExitState;
15720             break;
15721           }
15722         break;
15723       }
15724       case EnterNotify:
15725       {
15726         /*
15727           Selectively install colormap.
15728         */
15729         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15730           if (event.xcrossing.mode != NotifyUngrab)
15731             XInstallColormap(display,map_info->colormap);
15732         break;
15733       }
15734       case Expose:
15735       {
15736         if (display_image->debug != MagickFalse)
15737           (void) LogMagickEvent(X11Event,GetMagickModule(),
15738             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15739             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15740             event.xexpose.y);
15741         /*
15742           Refresh windows that are now exposed.
15743         */
15744         if ((event.xexpose.window == windows->image.id) &&
15745             (windows->image.mapped != MagickFalse))
15746           {
15747             XRefreshWindow(display,&windows->image,&event);
15748             delay=display_image->delay/MagickMax(
15749               display_image->ticks_per_second,1L);
15750             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15751             break;
15752           }
15753         if ((event.xexpose.window == windows->magnify.id) &&
15754             (windows->magnify.mapped != MagickFalse))
15755           {
15756             XMakeMagnifyImage(display,windows,exception);
15757             break;
15758           }
15759         if (event.xexpose.window == windows->pan.id)
15760           {
15761             XDrawPanRectangle(display,windows);
15762             break;
15763           }
15764         if (event.xexpose.window == windows->icon.id)
15765           {
15766             XRefreshWindow(display,&windows->icon,&event);
15767             break;
15768           }
15769         break;
15770       }
15771       case KeyPress:
15772       {
15773         int
15774           length;
15775
15776         /*
15777           Respond to a user key press.
15778         */
15779         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15780           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15781         *(command+length)='\0';
15782         if (display_image->debug != MagickFalse)
15783           (void) LogMagickEvent(X11Event,GetMagickModule(),
15784             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15785             key_symbol,command);
15786         if (event.xkey.window == windows->image.id)
15787           {
15788             command_type=XImageWindowCommand(display,resource_info,windows,
15789               event.xkey.state,key_symbol,&display_image,exception);
15790             if (command_type != NullCommand)
15791               nexus=XMagickCommand(display,resource_info,windows,command_type,
15792                 &display_image,exception);
15793           }
15794         if (event.xkey.window == windows->magnify.id)
15795           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15796             exception);
15797         if (event.xkey.window == windows->pan.id)
15798           {
15799             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15800               (void) XWithdrawWindow(display,windows->pan.id,
15801                 windows->pan.screen);
15802             else
15803               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15804                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15805                   "Help Viewer - Image Pan",ImagePanHelp);
15806               else
15807                 XTranslateImage(display,windows,*image,key_symbol);
15808           }
15809         delay=display_image->delay/MagickMax(
15810           display_image->ticks_per_second,1L);
15811         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15812         break;
15813       }
15814       case KeyRelease:
15815       {
15816         /*
15817           Respond to a user key release.
15818         */
15819         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15820           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15821         if (display_image->debug != MagickFalse)
15822           (void) LogMagickEvent(X11Event,GetMagickModule(),
15823             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15824         break;
15825       }
15826       case LeaveNotify:
15827       {
15828         /*
15829           Selectively uninstall colormap.
15830         */
15831         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15832           if (event.xcrossing.mode != NotifyUngrab)
15833             XUninstallColormap(display,map_info->colormap);
15834         break;
15835       }
15836       case MapNotify:
15837       {
15838         if (display_image->debug != MagickFalse)
15839           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15840             event.xmap.window);
15841         if (event.xmap.window == windows->backdrop.id)
15842           {
15843             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15844               CurrentTime);
15845             windows->backdrop.mapped=MagickTrue;
15846             break;
15847           }
15848         if (event.xmap.window == windows->image.id)
15849           {
15850             if (windows->backdrop.id != (Window) NULL)
15851               (void) XInstallColormap(display,map_info->colormap);
15852             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15853               {
15854                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15855                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15856               }
15857             if (((int) windows->image.width < windows->image.ximage->width) ||
15858                 ((int) windows->image.height < windows->image.ximage->height))
15859               (void) XMapRaised(display,windows->pan.id);
15860             windows->image.mapped=MagickTrue;
15861             break;
15862           }
15863         if (event.xmap.window == windows->magnify.id)
15864           {
15865             XMakeMagnifyImage(display,windows,exception);
15866             windows->magnify.mapped=MagickTrue;
15867             (void) XWithdrawWindow(display,windows->info.id,
15868               windows->info.screen);
15869             break;
15870           }
15871         if (event.xmap.window == windows->pan.id)
15872           {
15873             XMakePanImage(display,resource_info,windows,display_image,
15874               exception);
15875             windows->pan.mapped=MagickTrue;
15876             break;
15877           }
15878         if (event.xmap.window == windows->info.id)
15879           {
15880             windows->info.mapped=MagickTrue;
15881             break;
15882           }
15883         if (event.xmap.window == windows->icon.id)
15884           {
15885             MagickBooleanType
15886               taint;
15887
15888             /*
15889               Create an icon image.
15890             */
15891             taint=display_image->taint;
15892             XMakeStandardColormap(display,icon_visual,icon_resources,
15893               display_image,icon_map,icon_pixel,exception);
15894             (void) XMakeImage(display,icon_resources,&windows->icon,
15895               display_image,windows->icon.width,windows->icon.height,
15896               exception);
15897             display_image->taint=taint;
15898             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15899               windows->icon.pixmap);
15900             (void) XClearWindow(display,windows->icon.id);
15901             (void) XWithdrawWindow(display,windows->info.id,
15902               windows->info.screen);
15903             windows->icon.mapped=MagickTrue;
15904             break;
15905           }
15906         if (event.xmap.window == windows->command.id)
15907           {
15908             windows->command.mapped=MagickTrue;
15909             break;
15910           }
15911         if (event.xmap.window == windows->popup.id)
15912           {
15913             windows->popup.mapped=MagickTrue;
15914             break;
15915           }
15916         if (event.xmap.window == windows->widget.id)
15917           {
15918             windows->widget.mapped=MagickTrue;
15919             break;
15920           }
15921         break;
15922       }
15923       case MappingNotify:
15924       {
15925         (void) XRefreshKeyboardMapping(&event.xmapping);
15926         break;
15927       }
15928       case NoExpose:
15929         break;
15930       case PropertyNotify:
15931       {
15932         Atom
15933           type;
15934
15935         int
15936           format,
15937           status;
15938
15939         unsigned char
15940           *data;
15941
15942         unsigned long
15943           after,
15944           length;
15945
15946         if (display_image->debug != MagickFalse)
15947           (void) LogMagickEvent(X11Event,GetMagickModule(),
15948             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15949             event.xproperty.atom,event.xproperty.state);
15950         if (event.xproperty.atom != windows->im_remote_command)
15951           break;
15952         /*
15953           Display image named by the remote command protocol.
15954         */
15955         status=XGetWindowProperty(display,event.xproperty.window,
15956           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15957           AnyPropertyType,&type,&format,&length,&after,&data);
15958         if ((status != Success) || (length == 0))
15959           break;
15960         if (LocaleCompare((char *) data,"-quit") == 0)
15961           {
15962             XClientMessage(display,windows->image.id,windows->im_protocols,
15963               windows->im_exit,CurrentTime);
15964             (void) XFree((void *) data);
15965             break;
15966           }
15967         (void) CopyMagickString(resource_info->image_info->filename,
15968           (char *) data,MaxTextExtent);
15969         (void) XFree((void *) data);
15970         nexus=ReadImage(resource_info->image_info,exception);
15971         CatchException(exception);
15972         if (nexus != (Image *) NULL)
15973           *state|=NextImageState | ExitState;
15974         break;
15975       }
15976       case ReparentNotify:
15977       {
15978         if (display_image->debug != MagickFalse)
15979           (void) LogMagickEvent(X11Event,GetMagickModule(),
15980             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15981             event.xreparent.window);
15982         break;
15983       }
15984       case UnmapNotify:
15985       {
15986         if (display_image->debug != MagickFalse)
15987           (void) LogMagickEvent(X11Event,GetMagickModule(),
15988             "Unmap Notify: 0x%lx",event.xunmap.window);
15989         if (event.xunmap.window == windows->backdrop.id)
15990           {
15991             windows->backdrop.mapped=MagickFalse;
15992             break;
15993           }
15994         if (event.xunmap.window == windows->image.id)
15995           {
15996             windows->image.mapped=MagickFalse;
15997             break;
15998           }
15999         if (event.xunmap.window == windows->magnify.id)
16000           {
16001             windows->magnify.mapped=MagickFalse;
16002             break;
16003           }
16004         if (event.xunmap.window == windows->pan.id)
16005           {
16006             windows->pan.mapped=MagickFalse;
16007             break;
16008           }
16009         if (event.xunmap.window == windows->info.id)
16010           {
16011             windows->info.mapped=MagickFalse;
16012             break;
16013           }
16014         if (event.xunmap.window == windows->icon.id)
16015           {
16016             if (map_info->colormap == icon_map->colormap)
16017               XConfigureImageColormap(display,resource_info,windows,
16018                 display_image,exception);
16019             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16020               icon_pixel);
16021             windows->icon.mapped=MagickFalse;
16022             break;
16023           }
16024         if (event.xunmap.window == windows->command.id)
16025           {
16026             windows->command.mapped=MagickFalse;
16027             break;
16028           }
16029         if (event.xunmap.window == windows->popup.id)
16030           {
16031             if (windows->backdrop.id != (Window) NULL)
16032               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16033                 CurrentTime);
16034             windows->popup.mapped=MagickFalse;
16035             break;
16036           }
16037         if (event.xunmap.window == windows->widget.id)
16038           {
16039             if (windows->backdrop.id != (Window) NULL)
16040               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16041                 CurrentTime);
16042             windows->widget.mapped=MagickFalse;
16043             break;
16044           }
16045         break;
16046       }
16047       default:
16048       {
16049         if (display_image->debug != MagickFalse)
16050           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16051             event.type);
16052         break;
16053       }
16054     }
16055   } while (!(*state & ExitState));
16056   if ((*state & ExitState) == 0)
16057     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16058       &display_image,exception);
16059   else
16060     if (resource_info->confirm_edit != MagickFalse)
16061       {
16062         /*
16063           Query user if image has changed.
16064         */
16065         if ((resource_info->immutable == MagickFalse) &&
16066             (display_image->taint != MagickFalse))
16067           {
16068             int
16069               status;
16070
16071             status=XConfirmWidget(display,windows,"Your image changed.",
16072               "Do you want to save it");
16073             if (status == 0)
16074               *state&=(~ExitState);
16075             else
16076               if (status > 0)
16077                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16078                   &display_image,exception);
16079           }
16080       }
16081   if ((windows->visual_info->klass == GrayScale) ||
16082       (windows->visual_info->klass == PseudoColor) ||
16083       (windows->visual_info->klass == DirectColor))
16084     {
16085       /*
16086         Withdraw pan and Magnify window.
16087       */
16088       if (windows->info.mapped != MagickFalse)
16089         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16090       if (windows->magnify.mapped != MagickFalse)
16091         (void) XWithdrawWindow(display,windows->magnify.id,
16092           windows->magnify.screen);
16093       if (windows->command.mapped != MagickFalse)
16094         (void) XWithdrawWindow(display,windows->command.id,
16095           windows->command.screen);
16096     }
16097   if (windows->pan.mapped != MagickFalse)
16098     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16099   if (resource_info->backdrop == MagickFalse)
16100     if (windows->backdrop.mapped)
16101       {
16102         (void) XWithdrawWindow(display,windows->backdrop.id,
16103           windows->backdrop.screen);
16104         (void) XDestroyWindow(display,windows->backdrop.id);
16105         windows->backdrop.id=(Window) NULL;
16106         (void) XWithdrawWindow(display,windows->image.id,
16107           windows->image.screen);
16108         (void) XDestroyWindow(display,windows->image.id);
16109         windows->image.id=(Window) NULL;
16110       }
16111   XSetCursorState(display,windows,MagickTrue);
16112   XCheckRefreshWindows(display,windows);
16113   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16114     *state&=(~ExitState);
16115   if (*state & ExitState)
16116     {
16117       /*
16118         Free Standard Colormap.
16119       */
16120       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16121       if (resource_info->map_type == (char *) NULL)
16122         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16123       /*
16124         Free X resources.
16125       */
16126       if (resource_info->copy_image != (Image *) NULL)
16127         {
16128           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16129           resource_info->copy_image=NewImageList();
16130         }
16131       DestroyXResources();
16132     }
16133   (void) XSync(display,MagickFalse);
16134   /*
16135     Restore our progress monitor and warning handlers.
16136   */
16137   (void) SetErrorHandler(warning_handler);
16138   (void) SetWarningHandler(warning_handler);
16139   /*
16140     Change to home directory.
16141   */
16142   directory=getcwd(working_directory,MaxTextExtent);
16143   (void) directory;
16144   {
16145     int
16146       status;
16147
16148     status=chdir(resource_info->home_directory);
16149     if (status == -1)
16150       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16151         "UnableToOpenFile","%s",resource_info->home_directory);
16152   }
16153   *image=display_image;
16154   return(nexus);
16155 }
16156 #else
16157 \f
16158 /*
16159 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16160 %                                                                             %
16161 %                                                                             %
16162 %                                                                             %
16163 +   D i s p l a y I m a g e s                                                 %
16164 %                                                                             %
16165 %                                                                             %
16166 %                                                                             %
16167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16168 %
16169 %  DisplayImages() displays an image sequence to any X window screen.  It
16170 %  returns a value other than 0 if successful.  Check the exception member
16171 %  of image to determine the reason for any failure.
16172 %
16173 %  The format of the DisplayImages method is:
16174 %
16175 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16176 %        Image *images,ExceptionInfo *exception)
16177 %
16178 %  A description of each parameter follows:
16179 %
16180 %    o image_info: the image info.
16181 %
16182 %    o image: the image.
16183 %
16184 %    o exception: return any errors or warnings in this structure.
16185 %
16186 */
16187 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16188   Image *image,ExceptionInfo *exception)
16189 {
16190   assert(image_info != (const ImageInfo *) NULL);
16191   assert(image_info->signature == MagickSignature);
16192   assert(image != (Image *) NULL);
16193   assert(image->signature == MagickSignature);
16194   if (image->debug != MagickFalse)
16195     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16196   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16197     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16198   return(MagickFalse);
16199 }
16200 \f
16201 /*
16202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16203 %                                                                             %
16204 %                                                                             %
16205 %                                                                             %
16206 +   R e m o t e D i s p l a y C o m m a n d                                   %
16207 %                                                                             %
16208 %                                                                             %
16209 %                                                                             %
16210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16211 %
16212 %  RemoteDisplayCommand() encourages a remote display program to display the
16213 %  specified image filename.
16214 %
16215 %  The format of the RemoteDisplayCommand method is:
16216 %
16217 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16218 %        const char *window,const char *filename,ExceptionInfo *exception)
16219 %
16220 %  A description of each parameter follows:
16221 %
16222 %    o image_info: the image info.
16223 %
16224 %    o window: Specifies the name or id of an X window.
16225 %
16226 %    o filename: the name of the image filename to display.
16227 %
16228 %    o exception: return any errors or warnings in this structure.
16229 %
16230 */
16231 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16232   const char *window,const char *filename,ExceptionInfo *exception)
16233 {
16234   assert(image_info != (const ImageInfo *) NULL);
16235   assert(image_info->signature == MagickSignature);
16236   assert(filename != (char *) NULL);
16237   (void) window;
16238   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16239   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16240     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16241   return(MagickFalse);
16242 }
16243 #endif