]> granicus.if.org Git - imagemagick/blob - MagickCore/display.c
(no commit message)
[imagemagick] / MagickCore / display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7 %               D   D    I    SS     P   P  L      A   A   Y Y                %
8 %               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9 %               D   D    I       SS  P      L      A   A    Y                 %
10 %               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11 %                                                                             %
12 %                                                                             %
13 %        MagickCore Methods to Interactively Display and Edit an Image        %
14 %                                                                             %
15 %                             Software Design                                 %
16 %                               John Cristy                                   %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/client.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/colorspace.h"
49 #include "MagickCore/composite.h"
50 #include "MagickCore/constitute.h"
51 #include "MagickCore/decorate.h"
52 #include "MagickCore/delegate.h"
53 #include "MagickCore/display.h"
54 #include "MagickCore/display-private.h"
55 #include "MagickCore/draw.h"
56 #include "MagickCore/effect.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/fx.h"
61 #include "MagickCore/geometry.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/magick.h"
67 #include "MagickCore/memory_.h"
68 #include "MagickCore/monitor.h"
69 #include "MagickCore/monitor-private.h"
70 #include "MagickCore/montage.h"
71 #include "MagickCore/option.h"
72 #include "MagickCore/paint.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/PreRvIcccm.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum.h"
78 #include "MagickCore/quantum-private.h"
79 #include "MagickCore/resize.h"
80 #include "MagickCore/resource_.h"
81 #include "MagickCore/shear.h"
82 #include "MagickCore/segment.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/threshold.h"
87 #include "MagickCore/utility.h"
88 #include "MagickCore/utility-private.h"
89 #include "MagickCore/version.h"
90 #include "MagickCore/widget.h"
91 #include "MagickCore/widget-private.h"
92 #include "MagickCore/xwindow.h"
93 #include "MagickCore/xwindow-private.h"
94 \f
95 #if defined(MAGICKCORE_X11_DELEGATE)
96 /*
97   Define declarations.
98 */
99 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
100 \f
101 /*
102   Constant declarations.
103 */
104 static const unsigned char
105   HighlightBitmap[8] =
106   {
107     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
108   },
109   OpaqueBitmap[8] =
110   {
111     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
112   },
113   ShadowBitmap[8] =
114   {
115     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
116   };
117
118 static const char
119   *PageSizes[] =
120   {
121     "Letter",
122     "Tabloid",
123     "Ledger",
124     "Legal",
125     "Statement",
126     "Executive",
127     "A3",
128     "A4",
129     "A5",
130     "B4",
131     "B5",
132     "Folio",
133     "Quarto",
134     "10x14",
135     (char *) NULL
136   };
137 \f
138 /*
139   Help widget declarations.
140 */
141 static const char
142   *ImageAnnotateHelp[] =
143   {
144     "In annotate mode, the Command widget has these options:",
145     "",
146     "    Font Name",
147     "      fixed",
148     "      variable",
149     "      5x8",
150     "      6x10",
151     "      7x13bold",
152     "      8x13bold",
153     "      9x15bold",
154     "      10x20",
155     "      12x24",
156     "      Browser...",
157     "    Font Color",
158     "      black",
159     "      blue",
160     "      cyan",
161     "      green",
162     "      gray",
163     "      red",
164     "      magenta",
165     "      yellow",
166     "      white",
167     "      transparent",
168     "      Browser...",
169     "    Font Color",
170     "      black",
171     "      blue",
172     "      cyan",
173     "      green",
174     "      gray",
175     "      red",
176     "      magenta",
177     "      yellow",
178     "      white",
179     "      transparent",
180     "      Browser...",
181     "    Rotate Text",
182     "      -90",
183     "      -45",
184     "      -30",
185     "      0",
186     "      30",
187     "      45",
188     "      90",
189     "      180",
190     "      Dialog...",
191     "    Help",
192     "    Dismiss",
193     "",
194     "Choose a font name from the Font Name sub-menu.  Additional",
195     "font names can be specified with the font browser.  You can",
196     "change the menu names by setting the X resources font1",
197     "through font9.",
198     "",
199     "Choose a font color from the Font Color sub-menu.",
200     "Additional font colors can be specified with the color",
201     "browser.  You can change the menu colors by setting the X",
202     "resources pen1 through pen9.",
203     "",
204     "If you select the color browser and press Grab, you can",
205     "choose the font color by moving the pointer to the desired",
206     "color on the screen and press any button.",
207     "",
208     "If you choose to rotate the text, choose Rotate Text from the",
209     "menu and select an angle.  Typically you will only want to",
210     "rotate one line of text at a time.  Depending on the angle you",
211     "choose, subsequent lines may end up overwriting each other.",
212     "",
213     "Choosing a font and its color is optional.  The default font",
214     "is fixed and the default color is black.  However, you must",
215     "choose a location to begin entering text and press button 1.",
216     "An underscore character will appear at the location of the",
217     "pointer.  The cursor changes to a pencil to indicate you are",
218     "in text mode.  To exit immediately, press Dismiss.",
219     "",
220     "In text mode, any key presses will display the character at",
221     "the location of the underscore and advance the underscore",
222     "cursor.  Enter your text and once completed press Apply to",
223     "finish your image annotation.  To correct errors press BACK",
224     "SPACE.  To delete an entire line of text, press DELETE.  Any",
225     "text that exceeds the boundaries of the image window is",
226     "automagically continued onto the next line.",
227     "",
228     "The actual color you request for the font is saved in the",
229     "image.  However, the color that appears in your image window",
230     "may be different.  For example, on a monochrome screen the",
231     "text will appear black or white even if you choose the color",
232     "red as the font color.  However, the image saved to a file",
233     "with -write is written with red lettering.  To assure the",
234     "correct color text in the final image, any PseudoClass image",
235     "is promoted to DirectClass (see miff(5)).  To force a",
236     "PseudoClass image to remain PseudoClass, use -colors.",
237     (char *) NULL,
238   },
239   *ImageChopHelp[] =
240   {
241     "In chop mode, the Command widget has these options:",
242     "",
243     "    Direction",
244     "      horizontal",
245     "      vertical",
246     "    Help",
247     "    Dismiss",
248     "",
249     "If the you choose the horizontal direction (this the",
250     "default), the area of the image between the two horizontal",
251     "endpoints of the chop line is removed.  Otherwise, the area",
252     "of the image between the two vertical endpoints of the chop",
253     "line is removed.",
254     "",
255     "Select a location within the image window to begin your chop,",
256     "press and hold any button.  Next, move the pointer to",
257     "another location in the image.  As you move a line will",
258     "connect the initial location and the pointer.  When you",
259     "release the button, the area within the image to chop is",
260     "determined by which direction you choose from the Command",
261     "widget.",
262     "",
263     "To cancel the image chopping, move the pointer back to the",
264     "starting point of the line and release the button.",
265     (char *) NULL,
266   },
267   *ImageColorEditHelp[] =
268   {
269     "In color edit mode, the Command widget has these options:",
270     "",
271     "    Method",
272     "      point",
273     "      replace",
274     "      floodfill",
275     "      filltoborder",
276     "      reset",
277     "    Pixel Color",
278     "      black",
279     "      blue",
280     "      cyan",
281     "      green",
282     "      gray",
283     "      red",
284     "      magenta",
285     "      yellow",
286     "      white",
287     "      Browser...",
288     "    Border Color",
289     "      black",
290     "      blue",
291     "      cyan",
292     "      green",
293     "      gray",
294     "      red",
295     "      magenta",
296     "      yellow",
297     "      white",
298     "      Browser...",
299     "    Fuzz",
300     "      0%",
301     "      2%",
302     "      5%",
303     "      10%",
304     "      15%",
305     "      Dialog...",
306     "    Undo",
307     "    Help",
308     "    Dismiss",
309     "",
310     "Choose a color editing method from the Method sub-menu",
311     "of the Command widget.  The point method recolors any pixel",
312     "selected with the pointer until the button is released.  The",
313     "replace method recolors any pixel that matches the color of",
314     "the pixel you select with a button press.  Floodfill recolors",
315     "any pixel that matches the color of the pixel you select with",
316     "a button press and is a neighbor.  Whereas filltoborder recolors",
317     "any neighbor pixel that is not the border color.  Finally reset",
318     "changes the entire image to the designated color.",
319     "",
320     "Next, choose a pixel color from the Pixel Color sub-menu.",
321     "Additional pixel colors can be specified with the color",
322     "browser.  You can change the menu colors by setting the X",
323     "resources pen1 through pen9.",
324     "",
325     "Now press button 1 to select a pixel within the image window",
326     "to change its color.  Additional pixels may be recolored as",
327     "prescribed by the method you choose.",
328     "",
329     "If the Magnify widget is mapped, it can be helpful in positioning",
330     "your pointer within the image (refer to button 2).",
331     "",
332     "The actual color you request for the pixels is saved in the",
333     "image.  However, the color that appears in your image window",
334     "may be different.  For example, on a monochrome screen the",
335     "pixel will appear black or white even if you choose the",
336     "color red as the pixel color.  However, the image saved to a",
337     "file with -write is written with red pixels.  To assure the",
338     "correct color text in the final image, any PseudoClass image",
339     "is promoted to DirectClass (see miff(5)).  To force a",
340     "PseudoClass image to remain PseudoClass, use -colors.",
341     (char *) NULL,
342   },
343   *ImageCompositeHelp[] =
344   {
345     "First a widget window is displayed requesting you to enter an",
346     "image name. Press Composite, Grab or type a file name.",
347     "Press Cancel if you choose not to create a composite image.",
348     "When you choose Grab, move the pointer to the desired window",
349     "and press any button.",
350     "",
351     "If the Composite image does not have any matte information,",
352     "you are informed and the file browser is displayed again.",
353     "Enter the name of a mask image.  The image is typically",
354     "grayscale and the same size as the composite image.  If the",
355     "image is not grayscale, it is converted to grayscale and the",
356     "resulting intensities are used as matte information.",
357     "",
358     "A small window appears showing the location of the cursor in",
359     "the image window. You are now in composite mode.  To exit",
360     "immediately, press Dismiss.  In composite mode, the Command",
361     "widget has these options:",
362     "",
363     "    Operators",
364     "      Over",
365     "      In",
366     "      Out",
367     "      Atop",
368     "      Xor",
369     "      Plus",
370     "      Minus",
371     "      Add",
372     "      Subtract",
373     "      Difference",
374     "      Multiply",
375     "      Bumpmap",
376     "      Copy",
377     "      CopyRed",
378     "      CopyGreen",
379     "      CopyBlue",
380     "      CopyOpacity",
381     "      Clear",
382     "    Dissolve",
383     "    Displace",
384     "    Help",
385     "    Dismiss",
386     "",
387     "Choose a composite operation from the Operators sub-menu of",
388     "the Command widget.  How each operator behaves is described",
389     "below.  Image window is the image currently displayed on",
390     "your X server and image is the image obtained with the File",
391     "Browser widget.",
392     "",
393     "Over     The result is the union of the two image shapes,",
394     "         with image obscuring image window in the region of",
395     "         overlap.",
396     "",
397     "In       The result is simply image cut by the shape of",
398     "         image window.  None of the image data of image",
399     "         window is in the result.",
400     "",
401     "Out      The resulting image is image with the shape of",
402     "         image window cut out.",
403     "",
404     "Atop     The result is the same shape as image image window,",
405     "         with image obscuring image window where the image",
406     "         shapes overlap.  Note this differs from over",
407     "         because the portion of image outside image window's",
408     "         shape does not appear in the result.",
409     "",
410     "Xor      The result is the image data from both image and",
411     "         image window that is outside the overlap region.",
412     "         The overlap region is blank.",
413     "",
414     "Plus     The result is just the sum of the image data.",
415     "         Output values are cropped to QuantumRange (no overflow).",
416     "",
417     "Minus    The result of image - image window, with underflow",
418     "         cropped to zero.",
419     "",
420     "Add      The result of image + image window, with overflow",
421     "         wrapping around (mod 256).",
422     "",
423     "Subtract The result of image - image window, with underflow",
424     "         wrapping around (mod 256).  The add and subtract",
425     "         operators can be used to perform reversible",
426     "         transformations.",
427     "",
428     "Difference",
429     "         The result of abs(image - image window).  This",
430     "         useful for comparing two very similar images.",
431     "",
432     "Multiply",
433     "         The result of image * image window.  This",
434     "         useful for the creation of drop-shadows.",
435     "",
436     "Bumpmap  The result of surface normals from image * image",
437     "         window.",
438     "",
439     "Copy     The resulting image is image window replaced with",
440     "         image.  Here the matte information is ignored.",
441     "",
442     "CopyRed  The red layer of the image window is replace with",
443     "         the red layer of the image.  The other layers are",
444     "         untouched.",
445     "",
446     "CopyGreen",
447     "         The green layer of the image window is replace with",
448     "         the green layer of the image.  The other layers are",
449     "         untouched.",
450     "",
451     "CopyBlue The blue layer of the image window is replace with",
452     "         the blue layer of the image.  The other layers are",
453     "         untouched.",
454     "",
455     "CopyOpacity",
456     "         The matte layer of the image window is replace with",
457     "         the matte layer of the image.  The other layers are",
458     "         untouched.",
459     "",
460     "The image compositor requires a matte, or alpha channel in",
461     "the image for some operations.  This extra channel usually",
462     "defines a mask which represents a sort of a cookie-cutter",
463     "for the image.  This the case when matte is opaque (full",
464     "coverage) for pixels inside the shape, zero outside, and",
465     "between 0 and QuantumRange on the boundary.  If image does not",
466     "have a matte channel, it is initialized with 0 for any pixel",
467     "matching in color to pixel location (0,0), otherwise QuantumRange.",
468     "",
469     "If you choose Dissolve, the composite operator becomes Over.  The",
470     "image matte channel percent transparency is initialized to factor.",
471     "The image window is initialized to (100-factor). Where factor is the",
472     "value you specify in the Dialog widget.",
473     "",
474     "Displace shifts the image pixels as defined by a displacement",
475     "map.  With this option, image is used as a displacement map.",
476     "Black, within the displacement map, is a maximum positive",
477     "displacement.  White is a maximum negative displacement and",
478     "middle gray is neutral.  The displacement is scaled to determine",
479     "the pixel shift.  By default, the displacement applies in both the",
480     "horizontal and vertical directions.  However, if you specify a mask,",
481     "image is the horizontal X displacement and mask the vertical Y",
482     "displacement.",
483     "",
484     "Note that matte information for image window is not retained",
485     "for colormapped X server visuals (e.g. StaticColor,",
486     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
487     "behavior may require a TrueColor or DirectColor visual or a",
488     "Standard Colormap.",
489     "",
490     "Choosing a composite operator is optional.  The default",
491     "operator is replace.  However, you must choose a location to",
492     "composite your image and press button 1.  Press and hold the",
493     "button before releasing and an outline of the image will",
494     "appear to help you identify your location.",
495     "",
496     "The actual colors of the composite image is saved.  However,",
497     "the color that appears in image window may be different.",
498     "For example, on a monochrome screen image window will appear",
499     "black or white even though your composited image may have",
500     "many colors.  If the image is saved to a file it is written",
501     "with the correct colors.  To assure the correct colors are",
502     "saved in the final image, any PseudoClass image is promoted",
503     "to DirectClass (see miff(5)).  To force a PseudoClass image",
504     "to remain PseudoClass, use -colors.",
505     (char *) NULL,
506   },
507   *ImageCutHelp[] =
508   {
509     "In cut mode, the Command widget has these options:",
510     "",
511     "    Help",
512     "    Dismiss",
513     "",
514     "To define a cut region, press button 1 and drag.  The",
515     "cut region is defined by a highlighted rectangle that",
516     "expands or contracts as it follows the pointer.  Once you",
517     "are satisfied with the cut region, release the button.",
518     "You are now in rectify mode.  In rectify mode, the Command",
519     "widget has these options:",
520     "",
521     "    Cut",
522     "    Help",
523     "    Dismiss",
524     "",
525     "You can make adjustments by moving the pointer to one of the",
526     "cut rectangle corners, pressing a button, and dragging.",
527     "Finally, press Cut to commit your copy region.  To",
528     "exit without cutting the image, press Dismiss.",
529     (char *) NULL,
530   },
531   *ImageCopyHelp[] =
532   {
533     "In copy mode, the Command widget has these options:",
534     "",
535     "    Help",
536     "    Dismiss",
537     "",
538     "To define a copy region, press button 1 and drag.  The",
539     "copy region is defined by a highlighted rectangle that",
540     "expands or contracts as it follows the pointer.  Once you",
541     "are satisfied with the copy region, release the button.",
542     "You are now in rectify mode.  In rectify mode, the Command",
543     "widget has these options:",
544     "",
545     "    Copy",
546     "    Help",
547     "    Dismiss",
548     "",
549     "You can make adjustments by moving the pointer to one of the",
550     "copy rectangle corners, pressing a button, and dragging.",
551     "Finally, press Copy to commit your copy region.  To",
552     "exit without copying the image, press Dismiss.",
553     (char *) NULL,
554   },
555   *ImageCropHelp[] =
556   {
557     "In crop mode, the Command widget has these options:",
558     "",
559     "    Help",
560     "    Dismiss",
561     "",
562     "To define a cropping region, press button 1 and drag.  The",
563     "cropping region is defined by a highlighted rectangle that",
564     "expands or contracts as it follows the pointer.  Once you",
565     "are satisfied with the cropping region, release the button.",
566     "You are now in rectify mode.  In rectify mode, the Command",
567     "widget has these options:",
568     "",
569     "    Crop",
570     "    Help",
571     "    Dismiss",
572     "",
573     "You can make adjustments by moving the pointer to one of the",
574     "cropping rectangle corners, pressing a button, and dragging.",
575     "Finally, press Crop to commit your cropping region.  To",
576     "exit without cropping the image, press Dismiss.",
577     (char *) NULL,
578   },
579   *ImageDrawHelp[] =
580   {
581     "The cursor changes to a crosshair to indicate you are in",
582     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
583     "the Command widget has these options:",
584     "",
585     "    Element",
586     "      point",
587     "      line",
588     "      rectangle",
589     "      fill rectangle",
590     "      circle",
591     "      fill circle",
592     "      ellipse",
593     "      fill ellipse",
594     "      polygon",
595     "      fill polygon",
596     "    Color",
597     "      black",
598     "      blue",
599     "      cyan",
600     "      green",
601     "      gray",
602     "      red",
603     "      magenta",
604     "      yellow",
605     "      white",
606     "      transparent",
607     "      Browser...",
608     "    Stipple",
609     "      Brick",
610     "      Diagonal",
611     "      Scales",
612     "      Vertical",
613     "      Wavy",
614     "      Translucent",
615     "      Opaque",
616     "      Open...",
617     "    Width",
618     "      1",
619     "      2",
620     "      4",
621     "      8",
622     "      16",
623     "      Dialog...",
624     "    Undo",
625     "    Help",
626     "    Dismiss",
627     "",
628     "Choose a drawing primitive from the Element sub-menu.",
629     "",
630     "Choose a color from the Color sub-menu.  Additional",
631     "colors can be specified with the color browser.",
632     "",
633     "If you choose the color browser and press Grab, you can",
634     "select the color by moving the pointer to the desired",
635     "color on the screen and press any button.  The transparent",
636     "color updates the image matte channel and is useful for",
637     "image compositing.",
638     "",
639     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
640     "Additional stipples can be specified with the file browser.",
641     "Stipples obtained from the file browser must be on disk in the",
642     "X11 bitmap format.",
643     "",
644     "Choose a width, if appropriate, from the Width sub-menu.  To",
645     "choose a specific width select the Dialog widget.",
646     "",
647     "Choose a point in the Image window and press button 1 and",
648     "hold.  Next, move the pointer to another location in the",
649     "image.  As you move, a line connects the initial location and",
650     "the pointer.  When you release the button, the image is",
651     "updated with the primitive you just drew.  For polygons, the",
652     "image is updated when you press and release the button without",
653     "moving the pointer.",
654     "",
655     "To cancel image drawing, move the pointer back to the",
656     "starting point of the line and release the button.",
657     (char *) NULL,
658   },
659   *DisplayHelp[] =
660   {
661     "BUTTONS",
662     "  The effects of each button press is described below.  Three",
663     "  buttons are required.  If you have a two button mouse,",
664     "  button 1 and 3 are returned.  Press ALT and button 3 to",
665     "  simulate button 2.",
666     "",
667     "  1    Press this button to map or unmap the Command widget.",
668     "",
669     "  2    Press and drag to define a region of the image to",
670     "       magnify.",
671     "",
672     "  3    Press and drag to choose from a select set of commands.",
673     "       This button behaves differently if the image being",
674     "       displayed is a visual image directory.  Here, choose a",
675     "       particular tile of the directory and press this button and",
676     "       drag to select a command from a pop-up menu.  Choose from",
677     "       these menu items:",
678     "",
679     "           Open",
680     "           Next",
681     "           Former",
682     "           Delete",
683     "           Update",
684     "",
685     "       If you choose Open, the image represented by the tile is",
686     "       displayed.  To return to the visual image directory, choose",
687     "       Next from the Command widget.  Next and Former moves to the",
688     "       next or former image respectively.  Choose Delete to delete",
689     "       a particular image tile.  Finally, choose Update to",
690     "       synchronize all the image tiles with their respective",
691     "       images.",
692     "",
693     "COMMAND WIDGET",
694     "  The Command widget lists a number of sub-menus and commands.",
695     "  They are",
696     "",
697     "      File",
698     "        Open...",
699     "        Next",
700     "        Former",
701     "        Select...",
702     "        Save...",
703     "        Print...",
704     "        Delete...",
705     "        New...",
706     "        Visual Directory...",
707     "        Quit",
708     "      Edit",
709     "        Undo",
710     "        Redo",
711     "        Cut",
712     "        Copy",
713     "        Paste",
714     "      View",
715     "        Half Size",
716     "        Original Size",
717     "        Double Size",
718     "        Resize...",
719     "        Apply",
720     "        Refresh",
721     "        Restore",
722     "      Transform",
723     "        Crop",
724     "        Chop",
725     "        Flop",
726     "        Flip",
727     "        Rotate Right",
728     "        Rotate Left",
729     "        Rotate...",
730     "        Shear...",
731     "        Roll...",
732     "        Trim Edges",
733     "      Enhance",
734     "        Brightness...",
735     "        Saturation...",
736     "        Hue...",
737     "        Gamma...",
738     "        Sharpen...",
739     "        Dull",
740     "        Contrast Stretch...",
741     "        Sigmoidal Contrast...",
742     "        Normalize",
743     "        Equalize",
744     "        Negate",
745     "        Grayscale",
746     "        Map...",
747     "        Quantize...",
748     "      Effects",
749     "        Despeckle",
750     "        Emboss",
751     "        Reduce Noise",
752     "        Add Noise",
753     "        Sharpen...",
754     "        Blur...",
755     "        Threshold...",
756     "        Edge Detect...",
757     "        Spread...",
758     "        Shade...",
759     "        Painting...",
760     "        Segment...",
761     "      F/X",
762     "        Solarize...",
763     "        Sepia Tone...",
764     "        Swirl...",
765     "        Implode...",
766     "        Vignette...",
767     "        Wave...",
768     "        Oil Painting...",
769     "        Charcoal Drawing...",
770     "      Image Edit",
771     "        Annotate...",
772     "        Draw...",
773     "        Color...",
774     "        Matte...",
775     "        Composite...",
776     "        Add Border...",
777     "        Add Frame...",
778     "        Comment...",
779     "        Launch...",
780     "        Region of Interest...",
781     "      Miscellany",
782     "        Image Info",
783     "        Zoom Image",
784     "        Show Preview...",
785     "        Show Histogram",
786     "        Show Matte",
787     "        Background...",
788     "        Slide Show",
789     "        Preferences...",
790     "      Help",
791     "        Overview",
792     "        Browse Documentation",
793     "        About Display",
794     "",
795     "  Menu items with a indented triangle have a sub-menu.  They",
796     "  are represented above as the indented items.  To access a",
797     "  sub-menu item, move the pointer to the appropriate menu and",
798     "  press a button and drag.  When you find the desired sub-menu",
799     "  item, release the button and the command is executed.  Move",
800     "  the pointer away from the sub-menu if you decide not to",
801     "  execute a particular command.",
802     "",
803     "KEYBOARD ACCELERATORS",
804     "  Accelerators are one or two key presses that effect a",
805     "  particular command.  The keyboard accelerators that",
806     "  display(1) understands is:",
807     "",
808     "  Ctl+O     Press to open an image from a file.",
809     "",
810     "  space     Press to display the next image.",
811     "",
812     "            If the image is a multi-paged document such as a Postscript",
813     "            document, you can skip ahead several pages by preceding",
814     "            this command with a number.  For example to display the",
815     "            third page beyond the current page, press 3<space>.",
816     "",
817     "  backspace Press to display the former image.",
818     "",
819     "            If the image is a multi-paged document such as a Postscript",
820     "            document, you can skip behind several pages by preceding",
821     "            this command with a number.  For example to display the",
822     "            third page preceding the current page, press 3<backspace>.",
823     "",
824     "  Ctl+S     Press to write the image to a file.",
825     "",
826     "  Ctl+P     Press to print the image to a Postscript printer.",
827     "",
828     "  Ctl+D     Press to delete an image file.",
829     "",
830     "  Ctl+N     Press to create a blank canvas.",
831     "",
832     "  Ctl+Q     Press to discard all images and exit program.",
833     "",
834     "  Ctl+Z     Press to undo last image transformation.",
835     "",
836     "  Ctl+R     Press to redo last image transformation.",
837     "",
838     "  Ctl+X     Press to cut a region of the image.",
839     "",
840     "  Ctl+C     Press to copy a region of the image.",
841     "",
842     "  Ctl+V     Press to paste a region to the image.",
843     "",
844     "  <         Press to half the image size.",
845     "",
846     "  -         Press to return to the original image size.",
847     "",
848     "  >         Press to double the image size.",
849     "",
850     "  %         Press to resize the image to a width and height you",
851     "            specify.",
852     "",
853     "Cmd-A       Press to make any image transformations permanent."
854     "",
855     "            By default, any image size transformations are applied",
856     "            to the original image to create the image displayed on",
857     "            the X server.  However, the transformations are not",
858     "            permanent (i.e. the original image does not change",
859     "            size only the X image does).  For example, if you",
860     "            press > the X image will appear to double in size,",
861     "            but the original image will in fact remain the same size.",
862     "            To force the original image to double in size, press >",
863     "            followed by Cmd-A.",
864     "",
865     "  @         Press to refresh the image window.",
866     "",
867     "  C         Press to cut out a rectangular region of the image.",
868     "",
869     "  [         Press to chop the image.",
870     "",
871     "  H         Press to flop image in the horizontal direction.",
872     "",
873     "  V         Press to flip image in the vertical direction.",
874     "",
875     "  /         Press to rotate the image 90 degrees clockwise.",
876     "",
877     " \\         Press to rotate the image 90 degrees counter-clockwise.",
878     "",
879     "  *         Press to rotate the image the number of degrees you",
880     "            specify.",
881     "",
882     "  S         Press to shear the image the number of degrees you",
883     "            specify.",
884     "",
885     "  R         Press to roll the image.",
886     "",
887     "  T         Press to trim the image edges.",
888     "",
889     "  Shft-H    Press to vary the image hue.",
890     "",
891     "  Shft-S    Press to vary the color saturation.",
892     "",
893     "  Shft-L    Press to vary the color brightness.",
894     "",
895     "  Shft-G    Press to gamma correct the image.",
896     "",
897     "  Shft-C    Press to sharpen the image contrast.",
898     "",
899     "  Shft-Z    Press to dull the image contrast.",
900     "",
901     "  =         Press to perform histogram equalization on the image.",
902     "",
903     "  Shft-N    Press to perform histogram normalization on the image.",
904     "",
905     "  Shft-~    Press to negate the colors of the image.",
906     "",
907     "  .         Press to convert the image colors to gray.",
908     "",
909     "  Shft-#    Press to set the maximum number of unique colors in the",
910     "            image.",
911     "",
912     "  F2        Press to reduce the speckles in an image.",
913     "",
914     "  F3        Press to eliminate peak noise from an image.",
915     "",
916     "  F4        Press to add noise to an image.",
917     "",
918     "  F5        Press to sharpen an image.",
919     "",
920     "  F6        Press to delete an image file.",
921     "",
922     "  F7        Press to threshold the image.",
923     "",
924     "  F8        Press to detect edges within an image.",
925     "",
926     "  F9        Press to emboss an image.",
927     "",
928     "  F10       Press to displace pixels by a random amount.",
929     "",
930     "  F11       Press to negate all pixels above the threshold level.",
931     "",
932     "  F12       Press to shade the image using a distant light source.",
933     "",
934     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
935     "",
936     "  F14       Press to segment the image by color.",
937     "",
938     "  Meta-S    Press to swirl image pixels about the center.",
939     "",
940     "  Meta-I    Press to implode image pixels about the center.",
941     "",
942     "  Meta-W    Press to alter an image along a sine wave.",
943     "",
944     "  Meta-P    Press to simulate an oil painting.",
945     "",
946     "  Meta-C    Press to simulate a charcoal drawing.",
947     "",
948     "  Alt-A     Press to annotate the image with text.",
949     "",
950     "  Alt-D     Press to draw on an image.",
951     "",
952     "  Alt-P     Press to edit an image pixel color.",
953     "",
954     "  Alt-M     Press to edit the image matte information.",
955     "",
956     "  Alt-V     Press to composite the image with another.",
957     "",
958     "  Alt-B     Press to add a border to the image.",
959     "",
960     "  Alt-F     Press to add an ornamental border to the image.",
961     "",
962     "  Alt-Shft-!",
963     "            Press to add an image comment.",
964     "",
965     "  Ctl-A     Press to apply image processing techniques to a region",
966     "            of interest.",
967     "",
968     "  Shft-?    Press to display information about the image.",
969     "",
970     "  Shft-+    Press to map the zoom image window.",
971     "",
972     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
973     "",
974     "  F1        Press to display helpful information about display(1).",
975     "",
976     "  Find      Press to browse documentation about ImageMagick.",
977     "",
978     "  1-9       Press to change the level of magnification.",
979     "",
980     "  Use the arrow keys to move the image one pixel up, down,",
981     "  left, or right within the magnify window.  Be sure to first",
982     "  map the magnify window by pressing button 2.",
983     "",
984     "  Press ALT and one of the arrow keys to trim off one pixel",
985     "  from any side of the image.",
986     (char *) NULL,
987   },
988   *ImageMatteEditHelp[] =
989   {
990     "Matte information within an image is useful for some",
991     "operations such as image compositing (See IMAGE",
992     "COMPOSITING).  This extra channel usually defines a mask",
993     "which represents a sort of a cookie-cutter for the image.",
994     "This the case when matte is opaque (full coverage) for",
995     "pixels inside the shape, zero outside, and between 0 and",
996     "QuantumRange on the boundary.",
997     "",
998     "A small window appears showing the location of the cursor in",
999     "the image window. You are now in matte edit mode.  To exit",
1000     "immediately, press Dismiss.  In matte edit mode, the Command",
1001     "widget has these options:",
1002     "",
1003     "    Method",
1004     "      point",
1005     "      replace",
1006     "      floodfill",
1007     "      filltoborder",
1008     "      reset",
1009     "    Border Color",
1010     "      black",
1011     "      blue",
1012     "      cyan",
1013     "      green",
1014     "      gray",
1015     "      red",
1016     "      magenta",
1017     "      yellow",
1018     "      white",
1019     "      Browser...",
1020     "    Fuzz",
1021     "      0%",
1022     "      2%",
1023     "      5%",
1024     "      10%",
1025     "      15%",
1026     "      Dialog...",
1027     "    Matte",
1028     "      Opaque",
1029     "      Transparent",
1030     "      Dialog...",
1031     "    Undo",
1032     "    Help",
1033     "    Dismiss",
1034     "",
1035     "Choose a matte editing method from the Method sub-menu of",
1036     "the Command widget.  The point method changes the matte value",
1037     "of any pixel selected with the pointer until the button is",
1038     "is released.  The replace method changes the matte value of",
1039     "any pixel that matches the color of the pixel you select with",
1040     "a button press.  Floodfill changes the matte value of any pixel",
1041     "that matches the color of the pixel you select with a button",
1042     "press and is a neighbor.  Whereas filltoborder changes the matte",
1043     "value any neighbor pixel that is not the border color.  Finally",
1044     "reset changes the entire image to the designated matte value.",
1045     "",
1046     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1047     "select the Dialog entry.  Here a dialog appears requesting a matte",
1048     "value.  The value you select is assigned as the opacity value of the",
1049     "selected pixel or pixels.",
1050     "",
1051     "Now, press any button to select a pixel within the image",
1052     "window to change its matte value.",
1053     "",
1054     "If the Magnify widget is mapped, it can be helpful in positioning",
1055     "your pointer within the image (refer to button 2).",
1056     "",
1057     "Matte information is only valid in a DirectClass image.",
1058     "Therefore, any PseudoClass image is promoted to DirectClass",
1059     "(see miff(5)).  Note that matte information for PseudoClass",
1060     "is not retained for colormapped X server visuals (e.g.",
1061     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1062     "immediately save your image to a file (refer to Write).",
1063     "Correct matte editing behavior may require a TrueColor or",
1064     "DirectColor visual or a Standard Colormap.",
1065     (char *) NULL,
1066   },
1067   *ImagePanHelp[] =
1068   {
1069     "When an image exceeds the width or height of the X server",
1070     "screen, display maps a small panning icon.  The rectangle",
1071     "within the panning icon shows the area that is currently",
1072     "displayed in the image window.  To pan about the image,",
1073     "press any button and drag the pointer within the panning",
1074     "icon.  The pan rectangle moves with the pointer and the",
1075     "image window is updated to reflect the location of the",
1076     "rectangle within the panning icon.  When you have selected",
1077     "the area of the image you wish to view, release the button.",
1078     "",
1079     "Use the arrow keys to pan the image one pixel up, down,",
1080     "left, or right within the image window.",
1081     "",
1082     "The panning icon is withdrawn if the image becomes smaller",
1083     "than the dimensions of the X server screen.",
1084     (char *) NULL,
1085   },
1086   *ImagePasteHelp[] =
1087   {
1088     "A small window appears showing the location of the cursor in",
1089     "the image window. You are now in paste mode.  To exit",
1090     "immediately, press Dismiss.  In paste mode, the Command",
1091     "widget has these options:",
1092     "",
1093     "    Operators",
1094     "      over",
1095     "      in",
1096     "      out",
1097     "      atop",
1098     "      xor",
1099     "      plus",
1100     "      minus",
1101     "      add",
1102     "      subtract",
1103     "      difference",
1104     "      replace",
1105     "    Help",
1106     "    Dismiss",
1107     "",
1108     "Choose a composite operation from the Operators sub-menu of",
1109     "the Command widget.  How each operator behaves is described",
1110     "below.  Image window is the image currently displayed on",
1111     "your X server and image is the image obtained with the File",
1112     "Browser widget.",
1113     "",
1114     "Over     The result is the union of the two image shapes,",
1115     "         with image obscuring image window in the region of",
1116     "         overlap.",
1117     "",
1118     "In       The result is simply image cut by the shape of",
1119     "         image window.  None of the image data of image",
1120     "         window is in the result.",
1121     "",
1122     "Out      The resulting image is image with the shape of",
1123     "         image window cut out.",
1124     "",
1125     "Atop     The result is the same shape as image image window,",
1126     "         with image obscuring image window where the image",
1127     "         shapes overlap.  Note this differs from over",
1128     "         because the portion of image outside image window's",
1129     "         shape does not appear in the result.",
1130     "",
1131     "Xor      The result is the image data from both image and",
1132     "         image window that is outside the overlap region.",
1133     "         The overlap region is blank.",
1134     "",
1135     "Plus     The result is just the sum of the image data.",
1136     "         Output values are cropped to QuantumRange (no overflow).",
1137     "         This operation is independent of the matte",
1138     "         channels.",
1139     "",
1140     "Minus    The result of image - image window, with underflow",
1141     "         cropped to zero.",
1142     "",
1143     "Add      The result of image + image window, with overflow",
1144     "         wrapping around (mod 256).",
1145     "",
1146     "Subtract The result of image - image window, with underflow",
1147     "         wrapping around (mod 256).  The add and subtract",
1148     "         operators can be used to perform reversible",
1149     "         transformations.",
1150     "",
1151     "Difference",
1152     "         The result of abs(image - image window).  This",
1153     "         useful for comparing two very similar images.",
1154     "",
1155     "Copy     The resulting image is image window replaced with",
1156     "         image.  Here the matte information is ignored.",
1157     "",
1158     "CopyRed  The red layer of the image window is replace with",
1159     "         the red layer of the image.  The other layers are",
1160     "         untouched.",
1161     "",
1162     "CopyGreen",
1163     "         The green layer of the image window is replace with",
1164     "         the green layer of the image.  The other layers are",
1165     "         untouched.",
1166     "",
1167     "CopyBlue The blue layer of the image window is replace with",
1168     "         the blue layer of the image.  The other layers are",
1169     "         untouched.",
1170     "",
1171     "CopyOpacity",
1172     "         The matte layer of the image window is replace with",
1173     "         the matte layer of the image.  The other layers are",
1174     "         untouched.",
1175     "",
1176     "The image compositor requires a matte, or alpha channel in",
1177     "the image for some operations.  This extra channel usually",
1178     "defines a mask which represents a sort of a cookie-cutter",
1179     "for the image.  This the case when matte is opaque (full",
1180     "coverage) for pixels inside the shape, zero outside, and",
1181     "between 0 and QuantumRange on the boundary.  If image does not",
1182     "have a matte channel, it is initialized with 0 for any pixel",
1183     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1184     "",
1185     "Note that matte information for image window is not retained",
1186     "for colormapped X server visuals (e.g. StaticColor,",
1187     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1188     "behavior may require a TrueColor or DirectColor visual or a",
1189     "Standard Colormap.",
1190     "",
1191     "Choosing a composite operator is optional.  The default",
1192     "operator is replace.  However, you must choose a location to",
1193     "paste your image and press button 1.  Press and hold the",
1194     "button before releasing and an outline of the image will",
1195     "appear to help you identify your location.",
1196     "",
1197     "The actual colors of the pasted image is saved.  However,",
1198     "the color that appears in image window may be different.",
1199     "For example, on a monochrome screen image window will appear",
1200     "black or white even though your pasted image may have",
1201     "many colors.  If the image is saved to a file it is written",
1202     "with the correct colors.  To assure the correct colors are",
1203     "saved in the final image, any PseudoClass image is promoted",
1204     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1205     "to remain PseudoClass, use -colors.",
1206     (char *) NULL,
1207   },
1208   *ImageROIHelp[] =
1209   {
1210     "In region of interest mode, the Command widget has these",
1211     "options:",
1212     "",
1213     "    Help",
1214     "    Dismiss",
1215     "",
1216     "To define a region of interest, press button 1 and drag.",
1217     "The region of interest is defined by a highlighted rectangle",
1218     "that expands or contracts as it follows the pointer.  Once",
1219     "you are satisfied with the region of interest, release the",
1220     "button.  You are now in apply mode.  In apply mode the",
1221     "Command widget has these options:",
1222     "",
1223     "      File",
1224     "        Save...",
1225     "        Print...",
1226     "      Edit",
1227     "        Undo",
1228     "        Redo",
1229     "      Transform",
1230     "        Flop",
1231     "        Flip",
1232     "        Rotate Right",
1233     "        Rotate Left",
1234     "      Enhance",
1235     "        Hue...",
1236     "        Saturation...",
1237     "        Brightness...",
1238     "        Gamma...",
1239     "        Spiff",
1240     "        Dull",
1241     "        Contrast Stretch",
1242     "        Sigmoidal Contrast...",
1243     "        Normalize",
1244     "        Equalize",
1245     "        Negate",
1246     "        Grayscale",
1247     "        Map...",
1248     "        Quantize...",
1249     "      Effects",
1250     "        Despeckle",
1251     "        Emboss",
1252     "        Reduce Noise",
1253     "        Sharpen...",
1254     "        Blur...",
1255     "        Threshold...",
1256     "        Edge Detect...",
1257     "        Spread...",
1258     "        Shade...",
1259     "        Raise...",
1260     "        Segment...",
1261     "      F/X",
1262     "        Solarize...",
1263     "        Sepia Tone...",
1264     "        Swirl...",
1265     "        Implode...",
1266     "        Vignette...",
1267     "        Wave...",
1268     "        Oil Painting...",
1269     "        Charcoal Drawing...",
1270     "      Miscellany",
1271     "        Image Info",
1272     "        Zoom Image",
1273     "        Show Preview...",
1274     "        Show Histogram",
1275     "        Show Matte",
1276     "      Help",
1277     "      Dismiss",
1278     "",
1279     "You can make adjustments to the region of interest by moving",
1280     "the pointer to one of the rectangle corners, pressing a",
1281     "button, and dragging.  Finally, choose an image processing",
1282     "technique from the Command widget.  You can choose more than",
1283     "one image processing technique to apply to an area.",
1284     "Alternatively, you can move the region of interest before",
1285     "applying another image processing technique.  To exit, press",
1286     "Dismiss.",
1287     (char *) NULL,
1288   },
1289   *ImageRotateHelp[] =
1290   {
1291     "In rotate mode, the Command widget has these options:",
1292     "",
1293     "    Pixel Color",
1294     "      black",
1295     "      blue",
1296     "      cyan",
1297     "      green",
1298     "      gray",
1299     "      red",
1300     "      magenta",
1301     "      yellow",
1302     "      white",
1303     "      Browser...",
1304     "    Direction",
1305     "      horizontal",
1306     "      vertical",
1307     "    Help",
1308     "    Dismiss",
1309     "",
1310     "Choose a background color from the Pixel Color sub-menu.",
1311     "Additional background colors can be specified with the color",
1312     "browser.  You can change the menu colors by setting the X",
1313     "resources pen1 through pen9.",
1314     "",
1315     "If you choose the color browser and press Grab, you can",
1316     "select the background color by moving the pointer to the",
1317     "desired color on the screen and press any button.",
1318     "",
1319     "Choose a point in the image window and press this button and",
1320     "hold.  Next, move the pointer to another location in the",
1321     "image.  As you move a line connects the initial location and",
1322     "the pointer.  When you release the button, the degree of",
1323     "image rotation is determined by the slope of the line you",
1324     "just drew.  The slope is relative to the direction you",
1325     "choose from the Direction sub-menu of the Command widget.",
1326     "",
1327     "To cancel the image rotation, move the pointer back to the",
1328     "starting point of the line and release the button.",
1329     (char *) NULL,
1330   };
1331 \f
1332 /*
1333   Enumeration declarations.
1334 */
1335 typedef enum
1336 {
1337   CopyMode,
1338   CropMode,
1339   CutMode
1340 } ClipboardMode;
1341
1342 typedef enum
1343 {
1344   OpenCommand,
1345   NextCommand,
1346   FormerCommand,
1347   SelectCommand,
1348   SaveCommand,
1349   PrintCommand,
1350   DeleteCommand,
1351   NewCommand,
1352   VisualDirectoryCommand,
1353   QuitCommand,
1354   UndoCommand,
1355   RedoCommand,
1356   CutCommand,
1357   CopyCommand,
1358   PasteCommand,
1359   HalfSizeCommand,
1360   OriginalSizeCommand,
1361   DoubleSizeCommand,
1362   ResizeCommand,
1363   ApplyCommand,
1364   RefreshCommand,
1365   RestoreCommand,
1366   CropCommand,
1367   ChopCommand,
1368   FlopCommand,
1369   FlipCommand,
1370   RotateRightCommand,
1371   RotateLeftCommand,
1372   RotateCommand,
1373   ShearCommand,
1374   RollCommand,
1375   TrimCommand,
1376   HueCommand,
1377   SaturationCommand,
1378   BrightnessCommand,
1379   GammaCommand,
1380   SpiffCommand,
1381   DullCommand,
1382   ContrastStretchCommand,
1383   SigmoidalContrastCommand,
1384   NormalizeCommand,
1385   EqualizeCommand,
1386   NegateCommand,
1387   GrayscaleCommand,
1388   MapCommand,
1389   QuantizeCommand,
1390   DespeckleCommand,
1391   EmbossCommand,
1392   ReduceNoiseCommand,
1393   AddNoiseCommand,
1394   SharpenCommand,
1395   BlurCommand,
1396   ThresholdCommand,
1397   EdgeDetectCommand,
1398   SpreadCommand,
1399   ShadeCommand,
1400   RaiseCommand,
1401   SegmentCommand,
1402   SolarizeCommand,
1403   SepiaToneCommand,
1404   SwirlCommand,
1405   ImplodeCommand,
1406   VignetteCommand,
1407   WaveCommand,
1408   OilPaintCommand,
1409   CharcoalDrawCommand,
1410   AnnotateCommand,
1411   DrawCommand,
1412   ColorCommand,
1413   MatteCommand,
1414   CompositeCommand,
1415   AddBorderCommand,
1416   AddFrameCommand,
1417   CommentCommand,
1418   LaunchCommand,
1419   RegionofInterestCommand,
1420   ROIHelpCommand,
1421   ROIDismissCommand,
1422   InfoCommand,
1423   ZoomCommand,
1424   ShowPreviewCommand,
1425   ShowHistogramCommand,
1426   ShowMatteCommand,
1427   BackgroundCommand,
1428   SlideShowCommand,
1429   PreferencesCommand,
1430   HelpCommand,
1431   BrowseDocumentationCommand,
1432   VersionCommand,
1433   SaveToUndoBufferCommand,
1434   FreeBuffersCommand,
1435   NullCommand
1436 } CommandType;
1437
1438 typedef enum
1439 {
1440   AnnotateNameCommand,
1441   AnnotateFontColorCommand,
1442   AnnotateBackgroundColorCommand,
1443   AnnotateRotateCommand,
1444   AnnotateHelpCommand,
1445   AnnotateDismissCommand,
1446   TextHelpCommand,
1447   TextApplyCommand,
1448   ChopDirectionCommand,
1449   ChopHelpCommand,
1450   ChopDismissCommand,
1451   HorizontalChopCommand,
1452   VerticalChopCommand,
1453   ColorEditMethodCommand,
1454   ColorEditColorCommand,
1455   ColorEditBorderCommand,
1456   ColorEditFuzzCommand,
1457   ColorEditUndoCommand,
1458   ColorEditHelpCommand,
1459   ColorEditDismissCommand,
1460   CompositeOperatorsCommand,
1461   CompositeDissolveCommand,
1462   CompositeDisplaceCommand,
1463   CompositeHelpCommand,
1464   CompositeDismissCommand,
1465   CropHelpCommand,
1466   CropDismissCommand,
1467   RectifyCopyCommand,
1468   RectifyHelpCommand,
1469   RectifyDismissCommand,
1470   DrawElementCommand,
1471   DrawColorCommand,
1472   DrawStippleCommand,
1473   DrawWidthCommand,
1474   DrawUndoCommand,
1475   DrawHelpCommand,
1476   DrawDismissCommand,
1477   MatteEditMethod,
1478   MatteEditBorderCommand,
1479   MatteEditFuzzCommand,
1480   MatteEditValueCommand,
1481   MatteEditUndoCommand,
1482   MatteEditHelpCommand,
1483   MatteEditDismissCommand,
1484   PasteOperatorsCommand,
1485   PasteHelpCommand,
1486   PasteDismissCommand,
1487   RotateColorCommand,
1488   RotateDirectionCommand,
1489   RotateCropCommand,
1490   RotateSharpenCommand,
1491   RotateHelpCommand,
1492   RotateDismissCommand,
1493   HorizontalRotateCommand,
1494   VerticalRotateCommand,
1495   TileLoadCommand,
1496   TileNextCommand,
1497   TileFormerCommand,
1498   TileDeleteCommand,
1499   TileUpdateCommand
1500 } ModeType;
1501 \f
1502 /*
1503   Stipples.
1504 */
1505 #define BricksWidth  20
1506 #define BricksHeight  20
1507 #define DiagonalWidth  16
1508 #define DiagonalHeight  16
1509 #define HighlightWidth  8
1510 #define HighlightHeight  8
1511 #define OpaqueWidth  8
1512 #define OpaqueHeight  8
1513 #define ScalesWidth  16
1514 #define ScalesHeight  16
1515 #define ShadowWidth  8
1516 #define ShadowHeight  8
1517 #define VerticalWidth  16
1518 #define VerticalHeight  16
1519 #define WavyWidth  16
1520 #define WavyHeight  16
1521 \f
1522 /*
1523   Constant declaration.
1524 */
1525 static const int
1526   RoiDelta = 8;
1527
1528 static const unsigned char
1529   BricksBitmap[] =
1530   {
1531     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1532     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1533     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1534     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1535     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1536   },
1537   DiagonalBitmap[] =
1538   {
1539     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1540     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1541     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1542   },
1543   ScalesBitmap[] =
1544   {
1545     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1546     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1547     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1548   },
1549   VerticalBitmap[] =
1550   {
1551     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1552     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1553     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1554   },
1555   WavyBitmap[] =
1556   {
1557     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1558     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1559     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1560   };
1561 \f
1562 /*
1563   Function prototypes.
1564 */
1565 static CommandType
1566   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1567     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1568
1569 static Image
1570   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1571     Image **,ExceptionInfo *),
1572   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1573   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1574     ExceptionInfo *),
1575   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1576     ExceptionInfo *);
1577
1578 static MagickBooleanType
1579   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1580     ExceptionInfo *),
1581   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1582     ExceptionInfo *),
1583   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1584     ExceptionInfo *),
1585   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1586     ExceptionInfo *),
1587   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1588     ExceptionInfo *),
1589   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1590     ExceptionInfo *),
1591   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1592   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1593     ExceptionInfo *),
1594   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1595     ExceptionInfo *),
1596   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1597   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1599     ExceptionInfo *),
1600   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1601   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1603
1604 static void
1605   XDrawPanRectangle(Display *,XWindows *),
1606   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1607     ExceptionInfo *),
1608   XMagnifyImage(Display *,XWindows *,XEvent *),
1609   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1610   XPanImage(Display *,XWindows *,XEvent *),
1611   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1612     const KeySym),
1613   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1614   XScreenEvent(Display *,XWindows *,XEvent *),
1615   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1616 \f
1617 /*
1618 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1619 %                                                                             %
1620 %                                                                             %
1621 %                                                                             %
1622 %   D i s p l a y I m a g e s                                                 %
1623 %                                                                             %
1624 %                                                                             %
1625 %                                                                             %
1626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1627 %
1628 %  DisplayImages() displays an image sequence to any X window screen.  It
1629 %  returns a value other than 0 if successful.  Check the exception member
1630 %  of image to determine the reason for any failure.
1631 %
1632 %  The format of the DisplayImages method is:
1633 %
1634 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1635 %        Image *images,ExceptionInfo *exception)
1636 %
1637 %  A description of each parameter follows:
1638 %
1639 %    o image_info: the image info.
1640 %
1641 %    o image: the image.
1642 %
1643 %    o exception: return any errors or warnings in this structure.
1644 %
1645 */
1646 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1647   Image *images,ExceptionInfo *exception)
1648 {
1649   char
1650     *argv[1];
1651
1652   Display
1653     *display;
1654
1655   Image
1656     *image;
1657
1658   register ssize_t
1659     i;
1660
1661   size_t
1662     state;
1663
1664   XrmDatabase
1665     resource_database;
1666
1667   XResourceInfo
1668     resource_info;
1669
1670   assert(image_info != (const ImageInfo *) NULL);
1671   assert(image_info->signature == MagickSignature);
1672   assert(images != (Image *) NULL);
1673   assert(images->signature == MagickSignature);
1674   if (images->debug != MagickFalse)
1675     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1676   display=XOpenDisplay(image_info->server_name);
1677   if (display == (Display *) NULL)
1678     {
1679       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1680         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1681       return(MagickFalse);
1682     }
1683   if (exception->severity != UndefinedException)
1684     CatchException(exception);
1685   (void) XSetErrorHandler(XError);
1686   resource_database=XGetResourceDatabase(display,GetClientName());
1687   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1688   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1689   if (image_info->page != (char *) NULL)
1690     resource_info.image_geometry=AcquireString(image_info->page);
1691   resource_info.immutable=MagickTrue;
1692   argv[0]=AcquireString(GetClientName());
1693   state=DefaultState;
1694   for (i=0; (state & ExitState) == 0; i++)
1695   {
1696     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1697       break;
1698     image=GetImageFromList(images,i % GetImageListLength(images));
1699     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1700   }
1701   SetErrorHandler((ErrorHandler) NULL);
1702   SetWarningHandler((WarningHandler) NULL);
1703   argv[0]=DestroyString(argv[0]);
1704   (void) XCloseDisplay(display);
1705   XDestroyResourceInfo(&resource_info);
1706   if (exception->severity != UndefinedException)
1707     return(MagickFalse);
1708   return(MagickTrue);
1709 }
1710 \f
1711 /*
1712 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1713 %                                                                             %
1714 %                                                                             %
1715 %                                                                             %
1716 %   R e m o t e D i s p l a y C o m m a n d                                   %
1717 %                                                                             %
1718 %                                                                             %
1719 %                                                                             %
1720 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1721 %
1722 %  RemoteDisplayCommand() encourages a remote display program to display the
1723 %  specified image filename.
1724 %
1725 %  The format of the RemoteDisplayCommand method is:
1726 %
1727 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1728 %        const char *window,const char *filename,ExceptionInfo *exception)
1729 %
1730 %  A description of each parameter follows:
1731 %
1732 %    o image_info: the image info.
1733 %
1734 %    o window: Specifies the name or id of an X window.
1735 %
1736 %    o filename: the name of the image filename to display.
1737 %
1738 %    o exception: return any errors or warnings in this structure.
1739 %
1740 */
1741 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1742   const char *window,const char *filename,ExceptionInfo *exception)
1743 {
1744   Display
1745     *display;
1746
1747   MagickStatusType
1748     status;
1749
1750   assert(image_info != (const ImageInfo *) NULL);
1751   assert(image_info->signature == MagickSignature);
1752   assert(filename != (char *) NULL);
1753   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1754   display=XOpenDisplay(image_info->server_name);
1755   if (display == (Display *) NULL)
1756     {
1757       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1758         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1759       return(MagickFalse);
1760     }
1761   (void) XSetErrorHandler(XError);
1762   status=XRemoteCommand(display,window,filename);
1763   (void) XCloseDisplay(display);
1764   return(status != 0 ? MagickTrue : MagickFalse);
1765 }
1766 \f
1767 /*
1768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1769 %                                                                             %
1770 %                                                                             %
1771 %                                                                             %
1772 +   X A n n o t a t e E d i t I m a g e                                       %
1773 %                                                                             %
1774 %                                                                             %
1775 %                                                                             %
1776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1777 %
1778 %  XAnnotateEditImage() annotates the image with text.
1779 %
1780 %  The format of the XAnnotateEditImage method is:
1781 %
1782 %      MagickBooleanType XAnnotateEditImage(Display *display,
1783 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1784 %        ExceptionInfo *exception)
1785 %
1786 %  A description of each parameter follows:
1787 %
1788 %    o display: Specifies a connection to an X server;  returned from
1789 %      XOpenDisplay.
1790 %
1791 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1792 %
1793 %    o windows: Specifies a pointer to a XWindows structure.
1794 %
1795 %    o image: the image; returned from ReadImage.
1796 %
1797 */
1798
1799 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1800 {
1801   if (x > y)
1802     return(x);
1803   return(y);
1804 }
1805
1806 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1807 {
1808   if (x < y)
1809     return(x);
1810   return(y);
1811 }
1812
1813 static MagickBooleanType XAnnotateEditImage(Display *display,
1814   XResourceInfo *resource_info,XWindows *windows,Image *image,
1815   ExceptionInfo *exception)
1816 {
1817   static const char
1818     *AnnotateMenu[] =
1819     {
1820       "Font Name",
1821       "Font Color",
1822       "Box Color",
1823       "Rotate Text",
1824       "Help",
1825       "Dismiss",
1826       (char *) NULL
1827     },
1828     *TextMenu[] =
1829     {
1830       "Help",
1831       "Apply",
1832       (char *) NULL
1833     };
1834
1835   static const ModeType
1836     AnnotateCommands[] =
1837     {
1838       AnnotateNameCommand,
1839       AnnotateFontColorCommand,
1840       AnnotateBackgroundColorCommand,
1841       AnnotateRotateCommand,
1842       AnnotateHelpCommand,
1843       AnnotateDismissCommand
1844     },
1845     TextCommands[] =
1846     {
1847       TextHelpCommand,
1848       TextApplyCommand
1849     };
1850
1851   static MagickBooleanType
1852     transparent_box = MagickTrue,
1853     transparent_pen = MagickFalse;
1854
1855   static MagickRealType
1856     degrees = 0.0;
1857
1858   static unsigned int
1859     box_id = MaxNumberPens-2,
1860     font_id = 0,
1861     pen_id = 0;
1862
1863   char
1864     command[MaxTextExtent],
1865     text[MaxTextExtent];
1866
1867   const char
1868     *ColorMenu[MaxNumberPens+1];
1869
1870   Cursor
1871     cursor;
1872
1873   GC
1874     annotate_context;
1875
1876   int
1877     id,
1878     pen_number,
1879     status,
1880     x,
1881     y;
1882
1883   KeySym
1884     key_symbol;
1885
1886   register char
1887     *p;
1888
1889   register ssize_t
1890     i;
1891
1892   unsigned int
1893     height,
1894     width;
1895
1896   size_t
1897     state;
1898
1899   XAnnotateInfo
1900     *annotate_info,
1901     *previous_info;
1902
1903   XColor
1904     color;
1905
1906   XFontStruct
1907     *font_info;
1908
1909   XEvent
1910     event,
1911     text_event;
1912
1913   /*
1914     Map Command widget.
1915   */
1916   (void) CloneString(&windows->command.name,"Annotate");
1917   windows->command.data=4;
1918   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1919   (void) XMapRaised(display,windows->command.id);
1920   XClientMessage(display,windows->image.id,windows->im_protocols,
1921     windows->im_update_widget,CurrentTime);
1922   /*
1923     Track pointer until button 1 is pressed.
1924   */
1925   XQueryPosition(display,windows->image.id,&x,&y);
1926   (void) XSelectInput(display,windows->image.id,
1927     windows->image.attributes.event_mask | PointerMotionMask);
1928   cursor=XCreateFontCursor(display,XC_left_side);
1929   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1930   state=DefaultState;
1931   do
1932   {
1933     if (windows->info.mapped != MagickFalse)
1934       {
1935         /*
1936           Display pointer position.
1937         */
1938         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1939           x+windows->image.x,y+windows->image.y);
1940         XInfoWidget(display,windows,text);
1941       }
1942     /*
1943       Wait for next event.
1944     */
1945     XScreenEvent(display,windows,&event);
1946     if (event.xany.window == windows->command.id)
1947       {
1948         /*
1949           Select a command from the Command widget.
1950         */
1951         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1952         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1953         if (id < 0)
1954           continue;
1955         switch (AnnotateCommands[id])
1956         {
1957           case AnnotateNameCommand:
1958           {
1959             const char
1960               *FontMenu[MaxNumberFonts];
1961
1962             int
1963               font_number;
1964
1965             /*
1966               Initialize menu selections.
1967             */
1968             for (i=0; i < MaxNumberFonts; i++)
1969               FontMenu[i]=resource_info->font_name[i];
1970             FontMenu[MaxNumberFonts-2]="Browser...";
1971             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1972             /*
1973               Select a font name from the pop-up menu.
1974             */
1975             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1976               (const char **) FontMenu,command);
1977             if (font_number < 0)
1978               break;
1979             if (font_number == (MaxNumberFonts-2))
1980               {
1981                 static char
1982                   font_name[MaxTextExtent] = "fixed";
1983
1984                 /*
1985                   Select a font name from a browser.
1986                 */
1987                 resource_info->font_name[font_number]=font_name;
1988                 XFontBrowserWidget(display,windows,"Select",font_name);
1989                 if (*font_name == '\0')
1990                   break;
1991               }
1992             /*
1993               Initialize font info.
1994             */
1995             font_info=XLoadQueryFont(display,resource_info->font_name[
1996               font_number]);
1997             if (font_info == (XFontStruct *) NULL)
1998               {
1999                 XNoticeWidget(display,windows,"Unable to load font:",
2000                   resource_info->font_name[font_number]);
2001                 break;
2002               }
2003             font_id=(unsigned int) font_number;
2004             (void) XFreeFont(display,font_info);
2005             break;
2006           }
2007           case AnnotateFontColorCommand:
2008           {
2009             /*
2010               Initialize menu selections.
2011             */
2012             for (i=0; i < (int) (MaxNumberPens-2); i++)
2013               ColorMenu[i]=resource_info->pen_colors[i];
2014             ColorMenu[MaxNumberPens-2]="transparent";
2015             ColorMenu[MaxNumberPens-1]="Browser...";
2016             ColorMenu[MaxNumberPens]=(const char *) NULL;
2017             /*
2018               Select a pen color from the pop-up menu.
2019             */
2020             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2021               (const char **) ColorMenu,command);
2022             if (pen_number < 0)
2023               break;
2024             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2025               MagickFalse;
2026             if (transparent_pen != MagickFalse)
2027               break;
2028             if (pen_number == (MaxNumberPens-1))
2029               {
2030                 static char
2031                   color_name[MaxTextExtent] = "gray";
2032
2033                 /*
2034                   Select a pen color from a dialog.
2035                 */
2036                 resource_info->pen_colors[pen_number]=color_name;
2037                 XColorBrowserWidget(display,windows,"Select",color_name);
2038                 if (*color_name == '\0')
2039                   break;
2040               }
2041             /*
2042               Set pen color.
2043             */
2044             (void) XParseColor(display,windows->map_info->colormap,
2045               resource_info->pen_colors[pen_number],&color);
2046             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2047               (unsigned int) MaxColors,&color);
2048             windows->pixel_info->pen_colors[pen_number]=color;
2049             pen_id=(unsigned int) pen_number;
2050             break;
2051           }
2052           case AnnotateBackgroundColorCommand:
2053           {
2054             /*
2055               Initialize menu selections.
2056             */
2057             for (i=0; i < (int) (MaxNumberPens-2); i++)
2058               ColorMenu[i]=resource_info->pen_colors[i];
2059             ColorMenu[MaxNumberPens-2]="transparent";
2060             ColorMenu[MaxNumberPens-1]="Browser...";
2061             ColorMenu[MaxNumberPens]=(const char *) NULL;
2062             /*
2063               Select a pen color from the pop-up menu.
2064             */
2065             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2066               (const char **) ColorMenu,command);
2067             if (pen_number < 0)
2068               break;
2069             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2070               MagickFalse;
2071             if (transparent_box != MagickFalse)
2072               break;
2073             if (pen_number == (MaxNumberPens-1))
2074               {
2075                 static char
2076                   color_name[MaxTextExtent] = "gray";
2077
2078                 /*
2079                   Select a pen color from a dialog.
2080                 */
2081                 resource_info->pen_colors[pen_number]=color_name;
2082                 XColorBrowserWidget(display,windows,"Select",color_name);
2083                 if (*color_name == '\0')
2084                   break;
2085               }
2086             /*
2087               Set pen color.
2088             */
2089             (void) XParseColor(display,windows->map_info->colormap,
2090               resource_info->pen_colors[pen_number],&color);
2091             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2092               (unsigned int) MaxColors,&color);
2093             windows->pixel_info->pen_colors[pen_number]=color;
2094             box_id=(unsigned int) pen_number;
2095             break;
2096           }
2097           case AnnotateRotateCommand:
2098           {
2099             int
2100               entry;
2101
2102             static char
2103               angle[MaxTextExtent] = "30.0";
2104
2105             static const char
2106               *RotateMenu[] =
2107               {
2108                 "-90",
2109                 "-45",
2110                 "-30",
2111                 "0",
2112                 "30",
2113                 "45",
2114                 "90",
2115                 "180",
2116                 "Dialog...",
2117                 (char *) NULL,
2118               };
2119
2120             /*
2121               Select a command from the pop-up menu.
2122             */
2123             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2124               command);
2125             if (entry < 0)
2126               break;
2127             if (entry != 8)
2128               {
2129                 degrees=InterpretLocaleValue(RotateMenu[entry],(char **) NULL);
2130                 break;
2131               }
2132             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2133               angle);
2134             if (*angle == '\0')
2135               break;
2136             degrees=InterpretLocaleValue(angle,(char **) NULL);
2137             break;
2138           }
2139           case AnnotateHelpCommand:
2140           {
2141             XTextViewWidget(display,resource_info,windows,MagickFalse,
2142               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2143             break;
2144           }
2145           case AnnotateDismissCommand:
2146           {
2147             /*
2148               Prematurely exit.
2149             */
2150             state|=EscapeState;
2151             state|=ExitState;
2152             break;
2153           }
2154           default:
2155             break;
2156         }
2157         continue;
2158       }
2159     switch (event.type)
2160     {
2161       case ButtonPress:
2162       {
2163         if (event.xbutton.button != Button1)
2164           break;
2165         if (event.xbutton.window != windows->image.id)
2166           break;
2167         /*
2168           Change to text entering mode.
2169         */
2170         x=event.xbutton.x;
2171         y=event.xbutton.y;
2172         state|=ExitState;
2173         break;
2174       }
2175       case ButtonRelease:
2176         break;
2177       case Expose:
2178         break;
2179       case KeyPress:
2180       {
2181         if (event.xkey.window != windows->image.id)
2182           break;
2183         /*
2184           Respond to a user key press.
2185         */
2186         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2187           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2188         switch ((int) key_symbol)
2189         {
2190           case XK_Escape:
2191           case XK_F20:
2192           {
2193             /*
2194               Prematurely exit.
2195             */
2196             state|=EscapeState;
2197             state|=ExitState;
2198             break;
2199           }
2200           case XK_F1:
2201           case XK_Help:
2202           {
2203             XTextViewWidget(display,resource_info,windows,MagickFalse,
2204               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2205             break;
2206           }
2207           default:
2208           {
2209             (void) XBell(display,0);
2210             break;
2211           }
2212         }
2213         break;
2214       }
2215       case MotionNotify:
2216       {
2217         /*
2218           Map and unmap Info widget as cursor crosses its boundaries.
2219         */
2220         x=event.xmotion.x;
2221         y=event.xmotion.y;
2222         if (windows->info.mapped != MagickFalse)
2223           {
2224             if ((x < (int) (windows->info.x+windows->info.width)) &&
2225                 (y < (int) (windows->info.y+windows->info.height)))
2226               (void) XWithdrawWindow(display,windows->info.id,
2227                 windows->info.screen);
2228           }
2229         else
2230           if ((x > (int) (windows->info.x+windows->info.width)) ||
2231               (y > (int) (windows->info.y+windows->info.height)))
2232             (void) XMapWindow(display,windows->info.id);
2233         break;
2234       }
2235       default:
2236         break;
2237     }
2238   } while ((state & ExitState) == 0);
2239   (void) XSelectInput(display,windows->image.id,
2240     windows->image.attributes.event_mask);
2241   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2242   if ((state & EscapeState) != 0)
2243     return(MagickTrue);
2244   /*
2245     Set font info and check boundary conditions.
2246   */
2247   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2248   if (font_info == (XFontStruct *) NULL)
2249     {
2250       XNoticeWidget(display,windows,"Unable to load font:",
2251         resource_info->font_name[font_id]);
2252       font_info=windows->font_info;
2253     }
2254   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2255     x=(int) windows->image.width-font_info->max_bounds.width;
2256   if (y < (int) (font_info->ascent+font_info->descent))
2257     y=(int) font_info->ascent+font_info->descent;
2258   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2259       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2260     return(MagickFalse);
2261   /*
2262     Initialize annotate structure.
2263   */
2264   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2265   if (annotate_info == (XAnnotateInfo *) NULL)
2266     return(MagickFalse);
2267   XGetAnnotateInfo(annotate_info);
2268   annotate_info->x=x;
2269   annotate_info->y=y;
2270   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2271     annotate_info->stencil=OpaqueStencil;
2272   else
2273     if (transparent_box == MagickFalse)
2274       annotate_info->stencil=BackgroundStencil;
2275     else
2276       annotate_info->stencil=ForegroundStencil;
2277   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2278   annotate_info->degrees=degrees;
2279   annotate_info->font_info=font_info;
2280   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2281     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2282     sizeof(*annotate_info->text));
2283   if (annotate_info->text == (char *) NULL)
2284     return(MagickFalse);
2285   /*
2286     Create cursor and set graphic context.
2287   */
2288   cursor=XCreateFontCursor(display,XC_pencil);
2289   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2290   annotate_context=windows->image.annotate_context;
2291   (void) XSetFont(display,annotate_context,font_info->fid);
2292   (void) XSetBackground(display,annotate_context,
2293     windows->pixel_info->pen_colors[box_id].pixel);
2294   (void) XSetForeground(display,annotate_context,
2295     windows->pixel_info->pen_colors[pen_id].pixel);
2296   /*
2297     Begin annotating the image with text.
2298   */
2299   (void) CloneString(&windows->command.name,"Text");
2300   windows->command.data=0;
2301   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2302   state=DefaultState;
2303   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2304   text_event.xexpose.width=(int) font_info->max_bounds.width;
2305   text_event.xexpose.height=font_info->max_bounds.ascent+
2306     font_info->max_bounds.descent;
2307   p=annotate_info->text;
2308   do
2309   {
2310     /*
2311       Display text cursor.
2312     */
2313     *p='\0';
2314     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2315     /*
2316       Wait for next event.
2317     */
2318     XScreenEvent(display,windows,&event);
2319     if (event.xany.window == windows->command.id)
2320       {
2321         /*
2322           Select a command from the Command widget.
2323         */
2324         (void) XSetBackground(display,annotate_context,
2325           windows->pixel_info->background_color.pixel);
2326         (void) XSetForeground(display,annotate_context,
2327           windows->pixel_info->foreground_color.pixel);
2328         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2329         (void) XSetBackground(display,annotate_context,
2330           windows->pixel_info->pen_colors[box_id].pixel);
2331         (void) XSetForeground(display,annotate_context,
2332           windows->pixel_info->pen_colors[pen_id].pixel);
2333         if (id < 0)
2334           continue;
2335         switch (TextCommands[id])
2336         {
2337           case TextHelpCommand:
2338           {
2339             XTextViewWidget(display,resource_info,windows,MagickFalse,
2340               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2341             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2342             break;
2343           }
2344           case TextApplyCommand:
2345           {
2346             /*
2347               Finished annotating.
2348             */
2349             annotate_info->width=(unsigned int) XTextWidth(font_info,
2350               annotate_info->text,(int) strlen(annotate_info->text));
2351             XRefreshWindow(display,&windows->image,&text_event);
2352             state|=ExitState;
2353             break;
2354           }
2355           default:
2356             break;
2357         }
2358         continue;
2359       }
2360     /*
2361       Erase text cursor.
2362     */
2363     text_event.xexpose.x=x;
2364     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2365     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2366       (unsigned int) text_event.xexpose.width,(unsigned int)
2367       text_event.xexpose.height,MagickFalse);
2368     XRefreshWindow(display,&windows->image,&text_event);
2369     switch (event.type)
2370     {
2371       case ButtonPress:
2372       {
2373         if (event.xbutton.window != windows->image.id)
2374           break;
2375         if (event.xbutton.button == Button2)
2376           {
2377             /*
2378               Request primary selection.
2379             */
2380             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2381               windows->image.id,CurrentTime);
2382             break;
2383           }
2384         break;
2385       }
2386       case Expose:
2387       {
2388         if (event.xexpose.count == 0)
2389           {
2390             XAnnotateInfo
2391               *text_info;
2392
2393             /*
2394               Refresh Image window.
2395             */
2396             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2397             text_info=annotate_info;
2398             while (text_info != (XAnnotateInfo *) NULL)
2399             {
2400               if (annotate_info->stencil == ForegroundStencil)
2401                 (void) XDrawString(display,windows->image.id,annotate_context,
2402                   text_info->x,text_info->y,text_info->text,
2403                   (int) strlen(text_info->text));
2404               else
2405                 (void) XDrawImageString(display,windows->image.id,
2406                   annotate_context,text_info->x,text_info->y,text_info->text,
2407                   (int) strlen(text_info->text));
2408               text_info=text_info->previous;
2409             }
2410             (void) XDrawString(display,windows->image.id,annotate_context,
2411               x,y,"_",1);
2412           }
2413         break;
2414       }
2415       case KeyPress:
2416       {
2417         int
2418           length;
2419
2420         if (event.xkey.window != windows->image.id)
2421           break;
2422         /*
2423           Respond to a user key press.
2424         */
2425         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2426           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2427         *(command+length)='\0';
2428         if (((event.xkey.state & ControlMask) != 0) ||
2429             ((event.xkey.state & Mod1Mask) != 0))
2430           state|=ModifierState;
2431         if ((state & ModifierState) != 0)
2432           switch ((int) key_symbol)
2433           {
2434             case XK_u:
2435             case XK_U:
2436             {
2437               key_symbol=DeleteCommand;
2438               break;
2439             }
2440             default:
2441               break;
2442           }
2443         switch ((int) key_symbol)
2444         {
2445           case XK_BackSpace:
2446           {
2447             /*
2448               Erase one character.
2449             */
2450             if (p == annotate_info->text)
2451               {
2452                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2453                   break;
2454                 else
2455                   {
2456                     /*
2457                       Go to end of the previous line of text.
2458                     */
2459                     annotate_info=annotate_info->previous;
2460                     p=annotate_info->text;
2461                     x=annotate_info->x+annotate_info->width;
2462                     y=annotate_info->y;
2463                     if (annotate_info->width != 0)
2464                       p+=strlen(annotate_info->text);
2465                     break;
2466                   }
2467               }
2468             p--;
2469             x-=XTextWidth(font_info,p,1);
2470             text_event.xexpose.x=x;
2471             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2472             XRefreshWindow(display,&windows->image,&text_event);
2473             break;
2474           }
2475           case XK_bracketleft:
2476           {
2477             key_symbol=XK_Escape;
2478             break;
2479           }
2480           case DeleteCommand:
2481           {
2482             /*
2483               Erase the entire line of text.
2484             */
2485             while (p != annotate_info->text)
2486             {
2487               p--;
2488               x-=XTextWidth(font_info,p,1);
2489               text_event.xexpose.x=x;
2490               XRefreshWindow(display,&windows->image,&text_event);
2491             }
2492             break;
2493           }
2494           case XK_Escape:
2495           case XK_F20:
2496           {
2497             /*
2498               Finished annotating.
2499             */
2500             annotate_info->width=(unsigned int) XTextWidth(font_info,
2501               annotate_info->text,(int) strlen(annotate_info->text));
2502             XRefreshWindow(display,&windows->image,&text_event);
2503             state|=ExitState;
2504             break;
2505           }
2506           default:
2507           {
2508             /*
2509               Draw a single character on the Image window.
2510             */
2511             if ((state & ModifierState) != 0)
2512               break;
2513             if (*command == '\0')
2514               break;
2515             *p=(*command);
2516             if (annotate_info->stencil == ForegroundStencil)
2517               (void) XDrawString(display,windows->image.id,annotate_context,
2518                 x,y,p,1);
2519             else
2520               (void) XDrawImageString(display,windows->image.id,
2521                 annotate_context,x,y,p,1);
2522             x+=XTextWidth(font_info,p,1);
2523             p++;
2524             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2525               break;
2526           }
2527           case XK_Return:
2528           case XK_KP_Enter:
2529           {
2530             /*
2531               Advance to the next line of text.
2532             */
2533             *p='\0';
2534             annotate_info->width=(unsigned int) XTextWidth(font_info,
2535               annotate_info->text,(int) strlen(annotate_info->text));
2536             if (annotate_info->next != (XAnnotateInfo *) NULL)
2537               {
2538                 /*
2539                   Line of text already exists.
2540                 */
2541                 annotate_info=annotate_info->next;
2542                 x=annotate_info->x;
2543                 y=annotate_info->y;
2544                 p=annotate_info->text;
2545                 break;
2546               }
2547             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2548               sizeof(*annotate_info->next));
2549             if (annotate_info->next == (XAnnotateInfo *) NULL)
2550               return(MagickFalse);
2551             *annotate_info->next=(*annotate_info);
2552             annotate_info->next->previous=annotate_info;
2553             annotate_info=annotate_info->next;
2554             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2555               windows->image.width/MagickMax((ssize_t)
2556               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2557             if (annotate_info->text == (char *) NULL)
2558               return(MagickFalse);
2559             annotate_info->y+=annotate_info->height;
2560             if (annotate_info->y > (int) windows->image.height)
2561               annotate_info->y=(int) annotate_info->height;
2562             annotate_info->next=(XAnnotateInfo *) NULL;
2563             x=annotate_info->x;
2564             y=annotate_info->y;
2565             p=annotate_info->text;
2566             break;
2567           }
2568         }
2569         break;
2570       }
2571       case KeyRelease:
2572       {
2573         /*
2574           Respond to a user key release.
2575         */
2576         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2577           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2578         state&=(~ModifierState);
2579         break;
2580       }
2581       case SelectionNotify:
2582       {
2583         Atom
2584           type;
2585
2586         int
2587           format;
2588
2589         unsigned char
2590           *data;
2591
2592         unsigned long
2593           after,
2594           length;
2595
2596         /*
2597           Obtain response from primary selection.
2598         */
2599         if (event.xselection.property == (Atom) None)
2600           break;
2601         status=XGetWindowProperty(display,event.xselection.requestor,
2602           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2603           &type,&format,&length,&after,&data);
2604         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2605             (length == 0))
2606           break;
2607         /*
2608           Annotate Image window with primary selection.
2609         */
2610         for (i=0; i < (ssize_t) length; i++)
2611         {
2612           if ((char) data[i] != '\n')
2613             {
2614               /*
2615                 Draw a single character on the Image window.
2616               */
2617               *p=(char) data[i];
2618               (void) XDrawString(display,windows->image.id,annotate_context,
2619                 x,y,p,1);
2620               x+=XTextWidth(font_info,p,1);
2621               p++;
2622               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2623                 continue;
2624             }
2625           /*
2626             Advance to the next line of text.
2627           */
2628           *p='\0';
2629           annotate_info->width=(unsigned int) XTextWidth(font_info,
2630             annotate_info->text,(int) strlen(annotate_info->text));
2631           if (annotate_info->next != (XAnnotateInfo *) NULL)
2632             {
2633               /*
2634                 Line of text already exists.
2635               */
2636               annotate_info=annotate_info->next;
2637               x=annotate_info->x;
2638               y=annotate_info->y;
2639               p=annotate_info->text;
2640               continue;
2641             }
2642           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2643             sizeof(*annotate_info->next));
2644           if (annotate_info->next == (XAnnotateInfo *) NULL)
2645             return(MagickFalse);
2646           *annotate_info->next=(*annotate_info);
2647           annotate_info->next->previous=annotate_info;
2648           annotate_info=annotate_info->next;
2649           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2650             windows->image.width/MagickMax((ssize_t)
2651             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2652           if (annotate_info->text == (char *) NULL)
2653             return(MagickFalse);
2654           annotate_info->y+=annotate_info->height;
2655           if (annotate_info->y > (int) windows->image.height)
2656             annotate_info->y=(int) annotate_info->height;
2657           annotate_info->next=(XAnnotateInfo *) NULL;
2658           x=annotate_info->x;
2659           y=annotate_info->y;
2660           p=annotate_info->text;
2661         }
2662         (void) XFree((void *) data);
2663         break;
2664       }
2665       default:
2666         break;
2667     }
2668   } while ((state & ExitState) == 0);
2669   (void) XFreeCursor(display,cursor);
2670   /*
2671     Annotation is relative to image configuration.
2672   */
2673   width=(unsigned int) image->columns;
2674   height=(unsigned int) image->rows;
2675   x=0;
2676   y=0;
2677   if (windows->image.crop_geometry != (char *) NULL)
2678     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2679   /*
2680     Initialize annotated image.
2681   */
2682   XSetCursorState(display,windows,MagickTrue);
2683   XCheckRefreshWindows(display,windows);
2684   while (annotate_info != (XAnnotateInfo *) NULL)
2685   {
2686     if (annotate_info->width == 0)
2687       {
2688         /*
2689           No text on this line--  go to the next line of text.
2690         */
2691         previous_info=annotate_info->previous;
2692         annotate_info->text=(char *)
2693           RelinquishMagickMemory(annotate_info->text);
2694         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2695         annotate_info=previous_info;
2696         continue;
2697       }
2698     /*
2699       Determine pixel index for box and pen color.
2700     */
2701     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2702     if (windows->pixel_info->colors != 0)
2703       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2704         if (windows->pixel_info->pixels[i] ==
2705             windows->pixel_info->pen_colors[box_id].pixel)
2706           {
2707             windows->pixel_info->box_index=(unsigned short) i;
2708             break;
2709           }
2710     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2711     if (windows->pixel_info->colors != 0)
2712       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2713         if (windows->pixel_info->pixels[i] ==
2714             windows->pixel_info->pen_colors[pen_id].pixel)
2715           {
2716             windows->pixel_info->pen_index=(unsigned short) i;
2717             break;
2718           }
2719     /*
2720       Define the annotate geometry string.
2721     */
2722     annotate_info->x=(int)
2723       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2724     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2725       windows->image.y)/windows->image.ximage->height;
2726     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2727       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2728       height*annotate_info->height/windows->image.ximage->height,
2729       annotate_info->x+x,annotate_info->y+y);
2730     /*
2731       Annotate image with text.
2732     */
2733     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2734     if (status == 0)
2735       return(MagickFalse);
2736     /*
2737       Free up memory.
2738     */
2739     previous_info=annotate_info->previous;
2740     annotate_info->text=DestroyString(annotate_info->text);
2741     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2742     annotate_info=previous_info;
2743   }
2744   (void) XSetForeground(display,annotate_context,
2745     windows->pixel_info->foreground_color.pixel);
2746   (void) XSetBackground(display,annotate_context,
2747     windows->pixel_info->background_color.pixel);
2748   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2749   XSetCursorState(display,windows,MagickFalse);
2750   (void) XFreeFont(display,font_info);
2751   /*
2752     Update image configuration.
2753   */
2754   XConfigureImageColormap(display,resource_info,windows,image);
2755   (void) XConfigureImage(display,resource_info,windows,image,exception);
2756   return(MagickTrue);
2757 }
2758 \f
2759 /*
2760 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2761 %                                                                             %
2762 %                                                                             %
2763 %                                                                             %
2764 +   X B a c k g r o u n d I m a g e                                           %
2765 %                                                                             %
2766 %                                                                             %
2767 %                                                                             %
2768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2769 %
2770 %  XBackgroundImage() displays the image in the background of a window.
2771 %
2772 %  The format of the XBackgroundImage method is:
2773 %
2774 %      MagickBooleanType XBackgroundImage(Display *display,
2775 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2776 %        ExceptionInfo *exception)
2777 %
2778 %  A description of each parameter follows:
2779 %
2780 %    o display: Specifies a connection to an X server; returned from
2781 %      XOpenDisplay.
2782 %
2783 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2784 %
2785 %    o windows: Specifies a pointer to a XWindows structure.
2786 %
2787 %    o image: the image.
2788 %
2789 %    o exception: return any errors or warnings in this structure.
2790 %
2791 */
2792 static MagickBooleanType XBackgroundImage(Display *display,
2793   XResourceInfo *resource_info,XWindows *windows,Image **image,
2794   ExceptionInfo *exception)
2795 {
2796 #define BackgroundImageTag  "Background/Image"
2797
2798   int
2799     status;
2800
2801   static char
2802     window_id[MaxTextExtent] = "root";
2803
2804   XResourceInfo
2805     background_resources;
2806
2807   /*
2808     Put image in background.
2809   */
2810   status=XDialogWidget(display,windows,"Background",
2811     "Enter window id (id 0x00 selects window with pointer):",window_id);
2812   if (*window_id == '\0')
2813     return(MagickFalse);
2814   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2815     exception);
2816   XInfoWidget(display,windows,BackgroundImageTag);
2817   XSetCursorState(display,windows,MagickTrue);
2818   XCheckRefreshWindows(display,windows);
2819   background_resources=(*resource_info);
2820   background_resources.window_id=window_id;
2821   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2822   status=XDisplayBackgroundImage(display,&background_resources,*image,
2823     exception);
2824   if (status != MagickFalse)
2825     XClientMessage(display,windows->image.id,windows->im_protocols,
2826       windows->im_retain_colors,CurrentTime);
2827   XSetCursorState(display,windows,MagickFalse);
2828   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2829     exception);
2830   return(MagickTrue);
2831 }
2832 \f
2833 /*
2834 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2835 %                                                                             %
2836 %                                                                             %
2837 %                                                                             %
2838 +   X C h o p I m a g e                                                       %
2839 %                                                                             %
2840 %                                                                             %
2841 %                                                                             %
2842 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2843 %
2844 %  XChopImage() chops the X image.
2845 %
2846 %  The format of the XChopImage method is:
2847 %
2848 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2849 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2850 %
2851 %  A description of each parameter follows:
2852 %
2853 %    o display: Specifies a connection to an X server; returned from
2854 %      XOpenDisplay.
2855 %
2856 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2857 %
2858 %    o windows: Specifies a pointer to a XWindows structure.
2859 %
2860 %    o image: the image.
2861 %
2862 %    o exception: return any errors or warnings in this structure.
2863 %
2864 */
2865 static MagickBooleanType XChopImage(Display *display,
2866   XResourceInfo *resource_info,XWindows *windows,Image **image,
2867   ExceptionInfo *exception)
2868 {
2869   static const char
2870     *ChopMenu[] =
2871     {
2872       "Direction",
2873       "Help",
2874       "Dismiss",
2875       (char *) NULL
2876     };
2877
2878   static ModeType
2879     direction = HorizontalChopCommand;
2880
2881   static const ModeType
2882     ChopCommands[] =
2883     {
2884       ChopDirectionCommand,
2885       ChopHelpCommand,
2886       ChopDismissCommand
2887     },
2888     DirectionCommands[] =
2889     {
2890       HorizontalChopCommand,
2891       VerticalChopCommand
2892     };
2893
2894   char
2895     text[MaxTextExtent];
2896
2897   Image
2898     *chop_image;
2899
2900   int
2901     id,
2902     x,
2903     y;
2904
2905   MagickRealType
2906     scale_factor;
2907
2908   RectangleInfo
2909     chop_info;
2910
2911   unsigned int
2912     distance,
2913     height,
2914     width;
2915
2916   size_t
2917     state;
2918
2919   XEvent
2920     event;
2921
2922   XSegment
2923     segment_info;
2924
2925   /*
2926     Map Command widget.
2927   */
2928   (void) CloneString(&windows->command.name,"Chop");
2929   windows->command.data=1;
2930   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2931   (void) XMapRaised(display,windows->command.id);
2932   XClientMessage(display,windows->image.id,windows->im_protocols,
2933     windows->im_update_widget,CurrentTime);
2934   /*
2935     Track pointer until button 1 is pressed.
2936   */
2937   XQueryPosition(display,windows->image.id,&x,&y);
2938   (void) XSelectInput(display,windows->image.id,
2939     windows->image.attributes.event_mask | PointerMotionMask);
2940   state=DefaultState;
2941   do
2942   {
2943     if (windows->info.mapped != MagickFalse)
2944       {
2945         /*
2946           Display pointer position.
2947         */
2948         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2949           x+windows->image.x,y+windows->image.y);
2950         XInfoWidget(display,windows,text);
2951       }
2952     /*
2953       Wait for next event.
2954     */
2955     XScreenEvent(display,windows,&event);
2956     if (event.xany.window == windows->command.id)
2957       {
2958         /*
2959           Select a command from the Command widget.
2960         */
2961         id=XCommandWidget(display,windows,ChopMenu,&event);
2962         if (id < 0)
2963           continue;
2964         switch (ChopCommands[id])
2965         {
2966           case ChopDirectionCommand:
2967           {
2968             char
2969               command[MaxTextExtent];
2970
2971             static const char
2972               *Directions[] =
2973               {
2974                 "horizontal",
2975                 "vertical",
2976                 (char *) NULL,
2977               };
2978
2979             /*
2980               Select a command from the pop-up menu.
2981             */
2982             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2983             if (id >= 0)
2984               direction=DirectionCommands[id];
2985             break;
2986           }
2987           case ChopHelpCommand:
2988           {
2989             XTextViewWidget(display,resource_info,windows,MagickFalse,
2990               "Help Viewer - Image Chop",ImageChopHelp);
2991             break;
2992           }
2993           case ChopDismissCommand:
2994           {
2995             /*
2996               Prematurely exit.
2997             */
2998             state|=EscapeState;
2999             state|=ExitState;
3000             break;
3001           }
3002           default:
3003             break;
3004         }
3005         continue;
3006       }
3007     switch (event.type)
3008     {
3009       case ButtonPress:
3010       {
3011         if (event.xbutton.button != Button1)
3012           break;
3013         if (event.xbutton.window != windows->image.id)
3014           break;
3015         /*
3016           User has committed to start point of chopping line.
3017         */
3018         segment_info.x1=(short int) event.xbutton.x;
3019         segment_info.x2=(short int) event.xbutton.x;
3020         segment_info.y1=(short int) event.xbutton.y;
3021         segment_info.y2=(short int) event.xbutton.y;
3022         state|=ExitState;
3023         break;
3024       }
3025       case ButtonRelease:
3026         break;
3027       case Expose:
3028         break;
3029       case KeyPress:
3030       {
3031         char
3032           command[MaxTextExtent];
3033
3034         KeySym
3035           key_symbol;
3036
3037         if (event.xkey.window != windows->image.id)
3038           break;
3039         /*
3040           Respond to a user key press.
3041         */
3042         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3043           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3044         switch ((int) key_symbol)
3045         {
3046           case XK_Escape:
3047           case XK_F20:
3048           {
3049             /*
3050               Prematurely exit.
3051             */
3052             state|=EscapeState;
3053             state|=ExitState;
3054             break;
3055           }
3056           case XK_F1:
3057           case XK_Help:
3058           {
3059             (void) XSetFunction(display,windows->image.highlight_context,
3060               GXcopy);
3061             XTextViewWidget(display,resource_info,windows,MagickFalse,
3062               "Help Viewer - Image Chop",ImageChopHelp);
3063             (void) XSetFunction(display,windows->image.highlight_context,
3064               GXinvert);
3065             break;
3066           }
3067           default:
3068           {
3069             (void) XBell(display,0);
3070             break;
3071           }
3072         }
3073         break;
3074       }
3075       case MotionNotify:
3076       {
3077         /*
3078           Map and unmap Info widget as text cursor crosses its boundaries.
3079         */
3080         x=event.xmotion.x;
3081         y=event.xmotion.y;
3082         if (windows->info.mapped != MagickFalse)
3083           {
3084             if ((x < (int) (windows->info.x+windows->info.width)) &&
3085                 (y < (int) (windows->info.y+windows->info.height)))
3086               (void) XWithdrawWindow(display,windows->info.id,
3087                 windows->info.screen);
3088           }
3089         else
3090           if ((x > (int) (windows->info.x+windows->info.width)) ||
3091               (y > (int) (windows->info.y+windows->info.height)))
3092             (void) XMapWindow(display,windows->info.id);
3093       }
3094     }
3095   } while ((state & ExitState) == 0);
3096   (void) XSelectInput(display,windows->image.id,
3097     windows->image.attributes.event_mask);
3098   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3099   if ((state & EscapeState) != 0)
3100     return(MagickTrue);
3101   /*
3102     Draw line as pointer moves until the mouse button is released.
3103   */
3104   chop_info.width=0;
3105   chop_info.height=0;
3106   chop_info.x=0;
3107   chop_info.y=0;
3108   distance=0;
3109   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3110   state=DefaultState;
3111   do
3112   {
3113     if (distance > 9)
3114       {
3115         /*
3116           Display info and draw chopping line.
3117         */
3118         if (windows->info.mapped == MagickFalse)
3119           (void) XMapWindow(display,windows->info.id);
3120         (void) FormatLocaleString(text,MaxTextExtent,
3121           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3122           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3123         XInfoWidget(display,windows,text);
3124         XHighlightLine(display,windows->image.id,
3125           windows->image.highlight_context,&segment_info);
3126       }
3127     else
3128       if (windows->info.mapped != MagickFalse)
3129         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3130     /*
3131       Wait for next event.
3132     */
3133     XScreenEvent(display,windows,&event);
3134     if (distance > 9)
3135       XHighlightLine(display,windows->image.id,
3136         windows->image.highlight_context,&segment_info);
3137     switch (event.type)
3138     {
3139       case ButtonPress:
3140       {
3141         segment_info.x2=(short int) event.xmotion.x;
3142         segment_info.y2=(short int) event.xmotion.y;
3143         break;
3144       }
3145       case ButtonRelease:
3146       {
3147         /*
3148           User has committed to chopping line.
3149         */
3150         segment_info.x2=(short int) event.xbutton.x;
3151         segment_info.y2=(short int) event.xbutton.y;
3152         state|=ExitState;
3153         break;
3154       }
3155       case Expose:
3156         break;
3157       case MotionNotify:
3158       {
3159         segment_info.x2=(short int) event.xmotion.x;
3160         segment_info.y2=(short int) event.xmotion.y;
3161       }
3162       default:
3163         break;
3164     }
3165     /*
3166       Check boundary conditions.
3167     */
3168     if (segment_info.x2 < 0)
3169       segment_info.x2=0;
3170     else
3171       if (segment_info.x2 > windows->image.ximage->width)
3172         segment_info.x2=windows->image.ximage->width;
3173     if (segment_info.y2 < 0)
3174       segment_info.y2=0;
3175     else
3176       if (segment_info.y2 > windows->image.ximage->height)
3177         segment_info.y2=windows->image.ximage->height;
3178     distance=(unsigned int)
3179       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3180        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3181     /*
3182       Compute chopping geometry.
3183     */
3184     if (direction == HorizontalChopCommand)
3185       {
3186         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3187         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3188         chop_info.height=0;
3189         chop_info.y=0;
3190         if (segment_info.x1 > (int) segment_info.x2)
3191           {
3192             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3193             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3194           }
3195       }
3196     else
3197       {
3198         chop_info.width=0;
3199         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3200         chop_info.x=0;
3201         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3202         if (segment_info.y1 > segment_info.y2)
3203           {
3204             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3205             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3206           }
3207       }
3208   } while ((state & ExitState) == 0);
3209   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3210   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3211   if (distance <= 9)
3212     return(MagickTrue);
3213   /*
3214     Image chopping is relative to image configuration.
3215   */
3216   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3217     exception);
3218   XSetCursorState(display,windows,MagickTrue);
3219   XCheckRefreshWindows(display,windows);
3220   windows->image.window_changes.width=windows->image.ximage->width-
3221     (unsigned int) chop_info.width;
3222   windows->image.window_changes.height=windows->image.ximage->height-
3223     (unsigned int) chop_info.height;
3224   width=(unsigned int) (*image)->columns;
3225   height=(unsigned int) (*image)->rows;
3226   x=0;
3227   y=0;
3228   if (windows->image.crop_geometry != (char *) NULL)
3229     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3230   scale_factor=(MagickRealType) width/windows->image.ximage->width;
3231   chop_info.x+=x;
3232   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3233   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3234   scale_factor=(MagickRealType) height/windows->image.ximage->height;
3235   chop_info.y+=y;
3236   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3237   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3238   /*
3239     Chop image.
3240   */
3241   chop_image=ChopImage(*image,&chop_info,exception);
3242   XSetCursorState(display,windows,MagickFalse);
3243   if (chop_image == (Image *) NULL)
3244     return(MagickFalse);
3245   *image=DestroyImage(*image);
3246   *image=chop_image;
3247   /*
3248     Update image configuration.
3249   */
3250   XConfigureImageColormap(display,resource_info,windows,*image);
3251   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3252   return(MagickTrue);
3253 }
3254 \f
3255 /*
3256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3257 %                                                                             %
3258 %                                                                             %
3259 %                                                                             %
3260 +   X C o l o r E d i t I m a g e                                             %
3261 %                                                                             %
3262 %                                                                             %
3263 %                                                                             %
3264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3265 %
3266 %  XColorEditImage() allows the user to interactively change the color of one
3267 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3268 %
3269 %  The format of the XColorEditImage method is:
3270 %
3271 %      MagickBooleanType XColorEditImage(Display *display,
3272 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3273 %          ExceptionInfo *exception)
3274 %
3275 %  A description of each parameter follows:
3276 %
3277 %    o display: Specifies a connection to an X server;  returned from
3278 %      XOpenDisplay.
3279 %
3280 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3281 %
3282 %    o windows: Specifies a pointer to a XWindows structure.
3283 %
3284 %    o image: the image; returned from ReadImage.
3285 %
3286 %    o exception: return any errors or warnings in this structure.
3287 %
3288 */
3289 static MagickBooleanType XColorEditImage(Display *display,
3290   XResourceInfo *resource_info,XWindows *windows,Image **image,
3291   ExceptionInfo *exception)
3292 {
3293   static const char
3294     *ColorEditMenu[] =
3295     {
3296       "Method",
3297       "Pixel Color",
3298       "Border Color",
3299       "Fuzz",
3300       "Undo",
3301       "Help",
3302       "Dismiss",
3303       (char *) NULL
3304     };
3305
3306   static const ModeType
3307     ColorEditCommands[] =
3308     {
3309       ColorEditMethodCommand,
3310       ColorEditColorCommand,
3311       ColorEditBorderCommand,
3312       ColorEditFuzzCommand,
3313       ColorEditUndoCommand,
3314       ColorEditHelpCommand,
3315       ColorEditDismissCommand
3316     };
3317
3318   static PaintMethod
3319     method = PointMethod;
3320
3321   static unsigned int
3322     pen_id = 0;
3323
3324   static XColor
3325     border_color = { 0, 0, 0, 0, 0, 0 };
3326
3327   char
3328     command[MaxTextExtent],
3329     text[MaxTextExtent];
3330
3331   Cursor
3332     cursor;
3333
3334   int
3335     entry,
3336     id,
3337     x,
3338     x_offset,
3339     y,
3340     y_offset;
3341
3342   register Quantum
3343     *q;
3344
3345   register ssize_t
3346     i;
3347
3348   unsigned int
3349     height,
3350     width;
3351
3352   size_t
3353     state;
3354
3355   XColor
3356     color;
3357
3358   XEvent
3359     event;
3360
3361   /*
3362     Map Command widget.
3363   */
3364   (void) CloneString(&windows->command.name,"Color Edit");
3365   windows->command.data=4;
3366   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3367   (void) XMapRaised(display,windows->command.id);
3368   XClientMessage(display,windows->image.id,windows->im_protocols,
3369     windows->im_update_widget,CurrentTime);
3370   /*
3371     Make cursor.
3372   */
3373   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3374     resource_info->background_color,resource_info->foreground_color);
3375   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3376   /*
3377     Track pointer until button 1 is pressed.
3378   */
3379   XQueryPosition(display,windows->image.id,&x,&y);
3380   (void) XSelectInput(display,windows->image.id,
3381     windows->image.attributes.event_mask | PointerMotionMask);
3382   state=DefaultState;
3383   do
3384   {
3385     if (windows->info.mapped != MagickFalse)
3386       {
3387         /*
3388           Display pointer position.
3389         */
3390         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3391           x+windows->image.x,y+windows->image.y);
3392         XInfoWidget(display,windows,text);
3393       }
3394     /*
3395       Wait for next event.
3396     */
3397     XScreenEvent(display,windows,&event);
3398     if (event.xany.window == windows->command.id)
3399       {
3400         /*
3401           Select a command from the Command widget.
3402         */
3403         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3404         if (id < 0)
3405           {
3406             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3407             continue;
3408           }
3409         switch (ColorEditCommands[id])
3410         {
3411           case ColorEditMethodCommand:
3412           {
3413             char
3414               **methods;
3415
3416             /*
3417               Select a method from the pop-up menu.
3418             */
3419             methods=(char **) GetCommandOptions(MagickMethodOptions);
3420             if (methods == (char **) NULL)
3421               break;
3422             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3423               (const char **) methods,command);
3424             if (entry >= 0)
3425               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3426                 MagickFalse,methods[entry]);
3427             methods=DestroyStringList(methods);
3428             break;
3429           }
3430           case ColorEditColorCommand:
3431           {
3432             const char
3433               *ColorMenu[MaxNumberPens];
3434
3435             int
3436               pen_number;
3437
3438             /*
3439               Initialize menu selections.
3440             */
3441             for (i=0; i < (int) (MaxNumberPens-2); i++)
3442               ColorMenu[i]=resource_info->pen_colors[i];
3443             ColorMenu[MaxNumberPens-2]="Browser...";
3444             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3445             /*
3446               Select a pen color from the pop-up menu.
3447             */
3448             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3449               (const char **) ColorMenu,command);
3450             if (pen_number < 0)
3451               break;
3452             if (pen_number == (MaxNumberPens-2))
3453               {
3454                 static char
3455                   color_name[MaxTextExtent] = "gray";
3456
3457                 /*
3458                   Select a pen color from a dialog.
3459                 */
3460                 resource_info->pen_colors[pen_number]=color_name;
3461                 XColorBrowserWidget(display,windows,"Select",color_name);
3462                 if (*color_name == '\0')
3463                   break;
3464               }
3465             /*
3466               Set pen color.
3467             */
3468             (void) XParseColor(display,windows->map_info->colormap,
3469               resource_info->pen_colors[pen_number],&color);
3470             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3471               (unsigned int) MaxColors,&color);
3472             windows->pixel_info->pen_colors[pen_number]=color;
3473             pen_id=(unsigned int) pen_number;
3474             break;
3475           }
3476           case ColorEditBorderCommand:
3477           {
3478             const char
3479               *ColorMenu[MaxNumberPens];
3480
3481             int
3482               pen_number;
3483
3484             /*
3485               Initialize menu selections.
3486             */
3487             for (i=0; i < (int) (MaxNumberPens-2); i++)
3488               ColorMenu[i]=resource_info->pen_colors[i];
3489             ColorMenu[MaxNumberPens-2]="Browser...";
3490             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3491             /*
3492               Select a pen color from the pop-up menu.
3493             */
3494             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3495               (const char **) ColorMenu,command);
3496             if (pen_number < 0)
3497               break;
3498             if (pen_number == (MaxNumberPens-2))
3499               {
3500                 static char
3501                   color_name[MaxTextExtent] = "gray";
3502
3503                 /*
3504                   Select a pen color from a dialog.
3505                 */
3506                 resource_info->pen_colors[pen_number]=color_name;
3507                 XColorBrowserWidget(display,windows,"Select",color_name);
3508                 if (*color_name == '\0')
3509                   break;
3510               }
3511             /*
3512               Set border color.
3513             */
3514             (void) XParseColor(display,windows->map_info->colormap,
3515               resource_info->pen_colors[pen_number],&border_color);
3516             break;
3517           }
3518           case ColorEditFuzzCommand:
3519           {
3520             static char
3521               fuzz[MaxTextExtent];
3522
3523             static const char
3524               *FuzzMenu[] =
3525               {
3526                 "0%",
3527                 "2%",
3528                 "5%",
3529                 "10%",
3530                 "15%",
3531                 "Dialog...",
3532                 (char *) NULL,
3533               };
3534
3535             /*
3536               Select a command from the pop-up menu.
3537             */
3538             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3539               command);
3540             if (entry < 0)
3541               break;
3542             if (entry != 5)
3543               {
3544                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
3545                   QuantumRange+1.0);
3546                 break;
3547               }
3548             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3549             (void) XDialogWidget(display,windows,"Ok",
3550               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3551             if (*fuzz == '\0')
3552               break;
3553             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3554             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
3555             break;
3556           }
3557           case ColorEditUndoCommand:
3558           {
3559             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3560               image,exception);
3561             break;
3562           }
3563           case ColorEditHelpCommand:
3564           default:
3565           {
3566             XTextViewWidget(display,resource_info,windows,MagickFalse,
3567               "Help Viewer - Image Annotation",ImageColorEditHelp);
3568             break;
3569           }
3570           case ColorEditDismissCommand:
3571           {
3572             /*
3573               Prematurely exit.
3574             */
3575             state|=EscapeState;
3576             state|=ExitState;
3577             break;
3578           }
3579         }
3580         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3581         continue;
3582       }
3583     switch (event.type)
3584     {
3585       case ButtonPress:
3586       {
3587         if (event.xbutton.button != Button1)
3588           break;
3589         if ((event.xbutton.window != windows->image.id) &&
3590             (event.xbutton.window != windows->magnify.id))
3591           break;
3592         /*
3593           exit loop.
3594         */
3595         x=event.xbutton.x;
3596         y=event.xbutton.y;
3597         (void) XMagickCommand(display,resource_info,windows,
3598           SaveToUndoBufferCommand,image,exception);
3599         state|=UpdateConfigurationState;
3600         break;
3601       }
3602       case ButtonRelease:
3603       {
3604         if (event.xbutton.button != Button1)
3605           break;
3606         if ((event.xbutton.window != windows->image.id) &&
3607             (event.xbutton.window != windows->magnify.id))
3608           break;
3609         /*
3610           Update colormap information.
3611         */
3612         x=event.xbutton.x;
3613         y=event.xbutton.y;
3614         XConfigureImageColormap(display,resource_info,windows,*image);
3615         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3616         XInfoWidget(display,windows,text);
3617         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3618         state&=(~UpdateConfigurationState);
3619         break;
3620       }
3621       case Expose:
3622         break;
3623       case KeyPress:
3624       {
3625         KeySym
3626           key_symbol;
3627
3628         if (event.xkey.window == windows->magnify.id)
3629           {
3630             Window
3631               window;
3632
3633             window=windows->magnify.id;
3634             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3635           }
3636         if (event.xkey.window != windows->image.id)
3637           break;
3638         /*
3639           Respond to a user key press.
3640         */
3641         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3642           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3643         switch ((int) key_symbol)
3644         {
3645           case XK_Escape:
3646           case XK_F20:
3647           {
3648             /*
3649               Prematurely exit.
3650             */
3651             state|=ExitState;
3652             break;
3653           }
3654           case XK_F1:
3655           case XK_Help:
3656           {
3657             XTextViewWidget(display,resource_info,windows,MagickFalse,
3658               "Help Viewer - Image Annotation",ImageColorEditHelp);
3659             break;
3660           }
3661           default:
3662           {
3663             (void) XBell(display,0);
3664             break;
3665           }
3666         }
3667         break;
3668       }
3669       case MotionNotify:
3670       {
3671         /*
3672           Map and unmap Info widget as cursor crosses its boundaries.
3673         */
3674         x=event.xmotion.x;
3675         y=event.xmotion.y;
3676         if (windows->info.mapped != MagickFalse)
3677           {
3678             if ((x < (int) (windows->info.x+windows->info.width)) &&
3679                 (y < (int) (windows->info.y+windows->info.height)))
3680               (void) XWithdrawWindow(display,windows->info.id,
3681                 windows->info.screen);
3682           }
3683         else
3684           if ((x > (int) (windows->info.x+windows->info.width)) ||
3685               (y > (int) (windows->info.y+windows->info.height)))
3686             (void) XMapWindow(display,windows->info.id);
3687         break;
3688       }
3689       default:
3690         break;
3691     }
3692     if (event.xany.window == windows->magnify.id)
3693       {
3694         x=windows->magnify.x-windows->image.x;
3695         y=windows->magnify.y-windows->image.y;
3696       }
3697     x_offset=x;
3698     y_offset=y;
3699     if ((state & UpdateConfigurationState) != 0)
3700       {
3701         CacheView
3702           *image_view;
3703
3704         int
3705           x,
3706           y;
3707
3708         /*
3709           Pixel edit is relative to image configuration.
3710         */
3711         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3712           MagickTrue);
3713         color=windows->pixel_info->pen_colors[pen_id];
3714         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3715         width=(unsigned int) (*image)->columns;
3716         height=(unsigned int) (*image)->rows;
3717         x=0;
3718         y=0;
3719         if (windows->image.crop_geometry != (char *) NULL)
3720           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3721             &width,&height);
3722         x_offset=(int)
3723           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3724         y_offset=(int)
3725           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3726         if ((x_offset < 0) || (y_offset < 0))
3727           continue;
3728         if ((x_offset >= (int) (*image)->columns) ||
3729             (y_offset >= (int) (*image)->rows))
3730           continue;
3731         image_view=AcquireCacheView(*image);
3732         switch (method)
3733         {
3734           case PointMethod:
3735           default:
3736           {
3737             /*
3738               Update color information using point algorithm.
3739             */
3740             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3741               return(MagickFalse);
3742             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3743               (ssize_t) y_offset,1,1,exception);
3744             if (q == (Quantum *) NULL)
3745               break;
3746             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3747             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3748             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3749             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3750             break;
3751           }
3752           case ReplaceMethod:
3753           {
3754             PixelPacket
3755               pixel,
3756               target;
3757
3758             /*
3759               Update color information using replace algorithm.
3760             */
3761             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3762               (ssize_t) y_offset,&target,exception);
3763             if ((*image)->storage_class == DirectClass)
3764               {
3765                 for (y=0; y < (int) (*image)->rows; y++)
3766                 {
3767                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3768                     (*image)->columns,1,exception);
3769                   if (q == (Quantum *) NULL)
3770                     break;
3771                   for (x=0; x < (int) (*image)->columns; x++)
3772                   {
3773                     GetPixelPacket(*image,q,&pixel);
3774                     if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
3775                       {
3776                         SetPixelRed(*image,ScaleShortToQuantum(
3777                           color.red),q);
3778                         SetPixelGreen(*image,ScaleShortToQuantum(
3779                           color.green),q);
3780                         SetPixelBlue(*image,ScaleShortToQuantum(
3781                           color.blue),q);
3782                       }
3783                     q+=GetPixelChannels(*image);
3784                   }
3785                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3786                     break;
3787                 }
3788               }
3789             else
3790               {
3791                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3792                   if (IsFuzzyEquivalencePixelPacket(*image,(*image)->colormap+i,&target))
3793                     {
3794                       (*image)->colormap[i].red=ScaleShortToQuantum(
3795                         color.red);
3796                       (*image)->colormap[i].green=ScaleShortToQuantum(
3797                         color.green);
3798                       (*image)->colormap[i].blue=ScaleShortToQuantum(
3799                         color.blue);
3800                     }
3801                 (void) SyncImage(*image);
3802               }
3803             break;
3804           }
3805           case FloodfillMethod:
3806           case FillToBorderMethod:
3807           {
3808             DrawInfo
3809               *draw_info;
3810
3811             PixelInfo
3812               target;
3813
3814             /*
3815               Update color information using floodfill algorithm.
3816             */
3817             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3818               (ssize_t) y_offset,&target,exception);
3819             if (method == FillToBorderMethod)
3820               {
3821                 target.red=(MagickRealType)
3822                   ScaleShortToQuantum(border_color.red);
3823                 target.green=(MagickRealType)
3824                   ScaleShortToQuantum(border_color.green);
3825                 target.blue=(MagickRealType)
3826                   ScaleShortToQuantum(border_color.blue);
3827               }
3828             draw_info=CloneDrawInfo(resource_info->image_info,
3829               (DrawInfo *) NULL);
3830             (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3831               &draw_info->fill,exception);
3832             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3833               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3834               MagickFalse : MagickTrue,exception);
3835             draw_info=DestroyDrawInfo(draw_info);
3836             break;
3837           }
3838           case ResetMethod:
3839           {
3840             /*
3841               Update color information using reset algorithm.
3842             */
3843             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3844               return(MagickFalse);
3845             for (y=0; y < (int) (*image)->rows; y++)
3846             {
3847               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3848                 (*image)->columns,1,exception);
3849               if (q == (Quantum *) NULL)
3850                 break;
3851               for (x=0; x < (int) (*image)->columns; x++)
3852               {
3853                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3854                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3855                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3856                 q+=GetPixelChannels(*image);
3857               }
3858               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3859                 break;
3860             }
3861             break;
3862           }
3863         }
3864         image_view=DestroyCacheView(image_view);
3865         state&=(~UpdateConfigurationState);
3866       }
3867   } while ((state & ExitState) == 0);
3868   (void) XSelectInput(display,windows->image.id,
3869     windows->image.attributes.event_mask);
3870   XSetCursorState(display,windows,MagickFalse);
3871   (void) XFreeCursor(display,cursor);
3872   return(MagickTrue);
3873 }
3874 \f
3875 /*
3876 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3877 %                                                                             %
3878 %                                                                             %
3879 %                                                                             %
3880 +   X C o m p o s i t e I m a g e                                             %
3881 %                                                                             %
3882 %                                                                             %
3883 %                                                                             %
3884 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3885 %
3886 %  XCompositeImage() requests an image name from the user, reads the image and
3887 %  composites it with the X window image at a location the user chooses with
3888 %  the pointer.
3889 %
3890 %  The format of the XCompositeImage method is:
3891 %
3892 %      MagickBooleanType XCompositeImage(Display *display,
3893 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3894 %        ExceptionInfo *exception)
3895 %
3896 %  A description of each parameter follows:
3897 %
3898 %    o display: Specifies a connection to an X server;  returned from
3899 %      XOpenDisplay.
3900 %
3901 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3902 %
3903 %    o windows: Specifies a pointer to a XWindows structure.
3904 %
3905 %    o image: the image; returned from ReadImage.
3906 %
3907 %    o exception: return any errors or warnings in this structure.
3908 %
3909 */
3910 static MagickBooleanType XCompositeImage(Display *display,
3911   XResourceInfo *resource_info,XWindows *windows,Image *image,
3912   ExceptionInfo *exception)
3913 {
3914   static char
3915     displacement_geometry[MaxTextExtent] = "30x30",
3916     filename[MaxTextExtent] = "\0";
3917
3918   static const char
3919     *CompositeMenu[] =
3920     {
3921       "Operators",
3922       "Dissolve",
3923       "Displace",
3924       "Help",
3925       "Dismiss",
3926       (char *) NULL
3927     };
3928
3929   static CompositeOperator
3930     compose = CopyCompositeOp;
3931
3932   static const ModeType
3933     CompositeCommands[] =
3934     {
3935       CompositeOperatorsCommand,
3936       CompositeDissolveCommand,
3937       CompositeDisplaceCommand,
3938       CompositeHelpCommand,
3939       CompositeDismissCommand
3940     };
3941
3942   char
3943     text[MaxTextExtent];
3944
3945   Cursor
3946     cursor;
3947
3948   Image
3949     *composite_image;
3950
3951   int
3952     entry,
3953     id,
3954     x,
3955     y;
3956
3957   MagickRealType
3958     blend,
3959     scale_factor;
3960
3961   RectangleInfo
3962     highlight_info,
3963     composite_info;
3964
3965   unsigned int
3966     height,
3967     width;
3968
3969   size_t
3970     state;
3971
3972   XEvent
3973     event;
3974
3975   /*
3976     Request image file name from user.
3977   */
3978   XFileBrowserWidget(display,windows,"Composite",filename);
3979   if (*filename == '\0')
3980     return(MagickTrue);
3981   /*
3982     Read image.
3983   */
3984   XSetCursorState(display,windows,MagickTrue);
3985   XCheckRefreshWindows(display,windows);
3986   (void) CopyMagickString(resource_info->image_info->filename,filename,
3987     MaxTextExtent);
3988   composite_image=ReadImage(resource_info->image_info,exception);
3989   CatchException(exception);
3990   XSetCursorState(display,windows,MagickFalse);
3991   if (composite_image == (Image *) NULL)
3992     return(MagickFalse);
3993   /*
3994     Map Command widget.
3995   */
3996   (void) CloneString(&windows->command.name,"Composite");
3997   windows->command.data=1;
3998   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3999   (void) XMapRaised(display,windows->command.id);
4000   XClientMessage(display,windows->image.id,windows->im_protocols,
4001     windows->im_update_widget,CurrentTime);
4002   /*
4003     Track pointer until button 1 is pressed.
4004   */
4005   XQueryPosition(display,windows->image.id,&x,&y);
4006   (void) XSelectInput(display,windows->image.id,
4007     windows->image.attributes.event_mask | PointerMotionMask);
4008   composite_info.x=(ssize_t) windows->image.x+x;
4009   composite_info.y=(ssize_t) windows->image.y+y;
4010   composite_info.width=0;
4011   composite_info.height=0;
4012   cursor=XCreateFontCursor(display,XC_ul_angle);
4013   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4014   blend=0.0;
4015   state=DefaultState;
4016   do
4017   {
4018     if (windows->info.mapped != MagickFalse)
4019       {
4020         /*
4021           Display pointer position.
4022         */
4023         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4024           (long) composite_info.x,(long) composite_info.y);
4025         XInfoWidget(display,windows,text);
4026       }
4027     highlight_info=composite_info;
4028     highlight_info.x=composite_info.x-windows->image.x;
4029     highlight_info.y=composite_info.y-windows->image.y;
4030     XHighlightRectangle(display,windows->image.id,
4031       windows->image.highlight_context,&highlight_info);
4032     /*
4033       Wait for next event.
4034     */
4035     XScreenEvent(display,windows,&event);
4036     XHighlightRectangle(display,windows->image.id,
4037       windows->image.highlight_context,&highlight_info);
4038     if (event.xany.window == windows->command.id)
4039       {
4040         /*
4041           Select a command from the Command widget.
4042         */
4043         id=XCommandWidget(display,windows,CompositeMenu,&event);
4044         if (id < 0)
4045           continue;
4046         switch (CompositeCommands[id])
4047         {
4048           case CompositeOperatorsCommand:
4049           {
4050             char
4051               command[MaxTextExtent],
4052               **operators;
4053
4054             /*
4055               Select a command from the pop-up menu.
4056             */
4057             operators=GetCommandOptions(MagickComposeOptions);
4058             if (operators == (char **) NULL)
4059               break;
4060             entry=XMenuWidget(display,windows,CompositeMenu[id],
4061               (const char **) operators,command);
4062             if (entry >= 0)
4063               compose=(CompositeOperator) ParseCommandOption(
4064                 MagickComposeOptions,MagickFalse,operators[entry]);
4065             operators=DestroyStringList(operators);
4066             break;
4067           }
4068           case CompositeDissolveCommand:
4069           {
4070             static char
4071               factor[MaxTextExtent] = "20.0";
4072
4073             /*
4074               Dissolve the two images a given percent.
4075             */
4076             (void) XSetFunction(display,windows->image.highlight_context,
4077               GXcopy);
4078             (void) XDialogWidget(display,windows,"Dissolve",
4079               "Enter the blend factor (0.0 - 99.9%):",factor);
4080             (void) XSetFunction(display,windows->image.highlight_context,
4081               GXinvert);
4082             if (*factor == '\0')
4083               break;
4084             blend=InterpretLocaleValue(factor,(char **) NULL);
4085             compose=DissolveCompositeOp;
4086             break;
4087           }
4088           case CompositeDisplaceCommand:
4089           {
4090             /*
4091               Get horizontal and vertical scale displacement geometry.
4092             */
4093             (void) XSetFunction(display,windows->image.highlight_context,
4094               GXcopy);
4095             (void) XDialogWidget(display,windows,"Displace",
4096               "Enter the horizontal and vertical scale:",displacement_geometry);
4097             (void) XSetFunction(display,windows->image.highlight_context,
4098               GXinvert);
4099             if (*displacement_geometry == '\0')
4100               break;
4101             compose=DisplaceCompositeOp;
4102             break;
4103           }
4104           case CompositeHelpCommand:
4105           {
4106             (void) XSetFunction(display,windows->image.highlight_context,
4107               GXcopy);
4108             XTextViewWidget(display,resource_info,windows,MagickFalse,
4109               "Help Viewer - Image Composite",ImageCompositeHelp);
4110             (void) XSetFunction(display,windows->image.highlight_context,
4111               GXinvert);
4112             break;
4113           }
4114           case CompositeDismissCommand:
4115           {
4116             /*
4117               Prematurely exit.
4118             */
4119             state|=EscapeState;
4120             state|=ExitState;
4121             break;
4122           }
4123           default:
4124             break;
4125         }
4126         continue;
4127       }
4128     switch (event.type)
4129     {
4130       case ButtonPress:
4131       {
4132         if (image->debug != MagickFalse)
4133           (void) LogMagickEvent(X11Event,GetMagickModule(),
4134             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4135             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4136         if (event.xbutton.button != Button1)
4137           break;
4138         if (event.xbutton.window != windows->image.id)
4139           break;
4140         /*
4141           Change cursor.
4142         */
4143         composite_info.width=composite_image->columns;
4144         composite_info.height=composite_image->rows;
4145         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4146         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4147         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4148         break;
4149       }
4150       case ButtonRelease:
4151       {
4152         if (image->debug != MagickFalse)
4153           (void) LogMagickEvent(X11Event,GetMagickModule(),
4154             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4155             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4156         if (event.xbutton.button != Button1)
4157           break;
4158         if (event.xbutton.window != windows->image.id)
4159           break;
4160         if ((composite_info.width != 0) && (composite_info.height != 0))
4161           {
4162             /*
4163               User has selected the location of the composite image.
4164             */
4165             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4166             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4167             state|=ExitState;
4168           }
4169         break;
4170       }
4171       case Expose:
4172         break;
4173       case KeyPress:
4174       {
4175         char
4176           command[MaxTextExtent];
4177
4178         KeySym
4179           key_symbol;
4180
4181         int
4182           length;
4183
4184         if (event.xkey.window != windows->image.id)
4185           break;
4186         /*
4187           Respond to a user key press.
4188         */
4189         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4190           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4191         *(command+length)='\0';
4192         if (image->debug != MagickFalse)
4193           (void) LogMagickEvent(X11Event,GetMagickModule(),
4194             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4195         switch ((int) key_symbol)
4196         {
4197           case XK_Escape:
4198           case XK_F20:
4199           {
4200             /*
4201               Prematurely exit.
4202             */
4203             composite_image=DestroyImage(composite_image);
4204             state|=EscapeState;
4205             state|=ExitState;
4206             break;
4207           }
4208           case XK_F1:
4209           case XK_Help:
4210           {
4211             (void) XSetFunction(display,windows->image.highlight_context,
4212               GXcopy);
4213             XTextViewWidget(display,resource_info,windows,MagickFalse,
4214               "Help Viewer - Image Composite",ImageCompositeHelp);
4215             (void) XSetFunction(display,windows->image.highlight_context,
4216               GXinvert);
4217             break;
4218           }
4219           default:
4220           {
4221             (void) XBell(display,0);
4222             break;
4223           }
4224         }
4225         break;
4226       }
4227       case MotionNotify:
4228       {
4229         /*
4230           Map and unmap Info widget as text cursor crosses its boundaries.
4231         */
4232         x=event.xmotion.x;
4233         y=event.xmotion.y;
4234         if (windows->info.mapped != MagickFalse)
4235           {
4236             if ((x < (int) (windows->info.x+windows->info.width)) &&
4237                 (y < (int) (windows->info.y+windows->info.height)))
4238               (void) XWithdrawWindow(display,windows->info.id,
4239                 windows->info.screen);
4240           }
4241         else
4242           if ((x > (int) (windows->info.x+windows->info.width)) ||
4243               (y > (int) (windows->info.y+windows->info.height)))
4244             (void) XMapWindow(display,windows->info.id);
4245         composite_info.x=(ssize_t) windows->image.x+x;
4246         composite_info.y=(ssize_t) windows->image.y+y;
4247         break;
4248       }
4249       default:
4250       {
4251         if (image->debug != MagickFalse)
4252           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4253             event.type);
4254         break;
4255       }
4256     }
4257   } while ((state & ExitState) == 0);
4258   (void) XSelectInput(display,windows->image.id,
4259     windows->image.attributes.event_mask);
4260   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4261   XSetCursorState(display,windows,MagickFalse);
4262   (void) XFreeCursor(display,cursor);
4263   if ((state & EscapeState) != 0)
4264     return(MagickTrue);
4265   /*
4266     Image compositing is relative to image configuration.
4267   */
4268   XSetCursorState(display,windows,MagickTrue);
4269   XCheckRefreshWindows(display,windows);
4270   width=(unsigned int) image->columns;
4271   height=(unsigned int) image->rows;
4272   x=0;
4273   y=0;
4274   if (windows->image.crop_geometry != (char *) NULL)
4275     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4276   scale_factor=(MagickRealType) width/windows->image.ximage->width;
4277   composite_info.x+=x;
4278   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4279   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4280   scale_factor=(MagickRealType) height/windows->image.ximage->height;
4281   composite_info.y+=y;
4282   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4283   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4284   if ((composite_info.width != composite_image->columns) ||
4285       (composite_info.height != composite_image->rows))
4286     {
4287       Image
4288         *resize_image;
4289
4290       /*
4291         Scale composite image.
4292       */
4293       resize_image=ResizeImage(composite_image,composite_info.width,
4294         composite_info.height,composite_image->filter,composite_image->blur,
4295         exception);
4296       composite_image=DestroyImage(composite_image);
4297       if (resize_image == (Image *) NULL)
4298         {
4299           XSetCursorState(display,windows,MagickFalse);
4300           return(MagickFalse);
4301         }
4302       composite_image=resize_image;
4303     }
4304   if (compose == DisplaceCompositeOp)
4305     (void) SetImageArtifact(composite_image,"compose:args",
4306       displacement_geometry);
4307   if (blend != 0.0)
4308     {
4309       CacheView
4310         *image_view;
4311
4312       int
4313         y;
4314
4315       Quantum
4316         opacity;
4317
4318       register int
4319         x;
4320
4321       register Quantum
4322         *q;
4323
4324       /*
4325         Create mattes for blending.
4326       */
4327       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4328       opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4329         ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4330       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4331         return(MagickFalse);
4332       image->matte=MagickTrue;
4333       image_view=AcquireCacheView(image);
4334       for (y=0; y < (int) image->rows; y++)
4335       {
4336         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4337           exception);
4338         if (q == (Quantum *) NULL)
4339           break;
4340         for (x=0; x < (int) image->columns; x++)
4341         {
4342           SetPixelAlpha(image,opacity,q);
4343           q+=GetPixelChannels(image);
4344         }
4345         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4346           break;
4347       }
4348       image_view=DestroyCacheView(image_view);
4349     }
4350   /*
4351     Composite image with X Image window.
4352   */
4353   (void) CompositeImage(image,compose,composite_image,composite_info.x,
4354     composite_info.y);
4355   composite_image=DestroyImage(composite_image);
4356   XSetCursorState(display,windows,MagickFalse);
4357   /*
4358     Update image configuration.
4359   */
4360   XConfigureImageColormap(display,resource_info,windows,image);
4361   (void) XConfigureImage(display,resource_info,windows,image,exception);
4362   return(MagickTrue);
4363 }
4364 \f
4365 /*
4366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4367 %                                                                             %
4368 %                                                                             %
4369 %                                                                             %
4370 +   X C o n f i g u r e I m a g e                                             %
4371 %                                                                             %
4372 %                                                                             %
4373 %                                                                             %
4374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4375 %
4376 %  XConfigureImage() creates a new X image.  It also notifies the window
4377 %  manager of the new image size and configures the transient widows.
4378 %
4379 %  The format of the XConfigureImage method is:
4380 %
4381 %      MagickBooleanType XConfigureImage(Display *display,
4382 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4383 %        ExceptionInfo *exception)
4384 %
4385 %  A description of each parameter follows:
4386 %
4387 %    o display: Specifies a connection to an X server; returned from
4388 %      XOpenDisplay.
4389 %
4390 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4391 %
4392 %    o windows: Specifies a pointer to a XWindows structure.
4393 %
4394 %    o image: the image.
4395 %
4396 %    o exception: return any errors or warnings in this structure.
4397 %
4398 %    o exception: return any errors or warnings in this structure.
4399 %
4400 */
4401 static MagickBooleanType XConfigureImage(Display *display,
4402   XResourceInfo *resource_info,XWindows *windows,Image *image,
4403   ExceptionInfo *exception)
4404 {
4405   char
4406     geometry[MaxTextExtent];
4407
4408   MagickStatusType
4409     status;
4410
4411   size_t
4412     mask,
4413     height,
4414     width;
4415
4416   ssize_t
4417     x,
4418     y;
4419
4420   XSizeHints
4421     *size_hints;
4422
4423   XWindowChanges
4424     window_changes;
4425
4426   /*
4427     Dismiss if window dimensions are zero.
4428   */
4429   width=(unsigned int) windows->image.window_changes.width;
4430   height=(unsigned int) windows->image.window_changes.height;
4431   if (image->debug != MagickFalse)
4432     (void) LogMagickEvent(X11Event,GetMagickModule(),
4433       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4434       windows->image.ximage->height,(double) width,(double) height);
4435   if ((width*height) == 0)
4436     return(MagickTrue);
4437   x=0;
4438   y=0;
4439   /*
4440     Resize image to fit Image window dimensions.
4441   */
4442   XSetCursorState(display,windows,MagickTrue);
4443   (void) XFlush(display);
4444   if (((int) width != windows->image.ximage->width) ||
4445       ((int) height != windows->image.ximage->height))
4446     image->taint=MagickTrue;
4447   windows->magnify.x=(int)
4448     width*windows->magnify.x/windows->image.ximage->width;
4449   windows->magnify.y=(int)
4450     height*windows->magnify.y/windows->image.ximage->height;
4451   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4452   windows->image.y=(int)
4453     (height*windows->image.y/windows->image.ximage->height);
4454   status=XMakeImage(display,resource_info,&windows->image,image,
4455     (unsigned int) width,(unsigned int) height,exception);
4456   if (status == MagickFalse)
4457     XNoticeWidget(display,windows,"Unable to configure X image:",
4458       windows->image.name);
4459   /*
4460     Notify window manager of the new configuration.
4461   */
4462   if (resource_info->image_geometry != (char *) NULL)
4463     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4464       resource_info->image_geometry);
4465   else
4466     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4467       XDisplayWidth(display,windows->image.screen),
4468       XDisplayHeight(display,windows->image.screen));
4469   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4470   window_changes.width=(int) width;
4471   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4472     window_changes.width=XDisplayWidth(display,windows->image.screen);
4473   window_changes.height=(int) height;
4474   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4475     window_changes.height=XDisplayHeight(display,windows->image.screen);
4476   mask=(size_t) (CWWidth | CWHeight);
4477   if (resource_info->backdrop)
4478     {
4479       mask|=CWX | CWY;
4480       window_changes.x=(int)
4481         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4482       window_changes.y=(int)
4483         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4484     }
4485   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4486     (unsigned int) mask,&window_changes);
4487   (void) XClearWindow(display,windows->image.id);
4488   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4489   /*
4490     Update Magnify window configuration.
4491   */
4492   if (windows->magnify.mapped != MagickFalse)
4493     XMakeMagnifyImage(display,windows);
4494   windows->pan.crop_geometry=windows->image.crop_geometry;
4495   XBestIconSize(display,&windows->pan,image);
4496   while (((windows->pan.width << 1) < MaxIconSize) &&
4497          ((windows->pan.height << 1) < MaxIconSize))
4498   {
4499     windows->pan.width<<=1;
4500     windows->pan.height<<=1;
4501   }
4502   if (windows->pan.geometry != (char *) NULL)
4503     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4504       &windows->pan.width,&windows->pan.height);
4505   window_changes.width=(int) windows->pan.width;
4506   window_changes.height=(int) windows->pan.height;
4507   size_hints=XAllocSizeHints();
4508   if (size_hints != (XSizeHints *) NULL)
4509     {
4510       /*
4511         Set new size hints.
4512       */
4513       size_hints->flags=PSize | PMinSize | PMaxSize;
4514       size_hints->width=window_changes.width;
4515       size_hints->height=window_changes.height;
4516       size_hints->min_width=size_hints->width;
4517       size_hints->min_height=size_hints->height;
4518       size_hints->max_width=size_hints->width;
4519       size_hints->max_height=size_hints->height;
4520       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4521       (void) XFree((void *) size_hints);
4522     }
4523   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4524     (unsigned int) (CWWidth | CWHeight),&window_changes);
4525   /*
4526     Update icon window configuration.
4527   */
4528   windows->icon.crop_geometry=windows->image.crop_geometry;
4529   XBestIconSize(display,&windows->icon,image);
4530   window_changes.width=(int) windows->icon.width;
4531   window_changes.height=(int) windows->icon.height;
4532   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4533     (unsigned int) (CWWidth | CWHeight),&window_changes);
4534   XSetCursorState(display,windows,MagickFalse);
4535   return(status != 0 ? MagickTrue : MagickFalse);
4536 }
4537 \f
4538 /*
4539 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4540 %                                                                             %
4541 %                                                                             %
4542 %                                                                             %
4543 +   X C r o p I m a g e                                                       %
4544 %                                                                             %
4545 %                                                                             %
4546 %                                                                             %
4547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4548 %
4549 %  XCropImage() allows the user to select a region of the image and crop, copy,
4550 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4551 %  the image with XPasteImage.
4552 %
4553 %  The format of the XCropImage method is:
4554 %
4555 %      MagickBooleanType XCropImage(Display *display,
4556 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4557 %        const ClipboardMode mode,ExceptionInfo *exception)
4558 %
4559 %  A description of each parameter follows:
4560 %
4561 %    o display: Specifies a connection to an X server; returned from
4562 %      XOpenDisplay.
4563 %
4564 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4565 %
4566 %    o windows: Specifies a pointer to a XWindows structure.
4567 %
4568 %    o image: the image; returned from ReadImage.
4569 %
4570 %    o mode: This unsigned value specified whether the image should be
4571 %      cropped, copied, or cut.
4572 %
4573 %    o exception: return any errors or warnings in this structure.
4574 %
4575 */
4576 static MagickBooleanType XCropImage(Display *display,
4577   XResourceInfo *resource_info,XWindows *windows,Image *image,
4578   const ClipboardMode mode,ExceptionInfo *exception)
4579 {
4580   static const char
4581     *CropModeMenu[] =
4582     {
4583       "Help",
4584       "Dismiss",
4585       (char *) NULL
4586     },
4587     *RectifyModeMenu[] =
4588     {
4589       "Crop",
4590       "Help",
4591       "Dismiss",
4592       (char *) NULL
4593     };
4594
4595   static const ModeType
4596     CropCommands[] =
4597     {
4598       CropHelpCommand,
4599       CropDismissCommand
4600     },
4601     RectifyCommands[] =
4602     {
4603       RectifyCopyCommand,
4604       RectifyHelpCommand,
4605       RectifyDismissCommand
4606     };
4607
4608   CacheView
4609     *image_view;
4610
4611   char
4612     command[MaxTextExtent],
4613     text[MaxTextExtent];
4614
4615   Cursor
4616     cursor;
4617
4618   int
4619     id,
4620     x,
4621     y;
4622
4623   KeySym
4624     key_symbol;
4625
4626   Image
4627     *crop_image;
4628
4629   MagickRealType
4630     scale_factor;
4631
4632   RectangleInfo
4633     crop_info,
4634     highlight_info;
4635
4636   register Quantum
4637     *q;
4638
4639   unsigned int
4640     height,
4641     width;
4642
4643   size_t
4644     state;
4645
4646   XEvent
4647     event;
4648
4649   /*
4650     Map Command widget.
4651   */
4652   switch (mode)
4653   {
4654     case CopyMode:
4655     {
4656       (void) CloneString(&windows->command.name,"Copy");
4657       break;
4658     }
4659     case CropMode:
4660     {
4661       (void) CloneString(&windows->command.name,"Crop");
4662       break;
4663     }
4664     case CutMode:
4665     {
4666       (void) CloneString(&windows->command.name,"Cut");
4667       break;
4668     }
4669   }
4670   RectifyModeMenu[0]=windows->command.name;
4671   windows->command.data=0;
4672   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4673   (void) XMapRaised(display,windows->command.id);
4674   XClientMessage(display,windows->image.id,windows->im_protocols,
4675     windows->im_update_widget,CurrentTime);
4676   /*
4677     Track pointer until button 1 is pressed.
4678   */
4679   XQueryPosition(display,windows->image.id,&x,&y);
4680   (void) XSelectInput(display,windows->image.id,
4681     windows->image.attributes.event_mask | PointerMotionMask);
4682   crop_info.x=(ssize_t) windows->image.x+x;
4683   crop_info.y=(ssize_t) windows->image.y+y;
4684   crop_info.width=0;
4685   crop_info.height=0;
4686   cursor=XCreateFontCursor(display,XC_fleur);
4687   state=DefaultState;
4688   do
4689   {
4690     if (windows->info.mapped != MagickFalse)
4691       {
4692         /*
4693           Display pointer position.
4694         */
4695         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4696           (long) crop_info.x,(long) crop_info.y);
4697         XInfoWidget(display,windows,text);
4698       }
4699     /*
4700       Wait for next event.
4701     */
4702     XScreenEvent(display,windows,&event);
4703     if (event.xany.window == windows->command.id)
4704       {
4705         /*
4706           Select a command from the Command widget.
4707         */
4708         id=XCommandWidget(display,windows,CropModeMenu,&event);
4709         if (id < 0)
4710           continue;
4711         switch (CropCommands[id])
4712         {
4713           case CropHelpCommand:
4714           {
4715             switch (mode)
4716             {
4717               case CopyMode:
4718               {
4719                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4720                   "Help Viewer - Image Copy",ImageCopyHelp);
4721                 break;
4722               }
4723               case CropMode:
4724               {
4725                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4726                   "Help Viewer - Image Crop",ImageCropHelp);
4727                 break;
4728               }
4729               case CutMode:
4730               {
4731                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4732                   "Help Viewer - Image Cut",ImageCutHelp);
4733                 break;
4734               }
4735             }
4736             break;
4737           }
4738           case CropDismissCommand:
4739           {
4740             /*
4741               Prematurely exit.
4742             */
4743             state|=EscapeState;
4744             state|=ExitState;
4745             break;
4746           }
4747           default:
4748             break;
4749         }
4750         continue;
4751       }
4752     switch (event.type)
4753     {
4754       case ButtonPress:
4755       {
4756         if (event.xbutton.button != Button1)
4757           break;
4758         if (event.xbutton.window != windows->image.id)
4759           break;
4760         /*
4761           Note first corner of cropping rectangle-- exit loop.
4762         */
4763         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4764         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4765         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4766         state|=ExitState;
4767         break;
4768       }
4769       case ButtonRelease:
4770         break;
4771       case Expose:
4772         break;
4773       case KeyPress:
4774       {
4775         if (event.xkey.window != windows->image.id)
4776           break;
4777         /*
4778           Respond to a user key press.
4779         */
4780         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4781           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4782         switch ((int) key_symbol)
4783         {
4784           case XK_Escape:
4785           case XK_F20:
4786           {
4787             /*
4788               Prematurely exit.
4789             */
4790             state|=EscapeState;
4791             state|=ExitState;
4792             break;
4793           }
4794           case XK_F1:
4795           case XK_Help:
4796           {
4797             switch (mode)
4798             {
4799               case CopyMode:
4800               {
4801                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4802                   "Help Viewer - Image Copy",ImageCopyHelp);
4803                 break;
4804               }
4805               case CropMode:
4806               {
4807                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4808                   "Help Viewer - Image Crop",ImageCropHelp);
4809                 break;
4810               }
4811               case CutMode:
4812               {
4813                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4814                   "Help Viewer - Image Cut",ImageCutHelp);
4815                 break;
4816               }
4817             }
4818             break;
4819           }
4820           default:
4821           {
4822             (void) XBell(display,0);
4823             break;
4824           }
4825         }
4826         break;
4827       }
4828       case MotionNotify:
4829       {
4830         if (event.xmotion.window != windows->image.id)
4831           break;
4832         /*
4833           Map and unmap Info widget as text cursor crosses its boundaries.
4834         */
4835         x=event.xmotion.x;
4836         y=event.xmotion.y;
4837         if (windows->info.mapped != MagickFalse)
4838           {
4839             if ((x < (int) (windows->info.x+windows->info.width)) &&
4840                 (y < (int) (windows->info.y+windows->info.height)))
4841               (void) XWithdrawWindow(display,windows->info.id,
4842                 windows->info.screen);
4843           }
4844         else
4845           if ((x > (int) (windows->info.x+windows->info.width)) ||
4846               (y > (int) (windows->info.y+windows->info.height)))
4847             (void) XMapWindow(display,windows->info.id);
4848         crop_info.x=(ssize_t) windows->image.x+x;
4849         crop_info.y=(ssize_t) windows->image.y+y;
4850         break;
4851       }
4852       default:
4853         break;
4854     }
4855   } while ((state & ExitState) == 0);
4856   (void) XSelectInput(display,windows->image.id,
4857     windows->image.attributes.event_mask);
4858   if ((state & EscapeState) != 0)
4859     {
4860       /*
4861         User want to exit without cropping.
4862       */
4863       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4864       (void) XFreeCursor(display,cursor);
4865       return(MagickTrue);
4866     }
4867   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4868   do
4869   {
4870     /*
4871       Size rectangle as pointer moves until the mouse button is released.
4872     */
4873     x=(int) crop_info.x;
4874     y=(int) crop_info.y;
4875     crop_info.width=0;
4876     crop_info.height=0;
4877     state=DefaultState;
4878     do
4879     {
4880       highlight_info=crop_info;
4881       highlight_info.x=crop_info.x-windows->image.x;
4882       highlight_info.y=crop_info.y-windows->image.y;
4883       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4884         {
4885           /*
4886             Display info and draw cropping rectangle.
4887           */
4888           if (windows->info.mapped == MagickFalse)
4889             (void) XMapWindow(display,windows->info.id);
4890           (void) FormatLocaleString(text,MaxTextExtent,
4891             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4892             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4893           XInfoWidget(display,windows,text);
4894           XHighlightRectangle(display,windows->image.id,
4895             windows->image.highlight_context,&highlight_info);
4896         }
4897       else
4898         if (windows->info.mapped != MagickFalse)
4899           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4900       /*
4901         Wait for next event.
4902       */
4903       XScreenEvent(display,windows,&event);
4904       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4905         XHighlightRectangle(display,windows->image.id,
4906           windows->image.highlight_context,&highlight_info);
4907       switch (event.type)
4908       {
4909         case ButtonPress:
4910         {
4911           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4912           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4913           break;
4914         }
4915         case ButtonRelease:
4916         {
4917           /*
4918             User has committed to cropping rectangle.
4919           */
4920           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4921           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4922           XSetCursorState(display,windows,MagickFalse);
4923           state|=ExitState;
4924           windows->command.data=0;
4925           (void) XCommandWidget(display,windows,RectifyModeMenu,
4926             (XEvent *) NULL);
4927           break;
4928         }
4929         case Expose:
4930           break;
4931         case MotionNotify:
4932         {
4933           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4934           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4935         }
4936         default:
4937           break;
4938       }
4939       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4940           ((state & ExitState) != 0))
4941         {
4942           /*
4943             Check boundary conditions.
4944           */
4945           if (crop_info.x < 0)
4946             crop_info.x=0;
4947           else
4948             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4949               crop_info.x=(ssize_t) windows->image.ximage->width;
4950           if ((int) crop_info.x < x)
4951             crop_info.width=(unsigned int) (x-crop_info.x);
4952           else
4953             {
4954               crop_info.width=(unsigned int) (crop_info.x-x);
4955               crop_info.x=(ssize_t) x;
4956             }
4957           if (crop_info.y < 0)
4958             crop_info.y=0;
4959           else
4960             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4961               crop_info.y=(ssize_t) windows->image.ximage->height;
4962           if ((int) crop_info.y < y)
4963             crop_info.height=(unsigned int) (y-crop_info.y);
4964           else
4965             {
4966               crop_info.height=(unsigned int) (crop_info.y-y);
4967               crop_info.y=(ssize_t) y;
4968             }
4969         }
4970     } while ((state & ExitState) == 0);
4971     /*
4972       Wait for user to grab a corner of the rectangle or press return.
4973     */
4974     state=DefaultState;
4975     (void) XMapWindow(display,windows->info.id);
4976     do
4977     {
4978       if (windows->info.mapped != MagickFalse)
4979         {
4980           /*
4981             Display pointer position.
4982           */
4983           (void) FormatLocaleString(text,MaxTextExtent,
4984             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4985             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4986           XInfoWidget(display,windows,text);
4987         }
4988       highlight_info=crop_info;
4989       highlight_info.x=crop_info.x-windows->image.x;
4990       highlight_info.y=crop_info.y-windows->image.y;
4991       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4992         {
4993           state|=EscapeState;
4994           state|=ExitState;
4995           break;
4996         }
4997       XHighlightRectangle(display,windows->image.id,
4998         windows->image.highlight_context,&highlight_info);
4999       XScreenEvent(display,windows,&event);
5000       if (event.xany.window == windows->command.id)
5001         {
5002           /*
5003             Select a command from the Command widget.
5004           */
5005           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5006           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5007           (void) XSetFunction(display,windows->image.highlight_context,
5008             GXinvert);
5009           XHighlightRectangle(display,windows->image.id,
5010             windows->image.highlight_context,&highlight_info);
5011           if (id >= 0)
5012             switch (RectifyCommands[id])
5013             {
5014               case RectifyCopyCommand:
5015               {
5016                 state|=ExitState;
5017                 break;
5018               }
5019               case RectifyHelpCommand:
5020               {
5021                 (void) XSetFunction(display,windows->image.highlight_context,
5022                   GXcopy);
5023                 switch (mode)
5024                 {
5025                   case CopyMode:
5026                   {
5027                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5028                       "Help Viewer - Image Copy",ImageCopyHelp);
5029                     break;
5030                   }
5031                   case CropMode:
5032                   {
5033                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5034                       "Help Viewer - Image Crop",ImageCropHelp);
5035                     break;
5036                   }
5037                   case CutMode:
5038                   {
5039                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5040                       "Help Viewer - Image Cut",ImageCutHelp);
5041                     break;
5042                   }
5043                 }
5044                 (void) XSetFunction(display,windows->image.highlight_context,
5045                   GXinvert);
5046                 break;
5047               }
5048               case RectifyDismissCommand:
5049               {
5050                 /*
5051                   Prematurely exit.
5052                 */
5053                 state|=EscapeState;
5054                 state|=ExitState;
5055                 break;
5056               }
5057               default:
5058                 break;
5059             }
5060           continue;
5061         }
5062       XHighlightRectangle(display,windows->image.id,
5063         windows->image.highlight_context,&highlight_info);
5064       switch (event.type)
5065       {
5066         case ButtonPress:
5067         {
5068           if (event.xbutton.button != Button1)
5069             break;
5070           if (event.xbutton.window != windows->image.id)
5071             break;
5072           x=windows->image.x+event.xbutton.x;
5073           y=windows->image.y+event.xbutton.y;
5074           if ((x < (int) (crop_info.x+RoiDelta)) &&
5075               (x > (int) (crop_info.x-RoiDelta)) &&
5076               (y < (int) (crop_info.y+RoiDelta)) &&
5077               (y > (int) (crop_info.y-RoiDelta)))
5078             {
5079               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5080               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5081               state|=UpdateConfigurationState;
5082               break;
5083             }
5084           if ((x < (int) (crop_info.x+RoiDelta)) &&
5085               (x > (int) (crop_info.x-RoiDelta)) &&
5086               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5087               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5088             {
5089               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5090               state|=UpdateConfigurationState;
5091               break;
5092             }
5093           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5094               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5095               (y < (int) (crop_info.y+RoiDelta)) &&
5096               (y > (int) (crop_info.y-RoiDelta)))
5097             {
5098               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5099               state|=UpdateConfigurationState;
5100               break;
5101             }
5102           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5103               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5104               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5105               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5106             {
5107               state|=UpdateConfigurationState;
5108               break;
5109             }
5110         }
5111         case ButtonRelease:
5112         {
5113           if (event.xbutton.window == windows->pan.id)
5114             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5115                 (highlight_info.y != crop_info.y-windows->image.y))
5116               XHighlightRectangle(display,windows->image.id,
5117                 windows->image.highlight_context,&highlight_info);
5118           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5119             event.xbutton.time);
5120           break;
5121         }
5122         case Expose:
5123         {
5124           if (event.xexpose.window == windows->image.id)
5125             if (event.xexpose.count == 0)
5126               {
5127                 event.xexpose.x=(int) highlight_info.x;
5128                 event.xexpose.y=(int) highlight_info.y;
5129                 event.xexpose.width=(int) highlight_info.width;
5130                 event.xexpose.height=(int) highlight_info.height;
5131                 XRefreshWindow(display,&windows->image,&event);
5132               }
5133           if (event.xexpose.window == windows->info.id)
5134             if (event.xexpose.count == 0)
5135               XInfoWidget(display,windows,text);
5136           break;
5137         }
5138         case KeyPress:
5139         {
5140           if (event.xkey.window != windows->image.id)
5141             break;
5142           /*
5143             Respond to a user key press.
5144           */
5145           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5146             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5147           switch ((int) key_symbol)
5148           {
5149             case XK_Escape:
5150             case XK_F20:
5151               state|=EscapeState;
5152             case XK_Return:
5153             {
5154               state|=ExitState;
5155               break;
5156             }
5157             case XK_Home:
5158             case XK_KP_Home:
5159             {
5160               crop_info.x=(ssize_t) (windows->image.width/2L-
5161                 crop_info.width/2L);
5162               crop_info.y=(ssize_t) (windows->image.height/2L-
5163                 crop_info.height/2L);
5164               break;
5165             }
5166             case XK_Left:
5167             case XK_KP_Left:
5168             {
5169               crop_info.x--;
5170               break;
5171             }
5172             case XK_Up:
5173             case XK_KP_Up:
5174             case XK_Next:
5175             {
5176               crop_info.y--;
5177               break;
5178             }
5179             case XK_Right:
5180             case XK_KP_Right:
5181             {
5182               crop_info.x++;
5183               break;
5184             }
5185             case XK_Prior:
5186             case XK_Down:
5187             case XK_KP_Down:
5188             {
5189               crop_info.y++;
5190               break;
5191             }
5192             case XK_F1:
5193             case XK_Help:
5194             {
5195               (void) XSetFunction(display,windows->image.highlight_context,
5196                 GXcopy);
5197               switch (mode)
5198               {
5199                 case CopyMode:
5200                 {
5201                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5202                     "Help Viewer - Image Copy",ImageCopyHelp);
5203                   break;
5204                 }
5205                 case CropMode:
5206                 {
5207                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5208                     "Help Viewer - Image Cropg",ImageCropHelp);
5209                   break;
5210                 }
5211                 case CutMode:
5212                 {
5213                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5214                     "Help Viewer - Image Cutg",ImageCutHelp);
5215                   break;
5216                 }
5217               }
5218               (void) XSetFunction(display,windows->image.highlight_context,
5219                 GXinvert);
5220               break;
5221             }
5222             default:
5223             {
5224               (void) XBell(display,0);
5225               break;
5226             }
5227           }
5228           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5229             event.xkey.time);
5230           break;
5231         }
5232         case KeyRelease:
5233           break;
5234         case MotionNotify:
5235         {
5236           if (event.xmotion.window != windows->image.id)
5237             break;
5238           /*
5239             Map and unmap Info widget as text cursor crosses its boundaries.
5240           */
5241           x=event.xmotion.x;
5242           y=event.xmotion.y;
5243           if (windows->info.mapped != MagickFalse)
5244             {
5245               if ((x < (int) (windows->info.x+windows->info.width)) &&
5246                   (y < (int) (windows->info.y+windows->info.height)))
5247                 (void) XWithdrawWindow(display,windows->info.id,
5248                   windows->info.screen);
5249             }
5250           else
5251             if ((x > (int) (windows->info.x+windows->info.width)) ||
5252                 (y > (int) (windows->info.y+windows->info.height)))
5253               (void) XMapWindow(display,windows->info.id);
5254           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5255           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5256           break;
5257         }
5258         case SelectionRequest:
5259         {
5260           XSelectionEvent
5261             notify;
5262
5263           XSelectionRequestEvent
5264             *request;
5265
5266           /*
5267             Set primary selection.
5268           */
5269           (void) FormatLocaleString(text,MaxTextExtent,
5270             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5271             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5272           request=(&(event.xselectionrequest));
5273           (void) XChangeProperty(request->display,request->requestor,
5274             request->property,request->target,8,PropModeReplace,
5275             (unsigned char *) text,(int) strlen(text));
5276           notify.type=SelectionNotify;
5277           notify.display=request->display;
5278           notify.requestor=request->requestor;
5279           notify.selection=request->selection;
5280           notify.target=request->target;
5281           notify.time=request->time;
5282           if (request->property == None)
5283             notify.property=request->target;
5284           else
5285             notify.property=request->property;
5286           (void) XSendEvent(request->display,request->requestor,False,0,
5287             (XEvent *) &notify);
5288         }
5289         default:
5290           break;
5291       }
5292       if ((state & UpdateConfigurationState) != 0)
5293         {
5294           (void) XPutBackEvent(display,&event);
5295           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5296           break;
5297         }
5298     } while ((state & ExitState) == 0);
5299   } while ((state & ExitState) == 0);
5300   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5301   XSetCursorState(display,windows,MagickFalse);
5302   if ((state & EscapeState) != 0)
5303     return(MagickTrue);
5304   if (mode == CropMode)
5305     if (((int) crop_info.width != windows->image.ximage->width) ||
5306         ((int) crop_info.height != windows->image.ximage->height))
5307       {
5308         /*
5309           Reconfigure Image window as defined by cropping rectangle.
5310         */
5311         XSetCropGeometry(display,windows,&crop_info,image);
5312         windows->image.window_changes.width=(int) crop_info.width;
5313         windows->image.window_changes.height=(int) crop_info.height;
5314         (void) XConfigureImage(display,resource_info,windows,image,exception);
5315         return(MagickTrue);
5316       }
5317   /*
5318     Copy image before applying image transforms.
5319   */
5320   XSetCursorState(display,windows,MagickTrue);
5321   XCheckRefreshWindows(display,windows);
5322   width=(unsigned int) image->columns;
5323   height=(unsigned int) image->rows;
5324   x=0;
5325   y=0;
5326   if (windows->image.crop_geometry != (char *) NULL)
5327     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5328   scale_factor=(MagickRealType) width/windows->image.ximage->width;
5329   crop_info.x+=x;
5330   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5331   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5332   scale_factor=(MagickRealType) height/windows->image.ximage->height;
5333   crop_info.y+=y;
5334   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5335   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5336   crop_image=CropImage(image,&crop_info,exception);
5337   XSetCursorState(display,windows,MagickFalse);
5338   if (crop_image == (Image *) NULL)
5339     return(MagickFalse);
5340   if (resource_info->copy_image != (Image *) NULL)
5341     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5342   resource_info->copy_image=crop_image;
5343   if (mode == CopyMode)
5344     {
5345       (void) XConfigureImage(display,resource_info,windows,image,exception);
5346       return(MagickTrue);
5347     }
5348   /*
5349     Cut image.
5350   */
5351   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5352     return(MagickFalse);
5353   image->matte=MagickTrue;
5354   image_view=AcquireCacheView(image);
5355   for (y=0; y < (int) crop_info.height; y++)
5356   {
5357     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5358       crop_info.width,1,exception);
5359     if (q == (Quantum *) NULL)
5360       break;
5361     for (x=0; x < (int) crop_info.width; x++)
5362     {
5363       SetPixelAlpha(image,TransparentAlpha,q);
5364       q+=GetPixelChannels(image);
5365     }
5366     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5367       break;
5368   }
5369   image_view=DestroyCacheView(image_view);
5370   /*
5371     Update image configuration.
5372   */
5373   XConfigureImageColormap(display,resource_info,windows,image);
5374   (void) XConfigureImage(display,resource_info,windows,image,exception);
5375   return(MagickTrue);
5376 }
5377 \f
5378 /*
5379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5380 %                                                                             %
5381 %                                                                             %
5382 %                                                                             %
5383 +   X D r a w I m a g e                                                       %
5384 %                                                                             %
5385 %                                                                             %
5386 %                                                                             %
5387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5388 %
5389 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5390 %  the image.
5391 %
5392 %  The format of the XDrawEditImage method is:
5393 %
5394 %      MagickBooleanType XDrawEditImage(Display *display,
5395 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5396 %        ExceptionInfo *exception)
5397 %
5398 %  A description of each parameter follows:
5399 %
5400 %    o display: Specifies a connection to an X server; returned from
5401 %      XOpenDisplay.
5402 %
5403 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5404 %
5405 %    o windows: Specifies a pointer to a XWindows structure.
5406 %
5407 %    o image: the image.
5408 %
5409 %    o exception: return any errors or warnings in this structure.
5410 %
5411 */
5412 static MagickBooleanType XDrawEditImage(Display *display,
5413   XResourceInfo *resource_info,XWindows *windows,Image **image,
5414   ExceptionInfo *exception)
5415 {
5416   static const char
5417     *DrawMenu[] =
5418     {
5419       "Element",
5420       "Color",
5421       "Stipple",
5422       "Width",
5423       "Undo",
5424       "Help",
5425       "Dismiss",
5426       (char *) NULL
5427     };
5428
5429   static ElementType
5430     element = PointElement;
5431
5432   static const ModeType
5433     DrawCommands[] =
5434     {
5435       DrawElementCommand,
5436       DrawColorCommand,
5437       DrawStippleCommand,
5438       DrawWidthCommand,
5439       DrawUndoCommand,
5440       DrawHelpCommand,
5441       DrawDismissCommand
5442     };
5443
5444   static Pixmap
5445     stipple = (Pixmap) NULL;
5446
5447   static unsigned int
5448     pen_id = 0,
5449     line_width = 1;
5450
5451   char
5452     command[MaxTextExtent],
5453     text[MaxTextExtent];
5454
5455   Cursor
5456     cursor;
5457
5458   int
5459     entry,
5460     id,
5461     number_coordinates,
5462     x,
5463     y;
5464
5465   MagickRealType
5466     degrees;
5467
5468   MagickStatusType
5469     status;
5470
5471   RectangleInfo
5472     rectangle_info;
5473
5474   register int
5475     i;
5476
5477   unsigned int
5478     distance,
5479     height,
5480     max_coordinates,
5481     width;
5482
5483   size_t
5484     state;
5485
5486   Window
5487     root_window;
5488
5489   XDrawInfo
5490     draw_info;
5491
5492   XEvent
5493     event;
5494
5495   XPoint
5496     *coordinate_info;
5497
5498   XSegment
5499     line_info;
5500
5501   /*
5502     Allocate polygon info.
5503   */
5504   max_coordinates=2048;
5505   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5506     sizeof(*coordinate_info));
5507   if (coordinate_info == (XPoint *) NULL)
5508     {
5509       (void) ThrowMagickException(exception,GetMagickModule(),
5510         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5511       return(MagickFalse);
5512     }
5513   /*
5514     Map Command widget.
5515   */
5516   (void) CloneString(&windows->command.name,"Draw");
5517   windows->command.data=4;
5518   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5519   (void) XMapRaised(display,windows->command.id);
5520   XClientMessage(display,windows->image.id,windows->im_protocols,
5521     windows->im_update_widget,CurrentTime);
5522   /*
5523     Wait for first button press.
5524   */
5525   root_window=XRootWindow(display,XDefaultScreen(display));
5526   draw_info.stencil=OpaqueStencil;
5527   status=MagickTrue;
5528   cursor=XCreateFontCursor(display,XC_tcross);
5529   for ( ; ; )
5530   {
5531     XQueryPosition(display,windows->image.id,&x,&y);
5532     (void) XSelectInput(display,windows->image.id,
5533       windows->image.attributes.event_mask | PointerMotionMask);
5534     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5535     state=DefaultState;
5536     do
5537     {
5538       if (windows->info.mapped != MagickFalse)
5539         {
5540           /*
5541             Display pointer position.
5542           */
5543           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5544             x+windows->image.x,y+windows->image.y);
5545           XInfoWidget(display,windows,text);
5546         }
5547       /*
5548         Wait for next event.
5549       */
5550       XScreenEvent(display,windows,&event);
5551       if (event.xany.window == windows->command.id)
5552         {
5553           /*
5554             Select a command from the Command widget.
5555           */
5556           id=XCommandWidget(display,windows,DrawMenu,&event);
5557           if (id < 0)
5558             continue;
5559           switch (DrawCommands[id])
5560           {
5561             case DrawElementCommand:
5562             {
5563               static const char
5564                 *Elements[] =
5565                 {
5566                   "point",
5567                   "line",
5568                   "rectangle",
5569                   "fill rectangle",
5570                   "circle",
5571                   "fill circle",
5572                   "ellipse",
5573                   "fill ellipse",
5574                   "polygon",
5575                   "fill polygon",
5576                   (char *) NULL,
5577                 };
5578
5579               /*
5580                 Select a command from the pop-up menu.
5581               */
5582               element=(ElementType) (XMenuWidget(display,windows,
5583                 DrawMenu[id],Elements,command)+1);
5584               break;
5585             }
5586             case DrawColorCommand:
5587             {
5588               const char
5589                 *ColorMenu[MaxNumberPens+1];
5590
5591               int
5592                 pen_number;
5593
5594               MagickBooleanType
5595                 transparent;
5596
5597               XColor
5598                 color;
5599
5600               /*
5601                 Initialize menu selections.
5602               */
5603               for (i=0; i < (int) (MaxNumberPens-2); i++)
5604                 ColorMenu[i]=resource_info->pen_colors[i];
5605               ColorMenu[MaxNumberPens-2]="transparent";
5606               ColorMenu[MaxNumberPens-1]="Browser...";
5607               ColorMenu[MaxNumberPens]=(char *) NULL;
5608               /*
5609                 Select a pen color from the pop-up menu.
5610               */
5611               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5612                 (const char **) ColorMenu,command);
5613               if (pen_number < 0)
5614                 break;
5615               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5616                 MagickFalse;
5617               if (transparent != MagickFalse)
5618                 {
5619                   draw_info.stencil=TransparentStencil;
5620                   break;
5621                 }
5622               if (pen_number == (MaxNumberPens-1))
5623                 {
5624                   static char
5625                     color_name[MaxTextExtent] = "gray";
5626
5627                   /*
5628                     Select a pen color from a dialog.
5629                   */
5630                   resource_info->pen_colors[pen_number]=color_name;
5631                   XColorBrowserWidget(display,windows,"Select",color_name);
5632                   if (*color_name == '\0')
5633                     break;
5634                 }
5635               /*
5636                 Set pen color.
5637               */
5638               (void) XParseColor(display,windows->map_info->colormap,
5639                 resource_info->pen_colors[pen_number],&color);
5640               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5641                 (unsigned int) MaxColors,&color);
5642               windows->pixel_info->pen_colors[pen_number]=color;
5643               pen_id=(unsigned int) pen_number;
5644               draw_info.stencil=OpaqueStencil;
5645               break;
5646             }
5647             case DrawStippleCommand:
5648             {
5649               Image
5650                 *stipple_image;
5651
5652               ImageInfo
5653                 *image_info;
5654
5655               int
5656                 status;
5657
5658               static char
5659                 filename[MaxTextExtent] = "\0";
5660
5661               static const char
5662                 *StipplesMenu[] =
5663                 {
5664                   "Brick",
5665                   "Diagonal",
5666                   "Scales",
5667                   "Vertical",
5668                   "Wavy",
5669                   "Translucent",
5670                   "Opaque",
5671                   (char *) NULL,
5672                   (char *) NULL,
5673                 };
5674
5675               /*
5676                 Select a command from the pop-up menu.
5677               */
5678               StipplesMenu[7]="Open...";
5679               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5680                 command);
5681               if (entry < 0)
5682                 break;
5683               if (stipple != (Pixmap) NULL)
5684                 (void) XFreePixmap(display,stipple);
5685               stipple=(Pixmap) NULL;
5686               if (entry != 7)
5687                 {
5688                   switch (entry)
5689                   {
5690                     case 0:
5691                     {
5692                       stipple=XCreateBitmapFromData(display,root_window,
5693                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5694                       break;
5695                     }
5696                     case 1:
5697                     {
5698                       stipple=XCreateBitmapFromData(display,root_window,
5699                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5700                       break;
5701                     }
5702                     case 2:
5703                     {
5704                       stipple=XCreateBitmapFromData(display,root_window,
5705                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5706                       break;
5707                     }
5708                     case 3:
5709                     {
5710                       stipple=XCreateBitmapFromData(display,root_window,
5711                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5712                       break;
5713                     }
5714                     case 4:
5715                     {
5716                       stipple=XCreateBitmapFromData(display,root_window,
5717                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5718                       break;
5719                     }
5720                     case 5:
5721                     {
5722                       stipple=XCreateBitmapFromData(display,root_window,
5723                         (char *) HighlightBitmap,HighlightWidth,
5724                         HighlightHeight);
5725                       break;
5726                     }
5727                     case 6:
5728                     default:
5729                     {
5730                       stipple=XCreateBitmapFromData(display,root_window,
5731                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5732                       break;
5733                     }
5734                   }
5735                   break;
5736                 }
5737               XFileBrowserWidget(display,windows,"Stipple",filename);
5738               if (*filename == '\0')
5739                 break;
5740               /*
5741                 Read image.
5742               */
5743               XSetCursorState(display,windows,MagickTrue);
5744               XCheckRefreshWindows(display,windows);
5745               image_info=AcquireImageInfo();
5746               (void) CopyMagickString(image_info->filename,filename,
5747                 MaxTextExtent);
5748               stipple_image=ReadImage(image_info,exception);
5749               CatchException(exception);
5750               XSetCursorState(display,windows,MagickFalse);
5751               if (stipple_image == (Image *) NULL)
5752                 break;
5753               (void) AcquireUniqueFileResource(filename);
5754               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5755                 "xbm:%s",filename);
5756               (void) WriteImage(image_info,stipple_image,exception);
5757               stipple_image=DestroyImage(stipple_image);
5758               image_info=DestroyImageInfo(image_info);
5759               status=XReadBitmapFile(display,root_window,filename,&width,
5760                 &height,&stipple,&x,&y);
5761               (void) RelinquishUniqueFileResource(filename);
5762               if ((status != BitmapSuccess) != 0)
5763                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5764                   filename);
5765               break;
5766             }
5767             case DrawWidthCommand:
5768             {
5769               static char
5770                 width[MaxTextExtent] = "0";
5771
5772               static const char
5773                 *WidthsMenu[] =
5774                 {
5775                   "1",
5776                   "2",
5777                   "4",
5778                   "8",
5779                   "16",
5780                   "Dialog...",
5781                   (char *) NULL,
5782                 };
5783
5784               /*
5785                 Select a command from the pop-up menu.
5786               */
5787               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5788                 command);
5789               if (entry < 0)
5790                 break;
5791               if (entry != 5)
5792                 {
5793                   line_width=(unsigned int) StringToUnsignedLong(
5794                     WidthsMenu[entry]);
5795                   break;
5796                 }
5797               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5798                 width);
5799               if (*width == '\0')
5800                 break;
5801               line_width=(unsigned int) StringToUnsignedLong(width);
5802               break;
5803             }
5804             case DrawUndoCommand:
5805             {
5806               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5807                 image,exception);
5808               break;
5809             }
5810             case DrawHelpCommand:
5811             {
5812               XTextViewWidget(display,resource_info,windows,MagickFalse,
5813                 "Help Viewer - Image Rotation",ImageDrawHelp);
5814               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5815               break;
5816             }
5817             case DrawDismissCommand:
5818             {
5819               /*
5820                 Prematurely exit.
5821               */
5822               state|=EscapeState;
5823               state|=ExitState;
5824               break;
5825             }
5826             default:
5827               break;
5828           }
5829           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5830           continue;
5831         }
5832       switch (event.type)
5833       {
5834         case ButtonPress:
5835         {
5836           if (event.xbutton.button != Button1)
5837             break;
5838           if (event.xbutton.window != windows->image.id)
5839             break;
5840           /*
5841             exit loop.
5842           */
5843           x=event.xbutton.x;
5844           y=event.xbutton.y;
5845           state|=ExitState;
5846           break;
5847         }
5848         case ButtonRelease:
5849           break;
5850         case Expose:
5851           break;
5852         case KeyPress:
5853         {
5854           KeySym
5855             key_symbol;
5856
5857           if (event.xkey.window != windows->image.id)
5858             break;
5859           /*
5860             Respond to a user key press.
5861           */
5862           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5863             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5864           switch ((int) key_symbol)
5865           {
5866             case XK_Escape:
5867             case XK_F20:
5868             {
5869               /*
5870                 Prematurely exit.
5871               */
5872               state|=EscapeState;
5873               state|=ExitState;
5874               break;
5875             }
5876             case XK_F1:
5877             case XK_Help:
5878             {
5879               XTextViewWidget(display,resource_info,windows,MagickFalse,
5880                 "Help Viewer - Image Rotation",ImageDrawHelp);
5881               break;
5882             }
5883             default:
5884             {
5885               (void) XBell(display,0);
5886               break;
5887             }
5888           }
5889           break;
5890         }
5891         case MotionNotify:
5892         {
5893           /*
5894             Map and unmap Info widget as text cursor crosses its boundaries.
5895           */
5896           x=event.xmotion.x;
5897           y=event.xmotion.y;
5898           if (windows->info.mapped != MagickFalse)
5899             {
5900               if ((x < (int) (windows->info.x+windows->info.width)) &&
5901                   (y < (int) (windows->info.y+windows->info.height)))
5902                 (void) XWithdrawWindow(display,windows->info.id,
5903                   windows->info.screen);
5904             }
5905           else
5906             if ((x > (int) (windows->info.x+windows->info.width)) ||
5907                 (y > (int) (windows->info.y+windows->info.height)))
5908               (void) XMapWindow(display,windows->info.id);
5909           break;
5910         }
5911       }
5912     } while ((state & ExitState) == 0);
5913     (void) XSelectInput(display,windows->image.id,
5914       windows->image.attributes.event_mask);
5915     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5916     if ((state & EscapeState) != 0)
5917       break;
5918     /*
5919       Draw element as pointer moves until the button is released.
5920     */
5921     distance=0;
5922     degrees=0.0;
5923     line_info.x1=x;
5924     line_info.y1=y;
5925     line_info.x2=x;
5926     line_info.y2=y;
5927     rectangle_info.x=(ssize_t) x;
5928     rectangle_info.y=(ssize_t) y;
5929     rectangle_info.width=0;
5930     rectangle_info.height=0;
5931     number_coordinates=1;
5932     coordinate_info->x=x;
5933     coordinate_info->y=y;
5934     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5935     state=DefaultState;
5936     do
5937     {
5938       switch (element)
5939       {
5940         case PointElement:
5941         default:
5942         {
5943           if (number_coordinates > 1)
5944             {
5945               (void) XDrawLines(display,windows->image.id,
5946                 windows->image.highlight_context,coordinate_info,
5947                 number_coordinates,CoordModeOrigin);
5948               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5949                 coordinate_info[number_coordinates-1].x,
5950                 coordinate_info[number_coordinates-1].y);
5951               XInfoWidget(display,windows,text);
5952             }
5953           break;
5954         }
5955         case LineElement:
5956         {
5957           if (distance > 9)
5958             {
5959               /*
5960                 Display angle of the line.
5961               */
5962               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5963                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5964               (void) FormatLocaleString(text,MaxTextExtent," %g",
5965                 (double) degrees);
5966               XInfoWidget(display,windows,text);
5967               XHighlightLine(display,windows->image.id,
5968                 windows->image.highlight_context,&line_info);
5969             }
5970           else
5971             if (windows->info.mapped != MagickFalse)
5972               (void) XWithdrawWindow(display,windows->info.id,
5973                 windows->info.screen);
5974           break;
5975         }
5976         case RectangleElement:
5977         case FillRectangleElement:
5978         {
5979           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5980             {
5981               /*
5982                 Display info and draw drawing rectangle.
5983               */
5984               (void) FormatLocaleString(text,MaxTextExtent,
5985                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5986                 (double) rectangle_info.height,(double) rectangle_info.x,
5987                 (double) rectangle_info.y);
5988               XInfoWidget(display,windows,text);
5989               XHighlightRectangle(display,windows->image.id,
5990                 windows->image.highlight_context,&rectangle_info);
5991             }
5992           else
5993             if (windows->info.mapped != MagickFalse)
5994               (void) XWithdrawWindow(display,windows->info.id,
5995                 windows->info.screen);
5996           break;
5997         }
5998         case CircleElement:
5999         case FillCircleElement:
6000         case EllipseElement:
6001         case FillEllipseElement:
6002         {
6003           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6004             {
6005               /*
6006                 Display info and draw drawing rectangle.
6007               */
6008               (void) FormatLocaleString(text,MaxTextExtent,
6009                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6010                 (double) rectangle_info.height,(double) rectangle_info.x,
6011                 (double) rectangle_info.y);
6012               XInfoWidget(display,windows,text);
6013               XHighlightEllipse(display,windows->image.id,
6014                 windows->image.highlight_context,&rectangle_info);
6015             }
6016           else
6017             if (windows->info.mapped != MagickFalse)
6018               (void) XWithdrawWindow(display,windows->info.id,
6019                 windows->info.screen);
6020           break;
6021         }
6022         case PolygonElement:
6023         case FillPolygonElement:
6024         {
6025           if (number_coordinates > 1)
6026             (void) XDrawLines(display,windows->image.id,
6027               windows->image.highlight_context,coordinate_info,
6028               number_coordinates,CoordModeOrigin);
6029           if (distance > 9)
6030             {
6031               /*
6032                 Display angle of the line.
6033               */
6034               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6035                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6036               (void) FormatLocaleString(text,MaxTextExtent," %g",
6037                 (double) degrees);
6038               XInfoWidget(display,windows,text);
6039               XHighlightLine(display,windows->image.id,
6040                 windows->image.highlight_context,&line_info);
6041             }
6042           else
6043             if (windows->info.mapped != MagickFalse)
6044               (void) XWithdrawWindow(display,windows->info.id,
6045                 windows->info.screen);
6046           break;
6047         }
6048       }
6049       /*
6050         Wait for next event.
6051       */
6052       XScreenEvent(display,windows,&event);
6053       switch (element)
6054       {
6055         case PointElement:
6056         default:
6057         {
6058           if (number_coordinates > 1)
6059             (void) XDrawLines(display,windows->image.id,
6060               windows->image.highlight_context,coordinate_info,
6061               number_coordinates,CoordModeOrigin);
6062           break;
6063         }
6064         case LineElement:
6065         {
6066           if (distance > 9)
6067             XHighlightLine(display,windows->image.id,
6068               windows->image.highlight_context,&line_info);
6069           break;
6070         }
6071         case RectangleElement:
6072         case FillRectangleElement:
6073         {
6074           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6075             XHighlightRectangle(display,windows->image.id,
6076               windows->image.highlight_context,&rectangle_info);
6077           break;
6078         }
6079         case CircleElement:
6080         case FillCircleElement:
6081         case EllipseElement:
6082         case FillEllipseElement:
6083         {
6084           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6085             XHighlightEllipse(display,windows->image.id,
6086               windows->image.highlight_context,&rectangle_info);
6087           break;
6088         }
6089         case PolygonElement:
6090         case FillPolygonElement:
6091         {
6092           if (number_coordinates > 1)
6093             (void) XDrawLines(display,windows->image.id,
6094               windows->image.highlight_context,coordinate_info,
6095               number_coordinates,CoordModeOrigin);
6096           if (distance > 9)
6097             XHighlightLine(display,windows->image.id,
6098               windows->image.highlight_context,&line_info);
6099           break;
6100         }
6101       }
6102       switch (event.type)
6103       {
6104         case ButtonPress:
6105           break;
6106         case ButtonRelease:
6107         {
6108           /*
6109             User has committed to element.
6110           */
6111           line_info.x2=event.xbutton.x;
6112           line_info.y2=event.xbutton.y;
6113           rectangle_info.x=(ssize_t) event.xbutton.x;
6114           rectangle_info.y=(ssize_t) event.xbutton.y;
6115           coordinate_info[number_coordinates].x=event.xbutton.x;
6116           coordinate_info[number_coordinates].y=event.xbutton.y;
6117           if (((element != PolygonElement) &&
6118                (element != FillPolygonElement)) || (distance <= 9))
6119             {
6120               state|=ExitState;
6121               break;
6122             }
6123           number_coordinates++;
6124           if (number_coordinates < (int) max_coordinates)
6125             {
6126               line_info.x1=event.xbutton.x;
6127               line_info.y1=event.xbutton.y;
6128               break;
6129             }
6130           max_coordinates<<=1;
6131           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6132             max_coordinates,sizeof(*coordinate_info));
6133           if (coordinate_info == (XPoint *) NULL)
6134             (void) ThrowMagickException(exception,GetMagickModule(),
6135               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6136           break;
6137         }
6138         case Expose:
6139           break;
6140         case MotionNotify:
6141         {
6142           if (event.xmotion.window != windows->image.id)
6143             break;
6144           if (element != PointElement)
6145             {
6146               line_info.x2=event.xmotion.x;
6147               line_info.y2=event.xmotion.y;
6148               rectangle_info.x=(ssize_t) event.xmotion.x;
6149               rectangle_info.y=(ssize_t) event.xmotion.y;
6150               break;
6151             }
6152           coordinate_info[number_coordinates].x=event.xbutton.x;
6153           coordinate_info[number_coordinates].y=event.xbutton.y;
6154           number_coordinates++;
6155           if (number_coordinates < (int) max_coordinates)
6156             break;
6157           max_coordinates<<=1;
6158           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6159             max_coordinates,sizeof(*coordinate_info));
6160           if (coordinate_info == (XPoint *) NULL)
6161             (void) ThrowMagickException(exception,GetMagickModule(),
6162               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6163           break;
6164         }
6165         default:
6166           break;
6167       }
6168       /*
6169         Check boundary conditions.
6170       */
6171       if (line_info.x2 < 0)
6172         line_info.x2=0;
6173       else
6174         if (line_info.x2 > (int) windows->image.width)
6175           line_info.x2=(short) windows->image.width;
6176       if (line_info.y2 < 0)
6177         line_info.y2=0;
6178       else
6179         if (line_info.y2 > (int) windows->image.height)
6180           line_info.y2=(short) windows->image.height;
6181       distance=(unsigned int)
6182         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6183          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6184       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6185           ((state & ExitState) != 0))
6186         {
6187           if (rectangle_info.x < 0)
6188             rectangle_info.x=0;
6189           else
6190             if (rectangle_info.x > (ssize_t) windows->image.width)
6191               rectangle_info.x=(ssize_t) windows->image.width;
6192           if ((int) rectangle_info.x < x)
6193             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6194           else
6195             {
6196               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6197               rectangle_info.x=(ssize_t) x;
6198             }
6199           if (rectangle_info.y < 0)
6200             rectangle_info.y=0;
6201           else
6202             if (rectangle_info.y > (ssize_t) windows->image.height)
6203               rectangle_info.y=(ssize_t) windows->image.height;
6204           if ((int) rectangle_info.y < y)
6205             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6206           else
6207             {
6208               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6209               rectangle_info.y=(ssize_t) y;
6210             }
6211         }
6212     } while ((state & ExitState) == 0);
6213     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6214     if ((element == PointElement) || (element == PolygonElement) ||
6215         (element == FillPolygonElement))
6216       {
6217         /*
6218           Determine polygon bounding box.
6219         */
6220         rectangle_info.x=(ssize_t) coordinate_info->x;
6221         rectangle_info.y=(ssize_t) coordinate_info->y;
6222         x=coordinate_info->x;
6223         y=coordinate_info->y;
6224         for (i=1; i < number_coordinates; i++)
6225         {
6226           if (coordinate_info[i].x > x)
6227             x=coordinate_info[i].x;
6228           if (coordinate_info[i].y > y)
6229             y=coordinate_info[i].y;
6230           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6231             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6232           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6233             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6234         }
6235         rectangle_info.width=(size_t) (x-rectangle_info.x);
6236         rectangle_info.height=(size_t) (y-rectangle_info.y);
6237         for (i=0; i < number_coordinates; i++)
6238         {
6239           coordinate_info[i].x-=rectangle_info.x;
6240           coordinate_info[i].y-=rectangle_info.y;
6241         }
6242       }
6243     else
6244       if (distance <= 9)
6245         continue;
6246       else
6247         if ((element == RectangleElement) ||
6248             (element == CircleElement) || (element == EllipseElement))
6249           {
6250             rectangle_info.width--;
6251             rectangle_info.height--;
6252           }
6253     /*
6254       Drawing is relative to image configuration.
6255     */
6256     draw_info.x=(int) rectangle_info.x;
6257     draw_info.y=(int) rectangle_info.y;
6258     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6259       image,exception);
6260     width=(unsigned int) (*image)->columns;
6261     height=(unsigned int) (*image)->rows;
6262     x=0;
6263     y=0;
6264     if (windows->image.crop_geometry != (char *) NULL)
6265       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6266     draw_info.x+=windows->image.x-(line_width/2);
6267     if (draw_info.x < 0)
6268       draw_info.x=0;
6269     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6270     draw_info.y+=windows->image.y-(line_width/2);
6271     if (draw_info.y < 0)
6272       draw_info.y=0;
6273     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6274     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6275     if (draw_info.width > (unsigned int) (*image)->columns)
6276       draw_info.width=(unsigned int) (*image)->columns;
6277     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6278     if (draw_info.height > (unsigned int) (*image)->rows)
6279       draw_info.height=(unsigned int) (*image)->rows;
6280     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6281       width*draw_info.width/windows->image.ximage->width,
6282       height*draw_info.height/windows->image.ximage->height,
6283       draw_info.x+x,draw_info.y+y);
6284     /*
6285       Initialize drawing attributes.
6286     */
6287     draw_info.degrees=0.0;
6288     draw_info.element=element;
6289     draw_info.stipple=stipple;
6290     draw_info.line_width=line_width;
6291     draw_info.line_info=line_info;
6292     if (line_info.x1 > (int) (line_width/2))
6293       draw_info.line_info.x1=(short) line_width/2;
6294     if (line_info.y1 > (int) (line_width/2))
6295       draw_info.line_info.y1=(short) line_width/2;
6296     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6297     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6298     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6299       {
6300         draw_info.line_info.x2=(-draw_info.line_info.x2);
6301         draw_info.line_info.y2=(-draw_info.line_info.y2);
6302       }
6303     if (draw_info.line_info.x2 < 0)
6304       {
6305         draw_info.line_info.x2=(-draw_info.line_info.x2);
6306         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6307       }
6308     if (draw_info.line_info.y2 < 0)
6309       {
6310         draw_info.line_info.y2=(-draw_info.line_info.y2);
6311         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6312       }
6313     draw_info.rectangle_info=rectangle_info;
6314     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6315       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6316     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6317       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6318     draw_info.number_coordinates=(unsigned int) number_coordinates;
6319     draw_info.coordinate_info=coordinate_info;
6320     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6321     /*
6322       Draw element on image.
6323     */
6324     XSetCursorState(display,windows,MagickTrue);
6325     XCheckRefreshWindows(display,windows);
6326     status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6327     XSetCursorState(display,windows,MagickFalse);
6328     /*
6329       Update image colormap and return to image drawing.
6330     */
6331     XConfigureImageColormap(display,resource_info,windows,*image);
6332     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6333   }
6334   XSetCursorState(display,windows,MagickFalse);
6335   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6336   return(status != 0 ? MagickTrue : MagickFalse);
6337 }
6338 \f
6339 /*
6340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6341 %                                                                             %
6342 %                                                                             %
6343 %                                                                             %
6344 +   X D r a w P a n R e c t a n g l e                                         %
6345 %                                                                             %
6346 %                                                                             %
6347 %                                                                             %
6348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6349 %
6350 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6351 %  displays a zoom image and the rectangle shows which portion of the image is
6352 %  displayed in the Image window.
6353 %
6354 %  The format of the XDrawPanRectangle method is:
6355 %
6356 %      XDrawPanRectangle(Display *display,XWindows *windows)
6357 %
6358 %  A description of each parameter follows:
6359 %
6360 %    o display: Specifies a connection to an X server;  returned from
6361 %      XOpenDisplay.
6362 %
6363 %    o windows: Specifies a pointer to a XWindows structure.
6364 %
6365 */
6366 static void XDrawPanRectangle(Display *display,XWindows *windows)
6367 {
6368   MagickRealType
6369     scale_factor;
6370
6371   RectangleInfo
6372     highlight_info;
6373
6374   /*
6375     Determine dimensions of the panning rectangle.
6376   */
6377   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6378   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6379   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6380   scale_factor=(MagickRealType)
6381     windows->pan.height/windows->image.ximage->height;
6382   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6383   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6384   /*
6385     Display the panning rectangle.
6386   */
6387   (void) XClearWindow(display,windows->pan.id);
6388   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6389     &highlight_info);
6390 }
6391 \f
6392 /*
6393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6394 %                                                                             %
6395 %                                                                             %
6396 %                                                                             %
6397 +   X I m a g e C a c h e                                                     %
6398 %                                                                             %
6399 %                                                                             %
6400 %                                                                             %
6401 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6402 %
6403 %  XImageCache() handles the creation, manipulation, and destruction of the
6404 %  image cache (undo and redo buffers).
6405 %
6406 %  The format of the XImageCache method is:
6407 %
6408 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6409 %        XWindows *windows,const CommandType command,Image **image,
6410 %        ExceptionInfo *exception)
6411 %
6412 %  A description of each parameter follows:
6413 %
6414 %    o display: Specifies a connection to an X server; returned from
6415 %      XOpenDisplay.
6416 %
6417 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6418 %
6419 %    o windows: Specifies a pointer to a XWindows structure.
6420 %
6421 %    o command: Specifies a command to perform.
6422 %
6423 %    o image: the image;  XImageCache may transform the image and return a new
6424 %      image pointer.
6425 %
6426 %    o exception: return any errors or warnings in this structure.
6427 %
6428 */
6429 static void XImageCache(Display *display,XResourceInfo *resource_info,
6430   XWindows *windows,const CommandType command,Image **image,
6431   ExceptionInfo *exception)
6432 {
6433   Image
6434     *cache_image;
6435
6436   static Image
6437     *redo_image = (Image *) NULL,
6438     *undo_image = (Image *) NULL;
6439
6440   switch (command)
6441   {
6442     case FreeBuffersCommand:
6443     {
6444       /*
6445         Free memory from the undo and redo cache.
6446       */
6447       while (undo_image != (Image *) NULL)
6448       {
6449         cache_image=undo_image;
6450         undo_image=GetPreviousImageInList(undo_image);
6451         cache_image->list=DestroyImage(cache_image->list);
6452         cache_image=DestroyImage(cache_image);
6453       }
6454       undo_image=NewImageList();
6455       if (redo_image != (Image *) NULL)
6456         redo_image=DestroyImage(redo_image);
6457       redo_image=NewImageList();
6458       return;
6459     }
6460     case UndoCommand:
6461     {
6462       char
6463         image_geometry[MaxTextExtent];
6464
6465       /*
6466         Undo the last image transformation.
6467       */
6468       if (undo_image == (Image *) NULL)
6469         {
6470           (void) XBell(display,0);
6471           return;
6472         }
6473       cache_image=undo_image;
6474       undo_image=GetPreviousImageInList(undo_image);
6475       windows->image.window_changes.width=(int) cache_image->columns;
6476       windows->image.window_changes.height=(int) cache_image->rows;
6477       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6478         windows->image.ximage->width,windows->image.ximage->height);
6479       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6480       if (windows->image.crop_geometry != (char *) NULL)
6481         windows->image.crop_geometry=(char *)
6482           RelinquishMagickMemory(windows->image.crop_geometry);
6483       windows->image.crop_geometry=cache_image->geometry;
6484       if (redo_image != (Image *) NULL)
6485         redo_image=DestroyImage(redo_image);
6486       redo_image=(*image);
6487       *image=cache_image->list;
6488       cache_image=DestroyImage(cache_image);
6489       if (windows->image.orphan != MagickFalse)
6490         return;
6491       XConfigureImageColormap(display,resource_info,windows,*image);
6492       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6493       return;
6494     }
6495     case CutCommand:
6496     case PasteCommand:
6497     case ApplyCommand:
6498     case HalfSizeCommand:
6499     case OriginalSizeCommand:
6500     case DoubleSizeCommand:
6501     case ResizeCommand:
6502     case TrimCommand:
6503     case CropCommand:
6504     case ChopCommand:
6505     case FlipCommand:
6506     case FlopCommand:
6507     case RotateRightCommand:
6508     case RotateLeftCommand:
6509     case RotateCommand:
6510     case ShearCommand:
6511     case RollCommand:
6512     case NegateCommand:
6513     case ContrastStretchCommand:
6514     case SigmoidalContrastCommand:
6515     case NormalizeCommand:
6516     case EqualizeCommand:
6517     case HueCommand:
6518     case SaturationCommand:
6519     case BrightnessCommand:
6520     case GammaCommand:
6521     case SpiffCommand:
6522     case DullCommand:
6523     case GrayscaleCommand:
6524     case MapCommand:
6525     case QuantizeCommand:
6526     case DespeckleCommand:
6527     case EmbossCommand:
6528     case ReduceNoiseCommand:
6529     case AddNoiseCommand:
6530     case SharpenCommand:
6531     case BlurCommand:
6532     case ThresholdCommand:
6533     case EdgeDetectCommand:
6534     case SpreadCommand:
6535     case ShadeCommand:
6536     case RaiseCommand:
6537     case SegmentCommand:
6538     case SolarizeCommand:
6539     case SepiaToneCommand:
6540     case SwirlCommand:
6541     case ImplodeCommand:
6542     case VignetteCommand:
6543     case WaveCommand:
6544     case OilPaintCommand:
6545     case CharcoalDrawCommand:
6546     case AnnotateCommand:
6547     case AddBorderCommand:
6548     case AddFrameCommand:
6549     case CompositeCommand:
6550     case CommentCommand:
6551     case LaunchCommand:
6552     case RegionofInterestCommand:
6553     case SaveToUndoBufferCommand:
6554     case RedoCommand:
6555     {
6556       Image
6557         *previous_image;
6558
6559       ssize_t
6560         bytes;
6561
6562       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6563       if (undo_image != (Image *) NULL)
6564         {
6565           /*
6566             Ensure the undo cache has enough memory available.
6567           */
6568           previous_image=undo_image;
6569           while (previous_image != (Image *) NULL)
6570           {
6571             bytes+=previous_image->list->columns*previous_image->list->rows*
6572               sizeof(PixelPacket);
6573             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6574               {
6575                 previous_image=GetPreviousImageInList(previous_image);
6576                 continue;
6577               }
6578             bytes-=previous_image->list->columns*previous_image->list->rows*
6579               sizeof(PixelPacket);
6580             if (previous_image == undo_image)
6581               undo_image=NewImageList();
6582             else
6583               previous_image->next->previous=NewImageList();
6584             break;
6585           }
6586           while (previous_image != (Image *) NULL)
6587           {
6588             /*
6589               Delete any excess memory from undo cache.
6590             */
6591             cache_image=previous_image;
6592             previous_image=GetPreviousImageInList(previous_image);
6593             cache_image->list=DestroyImage(cache_image->list);
6594             cache_image=DestroyImage(cache_image);
6595           }
6596         }
6597       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6598         break;
6599       /*
6600         Save image before transformations are applied.
6601       */
6602       cache_image=AcquireImage((ImageInfo *) NULL);
6603       if (cache_image == (Image *) NULL)
6604         break;
6605       XSetCursorState(display,windows,MagickTrue);
6606       XCheckRefreshWindows(display,windows);
6607       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6608       XSetCursorState(display,windows,MagickFalse);
6609       if (cache_image->list == (Image *) NULL)
6610         {
6611           cache_image=DestroyImage(cache_image);
6612           break;
6613         }
6614       cache_image->columns=(size_t) windows->image.ximage->width;
6615       cache_image->rows=(size_t) windows->image.ximage->height;
6616       cache_image->geometry=windows->image.crop_geometry;
6617       if (windows->image.crop_geometry != (char *) NULL)
6618         {
6619           cache_image->geometry=AcquireString((char *) NULL);
6620           (void) CopyMagickString(cache_image->geometry,
6621             windows->image.crop_geometry,MaxTextExtent);
6622         }
6623       if (undo_image == (Image *) NULL)
6624         {
6625           undo_image=cache_image;
6626           break;
6627         }
6628       undo_image->next=cache_image;
6629       undo_image->next->previous=undo_image;
6630       undo_image=undo_image->next;
6631       break;
6632     }
6633     default:
6634       break;
6635   }
6636   if (command == RedoCommand)
6637     {
6638       /*
6639         Redo the last image transformation.
6640       */
6641       if (redo_image == (Image *) NULL)
6642         {
6643           (void) XBell(display,0);
6644           return;
6645         }
6646       windows->image.window_changes.width=(int) redo_image->columns;
6647       windows->image.window_changes.height=(int) redo_image->rows;
6648       if (windows->image.crop_geometry != (char *) NULL)
6649         windows->image.crop_geometry=(char *)
6650           RelinquishMagickMemory(windows->image.crop_geometry);
6651       windows->image.crop_geometry=redo_image->geometry;
6652       *image=DestroyImage(*image);
6653       *image=redo_image;
6654       redo_image=NewImageList();
6655       if (windows->image.orphan != MagickFalse)
6656         return;
6657       XConfigureImageColormap(display,resource_info,windows,*image);
6658       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6659       return;
6660     }
6661   if (command != InfoCommand)
6662     return;
6663   /*
6664     Display image info.
6665   */
6666   XSetCursorState(display,windows,MagickTrue);
6667   XCheckRefreshWindows(display,windows);
6668   XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6669   XSetCursorState(display,windows,MagickFalse);
6670 }
6671 \f
6672 /*
6673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6674 %                                                                             %
6675 %                                                                             %
6676 %                                                                             %
6677 +   X I m a g e W i n d o w C o m m a n d                                     %
6678 %                                                                             %
6679 %                                                                             %
6680 %                                                                             %
6681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6682 %
6683 %  XImageWindowCommand() makes a transform to the image or Image window as
6684 %  specified by a user menu button or keyboard command.
6685 %
6686 %  The format of the XImageWindowCommand method is:
6687 %
6688 %      CommandType XImageWindowCommand(Display *display,
6689 %        XResourceInfo *resource_info,XWindows *windows,
6690 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6691 %        ExceptionInfo *exception)
6692 %
6693 %  A description of each parameter follows:
6694 %
6695 %    o nexus:  Method XImageWindowCommand returns an image when the
6696 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6697 %      image is returned.
6698 %
6699 %    o display: Specifies a connection to an X server; returned from
6700 %      XOpenDisplay.
6701 %
6702 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6703 %
6704 %    o windows: Specifies a pointer to a XWindows structure.
6705 %
6706 %    o state: key mask.
6707 %
6708 %    o key_symbol: Specifies a command to perform.
6709 %
6710 %    o image: the image;  XImageWIndowCommand may transform the image and
6711 %      return a new image pointer.
6712 %
6713 %    o exception: return any errors or warnings in this structure.
6714 %
6715 */
6716 static CommandType XImageWindowCommand(Display *display,
6717   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6718   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6719 {
6720   static char
6721     delta[MaxTextExtent] = "";
6722
6723   static const char
6724     Digits[] = "01234567890";
6725
6726   static KeySym
6727     last_symbol = XK_0;
6728
6729   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6730     {
6731       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6732         {
6733           *delta='\0';
6734           resource_info->quantum=1;
6735         }
6736       last_symbol=key_symbol;
6737       delta[strlen(delta)+1]='\0';
6738       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6739       resource_info->quantum=StringToLong(delta);
6740       return(NullCommand);
6741     }
6742   last_symbol=key_symbol;
6743   if (resource_info->immutable)
6744     {
6745       /*
6746         Virtual image window has a restricted command set.
6747       */
6748       switch (key_symbol)
6749       {
6750         case XK_question:
6751           return(InfoCommand);
6752         case XK_p:
6753         case XK_Print:
6754           return(PrintCommand);
6755         case XK_space:
6756           return(NextCommand);
6757         case XK_q:
6758         case XK_Escape:
6759           return(QuitCommand);
6760         default:
6761           break;
6762       }
6763       return(NullCommand);
6764     }
6765   switch ((int) key_symbol)
6766   {
6767     case XK_o:
6768     {
6769       if ((state & ControlMask) == 0)
6770         break;
6771       return(OpenCommand);
6772     }
6773     case XK_space:
6774       return(NextCommand);
6775     case XK_BackSpace:
6776       return(FormerCommand);
6777     case XK_s:
6778     {
6779       if ((state & Mod1Mask) != 0)
6780         return(SwirlCommand);
6781       if ((state & ControlMask) == 0)
6782         return(ShearCommand);
6783       return(SaveCommand);
6784     }
6785     case XK_p:
6786     case XK_Print:
6787     {
6788       if ((state & Mod1Mask) != 0)
6789         return(OilPaintCommand);
6790       if ((state & Mod4Mask) != 0)
6791         return(ColorCommand);
6792       if ((state & ControlMask) == 0)
6793         return(NullCommand);
6794       return(PrintCommand);
6795     }
6796     case XK_d:
6797     {
6798       if ((state & Mod4Mask) != 0)
6799         return(DrawCommand);
6800       if ((state & ControlMask) == 0)
6801         return(NullCommand);
6802       return(DeleteCommand);
6803     }
6804     case XK_Select:
6805     {
6806       if ((state & ControlMask) == 0)
6807         return(NullCommand);
6808       return(SelectCommand);
6809     }
6810     case XK_n:
6811     {
6812       if ((state & ControlMask) == 0)
6813         return(NullCommand);
6814       return(NewCommand);
6815     }
6816     case XK_q:
6817     case XK_Escape:
6818       return(QuitCommand);
6819     case XK_z:
6820     case XK_Undo:
6821     {
6822       if ((state & ControlMask) == 0)
6823         return(NullCommand);
6824       return(UndoCommand);
6825     }
6826     case XK_r:
6827     case XK_Redo:
6828     {
6829       if ((state & ControlMask) == 0)
6830         return(RollCommand);
6831       return(RedoCommand);
6832     }
6833     case XK_x:
6834     {
6835       if ((state & ControlMask) == 0)
6836         return(NullCommand);
6837       return(CutCommand);
6838     }
6839     case XK_c:
6840     {
6841       if ((state & Mod1Mask) != 0)
6842         return(CharcoalDrawCommand);
6843       if ((state & ControlMask) == 0)
6844         return(CropCommand);
6845       return(CopyCommand);
6846     }
6847     case XK_v:
6848     case XK_Insert:
6849     {
6850       if ((state & Mod4Mask) != 0)
6851         return(CompositeCommand);
6852       if ((state & ControlMask) == 0)
6853         return(FlipCommand);
6854       return(PasteCommand);
6855     }
6856     case XK_less:
6857       return(HalfSizeCommand);
6858     case XK_minus:
6859       return(OriginalSizeCommand);
6860     case XK_greater:
6861       return(DoubleSizeCommand);
6862     case XK_percent:
6863       return(ResizeCommand);
6864     case XK_at:
6865       return(RefreshCommand);
6866     case XK_bracketleft:
6867       return(ChopCommand);
6868     case XK_h:
6869       return(FlopCommand);
6870     case XK_slash:
6871       return(RotateRightCommand);
6872     case XK_backslash:
6873       return(RotateLeftCommand);
6874     case XK_asterisk:
6875       return(RotateCommand);
6876     case XK_t:
6877       return(TrimCommand);
6878     case XK_H:
6879       return(HueCommand);
6880     case XK_S:
6881       return(SaturationCommand);
6882     case XK_L:
6883       return(BrightnessCommand);
6884     case XK_G:
6885       return(GammaCommand);
6886     case XK_C:
6887       return(SpiffCommand);
6888     case XK_Z:
6889       return(DullCommand);
6890     case XK_N:
6891       return(NormalizeCommand);
6892     case XK_equal:
6893       return(EqualizeCommand);
6894     case XK_asciitilde:
6895       return(NegateCommand);
6896     case XK_period:
6897       return(GrayscaleCommand);
6898     case XK_numbersign:
6899       return(QuantizeCommand);
6900     case XK_F2:
6901       return(DespeckleCommand);
6902     case XK_F3:
6903       return(EmbossCommand);
6904     case XK_F4:
6905       return(ReduceNoiseCommand);
6906     case XK_F5:
6907       return(AddNoiseCommand);
6908     case XK_F6:
6909       return(SharpenCommand);
6910     case XK_F7:
6911       return(BlurCommand);
6912     case XK_F8:
6913       return(ThresholdCommand);
6914     case XK_F9:
6915       return(EdgeDetectCommand);
6916     case XK_F10:
6917       return(SpreadCommand);
6918     case XK_F11:
6919       return(ShadeCommand);
6920     case XK_F12:
6921       return(RaiseCommand);
6922     case XK_F13:
6923       return(SegmentCommand);
6924     case XK_i:
6925     {
6926       if ((state & Mod1Mask) == 0)
6927         return(NullCommand);
6928       return(ImplodeCommand);
6929     }
6930     case XK_w:
6931     {
6932       if ((state & Mod1Mask) == 0)
6933         return(NullCommand);
6934       return(WaveCommand);
6935     }
6936     case XK_m:
6937     {
6938       if ((state & Mod4Mask) == 0)
6939         return(NullCommand);
6940       return(MatteCommand);
6941     }
6942     case XK_b:
6943     {
6944       if ((state & Mod4Mask) == 0)
6945         return(NullCommand);
6946       return(AddBorderCommand);
6947     }
6948     case XK_f:
6949     {
6950       if ((state & Mod4Mask) == 0)
6951         return(NullCommand);
6952       return(AddFrameCommand);
6953     }
6954     case XK_exclam:
6955     {
6956       if ((state & Mod4Mask) == 0)
6957         return(NullCommand);
6958       return(CommentCommand);
6959     }
6960     case XK_a:
6961     {
6962       if ((state & Mod1Mask) != 0)
6963         return(ApplyCommand);
6964       if ((state & Mod4Mask) != 0)
6965         return(AnnotateCommand);
6966       if ((state & ControlMask) == 0)
6967         return(NullCommand);
6968       return(RegionofInterestCommand);
6969     }
6970     case XK_question:
6971       return(InfoCommand);
6972     case XK_plus:
6973       return(ZoomCommand);
6974     case XK_P:
6975     {
6976       if ((state & ShiftMask) == 0)
6977         return(NullCommand);
6978       return(ShowPreviewCommand);
6979     }
6980     case XK_Execute:
6981       return(LaunchCommand);
6982     case XK_F1:
6983       return(HelpCommand);
6984     case XK_Find:
6985       return(BrowseDocumentationCommand);
6986     case XK_Menu:
6987     {
6988       (void) XMapRaised(display,windows->command.id);
6989       return(NullCommand);
6990     }
6991     case XK_Next:
6992     case XK_Prior:
6993     case XK_Home:
6994     case XK_KP_Home:
6995     {
6996       XTranslateImage(display,windows,*image,key_symbol);
6997       return(NullCommand);
6998     }
6999     case XK_Up:
7000     case XK_KP_Up:
7001     case XK_Down:
7002     case XK_KP_Down:
7003     case XK_Left:
7004     case XK_KP_Left:
7005     case XK_Right:
7006     case XK_KP_Right:
7007     {
7008       if ((state & Mod1Mask) != 0)
7009         {
7010           RectangleInfo
7011             crop_info;
7012
7013           /*
7014             Trim one pixel from edge of image.
7015           */
7016           crop_info.x=0;
7017           crop_info.y=0;
7018           crop_info.width=(size_t) windows->image.ximage->width;
7019           crop_info.height=(size_t) windows->image.ximage->height;
7020           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7021             {
7022               if (resource_info->quantum >= (int) crop_info.height)
7023                 resource_info->quantum=(int) crop_info.height-1;
7024               crop_info.height-=resource_info->quantum;
7025             }
7026           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7027             {
7028               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7029                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7030               crop_info.y+=resource_info->quantum;
7031               crop_info.height-=resource_info->quantum;
7032             }
7033           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7034             {
7035               if (resource_info->quantum >= (int) crop_info.width)
7036                 resource_info->quantum=(int) crop_info.width-1;
7037               crop_info.width-=resource_info->quantum;
7038             }
7039           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7040             {
7041               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7042                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7043               crop_info.x+=resource_info->quantum;
7044               crop_info.width-=resource_info->quantum;
7045             }
7046           if ((int) (windows->image.x+windows->image.width) >
7047               (int) crop_info.width)
7048             windows->image.x=(int) (crop_info.width-windows->image.width);
7049           if ((int) (windows->image.y+windows->image.height) >
7050               (int) crop_info.height)
7051             windows->image.y=(int) (crop_info.height-windows->image.height);
7052           XSetCropGeometry(display,windows,&crop_info,*image);
7053           windows->image.window_changes.width=(int) crop_info.width;
7054           windows->image.window_changes.height=(int) crop_info.height;
7055           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7056           (void) XConfigureImage(display,resource_info,windows,*image,
7057             exception);
7058           return(NullCommand);
7059         }
7060       XTranslateImage(display,windows,*image,key_symbol);
7061       return(NullCommand);
7062     }
7063     default:
7064       return(NullCommand);
7065   }
7066   return(NullCommand);
7067 }
7068 \f
7069 /*
7070 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7071 %                                                                             %
7072 %                                                                             %
7073 %                                                                             %
7074 +   X M a g i c k C o m m a n d                                               %
7075 %                                                                             %
7076 %                                                                             %
7077 %                                                                             %
7078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7079 %
7080 %  XMagickCommand() makes a transform to the image or Image window as
7081 %  specified by a user menu button or keyboard command.
7082 %
7083 %  The format of the XMagickCommand method is:
7084 %
7085 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7086 %        XWindows *windows,const CommandType command,Image **image,
7087 %        ExceptionInfo *exception)
7088 %
7089 %  A description of each parameter follows:
7090 %
7091 %    o display: Specifies a connection to an X server; returned from
7092 %      XOpenDisplay.
7093 %
7094 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7095 %
7096 %    o windows: Specifies a pointer to a XWindows structure.
7097 %
7098 %    o command: Specifies a command to perform.
7099 %
7100 %    o image: the image;  XMagickCommand may transform the image and return a
7101 %      new image pointer.
7102 %
7103 %    o exception: return any errors or warnings in this structure.
7104 %
7105 */
7106 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7107   XWindows *windows,const CommandType command,Image **image,
7108   ExceptionInfo *exception)
7109 {
7110   char
7111     filename[MaxTextExtent],
7112     geometry[MaxTextExtent],
7113     modulate_factors[MaxTextExtent];
7114
7115   GeometryInfo
7116     geometry_info;
7117
7118   Image
7119     *nexus;
7120
7121   ImageInfo
7122     *image_info;
7123
7124   int
7125     x,
7126     y;
7127
7128   MagickStatusType
7129     flags,
7130     status;
7131
7132   QuantizeInfo
7133     quantize_info;
7134
7135   RectangleInfo
7136     page_geometry;
7137
7138   register int
7139     i;
7140
7141   static char
7142     color[MaxTextExtent] = "gray";
7143
7144   unsigned int
7145     height,
7146     width;
7147
7148   /*
7149     Process user command.
7150   */
7151   XCheckRefreshWindows(display,windows);
7152   XImageCache(display,resource_info,windows,command,image,exception);
7153   nexus=NewImageList();
7154   windows->image.window_changes.width=windows->image.ximage->width;
7155   windows->image.window_changes.height=windows->image.ximage->height;
7156   image_info=CloneImageInfo(resource_info->image_info);
7157   SetGeometryInfo(&geometry_info);
7158   GetQuantizeInfo(&quantize_info);
7159   switch (command)
7160   {
7161     case OpenCommand:
7162     {
7163       /*
7164         Load image.
7165       */
7166       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7167       break;
7168     }
7169     case NextCommand:
7170     {
7171       /*
7172         Display next image.
7173       */
7174       for (i=0; i < resource_info->quantum; i++)
7175         XClientMessage(display,windows->image.id,windows->im_protocols,
7176           windows->im_next_image,CurrentTime);
7177       break;
7178     }
7179     case FormerCommand:
7180     {
7181       /*
7182         Display former image.
7183       */
7184       for (i=0; i < resource_info->quantum; i++)
7185         XClientMessage(display,windows->image.id,windows->im_protocols,
7186           windows->im_former_image,CurrentTime);
7187       break;
7188     }
7189     case SelectCommand:
7190     {
7191       int
7192         status;
7193
7194       /*
7195         Select image.
7196       */
7197       status=chdir(resource_info->home_directory);
7198       if (status == -1)
7199         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7200           "UnableToOpenFile","%s",resource_info->home_directory);
7201       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7202       break;
7203     }
7204     case SaveCommand:
7205     {
7206       /*
7207         Save image.
7208       */
7209       status=XSaveImage(display,resource_info,windows,*image,exception);
7210       if (status == MagickFalse)
7211         {
7212           XNoticeWidget(display,windows,"Unable to write X image:",
7213             (*image)->filename);
7214           break;
7215         }
7216       break;
7217     }
7218     case PrintCommand:
7219     {
7220       /*
7221         Print image.
7222       */
7223       status=XPrintImage(display,resource_info,windows,*image,exception);
7224       if (status == MagickFalse)
7225         {
7226           XNoticeWidget(display,windows,"Unable to print X image:",
7227             (*image)->filename);
7228           break;
7229         }
7230       break;
7231     }
7232     case DeleteCommand:
7233     {
7234       static char
7235         filename[MaxTextExtent] = "\0";
7236
7237       /*
7238         Delete image file.
7239       */
7240       XFileBrowserWidget(display,windows,"Delete",filename);
7241       if (*filename == '\0')
7242         break;
7243       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
7244       if (status != MagickFalse)
7245         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7246       break;
7247     }
7248     case NewCommand:
7249     {
7250       int
7251         status;
7252
7253       static char
7254         color[MaxTextExtent] = "gray",
7255         geometry[MaxTextExtent] = "640x480";
7256
7257       static const char
7258         *format = "gradient";
7259
7260       /*
7261         Query user for canvas geometry.
7262       */
7263       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7264         geometry);
7265       if (*geometry == '\0')
7266         break;
7267       if (status == 0)
7268         format="xc";
7269       XColorBrowserWidget(display,windows,"Select",color);
7270       if (*color == '\0')
7271         break;
7272       /*
7273         Create canvas.
7274       */
7275       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7276         "%s:%s",format,color);
7277       (void) CloneString(&image_info->size,geometry);
7278       nexus=ReadImage(image_info,exception);
7279       CatchException(exception);
7280       XClientMessage(display,windows->image.id,windows->im_protocols,
7281         windows->im_next_image,CurrentTime);
7282       break;
7283     }
7284     case VisualDirectoryCommand:
7285     {
7286       /*
7287         Visual Image directory.
7288       */
7289       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7290       break;
7291     }
7292     case QuitCommand:
7293     {
7294       /*
7295         exit program.
7296       */
7297       if (resource_info->confirm_exit == MagickFalse)
7298         XClientMessage(display,windows->image.id,windows->im_protocols,
7299           windows->im_exit,CurrentTime);
7300       else
7301         {
7302           int
7303             status;
7304
7305           /*
7306             Confirm program exit.
7307           */
7308           status=XConfirmWidget(display,windows,"Do you really want to exit",
7309             resource_info->client_name);
7310           if (status > 0)
7311             XClientMessage(display,windows->image.id,windows->im_protocols,
7312               windows->im_exit,CurrentTime);
7313         }
7314       break;
7315     }
7316     case CutCommand:
7317     {
7318       /*
7319         Cut image.
7320       */
7321       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7322       break;
7323     }
7324     case CopyCommand:
7325     {
7326       /*
7327         Copy image.
7328       */
7329       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7330         exception);
7331       break;
7332     }
7333     case PasteCommand:
7334     {
7335       /*
7336         Paste image.
7337       */
7338       status=XPasteImage(display,resource_info,windows,*image,exception);
7339       if (status == MagickFalse)
7340         {
7341           XNoticeWidget(display,windows,"Unable to paste X image",
7342             (*image)->filename);
7343           break;
7344         }
7345       break;
7346     }
7347     case HalfSizeCommand:
7348     {
7349       /*
7350         Half image size.
7351       */
7352       windows->image.window_changes.width=windows->image.ximage->width/2;
7353       windows->image.window_changes.height=windows->image.ximage->height/2;
7354       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7355       break;
7356     }
7357     case OriginalSizeCommand:
7358     {
7359       /*
7360         Original image size.
7361       */
7362       windows->image.window_changes.width=(int) (*image)->columns;
7363       windows->image.window_changes.height=(int) (*image)->rows;
7364       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7365       break;
7366     }
7367     case DoubleSizeCommand:
7368     {
7369       /*
7370         Double the image size.
7371       */
7372       windows->image.window_changes.width=windows->image.ximage->width << 1;
7373       windows->image.window_changes.height=windows->image.ximage->height << 1;
7374       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7375       break;
7376     }
7377     case ResizeCommand:
7378     {
7379       int
7380         status;
7381
7382       size_t
7383         height,
7384         width;
7385
7386       ssize_t
7387         x,
7388         y;
7389
7390       /*
7391         Resize image.
7392       */
7393       width=(size_t) windows->image.ximage->width;
7394       height=(size_t) windows->image.ximage->height;
7395       x=0;
7396       y=0;
7397       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7398         (double) width,(double) height);
7399       status=XDialogWidget(display,windows,"Resize",
7400         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7401       if (*geometry == '\0')
7402         break;
7403       if (status == 0)
7404         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7405       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7406       windows->image.window_changes.width=(int) width;
7407       windows->image.window_changes.height=(int) height;
7408       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7409       break;
7410     }
7411     case ApplyCommand:
7412     {
7413       char
7414         image_geometry[MaxTextExtent];
7415
7416       if ((windows->image.crop_geometry == (char *) NULL) &&
7417           ((int) (*image)->columns == windows->image.ximage->width) &&
7418           ((int) (*image)->rows == windows->image.ximage->height))
7419         break;
7420       /*
7421         Apply size transforms to image.
7422       */
7423       XSetCursorState(display,windows,MagickTrue);
7424       XCheckRefreshWindows(display,windows);
7425       /*
7426         Crop and/or scale displayed image.
7427       */
7428       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7429         windows->image.ximage->width,windows->image.ximage->height);
7430       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7431       if (windows->image.crop_geometry != (char *) NULL)
7432         windows->image.crop_geometry=(char *)
7433           RelinquishMagickMemory(windows->image.crop_geometry);
7434       windows->image.x=0;
7435       windows->image.y=0;
7436       XConfigureImageColormap(display,resource_info,windows,*image);
7437       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7438       break;
7439     }
7440     case RefreshCommand:
7441     {
7442       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7443       break;
7444     }
7445     case RestoreCommand:
7446     {
7447       /*
7448         Restore Image window to its original size.
7449       */
7450       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7451           (windows->image.height == (unsigned int) (*image)->rows) &&
7452           (windows->image.crop_geometry == (char *) NULL))
7453         {
7454           (void) XBell(display,0);
7455           break;
7456         }
7457       windows->image.window_changes.width=(int) (*image)->columns;
7458       windows->image.window_changes.height=(int) (*image)->rows;
7459       if (windows->image.crop_geometry != (char *) NULL)
7460         {
7461           windows->image.crop_geometry=(char *)
7462             RelinquishMagickMemory(windows->image.crop_geometry);
7463           windows->image.crop_geometry=(char *) NULL;
7464           windows->image.x=0;
7465           windows->image.y=0;
7466         }
7467       XConfigureImageColormap(display,resource_info,windows,*image);
7468       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7469       break;
7470     }
7471     case CropCommand:
7472     {
7473       /*
7474         Crop image.
7475       */
7476       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7477         exception);
7478       break;
7479     }
7480     case ChopCommand:
7481     {
7482       /*
7483         Chop image.
7484       */
7485       status=XChopImage(display,resource_info,windows,image,exception);
7486       if (status == MagickFalse)
7487         {
7488           XNoticeWidget(display,windows,"Unable to cut X image",
7489             (*image)->filename);
7490           break;
7491         }
7492       break;
7493     }
7494     case FlopCommand:
7495     {
7496       Image
7497         *flop_image;
7498
7499       /*
7500         Flop image scanlines.
7501       */
7502       XSetCursorState(display,windows,MagickTrue);
7503       XCheckRefreshWindows(display,windows);
7504       flop_image=FlopImage(*image,exception);
7505       if (flop_image != (Image *) NULL)
7506         {
7507           *image=DestroyImage(*image);
7508           *image=flop_image;
7509         }
7510       CatchException(exception);
7511       XSetCursorState(display,windows,MagickFalse);
7512       if (windows->image.crop_geometry != (char *) NULL)
7513         {
7514           /*
7515             Flop crop geometry.
7516           */
7517           width=(unsigned int) (*image)->columns;
7518           height=(unsigned int) (*image)->rows;
7519           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7520             &width,&height);
7521           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7522             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7523         }
7524       if (windows->image.orphan != MagickFalse)
7525         break;
7526       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7527       break;
7528     }
7529     case FlipCommand:
7530     {
7531       Image
7532         *flip_image;
7533
7534       /*
7535         Flip image scanlines.
7536       */
7537       XSetCursorState(display,windows,MagickTrue);
7538       XCheckRefreshWindows(display,windows);
7539       flip_image=FlipImage(*image,exception);
7540       if (flip_image != (Image *) NULL)
7541         {
7542           *image=DestroyImage(*image);
7543           *image=flip_image;
7544         }
7545       CatchException(exception);
7546       XSetCursorState(display,windows,MagickFalse);
7547       if (windows->image.crop_geometry != (char *) NULL)
7548         {
7549           /*
7550             Flip crop geometry.
7551           */
7552           width=(unsigned int) (*image)->columns;
7553           height=(unsigned int) (*image)->rows;
7554           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7555             &width,&height);
7556           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7557             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7558         }
7559       if (windows->image.orphan != MagickFalse)
7560         break;
7561       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7562       break;
7563     }
7564     case RotateRightCommand:
7565     {
7566       /*
7567         Rotate image 90 degrees clockwise.
7568       */
7569       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7570       if (status == MagickFalse)
7571         {
7572           XNoticeWidget(display,windows,"Unable to rotate X image",
7573             (*image)->filename);
7574           break;
7575         }
7576       break;
7577     }
7578     case RotateLeftCommand:
7579     {
7580       /*
7581         Rotate image 90 degrees counter-clockwise.
7582       */
7583       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7584       if (status == MagickFalse)
7585         {
7586           XNoticeWidget(display,windows,"Unable to rotate X image",
7587             (*image)->filename);
7588           break;
7589         }
7590       break;
7591     }
7592     case RotateCommand:
7593     {
7594       /*
7595         Rotate image.
7596       */
7597       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7598       if (status == MagickFalse)
7599         {
7600           XNoticeWidget(display,windows,"Unable to rotate X image",
7601             (*image)->filename);
7602           break;
7603         }
7604       break;
7605     }
7606     case ShearCommand:
7607     {
7608       Image
7609         *shear_image;
7610
7611       static char
7612         geometry[MaxTextExtent] = "45.0x45.0";
7613
7614       /*
7615         Query user for shear color and geometry.
7616       */
7617       XColorBrowserWidget(display,windows,"Select",color);
7618       if (*color == '\0')
7619         break;
7620       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7621         geometry);
7622       if (*geometry == '\0')
7623         break;
7624       /*
7625         Shear image.
7626       */
7627       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7628         exception);
7629       XSetCursorState(display,windows,MagickTrue);
7630       XCheckRefreshWindows(display,windows);
7631       (void) QueryColorDatabase(color,&(*image)->background_color,
7632         exception);
7633       flags=ParseGeometry(geometry,&geometry_info);
7634       if ((flags & SigmaValue) == 0)
7635         geometry_info.sigma=geometry_info.rho;
7636       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7637         exception);
7638       if (shear_image != (Image *) NULL)
7639         {
7640           *image=DestroyImage(*image);
7641           *image=shear_image;
7642         }
7643       CatchException(exception);
7644       XSetCursorState(display,windows,MagickFalse);
7645       if (windows->image.orphan != MagickFalse)
7646         break;
7647       windows->image.window_changes.width=(int) (*image)->columns;
7648       windows->image.window_changes.height=(int) (*image)->rows;
7649       XConfigureImageColormap(display,resource_info,windows,*image);
7650       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7651       break;
7652     }
7653     case RollCommand:
7654     {
7655       Image
7656         *roll_image;
7657
7658       static char
7659         geometry[MaxTextExtent] = "+2+2";
7660
7661       /*
7662         Query user for the roll geometry.
7663       */
7664       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7665         geometry);
7666       if (*geometry == '\0')
7667         break;
7668       /*
7669         Roll image.
7670       */
7671       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7672         exception);
7673       XSetCursorState(display,windows,MagickTrue);
7674       XCheckRefreshWindows(display,windows);
7675       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7676         exception);
7677       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7678         exception);
7679       if (roll_image != (Image *) NULL)
7680         {
7681           *image=DestroyImage(*image);
7682           *image=roll_image;
7683         }
7684       CatchException(exception);
7685       XSetCursorState(display,windows,MagickFalse);
7686       if (windows->image.orphan != MagickFalse)
7687         break;
7688       windows->image.window_changes.width=(int) (*image)->columns;
7689       windows->image.window_changes.height=(int) (*image)->rows;
7690       XConfigureImageColormap(display,resource_info,windows,*image);
7691       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7692       break;
7693     }
7694     case TrimCommand:
7695     {
7696       static char
7697         fuzz[MaxTextExtent];
7698
7699       /*
7700         Query user for the fuzz factor.
7701       */
7702       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7703         (*image)->fuzz/(QuantumRange+1.0));
7704       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7705       if (*fuzz == '\0')
7706         break;
7707       (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7708       /*
7709         Trim image.
7710       */
7711       status=XTrimImage(display,resource_info,windows,*image,exception);
7712       if (status == MagickFalse)
7713         {
7714           XNoticeWidget(display,windows,"Unable to trim X image",
7715             (*image)->filename);
7716           break;
7717         }
7718       break;
7719     }
7720     case HueCommand:
7721     {
7722       static char
7723         hue_percent[MaxTextExtent] = "110";
7724
7725       /*
7726         Query user for percent hue change.
7727       */
7728       (void) XDialogWidget(display,windows,"Apply",
7729         "Enter percent change in image hue (0-200):",hue_percent);
7730       if (*hue_percent == '\0')
7731         break;
7732       /*
7733         Vary the image hue.
7734       */
7735       XSetCursorState(display,windows,MagickTrue);
7736       XCheckRefreshWindows(display,windows);
7737       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7738       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7739         MaxTextExtent);
7740       (void) ModulateImage(*image,modulate_factors,exception);
7741       XSetCursorState(display,windows,MagickFalse);
7742       if (windows->image.orphan != MagickFalse)
7743         break;
7744       XConfigureImageColormap(display,resource_info,windows,*image);
7745       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7746       break;
7747     }
7748     case SaturationCommand:
7749     {
7750       static char
7751         saturation_percent[MaxTextExtent] = "110";
7752
7753       /*
7754         Query user for percent saturation change.
7755       */
7756       (void) XDialogWidget(display,windows,"Apply",
7757         "Enter percent change in color saturation (0-200):",saturation_percent);
7758       if (*saturation_percent == '\0')
7759         break;
7760       /*
7761         Vary color saturation.
7762       */
7763       XSetCursorState(display,windows,MagickTrue);
7764       XCheckRefreshWindows(display,windows);
7765       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7766       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7767         MaxTextExtent);
7768       (void) ModulateImage(*image,modulate_factors,exception);
7769       XSetCursorState(display,windows,MagickFalse);
7770       if (windows->image.orphan != MagickFalse)
7771         break;
7772       XConfigureImageColormap(display,resource_info,windows,*image);
7773       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7774       break;
7775     }
7776     case BrightnessCommand:
7777     {
7778       static char
7779         brightness_percent[MaxTextExtent] = "110";
7780
7781       /*
7782         Query user for percent brightness change.
7783       */
7784       (void) XDialogWidget(display,windows,"Apply",
7785         "Enter percent change in color brightness (0-200):",brightness_percent);
7786       if (*brightness_percent == '\0')
7787         break;
7788       /*
7789         Vary the color brightness.
7790       */
7791       XSetCursorState(display,windows,MagickTrue);
7792       XCheckRefreshWindows(display,windows);
7793       (void) CopyMagickString(modulate_factors,brightness_percent,
7794         MaxTextExtent);
7795       (void) ModulateImage(*image,modulate_factors,exception);
7796       XSetCursorState(display,windows,MagickFalse);
7797       if (windows->image.orphan != MagickFalse)
7798         break;
7799       XConfigureImageColormap(display,resource_info,windows,*image);
7800       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7801       break;
7802     }
7803     case GammaCommand:
7804     {
7805       static char
7806         factor[MaxTextExtent] = "1.6";
7807
7808       /*
7809         Query user for gamma value.
7810       */
7811       (void) XDialogWidget(display,windows,"Gamma",
7812         "Enter gamma value (e.g. 1.2):",factor);
7813       if (*factor == '\0')
7814         break;
7815       /*
7816         Gamma correct image.
7817       */
7818       XSetCursorState(display,windows,MagickTrue);
7819       XCheckRefreshWindows(display,windows);
7820       (void) GammaImage(*image,atof(factor),exception);
7821       XSetCursorState(display,windows,MagickFalse);
7822       if (windows->image.orphan != MagickFalse)
7823         break;
7824       XConfigureImageColormap(display,resource_info,windows,*image);
7825       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7826       break;
7827     }
7828     case SpiffCommand:
7829     {
7830       /*
7831         Sharpen the image contrast.
7832       */
7833       XSetCursorState(display,windows,MagickTrue);
7834       XCheckRefreshWindows(display,windows);
7835       (void) ContrastImage(*image,MagickTrue,exception);
7836       XSetCursorState(display,windows,MagickFalse);
7837       if (windows->image.orphan != MagickFalse)
7838         break;
7839       XConfigureImageColormap(display,resource_info,windows,*image);
7840       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7841       break;
7842     }
7843     case DullCommand:
7844     {
7845       /*
7846         Dull the image contrast.
7847       */
7848       XSetCursorState(display,windows,MagickTrue);
7849       XCheckRefreshWindows(display,windows);
7850       (void) ContrastImage(*image,MagickFalse,exception);
7851       XSetCursorState(display,windows,MagickFalse);
7852       if (windows->image.orphan != MagickFalse)
7853         break;
7854       XConfigureImageColormap(display,resource_info,windows,*image);
7855       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7856       break;
7857     }
7858     case ContrastStretchCommand:
7859     {
7860       double
7861         black_point,
7862         white_point;
7863
7864       static char
7865         levels[MaxTextExtent] = "1%";
7866
7867       /*
7868         Query user for gamma value.
7869       */
7870       (void) XDialogWidget(display,windows,"Contrast Stretch",
7871         "Enter black and white points:",levels);
7872       if (*levels == '\0')
7873         break;
7874       /*
7875         Contrast stretch image.
7876       */
7877       XSetCursorState(display,windows,MagickTrue);
7878       XCheckRefreshWindows(display,windows);
7879       flags=ParseGeometry(levels,&geometry_info);
7880       black_point=geometry_info.rho;
7881       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7882       if ((flags & PercentValue) != 0)
7883         {
7884           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7885           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7886         }
7887       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7888       (void) ContrastStretchImage(*image,black_point,white_point,
7889         exception);
7890       XSetCursorState(display,windows,MagickFalse);
7891       if (windows->image.orphan != MagickFalse)
7892         break;
7893       XConfigureImageColormap(display,resource_info,windows,*image);
7894       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7895       break;
7896     }
7897     case SigmoidalContrastCommand:
7898     {
7899       GeometryInfo
7900         geometry_info;
7901
7902       MagickStatusType
7903         flags;
7904
7905       static char
7906         levels[MaxTextExtent] = "3x50%";
7907
7908       /*
7909         Query user for gamma value.
7910       */
7911       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7912         "Enter contrast and midpoint:",levels);
7913       if (*levels == '\0')
7914         break;
7915       /*
7916         Contrast stretch image.
7917       */
7918       XSetCursorState(display,windows,MagickTrue);
7919       XCheckRefreshWindows(display,windows);
7920       flags=ParseGeometry(levels,&geometry_info);
7921       if ((flags & SigmaValue) == 0)
7922         geometry_info.sigma=1.0*QuantumRange/2.0;
7923       if ((flags & PercentValue) != 0)
7924         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7925       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7926         geometry_info.sigma,exception);
7927       XSetCursorState(display,windows,MagickFalse);
7928       if (windows->image.orphan != MagickFalse)
7929         break;
7930       XConfigureImageColormap(display,resource_info,windows,*image);
7931       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7932       break;
7933     }
7934     case NormalizeCommand:
7935     {
7936       /*
7937         Perform histogram normalization on the image.
7938       */
7939       XSetCursorState(display,windows,MagickTrue);
7940       XCheckRefreshWindows(display,windows);
7941       (void) NormalizeImage(*image,exception);
7942       XSetCursorState(display,windows,MagickFalse);
7943       if (windows->image.orphan != MagickFalse)
7944         break;
7945       XConfigureImageColormap(display,resource_info,windows,*image);
7946       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7947       break;
7948     }
7949     case EqualizeCommand:
7950     {
7951       /*
7952         Perform histogram equalization on the image.
7953       */
7954       XSetCursorState(display,windows,MagickTrue);
7955       XCheckRefreshWindows(display,windows);
7956       (void) EqualizeImage(*image,exception);
7957       XSetCursorState(display,windows,MagickFalse);
7958       if (windows->image.orphan != MagickFalse)
7959         break;
7960       XConfigureImageColormap(display,resource_info,windows,*image);
7961       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7962       break;
7963     }
7964     case NegateCommand:
7965     {
7966       /*
7967         Negate colors in image.
7968       */
7969       XSetCursorState(display,windows,MagickTrue);
7970       XCheckRefreshWindows(display,windows);
7971       (void) NegateImage(*image,MagickFalse,exception);
7972       XSetCursorState(display,windows,MagickFalse);
7973       if (windows->image.orphan != MagickFalse)
7974         break;
7975       XConfigureImageColormap(display,resource_info,windows,*image);
7976       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7977       break;
7978     }
7979     case GrayscaleCommand:
7980     {
7981       /*
7982         Convert image to grayscale.
7983       */
7984       XSetCursorState(display,windows,MagickTrue);
7985       XCheckRefreshWindows(display,windows);
7986       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7987         GrayscaleType : GrayscaleMatteType,exception);
7988       XSetCursorState(display,windows,MagickFalse);
7989       if (windows->image.orphan != MagickFalse)
7990         break;
7991       XConfigureImageColormap(display,resource_info,windows,*image);
7992       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7993       break;
7994     }
7995     case MapCommand:
7996     {
7997       Image
7998         *affinity_image;
7999
8000       static char
8001         filename[MaxTextExtent] = "\0";
8002
8003       /*
8004         Request image file name from user.
8005       */
8006       XFileBrowserWidget(display,windows,"Map",filename);
8007       if (*filename == '\0')
8008         break;
8009       /*
8010         Map image.
8011       */
8012       XSetCursorState(display,windows,MagickTrue);
8013       XCheckRefreshWindows(display,windows);
8014       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8015       affinity_image=ReadImage(image_info,exception);
8016       if (affinity_image != (Image *) NULL)
8017         {
8018           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8019           affinity_image=DestroyImage(affinity_image);
8020         }
8021       CatchException(exception);
8022       XSetCursorState(display,windows,MagickFalse);
8023       if (windows->image.orphan != MagickFalse)
8024         break;
8025       XConfigureImageColormap(display,resource_info,windows,*image);
8026       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8027       break;
8028     }
8029     case QuantizeCommand:
8030     {
8031       int
8032         status;
8033
8034       static char
8035         colors[MaxTextExtent] = "256";
8036
8037       /*
8038         Query user for maximum number of colors.
8039       */
8040       status=XDialogWidget(display,windows,"Quantize",
8041         "Maximum number of colors:",colors);
8042       if (*colors == '\0')
8043         break;
8044       /*
8045         Color reduce the image.
8046       */
8047       XSetCursorState(display,windows,MagickTrue);
8048       XCheckRefreshWindows(display,windows);
8049       quantize_info.number_colors=StringToUnsignedLong(colors);
8050       quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8051       (void) QuantizeImage(&quantize_info,*image,exception);
8052       XSetCursorState(display,windows,MagickFalse);
8053       if (windows->image.orphan != MagickFalse)
8054         break;
8055       XConfigureImageColormap(display,resource_info,windows,*image);
8056       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8057       break;
8058     }
8059     case DespeckleCommand:
8060     {
8061       Image
8062         *despeckle_image;
8063
8064       /*
8065         Despeckle image.
8066       */
8067       XSetCursorState(display,windows,MagickTrue);
8068       XCheckRefreshWindows(display,windows);
8069       despeckle_image=DespeckleImage(*image,exception);
8070       if (despeckle_image != (Image *) NULL)
8071         {
8072           *image=DestroyImage(*image);
8073           *image=despeckle_image;
8074         }
8075       CatchException(exception);
8076       XSetCursorState(display,windows,MagickFalse);
8077       if (windows->image.orphan != MagickFalse)
8078         break;
8079       XConfigureImageColormap(display,resource_info,windows,*image);
8080       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8081       break;
8082     }
8083     case EmbossCommand:
8084     {
8085       Image
8086         *emboss_image;
8087
8088       static char
8089         radius[MaxTextExtent] = "0.0x1.0";
8090
8091       /*
8092         Query user for emboss radius.
8093       */
8094       (void) XDialogWidget(display,windows,"Emboss",
8095         "Enter the emboss radius and standard deviation:",radius);
8096       if (*radius == '\0')
8097         break;
8098       /*
8099         Reduce noise in the image.
8100       */
8101       XSetCursorState(display,windows,MagickTrue);
8102       XCheckRefreshWindows(display,windows);
8103       flags=ParseGeometry(radius,&geometry_info);
8104       if ((flags & SigmaValue) == 0)
8105         geometry_info.sigma=1.0;
8106       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8107         exception);
8108       if (emboss_image != (Image *) NULL)
8109         {
8110           *image=DestroyImage(*image);
8111           *image=emboss_image;
8112         }
8113       CatchException(exception);
8114       XSetCursorState(display,windows,MagickFalse);
8115       if (windows->image.orphan != MagickFalse)
8116         break;
8117       XConfigureImageColormap(display,resource_info,windows,*image);
8118       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8119       break;
8120     }
8121     case ReduceNoiseCommand:
8122     {
8123       Image
8124         *noise_image;
8125
8126       static char
8127         radius[MaxTextExtent] = "0";
8128
8129       /*
8130         Query user for noise radius.
8131       */
8132       (void) XDialogWidget(display,windows,"Reduce Noise",
8133         "Enter the noise radius:",radius);
8134       if (*radius == '\0')
8135         break;
8136       /*
8137         Reduce noise in the image.
8138       */
8139       XSetCursorState(display,windows,MagickTrue);
8140       XCheckRefreshWindows(display,windows);
8141       flags=ParseGeometry(radius,&geometry_info);
8142       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8143         geometry_info.rho,(size_t) geometry_info.rho,exception);
8144       if (noise_image != (Image *) NULL)
8145         {
8146           *image=DestroyImage(*image);
8147           *image=noise_image;
8148         }
8149       CatchException(exception);
8150       XSetCursorState(display,windows,MagickFalse);
8151       if (windows->image.orphan != MagickFalse)
8152         break;
8153       XConfigureImageColormap(display,resource_info,windows,*image);
8154       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8155       break;
8156     }
8157     case AddNoiseCommand:
8158     {
8159       char
8160         **noises;
8161
8162       Image
8163         *noise_image;
8164
8165       static char
8166         noise_type[MaxTextExtent] = "Gaussian";
8167
8168       /*
8169         Add noise to the image.
8170       */
8171       noises=GetCommandOptions(MagickNoiseOptions);
8172       if (noises == (char **) NULL)
8173         break;
8174       XListBrowserWidget(display,windows,&windows->widget,
8175         (const char **) noises,"Add Noise",
8176         "Select a type of noise to add to your image:",noise_type);
8177       noises=DestroyStringList(noises);
8178       if (*noise_type == '\0')
8179         break;
8180       XSetCursorState(display,windows,MagickTrue);
8181       XCheckRefreshWindows(display,windows);
8182       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8183         MagickNoiseOptions,MagickFalse,noise_type),exception);
8184       if (noise_image != (Image *) NULL)
8185         {
8186           *image=DestroyImage(*image);
8187           *image=noise_image;
8188         }
8189       CatchException(exception);
8190       XSetCursorState(display,windows,MagickFalse);
8191       if (windows->image.orphan != MagickFalse)
8192         break;
8193       XConfigureImageColormap(display,resource_info,windows,*image);
8194       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8195       break;
8196     }
8197     case SharpenCommand:
8198     {
8199       Image
8200         *sharp_image;
8201
8202       static char
8203         radius[MaxTextExtent] = "0.0x1.0";
8204
8205       /*
8206         Query user for sharpen radius.
8207       */
8208       (void) XDialogWidget(display,windows,"Sharpen",
8209         "Enter the sharpen radius and standard deviation:",radius);
8210       if (*radius == '\0')
8211         break;
8212       /*
8213         Sharpen image scanlines.
8214       */
8215       XSetCursorState(display,windows,MagickTrue);
8216       XCheckRefreshWindows(display,windows);
8217       flags=ParseGeometry(radius,&geometry_info);
8218       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8219         geometry_info.xi,exception);
8220       if (sharp_image != (Image *) NULL)
8221         {
8222           *image=DestroyImage(*image);
8223           *image=sharp_image;
8224         }
8225       CatchException(exception);
8226       XSetCursorState(display,windows,MagickFalse);
8227       if (windows->image.orphan != MagickFalse)
8228         break;
8229       XConfigureImageColormap(display,resource_info,windows,*image);
8230       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8231       break;
8232     }
8233     case BlurCommand:
8234     {
8235       Image
8236         *blur_image;
8237
8238       static char
8239         radius[MaxTextExtent] = "0.0x1.0";
8240
8241       /*
8242         Query user for blur radius.
8243       */
8244       (void) XDialogWidget(display,windows,"Blur",
8245         "Enter the blur radius and standard deviation:",radius);
8246       if (*radius == '\0')
8247         break;
8248       /*
8249         Blur an image.
8250       */
8251       XSetCursorState(display,windows,MagickTrue);
8252       XCheckRefreshWindows(display,windows);
8253       flags=ParseGeometry(radius,&geometry_info);
8254       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8255         geometry_info.xi,exception);
8256       if (blur_image != (Image *) NULL)
8257         {
8258           *image=DestroyImage(*image);
8259           *image=blur_image;
8260         }
8261       CatchException(exception);
8262       XSetCursorState(display,windows,MagickFalse);
8263       if (windows->image.orphan != MagickFalse)
8264         break;
8265       XConfigureImageColormap(display,resource_info,windows,*image);
8266       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8267       break;
8268     }
8269     case ThresholdCommand:
8270     {
8271       double
8272         threshold;
8273
8274       static char
8275         factor[MaxTextExtent] = "128";
8276
8277       /*
8278         Query user for threshold value.
8279       */
8280       (void) XDialogWidget(display,windows,"Threshold",
8281         "Enter threshold value:",factor);
8282       if (*factor == '\0')
8283         break;
8284       /*
8285         Gamma correct image.
8286       */
8287       XSetCursorState(display,windows,MagickTrue);
8288       XCheckRefreshWindows(display,windows);
8289       threshold=SiPrefixToDouble(factor,QuantumRange);
8290       (void) BilevelImage(*image,threshold);
8291       XSetCursorState(display,windows,MagickFalse);
8292       if (windows->image.orphan != MagickFalse)
8293         break;
8294       XConfigureImageColormap(display,resource_info,windows,*image);
8295       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8296       break;
8297     }
8298     case EdgeDetectCommand:
8299     {
8300       Image
8301         *edge_image;
8302
8303       static char
8304         radius[MaxTextExtent] = "0";
8305
8306       /*
8307         Query user for edge factor.
8308       */
8309       (void) XDialogWidget(display,windows,"Detect Edges",
8310         "Enter the edge detect radius:",radius);
8311       if (*radius == '\0')
8312         break;
8313       /*
8314         Detect edge in image.
8315       */
8316       XSetCursorState(display,windows,MagickTrue);
8317       XCheckRefreshWindows(display,windows);
8318       flags=ParseGeometry(radius,&geometry_info);
8319       edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8320         exception);
8321       if (edge_image != (Image *) NULL)
8322         {
8323           *image=DestroyImage(*image);
8324           *image=edge_image;
8325         }
8326       CatchException(exception);
8327       XSetCursorState(display,windows,MagickFalse);
8328       if (windows->image.orphan != MagickFalse)
8329         break;
8330       XConfigureImageColormap(display,resource_info,windows,*image);
8331       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8332       break;
8333     }
8334     case SpreadCommand:
8335     {
8336       Image
8337         *spread_image;
8338
8339       static char
8340         amount[MaxTextExtent] = "2";
8341
8342       /*
8343         Query user for spread amount.
8344       */
8345       (void) XDialogWidget(display,windows,"Spread",
8346         "Enter the displacement amount:",amount);
8347       if (*amount == '\0')
8348         break;
8349       /*
8350         Displace image pixels by a random amount.
8351       */
8352       XSetCursorState(display,windows,MagickTrue);
8353       XCheckRefreshWindows(display,windows);
8354       flags=ParseGeometry(amount,&geometry_info);
8355       spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8356         exception);
8357       if (spread_image != (Image *) NULL)
8358         {
8359           *image=DestroyImage(*image);
8360           *image=spread_image;
8361         }
8362       CatchException(exception);
8363       XSetCursorState(display,windows,MagickFalse);
8364       if (windows->image.orphan != MagickFalse)
8365         break;
8366       XConfigureImageColormap(display,resource_info,windows,*image);
8367       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8368       break;
8369     }
8370     case ShadeCommand:
8371     {
8372       Image
8373         *shade_image;
8374
8375       int
8376         status;
8377
8378       static char
8379         geometry[MaxTextExtent] = "30x30";
8380
8381       /*
8382         Query user for the shade geometry.
8383       */
8384       status=XDialogWidget(display,windows,"Shade",
8385         "Enter the azimuth and elevation of the light source:",geometry);
8386       if (*geometry == '\0')
8387         break;
8388       /*
8389         Shade image pixels.
8390       */
8391       XSetCursorState(display,windows,MagickTrue);
8392       XCheckRefreshWindows(display,windows);
8393       flags=ParseGeometry(geometry,&geometry_info);
8394       if ((flags & SigmaValue) == 0)
8395         geometry_info.sigma=1.0;
8396       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8397         geometry_info.rho,geometry_info.sigma,exception);
8398       if (shade_image != (Image *) NULL)
8399         {
8400           *image=DestroyImage(*image);
8401           *image=shade_image;
8402         }
8403       CatchException(exception);
8404       XSetCursorState(display,windows,MagickFalse);
8405       if (windows->image.orphan != MagickFalse)
8406         break;
8407       XConfigureImageColormap(display,resource_info,windows,*image);
8408       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8409       break;
8410     }
8411     case RaiseCommand:
8412     {
8413       static char
8414         bevel_width[MaxTextExtent] = "10";
8415
8416       /*
8417         Query user for bevel width.
8418       */
8419       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8420       if (*bevel_width == '\0')
8421         break;
8422       /*
8423         Raise an image.
8424       */
8425       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8426         exception);
8427       XSetCursorState(display,windows,MagickTrue);
8428       XCheckRefreshWindows(display,windows);
8429       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8430         exception);
8431       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8432       XSetCursorState(display,windows,MagickFalse);
8433       if (windows->image.orphan != MagickFalse)
8434         break;
8435       XConfigureImageColormap(display,resource_info,windows,*image);
8436       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8437       break;
8438     }
8439     case SegmentCommand:
8440     {
8441       static char
8442         threshold[MaxTextExtent] = "1.0x1.5";
8443
8444       /*
8445         Query user for smoothing threshold.
8446       */
8447       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8448         threshold);
8449       if (*threshold == '\0')
8450         break;
8451       /*
8452         Segment an image.
8453       */
8454       XSetCursorState(display,windows,MagickTrue);
8455       XCheckRefreshWindows(display,windows);
8456       flags=ParseGeometry(threshold,&geometry_info);
8457       if ((flags & SigmaValue) == 0)
8458         geometry_info.sigma=1.0;
8459       (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8460         geometry_info.sigma,exception);
8461       XSetCursorState(display,windows,MagickFalse);
8462       if (windows->image.orphan != MagickFalse)
8463         break;
8464       XConfigureImageColormap(display,resource_info,windows,*image);
8465       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8466       break;
8467     }
8468     case SepiaToneCommand:
8469     {
8470       double
8471         threshold;
8472
8473       Image
8474         *sepia_image;
8475
8476       static char
8477         factor[MaxTextExtent] = "80%";
8478
8479       /*
8480         Query user for sepia-tone factor.
8481       */
8482       (void) XDialogWidget(display,windows,"Sepia Tone",
8483         "Enter the sepia tone factor (0 - 99.9%):",factor);
8484       if (*factor == '\0')
8485         break;
8486       /*
8487         Sepia tone image pixels.
8488       */
8489       XSetCursorState(display,windows,MagickTrue);
8490       XCheckRefreshWindows(display,windows);
8491       threshold=SiPrefixToDouble(factor,QuantumRange);
8492       sepia_image=SepiaToneImage(*image,threshold,exception);
8493       if (sepia_image != (Image *) NULL)
8494         {
8495           *image=DestroyImage(*image);
8496           *image=sepia_image;
8497         }
8498       CatchException(exception);
8499       XSetCursorState(display,windows,MagickFalse);
8500       if (windows->image.orphan != MagickFalse)
8501         break;
8502       XConfigureImageColormap(display,resource_info,windows,*image);
8503       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8504       break;
8505     }
8506     case SolarizeCommand:
8507     {
8508       double
8509         threshold;
8510
8511       static char
8512         factor[MaxTextExtent] = "60%";
8513
8514       /*
8515         Query user for solarize factor.
8516       */
8517       (void) XDialogWidget(display,windows,"Solarize",
8518         "Enter the solarize factor (0 - 99.9%):",factor);
8519       if (*factor == '\0')
8520         break;
8521       /*
8522         Solarize image pixels.
8523       */
8524       XSetCursorState(display,windows,MagickTrue);
8525       XCheckRefreshWindows(display,windows);
8526       threshold=SiPrefixToDouble(factor,QuantumRange);
8527       (void) SolarizeImage(*image,threshold,exception);
8528       XSetCursorState(display,windows,MagickFalse);
8529       if (windows->image.orphan != MagickFalse)
8530         break;
8531       XConfigureImageColormap(display,resource_info,windows,*image);
8532       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8533       break;
8534     }
8535     case SwirlCommand:
8536     {
8537       Image
8538         *swirl_image;
8539
8540       static char
8541         degrees[MaxTextExtent] = "60";
8542
8543       /*
8544         Query user for swirl angle.
8545       */
8546       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8547         degrees);
8548       if (*degrees == '\0')
8549         break;
8550       /*
8551         Swirl image pixels about the center.
8552       */
8553       XSetCursorState(display,windows,MagickTrue);
8554       XCheckRefreshWindows(display,windows);
8555       flags=ParseGeometry(degrees,&geometry_info);
8556       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8557         exception);
8558       if (swirl_image != (Image *) NULL)
8559         {
8560           *image=DestroyImage(*image);
8561           *image=swirl_image;
8562         }
8563       CatchException(exception);
8564       XSetCursorState(display,windows,MagickFalse);
8565       if (windows->image.orphan != MagickFalse)
8566         break;
8567       XConfigureImageColormap(display,resource_info,windows,*image);
8568       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8569       break;
8570     }
8571     case ImplodeCommand:
8572     {
8573       Image
8574         *implode_image;
8575
8576       static char
8577         factor[MaxTextExtent] = "0.3";
8578
8579       /*
8580         Query user for implode factor.
8581       */
8582       (void) XDialogWidget(display,windows,"Implode",
8583         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8584       if (*factor == '\0')
8585         break;
8586       /*
8587         Implode image pixels about the center.
8588       */
8589       XSetCursorState(display,windows,MagickTrue);
8590       XCheckRefreshWindows(display,windows);
8591       flags=ParseGeometry(factor,&geometry_info);
8592       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8593         exception);
8594       if (implode_image != (Image *) NULL)
8595         {
8596           *image=DestroyImage(*image);
8597           *image=implode_image;
8598         }
8599       CatchException(exception);
8600       XSetCursorState(display,windows,MagickFalse);
8601       if (windows->image.orphan != MagickFalse)
8602         break;
8603       XConfigureImageColormap(display,resource_info,windows,*image);
8604       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8605       break;
8606     }
8607     case VignetteCommand:
8608     {
8609       Image
8610         *vignette_image;
8611
8612       static char
8613         geometry[MaxTextExtent] = "0x20";
8614
8615       /*
8616         Query user for the vignette geometry.
8617       */
8618       (void) XDialogWidget(display,windows,"Vignette",
8619         "Enter the radius, sigma, and x and y offsets:",geometry);
8620       if (*geometry == '\0')
8621         break;
8622       /*
8623         Soften the edges of the image in vignette style
8624       */
8625       XSetCursorState(display,windows,MagickTrue);
8626       XCheckRefreshWindows(display,windows);
8627       flags=ParseGeometry(geometry,&geometry_info);
8628       if ((flags & SigmaValue) == 0)
8629         geometry_info.sigma=1.0;
8630       if ((flags & XiValue) == 0)
8631         geometry_info.xi=0.1*(*image)->columns;
8632       if ((flags & PsiValue) == 0)
8633         geometry_info.psi=0.1*(*image)->rows;
8634       vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8635         (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8636         0.5),exception);
8637       if (vignette_image != (Image *) NULL)
8638         {
8639           *image=DestroyImage(*image);
8640           *image=vignette_image;
8641         }
8642       CatchException(exception);
8643       XSetCursorState(display,windows,MagickFalse);
8644       if (windows->image.orphan != MagickFalse)
8645         break;
8646       XConfigureImageColormap(display,resource_info,windows,*image);
8647       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8648       break;
8649     }
8650     case WaveCommand:
8651     {
8652       Image
8653         *wave_image;
8654
8655       static char
8656         geometry[MaxTextExtent] = "25x150";
8657
8658       /*
8659         Query user for the wave geometry.
8660       */
8661       (void) XDialogWidget(display,windows,"Wave",
8662         "Enter the amplitude and length of the wave:",geometry);
8663       if (*geometry == '\0')
8664         break;
8665       /*
8666         Alter an image along a sine wave.
8667       */
8668       XSetCursorState(display,windows,MagickTrue);
8669       XCheckRefreshWindows(display,windows);
8670       flags=ParseGeometry(geometry,&geometry_info);
8671       if ((flags & SigmaValue) == 0)
8672         geometry_info.sigma=1.0;
8673       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8674         (*image)->interpolate,exception);
8675       if (wave_image != (Image *) NULL)
8676         {
8677           *image=DestroyImage(*image);
8678           *image=wave_image;
8679         }
8680       CatchException(exception);
8681       XSetCursorState(display,windows,MagickFalse);
8682       if (windows->image.orphan != MagickFalse)
8683         break;
8684       XConfigureImageColormap(display,resource_info,windows,*image);
8685       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8686       break;
8687     }
8688     case OilPaintCommand:
8689     {
8690       Image
8691         *paint_image;
8692
8693       static char
8694         radius[MaxTextExtent] = "0";
8695
8696       /*
8697         Query user for circular neighborhood radius.
8698       */
8699       (void) XDialogWidget(display,windows,"Oil Paint",
8700         "Enter the mask radius:",radius);
8701       if (*radius == '\0')
8702         break;
8703       /*
8704         OilPaint image scanlines.
8705       */
8706       XSetCursorState(display,windows,MagickTrue);
8707       XCheckRefreshWindows(display,windows);
8708       flags=ParseGeometry(radius,&geometry_info);
8709       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8710         exception);
8711       if (paint_image != (Image *) NULL)
8712         {
8713           *image=DestroyImage(*image);
8714           *image=paint_image;
8715         }
8716       CatchException(exception);
8717       XSetCursorState(display,windows,MagickFalse);
8718       if (windows->image.orphan != MagickFalse)
8719         break;
8720       XConfigureImageColormap(display,resource_info,windows,*image);
8721       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8722       break;
8723     }
8724     case CharcoalDrawCommand:
8725     {
8726       Image
8727         *charcoal_image;
8728
8729       static char
8730         radius[MaxTextExtent] = "0x1";
8731
8732       /*
8733         Query user for charcoal radius.
8734       */
8735       (void) XDialogWidget(display,windows,"Charcoal Draw",
8736         "Enter the charcoal radius and sigma:",radius);
8737       if (*radius == '\0')
8738         break;
8739       /*
8740         Charcoal the image.
8741       */
8742       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8743         exception);
8744       XSetCursorState(display,windows,MagickTrue);
8745       XCheckRefreshWindows(display,windows);
8746       flags=ParseGeometry(radius,&geometry_info);
8747       if ((flags & SigmaValue) == 0)
8748         geometry_info.sigma=geometry_info.rho;
8749       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8750         geometry_info.xi,exception);
8751       if (charcoal_image != (Image *) NULL)
8752         {
8753           *image=DestroyImage(*image);
8754           *image=charcoal_image;
8755         }
8756       CatchException(exception);
8757       XSetCursorState(display,windows,MagickFalse);
8758       if (windows->image.orphan != MagickFalse)
8759         break;
8760       XConfigureImageColormap(display,resource_info,windows,*image);
8761       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8762       break;
8763     }
8764     case AnnotateCommand:
8765     {
8766       /*
8767         Annotate the image with text.
8768       */
8769       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8770       if (status == MagickFalse)
8771         {
8772           XNoticeWidget(display,windows,"Unable to annotate X image",
8773             (*image)->filename);
8774           break;
8775         }
8776       break;
8777     }
8778     case DrawCommand:
8779     {
8780       /*
8781         Draw image.
8782       */
8783       status=XDrawEditImage(display,resource_info,windows,image,exception);
8784       if (status == MagickFalse)
8785         {
8786           XNoticeWidget(display,windows,"Unable to draw on the X image",
8787             (*image)->filename);
8788           break;
8789         }
8790       break;
8791     }
8792     case ColorCommand:
8793     {
8794       /*
8795         Color edit.
8796       */
8797       status=XColorEditImage(display,resource_info,windows,image,exception);
8798       if (status == MagickFalse)
8799         {
8800           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8801             (*image)->filename);
8802           break;
8803         }
8804       break;
8805     }
8806     case MatteCommand:
8807     {
8808       /*
8809         Matte edit.
8810       */
8811       status=XMatteEditImage(display,resource_info,windows,image,exception);
8812       if (status == MagickFalse)
8813         {
8814           XNoticeWidget(display,windows,"Unable to matte edit X image",
8815             (*image)->filename);
8816           break;
8817         }
8818       break;
8819     }
8820     case CompositeCommand:
8821     {
8822       /*
8823         Composite image.
8824       */
8825       status=XCompositeImage(display,resource_info,windows,*image,
8826         exception);
8827       if (status == MagickFalse)
8828         {
8829           XNoticeWidget(display,windows,"Unable to composite X image",
8830             (*image)->filename);
8831           break;
8832         }
8833       break;
8834     }
8835     case AddBorderCommand:
8836     {
8837       Image
8838         *border_image;
8839
8840       static char
8841         geometry[MaxTextExtent] = "6x6";
8842
8843       /*
8844         Query user for border color and geometry.
8845       */
8846       XColorBrowserWidget(display,windows,"Select",color);
8847       if (*color == '\0')
8848         break;
8849       (void) XDialogWidget(display,windows,"Add Border",
8850         "Enter border geometry:",geometry);
8851       if (*geometry == '\0')
8852         break;
8853       /*
8854         Add a border to the image.
8855       */
8856       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8857         exception);
8858       XSetCursorState(display,windows,MagickTrue);
8859       XCheckRefreshWindows(display,windows);
8860       (void) QueryColorDatabase(color,&(*image)->border_color,
8861         exception);
8862       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8863         exception);
8864       border_image=BorderImage(*image,&page_geometry,exception);
8865       if (border_image != (Image *) NULL)
8866         {
8867           *image=DestroyImage(*image);
8868           *image=border_image;
8869         }
8870       CatchException(exception);
8871       XSetCursorState(display,windows,MagickFalse);
8872       if (windows->image.orphan != MagickFalse)
8873         break;
8874       windows->image.window_changes.width=(int) (*image)->columns;
8875       windows->image.window_changes.height=(int) (*image)->rows;
8876       XConfigureImageColormap(display,resource_info,windows,*image);
8877       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8878       break;
8879     }
8880     case AddFrameCommand:
8881     {
8882       FrameInfo
8883         frame_info;
8884
8885       Image
8886         *frame_image;
8887
8888       static char
8889         geometry[MaxTextExtent] = "6x6";
8890
8891       /*
8892         Query user for frame color and geometry.
8893       */
8894       XColorBrowserWidget(display,windows,"Select",color);
8895       if (*color == '\0')
8896         break;
8897       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8898         geometry);
8899       if (*geometry == '\0')
8900         break;
8901       /*
8902         Surround image with an ornamental border.
8903       */
8904       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8905         exception);
8906       XSetCursorState(display,windows,MagickTrue);
8907       XCheckRefreshWindows(display,windows);
8908       (void) QueryColorDatabase(color,&(*image)->matte_color,
8909         exception);
8910       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8911         exception);
8912       frame_info.width=page_geometry.width;
8913       frame_info.height=page_geometry.height;
8914       frame_info.outer_bevel=page_geometry.x;
8915       frame_info.inner_bevel=page_geometry.y;
8916       frame_info.x=(ssize_t) frame_info.width;
8917       frame_info.y=(ssize_t) frame_info.height;
8918       frame_info.width=(*image)->columns+2*frame_info.width;
8919       frame_info.height=(*image)->rows+2*frame_info.height;
8920       frame_image=FrameImage(*image,&frame_info,exception);
8921       if (frame_image != (Image *) NULL)
8922         {
8923           *image=DestroyImage(*image);
8924           *image=frame_image;
8925         }
8926       CatchException(exception);
8927       XSetCursorState(display,windows,MagickFalse);
8928       if (windows->image.orphan != MagickFalse)
8929         break;
8930       windows->image.window_changes.width=(int) (*image)->columns;
8931       windows->image.window_changes.height=(int) (*image)->rows;
8932       XConfigureImageColormap(display,resource_info,windows,*image);
8933       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8934       break;
8935     }
8936     case CommentCommand:
8937     {
8938       const char
8939         *value;
8940
8941       FILE
8942         *file;
8943
8944       int
8945         unique_file;
8946
8947       /*
8948         Edit image comment.
8949       */
8950       unique_file=AcquireUniqueFileResource(image_info->filename);
8951       if (unique_file == -1)
8952         XNoticeWidget(display,windows,"Unable to edit image comment",
8953           image_info->filename);
8954       value=GetImageProperty(*image,"comment");
8955       if (value == (char *) NULL)
8956         unique_file=close(unique_file)-1;
8957       else
8958         {
8959           register const char
8960             *p;
8961
8962           file=fdopen(unique_file,"w");
8963           if (file == (FILE *) NULL)
8964             {
8965               XNoticeWidget(display,windows,"Unable to edit image comment",
8966                 image_info->filename);
8967               break;
8968             }
8969           for (p=value; *p != '\0'; p++)
8970             (void) fputc((int) *p,file);
8971           (void) fputc('\n',file);
8972           (void) fclose(file);
8973         }
8974       XSetCursorState(display,windows,MagickTrue);
8975       XCheckRefreshWindows(display,windows);
8976       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8977         exception);
8978       if (status == MagickFalse)
8979         XNoticeWidget(display,windows,"Unable to edit image comment",
8980           (char *) NULL);
8981       else
8982         {
8983           char
8984             *comment;
8985
8986           comment=FileToString(image_info->filename,~0UL,exception);
8987           if (comment != (char *) NULL)
8988             {
8989               (void) SetImageProperty(*image,"comment",comment);
8990               (*image)->taint=MagickTrue;
8991             }
8992         }
8993       (void) RelinquishUniqueFileResource(image_info->filename);
8994       XSetCursorState(display,windows,MagickFalse);
8995       break;
8996     }
8997     case LaunchCommand:
8998     {
8999       /*
9000         Launch program.
9001       */
9002       XSetCursorState(display,windows,MagickTrue);
9003       XCheckRefreshWindows(display,windows);
9004       (void) AcquireUniqueFilename(filename);
9005       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9006         filename);
9007       status=WriteImage(image_info,*image,exception);
9008       if (status == MagickFalse)
9009         XNoticeWidget(display,windows,"Unable to launch image editor",
9010           (char *) NULL);
9011       else
9012         {
9013           nexus=ReadImage(resource_info->image_info,exception);
9014           CatchException(exception);
9015           XClientMessage(display,windows->image.id,windows->im_protocols,
9016             windows->im_next_image,CurrentTime);
9017         }
9018       (void) RelinquishUniqueFileResource(filename);
9019       XSetCursorState(display,windows,MagickFalse);
9020       break;
9021     }
9022     case RegionofInterestCommand:
9023     {
9024       /*
9025         Apply an image processing technique to a region of interest.
9026       */
9027       (void) XROIImage(display,resource_info,windows,image,exception);
9028       break;
9029     }
9030     case InfoCommand:
9031       break;
9032     case ZoomCommand:
9033     {
9034       /*
9035         Zoom image.
9036       */
9037       if (windows->magnify.mapped != MagickFalse)
9038         (void) XRaiseWindow(display,windows->magnify.id);
9039       else
9040         {
9041           /*
9042             Make magnify image.
9043           */
9044           XSetCursorState(display,windows,MagickTrue);
9045           (void) XMapRaised(display,windows->magnify.id);
9046           XSetCursorState(display,windows,MagickFalse);
9047         }
9048       break;
9049     }
9050     case ShowPreviewCommand:
9051     {
9052       char
9053         **previews;
9054
9055       Image
9056         *preview_image;
9057
9058       static char
9059         preview_type[MaxTextExtent] = "Gamma";
9060
9061       /*
9062         Select preview type from menu.
9063       */
9064       previews=GetCommandOptions(MagickPreviewOptions);
9065       if (previews == (char **) NULL)
9066         break;
9067       XListBrowserWidget(display,windows,&windows->widget,
9068         (const char **) previews,"Preview",
9069         "Select an enhancement, effect, or F/X:",preview_type);
9070       previews=DestroyStringList(previews);
9071       if (*preview_type == '\0')
9072         break;
9073       /*
9074         Show image preview.
9075       */
9076       XSetCursorState(display,windows,MagickTrue);
9077       XCheckRefreshWindows(display,windows);
9078       image_info->preview_type=(PreviewType)
9079         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9080       image_info->group=(ssize_t) windows->image.id;
9081       (void) DeleteImageProperty(*image,"label");
9082       (void) SetImageProperty(*image,"label","Preview");
9083       (void) AcquireUniqueFilename(filename);
9084       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9085         filename);
9086       status=WriteImage(image_info,*image,exception);
9087       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9088       preview_image=ReadImage(image_info,exception);
9089       (void) RelinquishUniqueFileResource(filename);
9090       if (preview_image == (Image *) NULL)
9091         break;
9092       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9093         filename);
9094       status=WriteImage(image_info,preview_image,exception);
9095       preview_image=DestroyImage(preview_image);
9096       if (status == MagickFalse)
9097         XNoticeWidget(display,windows,"Unable to show image preview",
9098           (*image)->filename);
9099       XDelay(display,1500);
9100       XSetCursorState(display,windows,MagickFalse);
9101       break;
9102     }
9103     case ShowHistogramCommand:
9104     {
9105       Image
9106         *histogram_image;
9107
9108       /*
9109         Show image histogram.
9110       */
9111       XSetCursorState(display,windows,MagickTrue);
9112       XCheckRefreshWindows(display,windows);
9113       image_info->group=(ssize_t) windows->image.id;
9114       (void) DeleteImageProperty(*image,"label");
9115       (void) SetImageProperty(*image,"label","Histogram");
9116       (void) AcquireUniqueFilename(filename);
9117       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9118         filename);
9119       status=WriteImage(image_info,*image,exception);
9120       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9121       histogram_image=ReadImage(image_info,exception);
9122       (void) RelinquishUniqueFileResource(filename);
9123       if (histogram_image == (Image *) NULL)
9124         break;
9125       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9126         "show:%s",filename);
9127       status=WriteImage(image_info,histogram_image,exception);
9128       histogram_image=DestroyImage(histogram_image);
9129       if (status == MagickFalse)
9130         XNoticeWidget(display,windows,"Unable to show histogram",
9131           (*image)->filename);
9132       XDelay(display,1500);
9133       XSetCursorState(display,windows,MagickFalse);
9134       break;
9135     }
9136     case ShowMatteCommand:
9137     {
9138       Image
9139         *matte_image;
9140
9141       if ((*image)->matte == MagickFalse)
9142         {
9143           XNoticeWidget(display,windows,
9144             "Image does not have any matte information",(*image)->filename);
9145           break;
9146         }
9147       /*
9148         Show image matte.
9149       */
9150       XSetCursorState(display,windows,MagickTrue);
9151       XCheckRefreshWindows(display,windows);
9152       image_info->group=(ssize_t) windows->image.id;
9153       (void) DeleteImageProperty(*image,"label");
9154       (void) SetImageProperty(*image,"label","Matte");
9155       (void) AcquireUniqueFilename(filename);
9156       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9157         filename);
9158       status=WriteImage(image_info,*image,exception);
9159       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9160       matte_image=ReadImage(image_info,exception);
9161       (void) RelinquishUniqueFileResource(filename);
9162       if (matte_image == (Image *) NULL)
9163         break;
9164       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9165         filename);
9166       status=WriteImage(image_info,matte_image,exception);
9167       matte_image=DestroyImage(matte_image);
9168       if (status == MagickFalse)
9169         XNoticeWidget(display,windows,"Unable to show matte",
9170           (*image)->filename);
9171       XDelay(display,1500);
9172       XSetCursorState(display,windows,MagickFalse);
9173       break;
9174     }
9175     case BackgroundCommand:
9176     {
9177       /*
9178         Background image.
9179       */
9180       status=XBackgroundImage(display,resource_info,windows,image,exception);
9181       if (status == MagickFalse)
9182         break;
9183       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9184       if (nexus != (Image *) NULL)
9185         XClientMessage(display,windows->image.id,windows->im_protocols,
9186           windows->im_next_image,CurrentTime);
9187       break;
9188     }
9189     case SlideShowCommand:
9190     {
9191       static char
9192         delay[MaxTextExtent] = "5";
9193
9194       /*
9195         Display next image after pausing.
9196       */
9197       (void) XDialogWidget(display,windows,"Slide Show",
9198         "Pause how many 1/100ths of a second between images:",delay);
9199       if (*delay == '\0')
9200         break;
9201       resource_info->delay=StringToUnsignedLong(delay);
9202       XClientMessage(display,windows->image.id,windows->im_protocols,
9203         windows->im_next_image,CurrentTime);
9204       break;
9205     }
9206     case PreferencesCommand:
9207     {
9208       /*
9209         Set user preferences.
9210       */
9211       status=XPreferencesWidget(display,resource_info,windows);
9212       if (status == MagickFalse)
9213         break;
9214       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9215       if (nexus != (Image *) NULL)
9216         XClientMessage(display,windows->image.id,windows->im_protocols,
9217           windows->im_next_image,CurrentTime);
9218       break;
9219     }
9220     case HelpCommand:
9221     {
9222       /*
9223         User requested help.
9224       */
9225       XTextViewWidget(display,resource_info,windows,MagickFalse,
9226         "Help Viewer - Display",DisplayHelp);
9227       break;
9228     }
9229     case BrowseDocumentationCommand:
9230     {
9231       Atom
9232         mozilla_atom;
9233
9234       Window
9235         mozilla_window,
9236         root_window;
9237
9238       /*
9239         Browse the ImageMagick documentation.
9240       */
9241       root_window=XRootWindow(display,XDefaultScreen(display));
9242       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9243       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9244       if (mozilla_window != (Window) NULL)
9245         {
9246           char
9247             command[MaxTextExtent],
9248             *url;
9249
9250           /*
9251             Display documentation using Netscape remote control.
9252           */
9253           url=GetMagickHomeURL();
9254           (void) FormatLocaleString(command,MaxTextExtent,
9255             "openurl(%s,new-tab)",url);
9256           url=DestroyString(url);
9257           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9258           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9259             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9260           XSetCursorState(display,windows,MagickFalse);
9261           break;
9262         }
9263       XSetCursorState(display,windows,MagickTrue);
9264       XCheckRefreshWindows(display,windows);
9265       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9266         exception);
9267       if (status == MagickFalse)
9268         XNoticeWidget(display,windows,"Unable to browse documentation",
9269           (char *) NULL);
9270       XDelay(display,1500);
9271       XSetCursorState(display,windows,MagickFalse);
9272       break;
9273     }
9274     case VersionCommand:
9275     {
9276       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9277         GetMagickCopyright());
9278       break;
9279     }
9280     case SaveToUndoBufferCommand:
9281       break;
9282     default:
9283     {
9284       (void) XBell(display,0);
9285       break;
9286     }
9287   }
9288   image_info=DestroyImageInfo(image_info);
9289   return(nexus);
9290 }
9291 \f
9292 /*
9293 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9294 %                                                                             %
9295 %                                                                             %
9296 %                                                                             %
9297 +   X M a g n i f y I m a g e                                                 %
9298 %                                                                             %
9299 %                                                                             %
9300 %                                                                             %
9301 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9302 %
9303 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9304 %  The magnified portion is displayed in a separate window.
9305 %
9306 %  The format of the XMagnifyImage method is:
9307 %
9308 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9309 %
9310 %  A description of each parameter follows:
9311 %
9312 %    o display: Specifies a connection to an X server;  returned from
9313 %      XOpenDisplay.
9314 %
9315 %    o windows: Specifies a pointer to a XWindows structure.
9316 %
9317 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9318 %      the entire image is refreshed.
9319 %
9320 */
9321 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9322 {
9323   char
9324     text[MaxTextExtent];
9325
9326   register int
9327     x,
9328     y;
9329
9330   size_t
9331     state;
9332
9333   /*
9334     Update magnified image until the mouse button is released.
9335   */
9336   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9337   state=DefaultState;
9338   x=event->xbutton.x;
9339   y=event->xbutton.y;
9340   windows->magnify.x=(int) windows->image.x+x;
9341   windows->magnify.y=(int) windows->image.y+y;
9342   do
9343   {
9344     /*
9345       Map and unmap Info widget as text cursor crosses its boundaries.
9346     */
9347     if (windows->info.mapped != MagickFalse)
9348       {
9349         if ((x < (int) (windows->info.x+windows->info.width)) &&
9350             (y < (int) (windows->info.y+windows->info.height)))
9351           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9352       }
9353     else
9354       if ((x > (int) (windows->info.x+windows->info.width)) ||
9355           (y > (int) (windows->info.y+windows->info.height)))
9356         (void) XMapWindow(display,windows->info.id);
9357     if (windows->info.mapped != MagickFalse)
9358       {
9359         /*
9360           Display pointer position.
9361         */
9362         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9363           windows->magnify.x,windows->magnify.y);
9364         XInfoWidget(display,windows,text);
9365       }
9366     /*
9367       Wait for next event.
9368     */
9369     XScreenEvent(display,windows,event);
9370     switch (event->type)
9371     {
9372       case ButtonPress:
9373         break;
9374       case ButtonRelease:
9375       {
9376         /*
9377           User has finished magnifying image.
9378         */
9379         x=event->xbutton.x;
9380         y=event->xbutton.y;
9381         state|=ExitState;
9382         break;
9383       }
9384       case Expose:
9385         break;
9386       case MotionNotify:
9387       {
9388         x=event->xmotion.x;
9389         y=event->xmotion.y;
9390         break;
9391       }
9392       default:
9393         break;
9394     }
9395     /*
9396       Check boundary conditions.
9397     */
9398     if (x < 0)
9399       x=0;
9400     else
9401       if (x >= (int) windows->image.width)
9402         x=(int) windows->image.width-1;
9403     if (y < 0)
9404       y=0;
9405     else
9406      if (y >= (int) windows->image.height)
9407        y=(int) windows->image.height-1;
9408   } while ((state & ExitState) == 0);
9409   /*
9410     Display magnified image.
9411   */
9412   XSetCursorState(display,windows,MagickFalse);
9413 }
9414 \f
9415 /*
9416 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9417 %                                                                             %
9418 %                                                                             %
9419 %                                                                             %
9420 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9421 %                                                                             %
9422 %                                                                             %
9423 %                                                                             %
9424 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9425 %
9426 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9427 %  pixel as specified by the key symbol.
9428 %
9429 %  The format of the XMagnifyWindowCommand method is:
9430 %
9431 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9432 %        const MagickStatusType state,const KeySym key_symbol)
9433 %
9434 %  A description of each parameter follows:
9435 %
9436 %    o display: Specifies a connection to an X server; returned from
9437 %      XOpenDisplay.
9438 %
9439 %    o windows: Specifies a pointer to a XWindows structure.
9440 %
9441 %    o state: key mask.
9442 %
9443 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9444 %      to trim.
9445 %
9446 */
9447 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9448   const MagickStatusType state,const KeySym key_symbol)
9449 {
9450   unsigned int
9451     quantum;
9452
9453   /*
9454     User specified a magnify factor or position.
9455   */
9456   quantum=1;
9457   if ((state & Mod1Mask) != 0)
9458     quantum=10;
9459   switch ((int) key_symbol)
9460   {
9461     case QuitCommand:
9462     {
9463       (void) XWithdrawWindow(display,windows->magnify.id,
9464         windows->magnify.screen);
9465       break;
9466     }
9467     case XK_Home:
9468     case XK_KP_Home:
9469     {
9470       windows->magnify.x=(int) windows->image.width/2;
9471       windows->magnify.y=(int) windows->image.height/2;
9472       break;
9473     }
9474     case XK_Left:
9475     case XK_KP_Left:
9476     {
9477       if (windows->magnify.x > 0)
9478         windows->magnify.x-=quantum;
9479       break;
9480     }
9481     case XK_Up:
9482     case XK_KP_Up:
9483     {
9484       if (windows->magnify.y > 0)
9485         windows->magnify.y-=quantum;
9486       break;
9487     }
9488     case XK_Right:
9489     case XK_KP_Right:
9490     {
9491       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9492         windows->magnify.x+=quantum;
9493       break;
9494     }
9495     case XK_Down:
9496     case XK_KP_Down:
9497     {
9498       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9499         windows->magnify.y+=quantum;
9500       break;
9501     }
9502     case XK_0:
9503     case XK_1:
9504     case XK_2:
9505     case XK_3:
9506     case XK_4:
9507     case XK_5:
9508     case XK_6:
9509     case XK_7:
9510     case XK_8:
9511     case XK_9:
9512     {
9513       windows->magnify.data=(key_symbol-XK_0);
9514       break;
9515     }
9516     case XK_KP_0:
9517     case XK_KP_1:
9518     case XK_KP_2:
9519     case XK_KP_3:
9520     case XK_KP_4:
9521     case XK_KP_5:
9522     case XK_KP_6:
9523     case XK_KP_7:
9524     case XK_KP_8:
9525     case XK_KP_9:
9526     {
9527       windows->magnify.data=(key_symbol-XK_KP_0);
9528       break;
9529     }
9530     default:
9531       break;
9532   }
9533   XMakeMagnifyImage(display,windows);
9534 }
9535 \f
9536 /*
9537 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9538 %                                                                             %
9539 %                                                                             %
9540 %                                                                             %
9541 +   X M a k e P a n I m a g e                                                 %
9542 %                                                                             %
9543 %                                                                             %
9544 %                                                                             %
9545 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9546 %
9547 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9548 %  icon window.
9549 %
9550 %  The format of the XMakePanImage method is:
9551 %
9552 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9553 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9554 %
9555 %  A description of each parameter follows:
9556 %
9557 %    o display: Specifies a connection to an X server;  returned from
9558 %      XOpenDisplay.
9559 %
9560 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9561 %
9562 %    o windows: Specifies a pointer to a XWindows structure.
9563 %
9564 %    o image: the image.
9565 %
9566 %    o exception: return any errors or warnings in this structure.
9567 %
9568 */
9569 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9570   XWindows *windows,Image *image,ExceptionInfo *exception)
9571 {
9572   MagickStatusType
9573     status;
9574
9575   /*
9576     Create and display image for panning icon.
9577   */
9578   XSetCursorState(display,windows,MagickTrue);
9579   XCheckRefreshWindows(display,windows);
9580   windows->pan.x=(int) windows->image.x;
9581   windows->pan.y=(int) windows->image.y;
9582   status=XMakeImage(display,resource_info,&windows->pan,image,
9583     windows->pan.width,windows->pan.height,exception);
9584   if (status == MagickFalse)
9585     ThrowXWindowFatalException(ResourceLimitError,
9586      "MemoryAllocationFailed",image->filename);
9587   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9588     windows->pan.pixmap);
9589   (void) XClearWindow(display,windows->pan.id);
9590   XDrawPanRectangle(display,windows);
9591   XSetCursorState(display,windows,MagickFalse);
9592 }
9593 \f
9594 /*
9595 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9596 %                                                                             %
9597 %                                                                             %
9598 %                                                                             %
9599 +   X M a t t a E d i t I m a g e                                             %
9600 %                                                                             %
9601 %                                                                             %
9602 %                                                                             %
9603 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9604 %
9605 %  XMatteEditImage() allows the user to interactively change the Matte channel
9606 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9607 %  before the matte information is stored.
9608 %
9609 %  The format of the XMatteEditImage method is:
9610 %
9611 %      MagickBooleanType XMatteEditImage(Display *display,
9612 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9613 %        ExceptionInfo *exception)
9614 %
9615 %  A description of each parameter follows:
9616 %
9617 %    o display: Specifies a connection to an X server;  returned from
9618 %      XOpenDisplay.
9619 %
9620 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9621 %
9622 %    o windows: Specifies a pointer to a XWindows structure.
9623 %
9624 %    o image: the image; returned from ReadImage.
9625 %
9626 %    o exception: return any errors or warnings in this structure.
9627 %
9628 */
9629 static MagickBooleanType XMatteEditImage(Display *display,
9630   XResourceInfo *resource_info,XWindows *windows,Image **image,
9631   ExceptionInfo *exception)
9632 {
9633   static char
9634     matte[MaxTextExtent] = "0";
9635
9636   static const char
9637     *MatteEditMenu[] =
9638     {
9639       "Method",
9640       "Border Color",
9641       "Fuzz",
9642       "Matte Value",
9643       "Undo",
9644       "Help",
9645       "Dismiss",
9646       (char *) NULL
9647     };
9648
9649   static const ModeType
9650     MatteEditCommands[] =
9651     {
9652       MatteEditMethod,
9653       MatteEditBorderCommand,
9654       MatteEditFuzzCommand,
9655       MatteEditValueCommand,
9656       MatteEditUndoCommand,
9657       MatteEditHelpCommand,
9658       MatteEditDismissCommand
9659     };
9660
9661   static PaintMethod
9662     method = PointMethod;
9663
9664   static XColor
9665     border_color = { 0, 0, 0, 0, 0, 0 };
9666
9667   char
9668     command[MaxTextExtent],
9669     text[MaxTextExtent];
9670
9671   Cursor
9672     cursor;
9673
9674   int
9675     entry,
9676     id,
9677     x,
9678     x_offset,
9679     y,
9680     y_offset;
9681
9682   register int
9683     i;
9684
9685   register Quantum
9686     *q;
9687
9688   unsigned int
9689     height,
9690     width;
9691
9692   size_t
9693     state;
9694
9695   XEvent
9696     event;
9697
9698   /*
9699     Map Command widget.
9700   */
9701   (void) CloneString(&windows->command.name,"Matte Edit");
9702   windows->command.data=4;
9703   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9704   (void) XMapRaised(display,windows->command.id);
9705   XClientMessage(display,windows->image.id,windows->im_protocols,
9706     windows->im_update_widget,CurrentTime);
9707   /*
9708     Make cursor.
9709   */
9710   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9711     resource_info->background_color,resource_info->foreground_color);
9712   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9713   /*
9714     Track pointer until button 1 is pressed.
9715   */
9716   XQueryPosition(display,windows->image.id,&x,&y);
9717   (void) XSelectInput(display,windows->image.id,
9718     windows->image.attributes.event_mask | PointerMotionMask);
9719   state=DefaultState;
9720   do
9721   {
9722     if (windows->info.mapped != MagickFalse)
9723       {
9724         /*
9725           Display pointer position.
9726         */
9727         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9728           x+windows->image.x,y+windows->image.y);
9729         XInfoWidget(display,windows,text);
9730       }
9731     /*
9732       Wait for next event.
9733     */
9734     XScreenEvent(display,windows,&event);
9735     if (event.xany.window == windows->command.id)
9736       {
9737         /*
9738           Select a command from the Command widget.
9739         */
9740         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9741         if (id < 0)
9742           {
9743             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9744             continue;
9745           }
9746         switch (MatteEditCommands[id])
9747         {
9748           case MatteEditMethod:
9749           {
9750             char
9751               **methods;
9752
9753             /*
9754               Select a method from the pop-up menu.
9755             */
9756             methods=GetCommandOptions(MagickMethodOptions);
9757             if (methods == (char **) NULL)
9758               break;
9759             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9760               (const char **) methods,command);
9761             if (entry >= 0)
9762               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9763                 MagickFalse,methods[entry]);
9764             methods=DestroyStringList(methods);
9765             break;
9766           }
9767           case MatteEditBorderCommand:
9768           {
9769             const char
9770               *ColorMenu[MaxNumberPens];
9771
9772             int
9773               pen_number;
9774
9775             /*
9776               Initialize menu selections.
9777             */
9778             for (i=0; i < (int) (MaxNumberPens-2); i++)
9779               ColorMenu[i]=resource_info->pen_colors[i];
9780             ColorMenu[MaxNumberPens-2]="Browser...";
9781             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9782             /*
9783               Select a pen color from the pop-up menu.
9784             */
9785             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9786               (const char **) ColorMenu,command);
9787             if (pen_number < 0)
9788               break;
9789             if (pen_number == (MaxNumberPens-2))
9790               {
9791                 static char
9792                   color_name[MaxTextExtent] = "gray";
9793
9794                 /*
9795                   Select a pen color from a dialog.
9796                 */
9797                 resource_info->pen_colors[pen_number]=color_name;
9798                 XColorBrowserWidget(display,windows,"Select",color_name);
9799                 if (*color_name == '\0')
9800                   break;
9801               }
9802             /*
9803               Set border color.
9804             */
9805             (void) XParseColor(display,windows->map_info->colormap,
9806               resource_info->pen_colors[pen_number],&border_color);
9807             break;
9808           }
9809           case MatteEditFuzzCommand:
9810           {
9811             static char
9812               fuzz[MaxTextExtent];
9813
9814             static const char
9815               *FuzzMenu[] =
9816               {
9817                 "0%",
9818                 "2%",
9819                 "5%",
9820                 "10%",
9821                 "15%",
9822                 "Dialog...",
9823                 (char *) NULL,
9824               };
9825
9826             /*
9827               Select a command from the pop-up menu.
9828             */
9829             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9830               command);
9831             if (entry < 0)
9832               break;
9833             if (entry != 5)
9834               {
9835                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
9836                   QuantumRange+1.0);
9837                 break;
9838               }
9839             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9840             (void) XDialogWidget(display,windows,"Ok",
9841               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9842             if (*fuzz == '\0')
9843               break;
9844             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9845             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9846             break;
9847           }
9848           case MatteEditValueCommand:
9849           {
9850             static char
9851               message[MaxTextExtent];
9852
9853             static const char
9854               *MatteMenu[] =
9855               {
9856                 "Opaque",
9857                 "Transparent",
9858                 "Dialog...",
9859                 (char *) NULL,
9860               };
9861
9862             /*
9863               Select a command from the pop-up menu.
9864             */
9865             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9866               command);
9867             if (entry < 0)
9868               break;
9869             if (entry != 2)
9870               {
9871                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9872                   OpaqueAlpha);
9873                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9874                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9875                     (Quantum) TransparentAlpha);
9876                 break;
9877               }
9878             (void) FormatLocaleString(message,MaxTextExtent,
9879               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9880               QuantumRange);
9881             (void) XDialogWidget(display,windows,"Matte",message,matte);
9882             if (*matte == '\0')
9883               break;
9884             break;
9885           }
9886           case MatteEditUndoCommand:
9887           {
9888             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9889               image,exception);
9890             break;
9891           }
9892           case MatteEditHelpCommand:
9893           {
9894             XTextViewWidget(display,resource_info,windows,MagickFalse,
9895               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9896             break;
9897           }
9898           case MatteEditDismissCommand:
9899           {
9900             /*
9901               Prematurely exit.
9902             */
9903             state|=EscapeState;
9904             state|=ExitState;
9905             break;
9906           }
9907           default:
9908             break;
9909         }
9910         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9911         continue;
9912       }
9913     switch (event.type)
9914     {
9915       case ButtonPress:
9916       {
9917         if (event.xbutton.button != Button1)
9918           break;
9919         if ((event.xbutton.window != windows->image.id) &&
9920             (event.xbutton.window != windows->magnify.id))
9921           break;
9922         /*
9923           Update matte data.
9924         */
9925         x=event.xbutton.x;
9926         y=event.xbutton.y;
9927         (void) XMagickCommand(display,resource_info,windows,
9928           SaveToUndoBufferCommand,image,exception);
9929         state|=UpdateConfigurationState;
9930         break;
9931       }
9932       case ButtonRelease:
9933       {
9934         if (event.xbutton.button != Button1)
9935           break;
9936         if ((event.xbutton.window != windows->image.id) &&
9937             (event.xbutton.window != windows->magnify.id))
9938           break;
9939         /*
9940           Update colormap information.
9941         */
9942         x=event.xbutton.x;
9943         y=event.xbutton.y;
9944         XConfigureImageColormap(display,resource_info,windows,*image);
9945         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9946         XInfoWidget(display,windows,text);
9947         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9948         state&=(~UpdateConfigurationState);
9949         break;
9950       }
9951       case Expose:
9952         break;
9953       case KeyPress:
9954       {
9955         char
9956           command[MaxTextExtent];
9957
9958         KeySym
9959           key_symbol;
9960
9961         if (event.xkey.window == windows->magnify.id)
9962           {
9963             Window
9964               window;
9965
9966             window=windows->magnify.id;
9967             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9968           }
9969         if (event.xkey.window != windows->image.id)
9970           break;
9971         /*
9972           Respond to a user key press.
9973         */
9974         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9975           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9976         switch ((int) key_symbol)
9977         {
9978           case XK_Escape:
9979           case XK_F20:
9980           {
9981             /*
9982               Prematurely exit.
9983             */
9984             state|=ExitState;
9985             break;
9986           }
9987           case XK_F1:
9988           case XK_Help:
9989           {
9990             XTextViewWidget(display,resource_info,windows,MagickFalse,
9991               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9992             break;
9993           }
9994           default:
9995           {
9996             (void) XBell(display,0);
9997             break;
9998           }
9999         }
10000         break;
10001       }
10002       case MotionNotify:
10003       {
10004         /*
10005           Map and unmap Info widget as cursor crosses its boundaries.
10006         */
10007         x=event.xmotion.x;
10008         y=event.xmotion.y;
10009         if (windows->info.mapped != MagickFalse)
10010           {
10011             if ((x < (int) (windows->info.x+windows->info.width)) &&
10012                 (y < (int) (windows->info.y+windows->info.height)))
10013               (void) XWithdrawWindow(display,windows->info.id,
10014                 windows->info.screen);
10015           }
10016         else
10017           if ((x > (int) (windows->info.x+windows->info.width)) ||
10018               (y > (int) (windows->info.y+windows->info.height)))
10019             (void) XMapWindow(display,windows->info.id);
10020         break;
10021       }
10022       default:
10023         break;
10024     }
10025     if (event.xany.window == windows->magnify.id)
10026       {
10027         x=windows->magnify.x-windows->image.x;
10028         y=windows->magnify.y-windows->image.y;
10029       }
10030     x_offset=x;
10031     y_offset=y;
10032     if ((state & UpdateConfigurationState) != 0)
10033       {
10034         CacheView
10035           *image_view;
10036
10037         int
10038           x,
10039           y;
10040
10041         /*
10042           Matte edit is relative to image configuration.
10043         */
10044         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10045           MagickTrue);
10046         XPutPixel(windows->image.ximage,x_offset,y_offset,
10047           windows->pixel_info->background_color.pixel);
10048         width=(unsigned int) (*image)->columns;
10049         height=(unsigned int) (*image)->rows;
10050         x=0;
10051         y=0;
10052         if (windows->image.crop_geometry != (char *) NULL)
10053           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10054             &height);
10055         x_offset=(int) (width*(windows->image.x+x_offset)/
10056           windows->image.ximage->width+x);
10057         y_offset=(int) (height*(windows->image.y+y_offset)/
10058           windows->image.ximage->height+y);
10059         if ((x_offset < 0) || (y_offset < 0))
10060           continue;
10061         if ((x_offset >= (int) (*image)->columns) ||
10062             (y_offset >= (int) (*image)->rows))
10063           continue;
10064         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10065           return(MagickFalse);
10066         (*image)->matte=MagickTrue;
10067         image_view=AcquireCacheView(*image);
10068         switch (method)
10069         {
10070           case PointMethod:
10071           default:
10072           {
10073             /*
10074               Update matte information using point algorithm.
10075             */
10076             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10077               (ssize_t) y_offset,1,1,exception);
10078             if (q == (Quantum *) NULL)
10079               break;
10080             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10081             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10082             break;
10083           }
10084           case ReplaceMethod:
10085           {
10086             PixelPacket
10087               pixel,
10088               target;
10089
10090             /*
10091               Update matte information using replace algorithm.
10092             */
10093             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10094               (ssize_t) y_offset,&target,exception);
10095             for (y=0; y < (int) (*image)->rows; y++)
10096             {
10097               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10098                 (*image)->columns,1,exception);
10099               if (q == (Quantum *) NULL)
10100                 break;
10101               for (x=0; x < (int) (*image)->columns; x++)
10102               {
10103                 GetPixelPacket(*image,q,&pixel);
10104                 if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
10105                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10106                 q+=GetPixelChannels(*image);
10107               }
10108               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10109                 break;
10110             }
10111             break;
10112           }
10113           case FloodfillMethod:
10114           case FillToBorderMethod:
10115           {
10116             ChannelType
10117               channel_mask;
10118
10119             DrawInfo
10120               *draw_info;
10121
10122             PixelInfo
10123               target;
10124
10125             /*
10126               Update matte information using floodfill algorithm.
10127             */
10128             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10129               (ssize_t) y_offset,&target,exception);
10130             if (method == FillToBorderMethod)
10131               {
10132                 target.red=(MagickRealType) ScaleShortToQuantum(
10133                   border_color.red);
10134                 target.green=(MagickRealType) ScaleShortToQuantum(
10135                   border_color.green);
10136                 target.blue=(MagickRealType) ScaleShortToQuantum(
10137                   border_color.blue);
10138               }
10139             draw_info=CloneDrawInfo(resource_info->image_info,
10140               (DrawInfo *) NULL);
10141             draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10142               (char **) NULL));
10143             channel_mask=SetPixelChannelMask(*image,AlphaChannel); 
10144             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10145               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10146               MagickFalse : MagickTrue,exception);
10147             (void) SetPixelChannelMap(*image,channel_mask);
10148             draw_info=DestroyDrawInfo(draw_info);
10149             break;
10150           }
10151           case ResetMethod:
10152           {
10153             /*
10154               Update matte information using reset algorithm.
10155             */
10156             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10157               return(MagickFalse);
10158             for (y=0; y < (int) (*image)->rows; y++)
10159             {
10160               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10161                 (*image)->columns,1,exception);
10162               if (q == (Quantum *) NULL)
10163                 break;
10164               for (x=0; x < (int) (*image)->columns; x++)
10165               {
10166                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10167                 q+=GetPixelChannels(*image);
10168               }
10169               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10170                 break;
10171             }
10172             if (StringToLong(matte) == (long) OpaqueAlpha)
10173               (*image)->matte=MagickFalse;
10174             break;
10175           }
10176         }
10177         image_view=DestroyCacheView(image_view);
10178         state&=(~UpdateConfigurationState);
10179       }
10180   } while ((state & ExitState) == 0);
10181   (void) XSelectInput(display,windows->image.id,
10182     windows->image.attributes.event_mask);
10183   XSetCursorState(display,windows,MagickFalse);
10184   (void) XFreeCursor(display,cursor);
10185   return(MagickTrue);
10186 }
10187 \f
10188 /*
10189 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10190 %                                                                             %
10191 %                                                                             %
10192 %                                                                             %
10193 +   X O p e n I m a g e                                                       %
10194 %                                                                             %
10195 %                                                                             %
10196 %                                                                             %
10197 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10198 %
10199 %  XOpenImage() loads an image from a file.
10200 %
10201 %  The format of the XOpenImage method is:
10202 %
10203 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10204 %       XWindows *windows,const unsigned int command)
10205 %
10206 %  A description of each parameter follows:
10207 %
10208 %    o display: Specifies a connection to an X server; returned from
10209 %      XOpenDisplay.
10210 %
10211 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10212 %
10213 %    o windows: Specifies a pointer to a XWindows structure.
10214 %
10215 %    o command: A value other than zero indicates that the file is selected
10216 %      from the command line argument list.
10217 %
10218 */
10219 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10220   XWindows *windows,const MagickBooleanType command)
10221 {
10222   const MagickInfo
10223     *magick_info;
10224
10225   ExceptionInfo
10226     *exception;
10227
10228   Image
10229     *nexus;
10230
10231   ImageInfo
10232     *image_info;
10233
10234   static char
10235     filename[MaxTextExtent] = "\0";
10236
10237   /*
10238     Request file name from user.
10239   */
10240   if (command == MagickFalse)
10241     XFileBrowserWidget(display,windows,"Open",filename);
10242   else
10243     {
10244       char
10245         **filelist,
10246         **files;
10247
10248       int
10249         count,
10250         status;
10251
10252       register int
10253         i,
10254         j;
10255
10256       /*
10257         Select next image from the command line.
10258       */
10259       status=XGetCommand(display,windows->image.id,&files,&count);
10260       if (status == 0)
10261         {
10262           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10263           return((Image *) NULL);
10264         }
10265       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10266       if (filelist == (char **) NULL)
10267         {
10268           ThrowXWindowFatalException(ResourceLimitError,
10269             "MemoryAllocationFailed","...");
10270           (void) XFreeStringList(files);
10271           return((Image *) NULL);
10272         }
10273       j=0;
10274       for (i=1; i < count; i++)
10275         if (*files[i] != '-')
10276           filelist[j++]=files[i];
10277       filelist[j]=(char *) NULL;
10278       XListBrowserWidget(display,windows,&windows->widget,
10279         (const char **) filelist,"Load","Select Image to Load:",filename);
10280       filelist=(char **) RelinquishMagickMemory(filelist);
10281       (void) XFreeStringList(files);
10282     }
10283   if (*filename == '\0')
10284     return((Image *) NULL);
10285   image_info=CloneImageInfo(resource_info->image_info);
10286   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10287     (void *) NULL);
10288   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10289   exception=AcquireExceptionInfo();
10290   (void) SetImageInfo(image_info,0,exception);
10291   if (LocaleCompare(image_info->magick,"X") == 0)
10292     {
10293       char
10294         seconds[MaxTextExtent];
10295
10296       /*
10297         User may want to delay the X server screen grab.
10298       */
10299       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10300       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10301         seconds);
10302       if (*seconds == '\0')
10303         return((Image *) NULL);
10304       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10305     }
10306   magick_info=GetMagickInfo(image_info->magick,exception);
10307   if ((magick_info != (const MagickInfo *) NULL) &&
10308       (magick_info->raw != MagickFalse))
10309     {
10310       char
10311         geometry[MaxTextExtent];
10312
10313       /*
10314         Request image size from the user.
10315       */
10316       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10317       if (image_info->size != (char *) NULL)
10318         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10319       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10320         geometry);
10321       (void) CloneString(&image_info->size,geometry);
10322     }
10323   /*
10324     Load the image.
10325   */
10326   XSetCursorState(display,windows,MagickTrue);
10327   XCheckRefreshWindows(display,windows);
10328   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10329   nexus=ReadImage(image_info,exception);
10330   CatchException(exception);
10331   XSetCursorState(display,windows,MagickFalse);
10332   if (nexus != (Image *) NULL)
10333     XClientMessage(display,windows->image.id,windows->im_protocols,
10334       windows->im_next_image,CurrentTime);
10335   else
10336     {
10337       char
10338         *text,
10339         **textlist;
10340
10341       /*
10342         Unknown image format.
10343       */
10344       text=FileToString(filename,~0,exception);
10345       if (text == (char *) NULL)
10346         return((Image *) NULL);
10347       textlist=StringToList(text);
10348       if (textlist != (char **) NULL)
10349         {
10350           char
10351             title[MaxTextExtent];
10352
10353           register int
10354             i;
10355
10356           (void) FormatLocaleString(title,MaxTextExtent,
10357             "Unknown format: %s",filename);
10358           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10359             (const char **) textlist);
10360           for (i=0; textlist[i] != (char *) NULL; i++)
10361             textlist[i]=DestroyString(textlist[i]);
10362           textlist=(char **) RelinquishMagickMemory(textlist);
10363         }
10364       text=DestroyString(text);
10365     }
10366   exception=DestroyExceptionInfo(exception);
10367   image_info=DestroyImageInfo(image_info);
10368   return(nexus);
10369 }
10370 \f
10371 /*
10372 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10373 %                                                                             %
10374 %                                                                             %
10375 %                                                                             %
10376 +   X P a n I m a g e                                                         %
10377 %                                                                             %
10378 %                                                                             %
10379 %                                                                             %
10380 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10381 %
10382 %  XPanImage() pans the image until the mouse button is released.
10383 %
10384 %  The format of the XPanImage method is:
10385 %
10386 %      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10387 %
10388 %  A description of each parameter follows:
10389 %
10390 %    o display: Specifies a connection to an X server;  returned from
10391 %      XOpenDisplay.
10392 %
10393 %    o windows: Specifies a pointer to a XWindows structure.
10394 %
10395 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10396 %      the entire image is refreshed.
10397 %
10398 */
10399 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10400 {
10401   char
10402     text[MaxTextExtent];
10403
10404   Cursor
10405     cursor;
10406
10407   MagickRealType
10408     x_factor,
10409     y_factor;
10410
10411   RectangleInfo
10412     pan_info;
10413
10414   size_t
10415     state;
10416
10417   /*
10418     Define cursor.
10419   */
10420   if ((windows->image.ximage->width > (int) windows->image.width) &&
10421       (windows->image.ximage->height > (int) windows->image.height))
10422     cursor=XCreateFontCursor(display,XC_fleur);
10423   else
10424     if (windows->image.ximage->width > (int) windows->image.width)
10425       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10426     else
10427       if (windows->image.ximage->height > (int) windows->image.height)
10428         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10429       else
10430         cursor=XCreateFontCursor(display,XC_arrow);
10431   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10432   /*
10433     Pan image as pointer moves until the mouse button is released.
10434   */
10435   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10436   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10437   pan_info.width=windows->pan.width*windows->image.width/
10438     windows->image.ximage->width;
10439   pan_info.height=windows->pan.height*windows->image.height/
10440     windows->image.ximage->height;
10441   pan_info.x=0;
10442   pan_info.y=0;
10443   state=UpdateConfigurationState;
10444   do
10445   {
10446     switch (event->type)
10447     {
10448       case ButtonPress:
10449       {
10450         /*
10451           User choose an initial pan location.
10452         */
10453         pan_info.x=(ssize_t) event->xbutton.x;
10454         pan_info.y=(ssize_t) event->xbutton.y;
10455         state|=UpdateConfigurationState;
10456         break;
10457       }
10458       case ButtonRelease:
10459       {
10460         /*
10461           User has finished panning the image.
10462         */
10463         pan_info.x=(ssize_t) event->xbutton.x;
10464         pan_info.y=(ssize_t) event->xbutton.y;
10465         state|=UpdateConfigurationState | ExitState;
10466         break;
10467       }
10468       case MotionNotify:
10469       {
10470         pan_info.x=(ssize_t) event->xmotion.x;
10471         pan_info.y=(ssize_t) event->xmotion.y;
10472         state|=UpdateConfigurationState;
10473       }
10474       default:
10475         break;
10476     }
10477     if ((state & UpdateConfigurationState) != 0)
10478       {
10479         /*
10480           Check boundary conditions.
10481         */
10482         if (pan_info.x < (ssize_t) (pan_info.width/2))
10483           pan_info.x=0;
10484         else
10485           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10486         if (pan_info.x < 0)
10487           pan_info.x=0;
10488         else
10489           if ((int) (pan_info.x+windows->image.width) >
10490               windows->image.ximage->width)
10491             pan_info.x=(ssize_t)
10492               (windows->image.ximage->width-windows->image.width);
10493         if (pan_info.y < (ssize_t) (pan_info.height/2))
10494           pan_info.y=0;
10495         else
10496           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10497         if (pan_info.y < 0)
10498           pan_info.y=0;
10499         else
10500           if ((int) (pan_info.y+windows->image.height) >
10501               windows->image.ximage->height)
10502             pan_info.y=(ssize_t)
10503               (windows->image.ximage->height-windows->image.height);
10504         if ((windows->image.x != (int) pan_info.x) ||
10505             (windows->image.y != (int) pan_info.y))
10506           {
10507             /*
10508               Display image pan offset.
10509             */
10510             windows->image.x=(int) pan_info.x;
10511             windows->image.y=(int) pan_info.y;
10512             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10513               windows->image.width,windows->image.height,windows->image.x,
10514               windows->image.y);
10515             XInfoWidget(display,windows,text);
10516             /*
10517               Refresh Image window.
10518             */
10519             XDrawPanRectangle(display,windows);
10520             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10521           }
10522         state&=(~UpdateConfigurationState);
10523       }
10524     /*
10525       Wait for next event.
10526     */
10527     if ((state & ExitState) == 0)
10528       XScreenEvent(display,windows,event);
10529   } while ((state & ExitState) == 0);
10530   /*
10531     Restore cursor.
10532   */
10533   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10534   (void) XFreeCursor(display,cursor);
10535   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10536 }
10537 \f
10538 /*
10539 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10540 %                                                                             %
10541 %                                                                             %
10542 %                                                                             %
10543 +   X P a s t e I m a g e                                                     %
10544 %                                                                             %
10545 %                                                                             %
10546 %                                                                             %
10547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10548 %
10549 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10550 %  window image at a location the user chooses with the pointer.
10551 %
10552 %  The format of the XPasteImage method is:
10553 %
10554 %      MagickBooleanType XPasteImage(Display *display,
10555 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10556 %        ExceptionInfo *exception)
10557 %
10558 %  A description of each parameter follows:
10559 %
10560 %    o display: Specifies a connection to an X server;  returned from
10561 %      XOpenDisplay.
10562 %
10563 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10564 %
10565 %    o windows: Specifies a pointer to a XWindows structure.
10566 %
10567 %    o image: the image; returned from ReadImage.
10568 %
10569 %    o exception: return any errors or warnings in this structure.
10570 %
10571 */
10572 static MagickBooleanType XPasteImage(Display *display,
10573   XResourceInfo *resource_info,XWindows *windows,Image *image,
10574   ExceptionInfo *exception)
10575 {
10576   static const char
10577     *PasteMenu[] =
10578     {
10579       "Operator",
10580       "Help",
10581       "Dismiss",
10582       (char *) NULL
10583     };
10584
10585   static const ModeType
10586     PasteCommands[] =
10587     {
10588       PasteOperatorsCommand,
10589       PasteHelpCommand,
10590       PasteDismissCommand
10591     };
10592
10593   static CompositeOperator
10594     compose = CopyCompositeOp;
10595
10596   char
10597     text[MaxTextExtent];
10598
10599   Cursor
10600     cursor;
10601
10602   Image
10603     *paste_image;
10604
10605   int
10606     entry,
10607     id,
10608     x,
10609     y;
10610
10611   MagickRealType
10612     scale_factor;
10613
10614   RectangleInfo
10615     highlight_info,
10616     paste_info;
10617
10618   unsigned int
10619     height,
10620     width;
10621
10622   size_t
10623     state;
10624
10625   XEvent
10626     event;
10627
10628   /*
10629     Copy image.
10630   */
10631   if (resource_info->copy_image == (Image *) NULL)
10632     return(MagickFalse);
10633   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10634   /*
10635     Map Command widget.
10636   */
10637   (void) CloneString(&windows->command.name,"Paste");
10638   windows->command.data=1;
10639   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10640   (void) XMapRaised(display,windows->command.id);
10641   XClientMessage(display,windows->image.id,windows->im_protocols,
10642     windows->im_update_widget,CurrentTime);
10643   /*
10644     Track pointer until button 1 is pressed.
10645   */
10646   XSetCursorState(display,windows,MagickFalse);
10647   XQueryPosition(display,windows->image.id,&x,&y);
10648   (void) XSelectInput(display,windows->image.id,
10649     windows->image.attributes.event_mask | PointerMotionMask);
10650   paste_info.x=(ssize_t) windows->image.x+x;
10651   paste_info.y=(ssize_t) windows->image.y+y;
10652   paste_info.width=0;
10653   paste_info.height=0;
10654   cursor=XCreateFontCursor(display,XC_ul_angle);
10655   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10656   state=DefaultState;
10657   do
10658   {
10659     if (windows->info.mapped != MagickFalse)
10660       {
10661         /*
10662           Display pointer position.
10663         */
10664         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10665           (long) paste_info.x,(long) paste_info.y);
10666         XInfoWidget(display,windows,text);
10667       }
10668     highlight_info=paste_info;
10669     highlight_info.x=paste_info.x-windows->image.x;
10670     highlight_info.y=paste_info.y-windows->image.y;
10671     XHighlightRectangle(display,windows->image.id,
10672       windows->image.highlight_context,&highlight_info);
10673     /*
10674       Wait for next event.
10675     */
10676     XScreenEvent(display,windows,&event);
10677     XHighlightRectangle(display,windows->image.id,
10678       windows->image.highlight_context,&highlight_info);
10679     if (event.xany.window == windows->command.id)
10680       {
10681         /*
10682           Select a command from the Command widget.
10683         */
10684         id=XCommandWidget(display,windows,PasteMenu,&event);
10685         if (id < 0)
10686           continue;
10687         switch (PasteCommands[id])
10688         {
10689           case PasteOperatorsCommand:
10690           {
10691             char
10692               command[MaxTextExtent],
10693               **operators;
10694
10695             /*
10696               Select a command from the pop-up menu.
10697             */
10698             operators=GetCommandOptions(MagickComposeOptions);
10699             if (operators == (char **) NULL)
10700               break;
10701             entry=XMenuWidget(display,windows,PasteMenu[id],
10702               (const char **) operators,command);
10703             if (entry >= 0)
10704               compose=(CompositeOperator) ParseCommandOption(
10705                 MagickComposeOptions,MagickFalse,operators[entry]);
10706             operators=DestroyStringList(operators);
10707             break;
10708           }
10709           case PasteHelpCommand:
10710           {
10711             XTextViewWidget(display,resource_info,windows,MagickFalse,
10712               "Help Viewer - Image Composite",ImagePasteHelp);
10713             break;
10714           }
10715           case PasteDismissCommand:
10716           {
10717             /*
10718               Prematurely exit.
10719             */
10720             state|=EscapeState;
10721             state|=ExitState;
10722             break;
10723           }
10724           default:
10725             break;
10726         }
10727         continue;
10728       }
10729     switch (event.type)
10730     {
10731       case ButtonPress:
10732       {
10733         if (image->debug != MagickFalse)
10734           (void) LogMagickEvent(X11Event,GetMagickModule(),
10735             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10736             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10737         if (event.xbutton.button != Button1)
10738           break;
10739         if (event.xbutton.window != windows->image.id)
10740           break;
10741         /*
10742           Paste rectangle is relative to image configuration.
10743         */
10744         width=(unsigned int) image->columns;
10745         height=(unsigned int) image->rows;
10746         x=0;
10747         y=0;
10748         if (windows->image.crop_geometry != (char *) NULL)
10749           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10750             &width,&height);
10751         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10752         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10753         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10754         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10755         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10756         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10757         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10758         break;
10759       }
10760       case ButtonRelease:
10761       {
10762         if (image->debug != MagickFalse)
10763           (void) LogMagickEvent(X11Event,GetMagickModule(),
10764             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10765             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10766         if (event.xbutton.button != Button1)
10767           break;
10768         if (event.xbutton.window != windows->image.id)
10769           break;
10770         if ((paste_info.width != 0) && (paste_info.height != 0))
10771           {
10772             /*
10773               User has selected the location of the paste image.
10774             */
10775             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10776             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10777             state|=ExitState;
10778           }
10779         break;
10780       }
10781       case Expose:
10782         break;
10783       case KeyPress:
10784       {
10785         char
10786           command[MaxTextExtent];
10787
10788         KeySym
10789           key_symbol;
10790
10791         int
10792           length;
10793
10794         if (event.xkey.window != windows->image.id)
10795           break;
10796         /*
10797           Respond to a user key press.
10798         */
10799         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10800           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10801         *(command+length)='\0';
10802         if (image->debug != MagickFalse)
10803           (void) LogMagickEvent(X11Event,GetMagickModule(),
10804             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10805         switch ((int) key_symbol)
10806         {
10807           case XK_Escape:
10808           case XK_F20:
10809           {
10810             /*
10811               Prematurely exit.
10812             */
10813             paste_image=DestroyImage(paste_image);
10814             state|=EscapeState;
10815             state|=ExitState;
10816             break;
10817           }
10818           case XK_F1:
10819           case XK_Help:
10820           {
10821             (void) XSetFunction(display,windows->image.highlight_context,
10822               GXcopy);
10823             XTextViewWidget(display,resource_info,windows,MagickFalse,
10824               "Help Viewer - Image Composite",ImagePasteHelp);
10825             (void) XSetFunction(display,windows->image.highlight_context,
10826               GXinvert);
10827             break;
10828           }
10829           default:
10830           {
10831             (void) XBell(display,0);
10832             break;
10833           }
10834         }
10835         break;
10836       }
10837       case MotionNotify:
10838       {
10839         /*
10840           Map and unmap Info widget as text cursor crosses its boundaries.
10841         */
10842         x=event.xmotion.x;
10843         y=event.xmotion.y;
10844         if (windows->info.mapped != MagickFalse)
10845           {
10846             if ((x < (int) (windows->info.x+windows->info.width)) &&
10847                 (y < (int) (windows->info.y+windows->info.height)))
10848               (void) XWithdrawWindow(display,windows->info.id,
10849                 windows->info.screen);
10850           }
10851         else
10852           if ((x > (int) (windows->info.x+windows->info.width)) ||
10853               (y > (int) (windows->info.y+windows->info.height)))
10854             (void) XMapWindow(display,windows->info.id);
10855         paste_info.x=(ssize_t) windows->image.x+x;
10856         paste_info.y=(ssize_t) windows->image.y+y;
10857         break;
10858       }
10859       default:
10860       {
10861         if (image->debug != MagickFalse)
10862           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10863             event.type);
10864         break;
10865       }
10866     }
10867   } while ((state & ExitState) == 0);
10868   (void) XSelectInput(display,windows->image.id,
10869     windows->image.attributes.event_mask);
10870   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10871   XSetCursorState(display,windows,MagickFalse);
10872   (void) XFreeCursor(display,cursor);
10873   if ((state & EscapeState) != 0)
10874     return(MagickTrue);
10875   /*
10876     Image pasting is relative to image configuration.
10877   */
10878   XSetCursorState(display,windows,MagickTrue);
10879   XCheckRefreshWindows(display,windows);
10880   width=(unsigned int) image->columns;
10881   height=(unsigned int) image->rows;
10882   x=0;
10883   y=0;
10884   if (windows->image.crop_geometry != (char *) NULL)
10885     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10886   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10887   paste_info.x+=x;
10888   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10889   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10890   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10891   paste_info.y+=y;
10892   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10893   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10894   /*
10895     Paste image with X Image window.
10896   */
10897   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10898   paste_image=DestroyImage(paste_image);
10899   XSetCursorState(display,windows,MagickFalse);
10900   /*
10901     Update image colormap.
10902   */
10903   XConfigureImageColormap(display,resource_info,windows,image);
10904   (void) XConfigureImage(display,resource_info,windows,image,exception);
10905   return(MagickTrue);
10906 }
10907 \f
10908 /*
10909 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10910 %                                                                             %
10911 %                                                                             %
10912 %                                                                             %
10913 +   X P r i n t I m a g e                                                     %
10914 %                                                                             %
10915 %                                                                             %
10916 %                                                                             %
10917 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10918 %
10919 %  XPrintImage() prints an image to a Postscript printer.
10920 %
10921 %  The format of the XPrintImage method is:
10922 %
10923 %      MagickBooleanType XPrintImage(Display *display,
10924 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10925 %        ExceptionInfo *exception)
10926 %
10927 %  A description of each parameter follows:
10928 %
10929 %    o display: Specifies a connection to an X server; returned from
10930 %      XOpenDisplay.
10931 %
10932 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10933 %
10934 %    o windows: Specifies a pointer to a XWindows structure.
10935 %
10936 %    o image: the image.
10937 %
10938 %    o exception: return any errors or warnings in this structure.
10939 %
10940 */
10941 static MagickBooleanType XPrintImage(Display *display,
10942   XResourceInfo *resource_info,XWindows *windows,Image *image,
10943   ExceptionInfo *exception)
10944 {
10945   char
10946     filename[MaxTextExtent],
10947     geometry[MaxTextExtent];
10948
10949   Image
10950     *print_image;
10951
10952   ImageInfo
10953     *image_info;
10954
10955   MagickStatusType
10956     status;
10957
10958   /*
10959     Request Postscript page geometry from user.
10960   */
10961   image_info=CloneImageInfo(resource_info->image_info);
10962   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10963   if (image_info->page != (char *) NULL)
10964     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10965   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10966     "Select Postscript Page Geometry:",geometry);
10967   if (*geometry == '\0')
10968     return(MagickTrue);
10969   image_info->page=GetPageGeometry(geometry);
10970   /*
10971     Apply image transforms.
10972   */
10973   XSetCursorState(display,windows,MagickTrue);
10974   XCheckRefreshWindows(display,windows);
10975   print_image=CloneImage(image,0,0,MagickTrue,exception);
10976   if (print_image == (Image *) NULL)
10977     return(MagickFalse);
10978   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10979     windows->image.ximage->width,windows->image.ximage->height);
10980   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10981   /*
10982     Print image.
10983   */
10984   (void) AcquireUniqueFilename(filename);
10985   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10986     filename);
10987   status=WriteImage(image_info,print_image,exception);
10988   (void) RelinquishUniqueFileResource(filename);
10989   print_image=DestroyImage(print_image);
10990   image_info=DestroyImageInfo(image_info);
10991   XSetCursorState(display,windows,MagickFalse);
10992   return(status != 0 ? MagickTrue : MagickFalse);
10993 }
10994 \f
10995 /*
10996 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10997 %                                                                             %
10998 %                                                                             %
10999 %                                                                             %
11000 +   X R O I I m a g e                                                         %
11001 %                                                                             %
11002 %                                                                             %
11003 %                                                                             %
11004 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11005 %
11006 %  XROIImage() applies an image processing technique to a region of interest.
11007 %
11008 %  The format of the XROIImage method is:
11009 %
11010 %      MagickBooleanType XROIImage(Display *display,
11011 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11012 %        ExceptionInfo *exception)
11013 %
11014 %  A description of each parameter follows:
11015 %
11016 %    o display: Specifies a connection to an X server; returned from
11017 %      XOpenDisplay.
11018 %
11019 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11020 %
11021 %    o windows: Specifies a pointer to a XWindows structure.
11022 %
11023 %    o image: the image; returned from ReadImage.
11024 %
11025 %    o exception: return any errors or warnings in this structure.
11026 %
11027 */
11028 static MagickBooleanType XROIImage(Display *display,
11029   XResourceInfo *resource_info,XWindows *windows,Image **image,
11030   ExceptionInfo *exception)
11031 {
11032 #define ApplyMenus  7
11033
11034   static const char
11035     *ROIMenu[] =
11036     {
11037       "Help",
11038       "Dismiss",
11039       (char *) NULL
11040     },
11041     *ApplyMenu[] =
11042     {
11043       "File",
11044       "Edit",
11045       "Transform",
11046       "Enhance",
11047       "Effects",
11048       "F/X",
11049       "Miscellany",
11050       "Help",
11051       "Dismiss",
11052       (char *) NULL
11053     },
11054     *FileMenu[] =
11055     {
11056       "Save...",
11057       "Print...",
11058       (char *) NULL
11059     },
11060     *EditMenu[] =
11061     {
11062       "Undo",
11063       "Redo",
11064       (char *) NULL
11065     },
11066     *TransformMenu[] =
11067     {
11068       "Flop",
11069       "Flip",
11070       "Rotate Right",
11071       "Rotate Left",
11072       (char *) NULL
11073     },
11074     *EnhanceMenu[] =
11075     {
11076       "Hue...",
11077       "Saturation...",
11078       "Brightness...",
11079       "Gamma...",
11080       "Spiff",
11081       "Dull",
11082       "Contrast Stretch...",
11083       "Sigmoidal Contrast...",
11084       "Normalize",
11085       "Equalize",
11086       "Negate",
11087       "Grayscale",
11088       "Map...",
11089       "Quantize...",
11090       (char *) NULL
11091     },
11092     *EffectsMenu[] =
11093     {
11094       "Despeckle",
11095       "Emboss",
11096       "Reduce Noise",
11097       "Add Noise",
11098       "Sharpen...",
11099       "Blur...",
11100       "Threshold...",
11101       "Edge Detect...",
11102       "Spread...",
11103       "Shade...",
11104       "Raise...",
11105       "Segment...",
11106       (char *) NULL
11107     },
11108     *FXMenu[] =
11109     {
11110       "Solarize...",
11111       "Sepia Tone...",
11112       "Swirl...",
11113       "Implode...",
11114       "Vignette...",
11115       "Wave...",
11116       "Oil Paint...",
11117       "Charcoal Draw...",
11118       (char *) NULL
11119     },
11120     *MiscellanyMenu[] =
11121     {
11122       "Image Info",
11123       "Zoom Image",
11124       "Show Preview...",
11125       "Show Histogram",
11126       "Show Matte",
11127       (char *) NULL
11128     };
11129
11130   static const char
11131     **Menus[ApplyMenus] =
11132     {
11133       FileMenu,
11134       EditMenu,
11135       TransformMenu,
11136       EnhanceMenu,
11137       EffectsMenu,
11138       FXMenu,
11139       MiscellanyMenu
11140     };
11141
11142   static const CommandType
11143     ApplyCommands[] =
11144     {
11145       NullCommand,
11146       NullCommand,
11147       NullCommand,
11148       NullCommand,
11149       NullCommand,
11150       NullCommand,
11151       NullCommand,
11152       HelpCommand,
11153       QuitCommand
11154     },
11155     FileCommands[] =
11156     {
11157       SaveCommand,
11158       PrintCommand
11159     },
11160     EditCommands[] =
11161     {
11162       UndoCommand,
11163       RedoCommand
11164     },
11165     TransformCommands[] =
11166     {
11167       FlopCommand,
11168       FlipCommand,
11169       RotateRightCommand,
11170       RotateLeftCommand
11171     },
11172     EnhanceCommands[] =
11173     {
11174       HueCommand,
11175       SaturationCommand,
11176       BrightnessCommand,
11177       GammaCommand,
11178       SpiffCommand,
11179       DullCommand,
11180       ContrastStretchCommand,
11181       SigmoidalContrastCommand,
11182       NormalizeCommand,
11183       EqualizeCommand,
11184       NegateCommand,
11185       GrayscaleCommand,
11186       MapCommand,
11187       QuantizeCommand
11188     },
11189     EffectsCommands[] =
11190     {
11191       DespeckleCommand,
11192       EmbossCommand,
11193       ReduceNoiseCommand,
11194       AddNoiseCommand,
11195       SharpenCommand,
11196       BlurCommand,
11197       EdgeDetectCommand,
11198       SpreadCommand,
11199       ShadeCommand,
11200       RaiseCommand,
11201       SegmentCommand
11202     },
11203     FXCommands[] =
11204     {
11205       SolarizeCommand,
11206       SepiaToneCommand,
11207       SwirlCommand,
11208       ImplodeCommand,
11209       VignetteCommand,
11210       WaveCommand,
11211       OilPaintCommand,
11212       CharcoalDrawCommand
11213     },
11214     MiscellanyCommands[] =
11215     {
11216       InfoCommand,
11217       ZoomCommand,
11218       ShowPreviewCommand,
11219       ShowHistogramCommand,
11220       ShowMatteCommand
11221     },
11222     ROICommands[] =
11223     {
11224       ROIHelpCommand,
11225       ROIDismissCommand
11226     };
11227
11228   static const CommandType
11229     *Commands[ApplyMenus] =
11230     {
11231       FileCommands,
11232       EditCommands,
11233       TransformCommands,
11234       EnhanceCommands,
11235       EffectsCommands,
11236       FXCommands,
11237       MiscellanyCommands
11238     };
11239
11240   char
11241     command[MaxTextExtent],
11242     text[MaxTextExtent];
11243
11244   CommandType
11245     command_type;
11246
11247   Cursor
11248     cursor;
11249
11250   Image
11251     *roi_image;
11252
11253   int
11254     entry,
11255     id,
11256     x,
11257     y;
11258
11259   MagickRealType
11260     scale_factor;
11261
11262   MagickProgressMonitor
11263     progress_monitor;
11264
11265   RectangleInfo
11266     crop_info,
11267     highlight_info,
11268     roi_info;
11269
11270   unsigned int
11271     height,
11272     width;
11273
11274   size_t
11275     state;
11276
11277   XEvent
11278     event;
11279
11280   /*
11281     Map Command widget.
11282   */
11283   (void) CloneString(&windows->command.name,"ROI");
11284   windows->command.data=0;
11285   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11286   (void) XMapRaised(display,windows->command.id);
11287   XClientMessage(display,windows->image.id,windows->im_protocols,
11288     windows->im_update_widget,CurrentTime);
11289   /*
11290     Track pointer until button 1 is pressed.
11291   */
11292   XQueryPosition(display,windows->image.id,&x,&y);
11293   (void) XSelectInput(display,windows->image.id,
11294     windows->image.attributes.event_mask | PointerMotionMask);
11295   roi_info.x=(ssize_t) windows->image.x+x;
11296   roi_info.y=(ssize_t) windows->image.y+y;
11297   roi_info.width=0;
11298   roi_info.height=0;
11299   cursor=XCreateFontCursor(display,XC_fleur);
11300   state=DefaultState;
11301   do
11302   {
11303     if (windows->info.mapped != MagickFalse)
11304       {
11305         /*
11306           Display pointer position.
11307         */
11308         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11309           (long) roi_info.x,(long) roi_info.y);
11310         XInfoWidget(display,windows,text);
11311       }
11312     /*
11313       Wait for next event.
11314     */
11315     XScreenEvent(display,windows,&event);
11316     if (event.xany.window == windows->command.id)
11317       {
11318         /*
11319           Select a command from the Command widget.
11320         */
11321         id=XCommandWidget(display,windows,ROIMenu,&event);
11322         if (id < 0)
11323           continue;
11324         switch (ROICommands[id])
11325         {
11326           case ROIHelpCommand:
11327           {
11328             XTextViewWidget(display,resource_info,windows,MagickFalse,
11329               "Help Viewer - Region of Interest",ImageROIHelp);
11330             break;
11331           }
11332           case ROIDismissCommand:
11333           {
11334             /*
11335               Prematurely exit.
11336             */
11337             state|=EscapeState;
11338             state|=ExitState;
11339             break;
11340           }
11341           default:
11342             break;
11343         }
11344         continue;
11345       }
11346     switch (event.type)
11347     {
11348       case ButtonPress:
11349       {
11350         if (event.xbutton.button != Button1)
11351           break;
11352         if (event.xbutton.window != windows->image.id)
11353           break;
11354         /*
11355           Note first corner of region of interest rectangle-- exit loop.
11356         */
11357         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11358         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11359         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11360         state|=ExitState;
11361         break;
11362       }
11363       case ButtonRelease:
11364         break;
11365       case Expose:
11366         break;
11367       case KeyPress:
11368       {
11369         KeySym
11370           key_symbol;
11371
11372         if (event.xkey.window != windows->image.id)
11373           break;
11374         /*
11375           Respond to a user key press.
11376         */
11377         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11378           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11379         switch ((int) key_symbol)
11380         {
11381           case XK_Escape:
11382           case XK_F20:
11383           {
11384             /*
11385               Prematurely exit.
11386             */
11387             state|=EscapeState;
11388             state|=ExitState;
11389             break;
11390           }
11391           case XK_F1:
11392           case XK_Help:
11393           {
11394             XTextViewWidget(display,resource_info,windows,MagickFalse,
11395               "Help Viewer - Region of Interest",ImageROIHelp);
11396             break;
11397           }
11398           default:
11399           {
11400             (void) XBell(display,0);
11401             break;
11402           }
11403         }
11404         break;
11405       }
11406       case MotionNotify:
11407       {
11408         /*
11409           Map and unmap Info widget as text cursor crosses its boundaries.
11410         */
11411         x=event.xmotion.x;
11412         y=event.xmotion.y;
11413         if (windows->info.mapped != MagickFalse)
11414           {
11415             if ((x < (int) (windows->info.x+windows->info.width)) &&
11416                 (y < (int) (windows->info.y+windows->info.height)))
11417               (void) XWithdrawWindow(display,windows->info.id,
11418                 windows->info.screen);
11419           }
11420         else
11421           if ((x > (int) (windows->info.x+windows->info.width)) ||
11422               (y > (int) (windows->info.y+windows->info.height)))
11423             (void) XMapWindow(display,windows->info.id);
11424         roi_info.x=(ssize_t) windows->image.x+x;
11425         roi_info.y=(ssize_t) windows->image.y+y;
11426         break;
11427       }
11428       default:
11429         break;
11430     }
11431   } while ((state & ExitState) == 0);
11432   (void) XSelectInput(display,windows->image.id,
11433     windows->image.attributes.event_mask);
11434   if ((state & EscapeState) != 0)
11435     {
11436       /*
11437         User want to exit without region of interest.
11438       */
11439       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11440       (void) XFreeCursor(display,cursor);
11441       return(MagickTrue);
11442     }
11443   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11444   do
11445   {
11446     /*
11447       Size rectangle as pointer moves until the mouse button is released.
11448     */
11449     x=(int) roi_info.x;
11450     y=(int) roi_info.y;
11451     roi_info.width=0;
11452     roi_info.height=0;
11453     state=DefaultState;
11454     do
11455     {
11456       highlight_info=roi_info;
11457       highlight_info.x=roi_info.x-windows->image.x;
11458       highlight_info.y=roi_info.y-windows->image.y;
11459       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11460         {
11461           /*
11462             Display info and draw region of interest rectangle.
11463           */
11464           if (windows->info.mapped == MagickFalse)
11465             (void) XMapWindow(display,windows->info.id);
11466           (void) FormatLocaleString(text,MaxTextExtent,
11467             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11468             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11469           XInfoWidget(display,windows,text);
11470           XHighlightRectangle(display,windows->image.id,
11471             windows->image.highlight_context,&highlight_info);
11472         }
11473       else
11474         if (windows->info.mapped != MagickFalse)
11475           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11476       /*
11477         Wait for next event.
11478       */
11479       XScreenEvent(display,windows,&event);
11480       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11481         XHighlightRectangle(display,windows->image.id,
11482           windows->image.highlight_context,&highlight_info);
11483       switch (event.type)
11484       {
11485         case ButtonPress:
11486         {
11487           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11488           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11489           break;
11490         }
11491         case ButtonRelease:
11492         {
11493           /*
11494             User has committed to region of interest rectangle.
11495           */
11496           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11497           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11498           XSetCursorState(display,windows,MagickFalse);
11499           state|=ExitState;
11500           if (LocaleCompare(windows->command.name,"Apply") == 0)
11501             break;
11502           (void) CloneString(&windows->command.name,"Apply");
11503           windows->command.data=ApplyMenus;
11504           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11505           break;
11506         }
11507         case Expose:
11508           break;
11509         case MotionNotify:
11510         {
11511           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11512           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11513         }
11514         default:
11515           break;
11516       }
11517       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11518           ((state & ExitState) != 0))
11519         {
11520           /*
11521             Check boundary conditions.
11522           */
11523           if (roi_info.x < 0)
11524             roi_info.x=0;
11525           else
11526             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11527               roi_info.x=(ssize_t) windows->image.ximage->width;
11528           if ((int) roi_info.x < x)
11529             roi_info.width=(unsigned int) (x-roi_info.x);
11530           else
11531             {
11532               roi_info.width=(unsigned int) (roi_info.x-x);
11533               roi_info.x=(ssize_t) x;
11534             }
11535           if (roi_info.y < 0)
11536             roi_info.y=0;
11537           else
11538             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11539               roi_info.y=(ssize_t) windows->image.ximage->height;
11540           if ((int) roi_info.y < y)
11541             roi_info.height=(unsigned int) (y-roi_info.y);
11542           else
11543             {
11544               roi_info.height=(unsigned int) (roi_info.y-y);
11545               roi_info.y=(ssize_t) y;
11546             }
11547         }
11548     } while ((state & ExitState) == 0);
11549     /*
11550       Wait for user to grab a corner of the rectangle or press return.
11551     */
11552     state=DefaultState;
11553     command_type=NullCommand;
11554     (void) XMapWindow(display,windows->info.id);
11555     do
11556     {
11557       if (windows->info.mapped != MagickFalse)
11558         {
11559           /*
11560             Display pointer position.
11561           */
11562           (void) FormatLocaleString(text,MaxTextExtent,
11563             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11564             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11565           XInfoWidget(display,windows,text);
11566         }
11567       highlight_info=roi_info;
11568       highlight_info.x=roi_info.x-windows->image.x;
11569       highlight_info.y=roi_info.y-windows->image.y;
11570       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11571         {
11572           state|=EscapeState;
11573           state|=ExitState;
11574           break;
11575         }
11576       if ((state & UpdateRegionState) != 0)
11577         {
11578           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11579           switch (command_type)
11580           {
11581             case UndoCommand:
11582             case RedoCommand:
11583             {
11584               (void) XMagickCommand(display,resource_info,windows,command_type,
11585                 image,exception);
11586               break;
11587             }
11588             default:
11589             {
11590               /*
11591                 Region of interest is relative to image configuration.
11592               */
11593               progress_monitor=SetImageProgressMonitor(*image,
11594                 (MagickProgressMonitor) NULL,(*image)->client_data);
11595               crop_info=roi_info;
11596               width=(unsigned int) (*image)->columns;
11597               height=(unsigned int) (*image)->rows;
11598               x=0;
11599               y=0;
11600               if (windows->image.crop_geometry != (char *) NULL)
11601                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11602                   &width,&height);
11603               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11604               crop_info.x+=x;
11605               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11606               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11607               scale_factor=(MagickRealType)
11608                 height/windows->image.ximage->height;
11609               crop_info.y+=y;
11610               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11611               crop_info.height=(unsigned int)
11612                 (scale_factor*crop_info.height+0.5);
11613               roi_image=CropImage(*image,&crop_info,exception);
11614               (void) SetImageProgressMonitor(*image,progress_monitor,
11615                 (*image)->client_data);
11616               if (roi_image == (Image *) NULL)
11617                 continue;
11618               /*
11619                 Apply image processing technique to the region of interest.
11620               */
11621               windows->image.orphan=MagickTrue;
11622               (void) XMagickCommand(display,resource_info,windows,command_type,
11623                 &roi_image,exception);
11624               progress_monitor=SetImageProgressMonitor(*image,
11625                 (MagickProgressMonitor) NULL,(*image)->client_data);
11626               (void) XMagickCommand(display,resource_info,windows,
11627                 SaveToUndoBufferCommand,image,exception);
11628               windows->image.orphan=MagickFalse;
11629               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11630                 crop_info.x,crop_info.y);
11631               roi_image=DestroyImage(roi_image);
11632               (void) SetImageProgressMonitor(*image,progress_monitor,
11633                 (*image)->client_data);
11634               break;
11635             }
11636           }
11637           if (command_type != InfoCommand)
11638             {
11639               XConfigureImageColormap(display,resource_info,windows,*image);
11640               (void) XConfigureImage(display,resource_info,windows,*image,exception);
11641             }
11642           XCheckRefreshWindows(display,windows);
11643           XInfoWidget(display,windows,text);
11644           (void) XSetFunction(display,windows->image.highlight_context,
11645             GXinvert);
11646           state&=(~UpdateRegionState);
11647         }
11648       XHighlightRectangle(display,windows->image.id,
11649         windows->image.highlight_context,&highlight_info);
11650       XScreenEvent(display,windows,&event);
11651       if (event.xany.window == windows->command.id)
11652         {
11653           /*
11654             Select a command from the Command widget.
11655           */
11656           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11657           command_type=NullCommand;
11658           id=XCommandWidget(display,windows,ApplyMenu,&event);
11659           if (id >= 0)
11660             {
11661               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11662               command_type=ApplyCommands[id];
11663               if (id < ApplyMenus)
11664                 {
11665                   /*
11666                     Select a command from a pop-up menu.
11667                   */
11668                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11669                     (const char **) Menus[id],command);
11670                   if (entry >= 0)
11671                     {
11672                       (void) CopyMagickString(command,Menus[id][entry],
11673                         MaxTextExtent);
11674                       command_type=Commands[id][entry];
11675                     }
11676                 }
11677             }
11678           (void) XSetFunction(display,windows->image.highlight_context,
11679             GXinvert);
11680           XHighlightRectangle(display,windows->image.id,
11681             windows->image.highlight_context,&highlight_info);
11682           if (command_type == HelpCommand)
11683             {
11684               (void) XSetFunction(display,windows->image.highlight_context,
11685                 GXcopy);
11686               XTextViewWidget(display,resource_info,windows,MagickFalse,
11687                 "Help Viewer - Region of Interest",ImageROIHelp);
11688               (void) XSetFunction(display,windows->image.highlight_context,
11689                 GXinvert);
11690               continue;
11691             }
11692           if (command_type == QuitCommand)
11693             {
11694               /*
11695                 exit.
11696               */
11697               state|=EscapeState;
11698               state|=ExitState;
11699               continue;
11700             }
11701           if (command_type != NullCommand)
11702             state|=UpdateRegionState;
11703           continue;
11704         }
11705       XHighlightRectangle(display,windows->image.id,
11706         windows->image.highlight_context,&highlight_info);
11707       switch (event.type)
11708       {
11709         case ButtonPress:
11710         {
11711           x=windows->image.x;
11712           y=windows->image.y;
11713           if (event.xbutton.button != Button1)
11714             break;
11715           if (event.xbutton.window != windows->image.id)
11716             break;
11717           x=windows->image.x+event.xbutton.x;
11718           y=windows->image.y+event.xbutton.y;
11719           if ((x < (int) (roi_info.x+RoiDelta)) &&
11720               (x > (int) (roi_info.x-RoiDelta)) &&
11721               (y < (int) (roi_info.y+RoiDelta)) &&
11722               (y > (int) (roi_info.y-RoiDelta)))
11723             {
11724               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11725               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11726               state|=UpdateConfigurationState;
11727               break;
11728             }
11729           if ((x < (int) (roi_info.x+RoiDelta)) &&
11730               (x > (int) (roi_info.x-RoiDelta)) &&
11731               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11732               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11733             {
11734               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11735               state|=UpdateConfigurationState;
11736               break;
11737             }
11738           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11739               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11740               (y < (int) (roi_info.y+RoiDelta)) &&
11741               (y > (int) (roi_info.y-RoiDelta)))
11742             {
11743               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11744               state|=UpdateConfigurationState;
11745               break;
11746             }
11747           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11748               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11749               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11750               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11751             {
11752               state|=UpdateConfigurationState;
11753               break;
11754             }
11755         }
11756         case ButtonRelease:
11757         {
11758           if (event.xbutton.window == windows->pan.id)
11759             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11760                 (highlight_info.y != crop_info.y-windows->image.y))
11761               XHighlightRectangle(display,windows->image.id,
11762                 windows->image.highlight_context,&highlight_info);
11763           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11764             event.xbutton.time);
11765           break;
11766         }
11767         case Expose:
11768         {
11769           if (event.xexpose.window == windows->image.id)
11770             if (event.xexpose.count == 0)
11771               {
11772                 event.xexpose.x=(int) highlight_info.x;
11773                 event.xexpose.y=(int) highlight_info.y;
11774                 event.xexpose.width=(int) highlight_info.width;
11775                 event.xexpose.height=(int) highlight_info.height;
11776                 XRefreshWindow(display,&windows->image,&event);
11777               }
11778           if (event.xexpose.window == windows->info.id)
11779             if (event.xexpose.count == 0)
11780               XInfoWidget(display,windows,text);
11781           break;
11782         }
11783         case KeyPress:
11784         {
11785           KeySym
11786             key_symbol;
11787
11788           if (event.xkey.window != windows->image.id)
11789             break;
11790           /*
11791             Respond to a user key press.
11792           */
11793           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11794             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11795           switch ((int) key_symbol)
11796           {
11797             case XK_Shift_L:
11798             case XK_Shift_R:
11799               break;
11800             case XK_Escape:
11801             case XK_F20:
11802               state|=EscapeState;
11803             case XK_Return:
11804             {
11805               state|=ExitState;
11806               break;
11807             }
11808             case XK_Home:
11809             case XK_KP_Home:
11810             {
11811               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11812               roi_info.y=(ssize_t) (windows->image.height/2L-
11813                 roi_info.height/2L);
11814               break;
11815             }
11816             case XK_Left:
11817             case XK_KP_Left:
11818             {
11819               roi_info.x--;
11820               break;
11821             }
11822             case XK_Up:
11823             case XK_KP_Up:
11824             case XK_Next:
11825             {
11826               roi_info.y--;
11827               break;
11828             }
11829             case XK_Right:
11830             case XK_KP_Right:
11831             {
11832               roi_info.x++;
11833               break;
11834             }
11835             case XK_Prior:
11836             case XK_Down:
11837             case XK_KP_Down:
11838             {
11839               roi_info.y++;
11840               break;
11841             }
11842             case XK_F1:
11843             case XK_Help:
11844             {
11845               (void) XSetFunction(display,windows->image.highlight_context,
11846                 GXcopy);
11847               XTextViewWidget(display,resource_info,windows,MagickFalse,
11848                 "Help Viewer - Region of Interest",ImageROIHelp);
11849               (void) XSetFunction(display,windows->image.highlight_context,
11850                 GXinvert);
11851               break;
11852             }
11853             default:
11854             {
11855               command_type=XImageWindowCommand(display,resource_info,windows,
11856                 event.xkey.state,key_symbol,image,exception);
11857               if (command_type != NullCommand)
11858                 state|=UpdateRegionState;
11859               break;
11860             }
11861           }
11862           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11863             event.xkey.time);
11864           break;
11865         }
11866         case KeyRelease:
11867           break;
11868         case MotionNotify:
11869         {
11870           if (event.xbutton.window != windows->image.id)
11871             break;
11872           /*
11873             Map and unmap Info widget as text cursor crosses its boundaries.
11874           */
11875           x=event.xmotion.x;
11876           y=event.xmotion.y;
11877           if (windows->info.mapped != MagickFalse)
11878             {
11879               if ((x < (int) (windows->info.x+windows->info.width)) &&
11880                   (y < (int) (windows->info.y+windows->info.height)))
11881                 (void) XWithdrawWindow(display,windows->info.id,
11882                   windows->info.screen);
11883             }
11884           else
11885             if ((x > (int) (windows->info.x+windows->info.width)) ||
11886                 (y > (int) (windows->info.y+windows->info.height)))
11887               (void) XMapWindow(display,windows->info.id);
11888           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11889           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11890           break;
11891         }
11892         case SelectionRequest:
11893         {
11894           XSelectionEvent
11895             notify;
11896
11897           XSelectionRequestEvent
11898             *request;
11899
11900           /*
11901             Set primary selection.
11902           */
11903           (void) FormatLocaleString(text,MaxTextExtent,
11904             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11905             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11906           request=(&(event.xselectionrequest));
11907           (void) XChangeProperty(request->display,request->requestor,
11908             request->property,request->target,8,PropModeReplace,
11909             (unsigned char *) text,(int) strlen(text));
11910           notify.type=SelectionNotify;
11911           notify.display=request->display;
11912           notify.requestor=request->requestor;
11913           notify.selection=request->selection;
11914           notify.target=request->target;
11915           notify.time=request->time;
11916           if (request->property == None)
11917             notify.property=request->target;
11918           else
11919             notify.property=request->property;
11920           (void) XSendEvent(request->display,request->requestor,False,0,
11921             (XEvent *) &notify);
11922         }
11923         default:
11924           break;
11925       }
11926       if ((state & UpdateConfigurationState) != 0)
11927         {
11928           (void) XPutBackEvent(display,&event);
11929           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11930           break;
11931         }
11932     } while ((state & ExitState) == 0);
11933   } while ((state & ExitState) == 0);
11934   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11935   XSetCursorState(display,windows,MagickFalse);
11936   if ((state & EscapeState) != 0)
11937     return(MagickTrue);
11938   return(MagickTrue);
11939 }
11940 \f
11941 /*
11942 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11943 %                                                                             %
11944 %                                                                             %
11945 %                                                                             %
11946 +   X R o t a t e I m a g e                                                   %
11947 %                                                                             %
11948 %                                                                             %
11949 %                                                                             %
11950 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11951 %
11952 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11953 %  rotation angle is computed from the slope of a line drawn by the user.
11954 %
11955 %  The format of the XRotateImage method is:
11956 %
11957 %      MagickBooleanType XRotateImage(Display *display,
11958 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11959 %        Image **image,ExceptionInfo *exception)
11960 %
11961 %  A description of each parameter follows:
11962 %
11963 %    o display: Specifies a connection to an X server; returned from
11964 %      XOpenDisplay.
11965 %
11966 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11967 %
11968 %    o windows: Specifies a pointer to a XWindows structure.
11969 %
11970 %    o degrees: Specifies the number of degrees to rotate the image.
11971 %
11972 %    o image: the image.
11973 %
11974 %    o exception: return any errors or warnings in this structure.
11975 %
11976 */
11977 static MagickBooleanType XRotateImage(Display *display,
11978   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
11979   ExceptionInfo *exception)
11980 {
11981   static const char
11982     *RotateMenu[] =
11983     {
11984       "Pixel Color",
11985       "Direction",
11986       "Help",
11987       "Dismiss",
11988       (char *) NULL
11989     };
11990
11991   static ModeType
11992     direction = HorizontalRotateCommand;
11993
11994   static const ModeType
11995     DirectionCommands[] =
11996     {
11997       HorizontalRotateCommand,
11998       VerticalRotateCommand
11999     },
12000     RotateCommands[] =
12001     {
12002       RotateColorCommand,
12003       RotateDirectionCommand,
12004       RotateHelpCommand,
12005       RotateDismissCommand
12006     };
12007
12008   static unsigned int
12009     pen_id = 0;
12010
12011   char
12012     command[MaxTextExtent],
12013     text[MaxTextExtent];
12014
12015   Image
12016     *rotate_image;
12017
12018   int
12019     id,
12020     x,
12021     y;
12022
12023   MagickRealType
12024     normalized_degrees;
12025
12026   register int
12027     i;
12028
12029   unsigned int
12030     height,
12031     rotations,
12032     width;
12033
12034   if (degrees == 0.0)
12035     {
12036       unsigned int
12037         distance;
12038
12039       size_t
12040         state;
12041
12042       XEvent
12043         event;
12044
12045       XSegment
12046         rotate_info;
12047
12048       /*
12049         Map Command widget.
12050       */
12051       (void) CloneString(&windows->command.name,"Rotate");
12052       windows->command.data=2;
12053       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12054       (void) XMapRaised(display,windows->command.id);
12055       XClientMessage(display,windows->image.id,windows->im_protocols,
12056         windows->im_update_widget,CurrentTime);
12057       /*
12058         Wait for first button press.
12059       */
12060       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12061       XQueryPosition(display,windows->image.id,&x,&y);
12062       rotate_info.x1=x;
12063       rotate_info.y1=y;
12064       rotate_info.x2=x;
12065       rotate_info.y2=y;
12066       state=DefaultState;
12067       do
12068       {
12069         XHighlightLine(display,windows->image.id,
12070           windows->image.highlight_context,&rotate_info);
12071         /*
12072           Wait for next event.
12073         */
12074         XScreenEvent(display,windows,&event);
12075         XHighlightLine(display,windows->image.id,
12076           windows->image.highlight_context,&rotate_info);
12077         if (event.xany.window == windows->command.id)
12078           {
12079             /*
12080               Select a command from the Command widget.
12081             */
12082             id=XCommandWidget(display,windows,RotateMenu,&event);
12083             if (id < 0)
12084               continue;
12085             (void) XSetFunction(display,windows->image.highlight_context,
12086               GXcopy);
12087             switch (RotateCommands[id])
12088             {
12089               case RotateColorCommand:
12090               {
12091                 const char
12092                   *ColorMenu[MaxNumberPens];
12093
12094                 int
12095                   pen_number;
12096
12097                 XColor
12098                   color;
12099
12100                 /*
12101                   Initialize menu selections.
12102                 */
12103                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12104                   ColorMenu[i]=resource_info->pen_colors[i];
12105                 ColorMenu[MaxNumberPens-2]="Browser...";
12106                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12107                 /*
12108                   Select a pen color from the pop-up menu.
12109                 */
12110                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12111                   (const char **) ColorMenu,command);
12112                 if (pen_number < 0)
12113                   break;
12114                 if (pen_number == (MaxNumberPens-2))
12115                   {
12116                     static char
12117                       color_name[MaxTextExtent] = "gray";
12118
12119                     /*
12120                       Select a pen color from a dialog.
12121                     */
12122                     resource_info->pen_colors[pen_number]=color_name;
12123                     XColorBrowserWidget(display,windows,"Select",color_name);
12124                     if (*color_name == '\0')
12125                       break;
12126                   }
12127                 /*
12128                   Set pen color.
12129                 */
12130                 (void) XParseColor(display,windows->map_info->colormap,
12131                   resource_info->pen_colors[pen_number],&color);
12132                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12133                   (unsigned int) MaxColors,&color);
12134                 windows->pixel_info->pen_colors[pen_number]=color;
12135                 pen_id=(unsigned int) pen_number;
12136                 break;
12137               }
12138               case RotateDirectionCommand:
12139               {
12140                 static const char
12141                   *Directions[] =
12142                   {
12143                     "horizontal",
12144                     "vertical",
12145                     (char *) NULL,
12146                   };
12147
12148                 /*
12149                   Select a command from the pop-up menu.
12150                 */
12151                 id=XMenuWidget(display,windows,RotateMenu[id],
12152                   Directions,command);
12153                 if (id >= 0)
12154                   direction=DirectionCommands[id];
12155                 break;
12156               }
12157               case RotateHelpCommand:
12158               {
12159                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12160                   "Help Viewer - Image Rotation",ImageRotateHelp);
12161                 break;
12162               }
12163               case RotateDismissCommand:
12164               {
12165                 /*
12166                   Prematurely exit.
12167                 */
12168                 state|=EscapeState;
12169                 state|=ExitState;
12170                 break;
12171               }
12172               default:
12173                 break;
12174             }
12175             (void) XSetFunction(display,windows->image.highlight_context,
12176               GXinvert);
12177             continue;
12178           }
12179         switch (event.type)
12180         {
12181           case ButtonPress:
12182           {
12183             if (event.xbutton.button != Button1)
12184               break;
12185             if (event.xbutton.window != windows->image.id)
12186               break;
12187             /*
12188               exit loop.
12189             */
12190             (void) XSetFunction(display,windows->image.highlight_context,
12191               GXcopy);
12192             rotate_info.x1=event.xbutton.x;
12193             rotate_info.y1=event.xbutton.y;
12194             state|=ExitState;
12195             break;
12196           }
12197           case ButtonRelease:
12198             break;
12199           case Expose:
12200             break;
12201           case KeyPress:
12202           {
12203             char
12204               command[MaxTextExtent];
12205
12206             KeySym
12207               key_symbol;
12208
12209             if (event.xkey.window != windows->image.id)
12210               break;
12211             /*
12212               Respond to a user key press.
12213             */
12214             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12215               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12216             switch ((int) key_symbol)
12217             {
12218               case XK_Escape:
12219               case XK_F20:
12220               {
12221                 /*
12222                   Prematurely exit.
12223                 */
12224                 state|=EscapeState;
12225                 state|=ExitState;
12226                 break;
12227               }
12228               case XK_F1:
12229               case XK_Help:
12230               {
12231                 (void) XSetFunction(display,windows->image.highlight_context,
12232                   GXcopy);
12233                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12234                   "Help Viewer - Image Rotation",ImageRotateHelp);
12235                 (void) XSetFunction(display,windows->image.highlight_context,
12236                   GXinvert);
12237                 break;
12238               }
12239               default:
12240               {
12241                 (void) XBell(display,0);
12242                 break;
12243               }
12244             }
12245             break;
12246           }
12247           case MotionNotify:
12248           {
12249             rotate_info.x1=event.xmotion.x;
12250             rotate_info.y1=event.xmotion.y;
12251           }
12252         }
12253         rotate_info.x2=rotate_info.x1;
12254         rotate_info.y2=rotate_info.y1;
12255         if (direction == HorizontalRotateCommand)
12256           rotate_info.x2+=32;
12257         else
12258           rotate_info.y2-=32;
12259       } while ((state & ExitState) == 0);
12260       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12261       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12262       if ((state & EscapeState) != 0)
12263         return(MagickTrue);
12264       /*
12265         Draw line as pointer moves until the mouse button is released.
12266       */
12267       distance=0;
12268       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12269       state=DefaultState;
12270       do
12271       {
12272         if (distance > 9)
12273           {
12274             /*
12275               Display info and draw rotation line.
12276             */
12277             if (windows->info.mapped == MagickFalse)
12278               (void) XMapWindow(display,windows->info.id);
12279             (void) FormatLocaleString(text,MaxTextExtent," %g",
12280               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12281             XInfoWidget(display,windows,text);
12282             XHighlightLine(display,windows->image.id,
12283               windows->image.highlight_context,&rotate_info);
12284           }
12285         else
12286           if (windows->info.mapped != MagickFalse)
12287             (void) XWithdrawWindow(display,windows->info.id,
12288               windows->info.screen);
12289         /*
12290           Wait for next event.
12291         */
12292         XScreenEvent(display,windows,&event);
12293         if (distance > 9)
12294           XHighlightLine(display,windows->image.id,
12295             windows->image.highlight_context,&rotate_info);
12296         switch (event.type)
12297         {
12298           case ButtonPress:
12299             break;
12300           case ButtonRelease:
12301           {
12302             /*
12303               User has committed to rotation line.
12304             */
12305             rotate_info.x2=event.xbutton.x;
12306             rotate_info.y2=event.xbutton.y;
12307             state|=ExitState;
12308             break;
12309           }
12310           case Expose:
12311             break;
12312           case MotionNotify:
12313           {
12314             rotate_info.x2=event.xmotion.x;
12315             rotate_info.y2=event.xmotion.y;
12316           }
12317           default:
12318             break;
12319         }
12320         /*
12321           Check boundary conditions.
12322         */
12323         if (rotate_info.x2 < 0)
12324           rotate_info.x2=0;
12325         else
12326           if (rotate_info.x2 > (int) windows->image.width)
12327             rotate_info.x2=(short) windows->image.width;
12328         if (rotate_info.y2 < 0)
12329           rotate_info.y2=0;
12330         else
12331           if (rotate_info.y2 > (int) windows->image.height)
12332             rotate_info.y2=(short) windows->image.height;
12333         /*
12334           Compute rotation angle from the slope of the line.
12335         */
12336         degrees=0.0;
12337         distance=(unsigned int)
12338           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12339           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12340         if (distance > 9)
12341           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12342             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12343       } while ((state & ExitState) == 0);
12344       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12345       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12346       if (distance <= 9)
12347         return(MagickTrue);
12348     }
12349   if (direction == VerticalRotateCommand)
12350     degrees-=90.0;
12351   if (degrees == 0.0)
12352     return(MagickTrue);
12353   /*
12354     Rotate image.
12355   */
12356   normalized_degrees=degrees;
12357   while (normalized_degrees < -45.0)
12358     normalized_degrees+=360.0;
12359   for (rotations=0; normalized_degrees > 45.0; rotations++)
12360     normalized_degrees-=90.0;
12361   if (normalized_degrees != 0.0)
12362     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12363       exception);
12364   XSetCursorState(display,windows,MagickTrue);
12365   XCheckRefreshWindows(display,windows);
12366   (*image)->background_color.red=ScaleShortToQuantum(
12367     windows->pixel_info->pen_colors[pen_id].red);
12368   (*image)->background_color.green=ScaleShortToQuantum(
12369     windows->pixel_info->pen_colors[pen_id].green);
12370   (*image)->background_color.blue=ScaleShortToQuantum(
12371     windows->pixel_info->pen_colors[pen_id].blue);
12372   rotate_image=RotateImage(*image,degrees,exception);
12373   XSetCursorState(display,windows,MagickFalse);
12374   if (rotate_image == (Image *) NULL)
12375     return(MagickFalse);
12376   *image=DestroyImage(*image);
12377   *image=rotate_image;
12378   if (windows->image.crop_geometry != (char *) NULL)
12379     {
12380       /*
12381         Rotate crop geometry.
12382       */
12383       width=(unsigned int) (*image)->columns;
12384       height=(unsigned int) (*image)->rows;
12385       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12386       switch (rotations % 4)
12387       {
12388         default:
12389         case 0:
12390           break;
12391         case 1:
12392         {
12393           /*
12394             Rotate 90 degrees.
12395           */
12396           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12397             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12398             (int) height-y,x);
12399           break;
12400         }
12401         case 2:
12402         {
12403           /*
12404             Rotate 180 degrees.
12405           */
12406           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12407             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12408           break;
12409         }
12410         case 3:
12411         {
12412           /*
12413             Rotate 270 degrees.
12414           */
12415           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12416             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12417           break;
12418         }
12419       }
12420     }
12421   if (windows->image.orphan != MagickFalse)
12422     return(MagickTrue);
12423   if (normalized_degrees != 0.0)
12424     {
12425       /*
12426         Update image colormap.
12427       */
12428       windows->image.window_changes.width=(int) (*image)->columns;
12429       windows->image.window_changes.height=(int) (*image)->rows;
12430       if (windows->image.crop_geometry != (char *) NULL)
12431         {
12432           /*
12433             Obtain dimensions of image from crop geometry.
12434           */
12435           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12436             &width,&height);
12437           windows->image.window_changes.width=(int) width;
12438           windows->image.window_changes.height=(int) height;
12439         }
12440       XConfigureImageColormap(display,resource_info,windows,*image);
12441     }
12442   else
12443     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12444       {
12445         windows->image.window_changes.width=windows->image.ximage->height;
12446         windows->image.window_changes.height=windows->image.ximage->width;
12447       }
12448   /*
12449     Update image configuration.
12450   */
12451   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12452   return(MagickTrue);
12453 }
12454 \f
12455 /*
12456 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12457 %                                                                             %
12458 %                                                                             %
12459 %                                                                             %
12460 +   X S a v e I m a g e                                                       %
12461 %                                                                             %
12462 %                                                                             %
12463 %                                                                             %
12464 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12465 %
12466 %  XSaveImage() saves an image to a file.
12467 %
12468 %  The format of the XSaveImage method is:
12469 %
12470 %      MagickBooleanType XSaveImage(Display *display,
12471 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12472 %        ExceptionInfo *exception)
12473 %
12474 %  A description of each parameter follows:
12475 %
12476 %    o display: Specifies a connection to an X server; returned from
12477 %      XOpenDisplay.
12478 %
12479 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12480 %
12481 %    o windows: Specifies a pointer to a XWindows structure.
12482 %
12483 %    o image: the image.
12484 %
12485 %    o exception: return any errors or warnings in this structure.
12486 %
12487 */
12488 static MagickBooleanType XSaveImage(Display *display,
12489   XResourceInfo *resource_info,XWindows *windows,Image *image,
12490   ExceptionInfo *exception)
12491 {
12492   char
12493     filename[MaxTextExtent],
12494     geometry[MaxTextExtent];
12495
12496   Image
12497     *save_image;
12498
12499   ImageInfo
12500     *image_info;
12501
12502   MagickStatusType
12503     status;
12504
12505   /*
12506     Request file name from user.
12507   */
12508   if (resource_info->write_filename != (char *) NULL)
12509     (void) CopyMagickString(filename,resource_info->write_filename,
12510       MaxTextExtent);
12511   else
12512     {
12513       char
12514         path[MaxTextExtent];
12515
12516       int
12517         status;
12518
12519       GetPathComponent(image->filename,HeadPath,path);
12520       GetPathComponent(image->filename,TailPath,filename);
12521       if (*path != '\0')
12522         {
12523           status=chdir(path);
12524           if (status == -1)
12525             (void) ThrowMagickException(exception,GetMagickModule(),
12526               FileOpenError,"UnableToOpenFile","%s",path);
12527         }
12528     }
12529   XFileBrowserWidget(display,windows,"Save",filename);
12530   if (*filename == '\0')
12531     return(MagickTrue);
12532   if (IsPathAccessible(filename) != MagickFalse)
12533     {
12534       int
12535         status;
12536
12537       /*
12538         File exists-- seek user's permission before overwriting.
12539       */
12540       status=XConfirmWidget(display,windows,"Overwrite",filename);
12541       if (status <= 0)
12542         return(MagickTrue);
12543     }
12544   image_info=CloneImageInfo(resource_info->image_info);
12545   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12546   (void) SetImageInfo(image_info,1,exception);
12547   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12548       (LocaleCompare(image_info->magick,"JPG") == 0))
12549     {
12550       char
12551         quality[MaxTextExtent];
12552
12553       int
12554         status;
12555
12556       /*
12557         Request JPEG quality from user.
12558       */
12559       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12560         image->quality);
12561       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12562         quality);
12563       if (*quality == '\0')
12564         return(MagickTrue);
12565       image->quality=StringToUnsignedLong(quality);
12566       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12567     }
12568   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12569       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12570       (LocaleCompare(image_info->magick,"PS") == 0) ||
12571       (LocaleCompare(image_info->magick,"PS2") == 0))
12572     {
12573       char
12574         geometry[MaxTextExtent];
12575
12576       /*
12577         Request page geometry from user.
12578       */
12579       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12580       if (LocaleCompare(image_info->magick,"PDF") == 0)
12581         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12582       if (image_info->page != (char *) NULL)
12583         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12584       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12585         "Select page geometry:",geometry);
12586       if (*geometry != '\0')
12587         image_info->page=GetPageGeometry(geometry);
12588     }
12589   /*
12590     Apply image transforms.
12591   */
12592   XSetCursorState(display,windows,MagickTrue);
12593   XCheckRefreshWindows(display,windows);
12594   save_image=CloneImage(image,0,0,MagickTrue,exception);
12595   if (save_image == (Image *) NULL)
12596     return(MagickFalse);
12597   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12598     windows->image.ximage->width,windows->image.ximage->height);
12599   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12600   /*
12601     Write image.
12602   */
12603   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12604   status=WriteImage(image_info,save_image,exception);
12605   if (status != MagickFalse)
12606     image->taint=MagickFalse;
12607   save_image=DestroyImage(save_image);
12608   image_info=DestroyImageInfo(image_info);
12609   XSetCursorState(display,windows,MagickFalse);
12610   return(status != 0 ? MagickTrue : MagickFalse);
12611 }
12612 \f
12613 /*
12614 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12615 %                                                                             %
12616 %                                                                             %
12617 %                                                                             %
12618 +   X S c r e e n E v e n t                                                   %
12619 %                                                                             %
12620 %                                                                             %
12621 %                                                                             %
12622 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12623 %
12624 %  XScreenEvent() handles global events associated with the Pan and Magnify
12625 %  windows.
12626 %
12627 %  The format of the XScreenEvent function is:
12628 %
12629 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12630 %
12631 %  A description of each parameter follows:
12632 %
12633 %    o display: Specifies a pointer to the Display structure;  returned from
12634 %      XOpenDisplay.
12635 %
12636 %    o windows: Specifies a pointer to a XWindows structure.
12637 %
12638 %    o event: Specifies a pointer to a X11 XEvent structure.
12639 %
12640 %
12641 */
12642
12643 #if defined(__cplusplus) || defined(c_plusplus)
12644 extern "C" {
12645 #endif
12646
12647 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12648 {
12649   register XWindows
12650     *windows;
12651
12652   windows=(XWindows *) data;
12653   if ((event->type == ClientMessage) &&
12654       (event->xclient.window == windows->image.id))
12655     return(MagickFalse);
12656   return(MagickTrue);
12657 }
12658
12659 #if defined(__cplusplus) || defined(c_plusplus)
12660 }
12661 #endif
12662
12663 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12664 {
12665   register int
12666     x,
12667     y;
12668
12669   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12670   if (event->xany.window == windows->command.id)
12671     return;
12672   switch (event->type)
12673   {
12674     case ButtonPress:
12675     case ButtonRelease:
12676     {
12677       if ((event->xbutton.button == Button3) &&
12678           (event->xbutton.state & Mod1Mask))
12679         {
12680           /*
12681             Convert Alt-Button3 to Button2.
12682           */
12683           event->xbutton.button=Button2;
12684           event->xbutton.state&=(~Mod1Mask);
12685         }
12686       if (event->xbutton.window == windows->backdrop.id)
12687         {
12688           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12689             event->xbutton.time);
12690           break;
12691         }
12692       if (event->xbutton.window == windows->pan.id)
12693         {
12694           XPanImage(display,windows,event);
12695           break;
12696         }
12697       if (event->xbutton.window == windows->image.id)
12698         if (event->xbutton.button == Button2)
12699           {
12700             /*
12701               Update magnified image.
12702             */
12703             x=event->xbutton.x;
12704             y=event->xbutton.y;
12705             if (x < 0)
12706               x=0;
12707             else
12708               if (x >= (int) windows->image.width)
12709                 x=(int) (windows->image.width-1);
12710             windows->magnify.x=(int) windows->image.x+x;
12711             if (y < 0)
12712               y=0;
12713             else
12714              if (y >= (int) windows->image.height)
12715                y=(int) (windows->image.height-1);
12716             windows->magnify.y=windows->image.y+y;
12717             if (windows->magnify.mapped == MagickFalse)
12718               (void) XMapRaised(display,windows->magnify.id);
12719             XMakeMagnifyImage(display,windows);
12720             if (event->type == ButtonRelease)
12721               (void) XWithdrawWindow(display,windows->info.id,
12722                 windows->info.screen);
12723             break;
12724           }
12725       break;
12726     }
12727     case ClientMessage:
12728     {
12729       /*
12730         If client window delete message, exit.
12731       */
12732       if (event->xclient.message_type != windows->wm_protocols)
12733         break;
12734       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12735         break;
12736       if (event->xclient.window == windows->magnify.id)
12737         {
12738           (void) XWithdrawWindow(display,windows->magnify.id,
12739             windows->magnify.screen);
12740           break;
12741         }
12742       break;
12743     }
12744     case ConfigureNotify:
12745     {
12746       if (event->xconfigure.window == windows->magnify.id)
12747         {
12748           unsigned int
12749             magnify;
12750
12751           /*
12752             Magnify window has a new configuration.
12753           */
12754           windows->magnify.width=(unsigned int) event->xconfigure.width;
12755           windows->magnify.height=(unsigned int) event->xconfigure.height;
12756           if (windows->magnify.mapped == MagickFalse)
12757             break;
12758           magnify=1;
12759           while ((int) magnify <= event->xconfigure.width)
12760             magnify<<=1;
12761           while ((int) magnify <= event->xconfigure.height)
12762             magnify<<=1;
12763           magnify>>=1;
12764           if (((int) magnify != event->xconfigure.width) ||
12765               ((int) magnify != event->xconfigure.height))
12766             {
12767               XWindowChanges
12768                 window_changes;
12769
12770               window_changes.width=(int) magnify;
12771               window_changes.height=(int) magnify;
12772               (void) XReconfigureWMWindow(display,windows->magnify.id,
12773                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12774                 &window_changes);
12775               break;
12776             }
12777           XMakeMagnifyImage(display,windows);
12778           break;
12779         }
12780       break;
12781     }
12782     case Expose:
12783     {
12784       if (event->xexpose.window == windows->image.id)
12785         {
12786           XRefreshWindow(display,&windows->image,event);
12787           break;
12788         }
12789       if (event->xexpose.window == windows->pan.id)
12790         if (event->xexpose.count == 0)
12791           {
12792             XDrawPanRectangle(display,windows);
12793             break;
12794           }
12795       if (event->xexpose.window == windows->magnify.id)
12796         if (event->xexpose.count == 0)
12797           {
12798             XMakeMagnifyImage(display,windows);
12799             break;
12800           }
12801       break;
12802     }
12803     case KeyPress:
12804     {
12805       char
12806         command[MaxTextExtent];
12807
12808       KeySym
12809         key_symbol;
12810
12811       if (event->xkey.window != windows->magnify.id)
12812         break;
12813       /*
12814         Respond to a user key press.
12815       */
12816       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12817         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12818       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12819       break;
12820     }
12821     case MapNotify:
12822     {
12823       if (event->xmap.window == windows->magnify.id)
12824         {
12825           windows->magnify.mapped=MagickTrue;
12826           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12827           break;
12828         }
12829       if (event->xmap.window == windows->info.id)
12830         {
12831           windows->info.mapped=MagickTrue;
12832           break;
12833         }
12834       break;
12835     }
12836     case MotionNotify:
12837     {
12838       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12839       if (event->xmotion.window == windows->image.id)
12840         if (windows->magnify.mapped != MagickFalse)
12841           {
12842             /*
12843               Update magnified image.
12844             */
12845             x=event->xmotion.x;
12846             y=event->xmotion.y;
12847             if (x < 0)
12848               x=0;
12849             else
12850               if (x >= (int) windows->image.width)
12851                 x=(int) (windows->image.width-1);
12852             windows->magnify.x=(int) windows->image.x+x;
12853             if (y < 0)
12854               y=0;
12855             else
12856              if (y >= (int) windows->image.height)
12857                y=(int) (windows->image.height-1);
12858             windows->magnify.y=windows->image.y+y;
12859             XMakeMagnifyImage(display,windows);
12860           }
12861       break;
12862     }
12863     case UnmapNotify:
12864     {
12865       if (event->xunmap.window == windows->magnify.id)
12866         {
12867           windows->magnify.mapped=MagickFalse;
12868           break;
12869         }
12870       if (event->xunmap.window == windows->info.id)
12871         {
12872           windows->info.mapped=MagickFalse;
12873           break;
12874         }
12875       break;
12876     }
12877     default:
12878       break;
12879   }
12880 }
12881 \f
12882 /*
12883 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12884 %                                                                             %
12885 %                                                                             %
12886 %                                                                             %
12887 +   X S e t C r o p G e o m e t r y                                           %
12888 %                                                                             %
12889 %                                                                             %
12890 %                                                                             %
12891 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12892 %
12893 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12894 %  and translates it to a cropping geometry relative to the image.
12895 %
12896 %  The format of the XSetCropGeometry method is:
12897 %
12898 %      void XSetCropGeometry(Display *display,XWindows *windows,
12899 %        RectangleInfo *crop_info,Image *image)
12900 %
12901 %  A description of each parameter follows:
12902 %
12903 %    o display: Specifies a connection to an X server; returned from
12904 %      XOpenDisplay.
12905 %
12906 %    o windows: Specifies a pointer to a XWindows structure.
12907 %
12908 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12909 %      Image window to crop.
12910 %
12911 %    o image: the image.
12912 %
12913 */
12914 static void XSetCropGeometry(Display *display,XWindows *windows,
12915   RectangleInfo *crop_info,Image *image)
12916 {
12917   char
12918     text[MaxTextExtent];
12919
12920   int
12921     x,
12922     y;
12923
12924   MagickRealType
12925     scale_factor;
12926
12927   unsigned int
12928     height,
12929     width;
12930
12931   if (windows->info.mapped != MagickFalse)
12932     {
12933       /*
12934         Display info on cropping rectangle.
12935       */
12936       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12937         (double) crop_info->width,(double) crop_info->height,(double)
12938         crop_info->x,(double) crop_info->y);
12939       XInfoWidget(display,windows,text);
12940     }
12941   /*
12942     Cropping geometry is relative to any previous crop geometry.
12943   */
12944   x=0;
12945   y=0;
12946   width=(unsigned int) image->columns;
12947   height=(unsigned int) image->rows;
12948   if (windows->image.crop_geometry != (char *) NULL)
12949     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12950   else
12951     windows->image.crop_geometry=AcquireString((char *) NULL);
12952   /*
12953     Define the crop geometry string from the cropping rectangle.
12954   */
12955   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12956   if (crop_info->x > 0)
12957     x+=(int) (scale_factor*crop_info->x+0.5);
12958   width=(unsigned int) (scale_factor*crop_info->width+0.5);
12959   if (width == 0)
12960     width=1;
12961   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12962   if (crop_info->y > 0)
12963     y+=(int) (scale_factor*crop_info->y+0.5);
12964   height=(unsigned int) (scale_factor*crop_info->height+0.5);
12965   if (height == 0)
12966     height=1;
12967   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12968     "%ux%u%+d%+d",width,height,x,y);
12969 }
12970 \f
12971 /*
12972 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12973 %                                                                             %
12974 %                                                                             %
12975 %                                                                             %
12976 +   X T i l e I m a g e                                                       %
12977 %                                                                             %
12978 %                                                                             %
12979 %                                                                             %
12980 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12981 %
12982 %  XTileImage() loads or deletes a selected tile from a visual image directory.
12983 %  The load or delete command is chosen from a menu.
12984 %
12985 %  The format of the XTileImage method is:
12986 %
12987 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
12988 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
12989 %
12990 %  A description of each parameter follows:
12991 %
12992 %    o tile_image:  XTileImage reads or deletes the tile image
12993 %      and returns it.  A null image is returned if an error occurs.
12994 %
12995 %    o display: Specifies a connection to an X server;  returned from
12996 %      XOpenDisplay.
12997 %
12998 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12999 %
13000 %    o windows: Specifies a pointer to a XWindows structure.
13001 %
13002 %    o image: the image; returned from ReadImage.
13003 %
13004 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13005 %      the entire image is refreshed.
13006 %
13007 %    o exception: return any errors or warnings in this structure.
13008 %
13009 */
13010 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13011   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13012 {
13013   static const char
13014     *VerbMenu[] =
13015     {
13016       "Load",
13017       "Next",
13018       "Former",
13019       "Delete",
13020       "Update",
13021       (char *) NULL,
13022     };
13023
13024   static const ModeType
13025     TileCommands[] =
13026     {
13027       TileLoadCommand,
13028       TileNextCommand,
13029       TileFormerCommand,
13030       TileDeleteCommand,
13031       TileUpdateCommand
13032     };
13033
13034   char
13035     command[MaxTextExtent],
13036     filename[MaxTextExtent];
13037
13038   Image
13039     *tile_image;
13040
13041   int
13042     id,
13043     status,
13044     tile,
13045     x,
13046     y;
13047
13048   MagickRealType
13049     scale_factor;
13050
13051   register char
13052     *p,
13053     *q;
13054
13055   register int
13056     i;
13057
13058   unsigned int
13059     height,
13060     width;
13061
13062   /*
13063     Tile image is relative to montage image configuration.
13064   */
13065   x=0;
13066   y=0;
13067   width=(unsigned int) image->columns;
13068   height=(unsigned int) image->rows;
13069   if (windows->image.crop_geometry != (char *) NULL)
13070     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13071   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13072   event->xbutton.x+=windows->image.x;
13073   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13074   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13075   event->xbutton.y+=windows->image.y;
13076   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13077   /*
13078     Determine size and location of each tile in the visual image directory.
13079   */
13080   width=(unsigned int) image->columns;
13081   height=(unsigned int) image->rows;
13082   x=0;
13083   y=0;
13084   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13085   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13086     (event->xbutton.x-x)/width;
13087   if (tile < 0)
13088     {
13089       /*
13090         Button press is outside any tile.
13091       */
13092       (void) XBell(display,0);
13093       return((Image *) NULL);
13094     }
13095   /*
13096     Determine file name from the tile directory.
13097   */
13098   p=image->directory;
13099   for (i=tile; (i != 0) && (*p != '\0'); )
13100   {
13101     if (*p == '\n')
13102       i--;
13103     p++;
13104   }
13105   if (*p == '\0')
13106     {
13107       /*
13108         Button press is outside any tile.
13109       */
13110       (void) XBell(display,0);
13111       return((Image *) NULL);
13112     }
13113   /*
13114     Select a command from the pop-up menu.
13115   */
13116   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13117   if (id < 0)
13118     return((Image *) NULL);
13119   q=p;
13120   while ((*q != '\n') && (*q != '\0'))
13121     q++;
13122   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13123   /*
13124     Perform command for the selected tile.
13125   */
13126   XSetCursorState(display,windows,MagickTrue);
13127   XCheckRefreshWindows(display,windows);
13128   tile_image=NewImageList();
13129   switch (TileCommands[id])
13130   {
13131     case TileLoadCommand:
13132     {
13133       /*
13134         Load tile image.
13135       */
13136       XCheckRefreshWindows(display,windows);
13137       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13138         MaxTextExtent);
13139       (void) CopyMagickString(resource_info->image_info->filename,filename,
13140         MaxTextExtent);
13141       tile_image=ReadImage(resource_info->image_info,exception);
13142       CatchException(exception);
13143       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13144       break;
13145     }
13146     case TileNextCommand:
13147     {
13148       /*
13149         Display next image.
13150       */
13151       XClientMessage(display,windows->image.id,windows->im_protocols,
13152         windows->im_next_image,CurrentTime);
13153       break;
13154     }
13155     case TileFormerCommand:
13156     {
13157       /*
13158         Display former image.
13159       */
13160       XClientMessage(display,windows->image.id,windows->im_protocols,
13161         windows->im_former_image,CurrentTime);
13162       break;
13163     }
13164     case TileDeleteCommand:
13165     {
13166       /*
13167         Delete tile image.
13168       */
13169       if (IsPathAccessible(filename) == MagickFalse)
13170         {
13171           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13172           break;
13173         }
13174       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13175       if (status <= 0)
13176         break;
13177       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13178       if (status != MagickFalse)
13179         {
13180           XNoticeWidget(display,windows,"Unable to delete image file:",
13181             filename);
13182           break;
13183         }
13184     }
13185     case TileUpdateCommand:
13186     {
13187       int
13188         x_offset,
13189         y_offset;
13190
13191       PixelPacket
13192         pixel;
13193
13194       register int
13195         j;
13196
13197       register Quantum
13198         *s;
13199
13200       /*
13201         Ensure all the images exist.
13202       */
13203       tile=0;
13204       for (p=image->directory; *p != '\0'; p++)
13205       {
13206         CacheView
13207           *image_view;
13208
13209         q=p;
13210         while ((*q != '\n') && (*q != '\0'))
13211           q++;
13212         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13213         p=q;
13214         if (IsPathAccessible(filename) != MagickFalse)
13215           {
13216             tile++;
13217             continue;
13218           }
13219         /*
13220           Overwrite tile with background color.
13221         */
13222         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13223         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13224         image_view=AcquireCacheView(image);
13225         (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13226         for (i=0; i < (int) height; i++)
13227         {
13228           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13229             y_offset+i,width,1,exception);
13230           if (s == (Quantum *) NULL)
13231             break;
13232           for (j=0; j < (int) width; j++)
13233           {
13234             SetPixelPacket(image,&pixel,s);
13235             s+=GetPixelChannels(image);
13236           }
13237           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13238             break;
13239         }
13240         image_view=DestroyCacheView(image_view);
13241         tile++;
13242       }
13243       windows->image.window_changes.width=(int) image->columns;
13244       windows->image.window_changes.height=(int) image->rows;
13245       XConfigureImageColormap(display,resource_info,windows,image);
13246       (void) XConfigureImage(display,resource_info,windows,image,exception);
13247       break;
13248     }
13249     default:
13250       break;
13251   }
13252   XSetCursorState(display,windows,MagickFalse);
13253   return(tile_image);
13254 }
13255 \f
13256 /*
13257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13258 %                                                                             %
13259 %                                                                             %
13260 %                                                                             %
13261 +   X T r a n s l a t e I m a g e                                             %
13262 %                                                                             %
13263 %                                                                             %
13264 %                                                                             %
13265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13266 %
13267 %  XTranslateImage() translates the image within an Image window by one pixel
13268 %  as specified by the key symbol.  If the image has a `montage string the
13269 %  translation is respect to the width and height contained within the string.
13270 %
13271 %  The format of the XTranslateImage method is:
13272 %
13273 %      void XTranslateImage(Display *display,XWindows *windows,
13274 %        Image *image,const KeySym key_symbol)
13275 %
13276 %  A description of each parameter follows:
13277 %
13278 %    o display: Specifies a connection to an X server; returned from
13279 %      XOpenDisplay.
13280 %
13281 %    o windows: Specifies a pointer to a XWindows structure.
13282 %
13283 %    o image: the image.
13284 %
13285 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13286 %      to trim.
13287 %
13288 */
13289 static void XTranslateImage(Display *display,XWindows *windows,
13290   Image *image,const KeySym key_symbol)
13291 {
13292   char
13293     text[MaxTextExtent];
13294
13295   int
13296     x,
13297     y;
13298
13299   unsigned int
13300     x_offset,
13301     y_offset;
13302
13303   /*
13304     User specified a pan position offset.
13305   */
13306   x_offset=windows->image.width;
13307   y_offset=windows->image.height;
13308   if (image->montage != (char *) NULL)
13309     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13310   switch ((int) key_symbol)
13311   {
13312     case XK_Home:
13313     case XK_KP_Home:
13314     {
13315       windows->image.x=(int) windows->image.width/2;
13316       windows->image.y=(int) windows->image.height/2;
13317       break;
13318     }
13319     case XK_Left:
13320     case XK_KP_Left:
13321     {
13322       windows->image.x-=x_offset;
13323       break;
13324     }
13325     case XK_Next:
13326     case XK_Up:
13327     case XK_KP_Up:
13328     {
13329       windows->image.y-=y_offset;
13330       break;
13331     }
13332     case XK_Right:
13333     case XK_KP_Right:
13334     {
13335       windows->image.x+=x_offset;
13336       break;
13337     }
13338     case XK_Prior:
13339     case XK_Down:
13340     case XK_KP_Down:
13341     {
13342       windows->image.y+=y_offset;
13343       break;
13344     }
13345     default:
13346       return;
13347   }
13348   /*
13349     Check boundary conditions.
13350   */
13351   if (windows->image.x < 0)
13352     windows->image.x=0;
13353   else
13354     if ((int) (windows->image.x+windows->image.width) >
13355         windows->image.ximage->width)
13356       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13357   if (windows->image.y < 0)
13358     windows->image.y=0;
13359   else
13360     if ((int) (windows->image.y+windows->image.height) >
13361         windows->image.ximage->height)
13362       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13363   /*
13364     Refresh Image window.
13365   */
13366   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13367     windows->image.width,windows->image.height,windows->image.x,
13368     windows->image.y);
13369   XInfoWidget(display,windows,text);
13370   XCheckRefreshWindows(display,windows);
13371   XDrawPanRectangle(display,windows);
13372   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13373   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13374 }
13375 \f
13376 /*
13377 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13378 %                                                                             %
13379 %                                                                             %
13380 %                                                                             %
13381 +   X T r i m I m a g e                                                       %
13382 %                                                                             %
13383 %                                                                             %
13384 %                                                                             %
13385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13386 %
13387 %  XTrimImage() trims the edges from the Image window.
13388 %
13389 %  The format of the XTrimImage method is:
13390 %
13391 %      MagickBooleanType XTrimImage(Display *display,
13392 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13393 %        ExceptionInfo *exception)
13394 %
13395 %  A description of each parameter follows:
13396 %
13397 %    o display: Specifies a connection to an X server; returned from
13398 %      XOpenDisplay.
13399 %
13400 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13401 %
13402 %    o windows: Specifies a pointer to a XWindows structure.
13403 %
13404 %    o image: the image.
13405 %
13406 %    o exception: return any errors or warnings in this structure.
13407 %
13408 */
13409 static MagickBooleanType XTrimImage(Display *display,
13410   XResourceInfo *resource_info,XWindows *windows,Image *image,
13411   ExceptionInfo *exception)
13412 {
13413   RectangleInfo
13414     trim_info;
13415
13416   register int
13417     x,
13418     y;
13419
13420   size_t
13421     background,
13422     pixel;
13423
13424   /*
13425     Trim edges from image.
13426   */
13427   XSetCursorState(display,windows,MagickTrue);
13428   XCheckRefreshWindows(display,windows);
13429   /*
13430     Crop the left edge.
13431   */
13432   background=XGetPixel(windows->image.ximage,0,0);
13433   trim_info.width=(size_t) windows->image.ximage->width;
13434   for (x=0; x < windows->image.ximage->width; x++)
13435   {
13436     for (y=0; y < windows->image.ximage->height; y++)
13437     {
13438       pixel=XGetPixel(windows->image.ximage,x,y);
13439       if (pixel != background)
13440         break;
13441     }
13442     if (y < windows->image.ximage->height)
13443       break;
13444   }
13445   trim_info.x=(ssize_t) x;
13446   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13447     {
13448       XSetCursorState(display,windows,MagickFalse);
13449       return(MagickFalse);
13450     }
13451   /*
13452     Crop the right edge.
13453   */
13454   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13455   for (x=windows->image.ximage->width-1; x != 0; x--)
13456   {
13457     for (y=0; y < windows->image.ximage->height; y++)
13458     {
13459       pixel=XGetPixel(windows->image.ximage,x,y);
13460       if (pixel != background)
13461         break;
13462     }
13463     if (y < windows->image.ximage->height)
13464       break;
13465   }
13466   trim_info.width=(size_t) (x-trim_info.x+1);
13467   /*
13468     Crop the top edge.
13469   */
13470   background=XGetPixel(windows->image.ximage,0,0);
13471   trim_info.height=(size_t) windows->image.ximage->height;
13472   for (y=0; y < windows->image.ximage->height; y++)
13473   {
13474     for (x=0; x < windows->image.ximage->width; x++)
13475     {
13476       pixel=XGetPixel(windows->image.ximage,x,y);
13477       if (pixel != background)
13478         break;
13479     }
13480     if (x < windows->image.ximage->width)
13481       break;
13482   }
13483   trim_info.y=(ssize_t) y;
13484   /*
13485     Crop the bottom edge.
13486   */
13487   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13488   for (y=windows->image.ximage->height-1; y != 0; y--)
13489   {
13490     for (x=0; x < windows->image.ximage->width; x++)
13491     {
13492       pixel=XGetPixel(windows->image.ximage,x,y);
13493       if (pixel != background)
13494         break;
13495     }
13496     if (x < windows->image.ximage->width)
13497       break;
13498   }
13499   trim_info.height=(size_t) y-trim_info.y+1;
13500   if (((unsigned int) trim_info.width != windows->image.width) ||
13501       ((unsigned int) trim_info.height != windows->image.height))
13502     {
13503       /*
13504         Reconfigure Image window as defined by the trimming rectangle.
13505       */
13506       XSetCropGeometry(display,windows,&trim_info,image);
13507       windows->image.window_changes.width=(int) trim_info.width;
13508       windows->image.window_changes.height=(int) trim_info.height;
13509       (void) XConfigureImage(display,resource_info,windows,image,exception);
13510     }
13511   XSetCursorState(display,windows,MagickFalse);
13512   return(MagickTrue);
13513 }
13514 \f
13515 /*
13516 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13517 %                                                                             %
13518 %                                                                             %
13519 %                                                                             %
13520 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13521 %                                                                             %
13522 %                                                                             %
13523 %                                                                             %
13524 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13525 %
13526 %  XVisualDirectoryImage() creates a Visual Image Directory.
13527 %
13528 %  The format of the XVisualDirectoryImage method is:
13529 %
13530 %      Image *XVisualDirectoryImage(Display *display,
13531 %        XResourceInfo *resource_info,XWindows *windows,
13532 %        ExceptionInfo *exception)
13533 %
13534 %  A description of each parameter follows:
13535 %
13536 %    o display: Specifies a connection to an X server; returned from
13537 %      XOpenDisplay.
13538 %
13539 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13540 %
13541 %    o windows: Specifies a pointer to a XWindows structure.
13542 %
13543 %    o exception: return any errors or warnings in this structure.
13544 %
13545 */
13546 static Image *XVisualDirectoryImage(Display *display,
13547   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13548 {
13549 #define TileImageTag  "Scale/Image"
13550 #define XClientName  "montage"
13551
13552   char
13553     **filelist;
13554
13555   Image
13556     *images,
13557     *montage_image,
13558     *next_image,
13559     *thumbnail_image;
13560
13561   ImageInfo
13562     *read_info;
13563
13564   int
13565     number_files;
13566
13567   MagickBooleanType
13568     backdrop;
13569
13570   MagickStatusType
13571     status;
13572
13573   MontageInfo
13574     *montage_info;
13575
13576   RectangleInfo
13577     geometry;
13578
13579   register int
13580     i;
13581
13582   static char
13583     filename[MaxTextExtent] = "\0",
13584     filenames[MaxTextExtent] = "*";
13585
13586   XResourceInfo
13587     background_resources;
13588
13589   /*
13590     Request file name from user.
13591   */
13592   XFileBrowserWidget(display,windows,"Directory",filenames);
13593   if (*filenames == '\0')
13594     return((Image *) NULL);
13595   /*
13596     Expand the filenames.
13597   */
13598   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13599   if (filelist == (char **) NULL)
13600     {
13601       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13602         filenames);
13603       return((Image *) NULL);
13604     }
13605   number_files=1;
13606   filelist[0]=filenames;
13607   status=ExpandFilenames(&number_files,&filelist);
13608   if ((status == MagickFalse) || (number_files == 0))
13609     {
13610       if (number_files == 0)
13611         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13612       else
13613         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13614           filenames);
13615       return((Image *) NULL);
13616     }
13617   /*
13618     Set image background resources.
13619   */
13620   background_resources=(*resource_info);
13621   background_resources.window_id=AcquireString("");
13622   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13623     "0x%lx",windows->image.id);
13624   background_resources.backdrop=MagickTrue;
13625   /*
13626     Read each image and convert them to a tile.
13627   */
13628   backdrop=(windows->visual_info->klass == TrueColor) ||
13629     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13630   read_info=CloneImageInfo(resource_info->image_info);
13631   (void) SetImageOption(read_info,"jpeg:size","120x120");
13632   (void) CloneString(&read_info->size,DefaultTileGeometry);
13633   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13634     (void *) NULL);
13635   images=NewImageList();
13636   XSetCursorState(display,windows,MagickTrue);
13637   XCheckRefreshWindows(display,windows);
13638   for (i=0; i < (int) number_files; i++)
13639   {
13640     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13641     filelist[i]=DestroyString(filelist[i]);
13642     *read_info->magick='\0';
13643     next_image=ReadImage(read_info,exception);
13644     CatchException(exception);
13645     if (next_image != (Image *) NULL)
13646       {
13647         (void) DeleteImageProperty(next_image,"label");
13648         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13649           read_info,next_image,DefaultTileLabel,exception));
13650         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13651           exception);
13652         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13653           geometry.height,exception);
13654         if (thumbnail_image != (Image *) NULL)
13655           {
13656             next_image=DestroyImage(next_image);
13657             next_image=thumbnail_image;
13658           }
13659         if (backdrop)
13660           {
13661             (void) XDisplayBackgroundImage(display,&background_resources,
13662               next_image,exception);
13663             XSetCursorState(display,windows,MagickTrue);
13664           }
13665         AppendImageToList(&images,next_image);
13666         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13667           {
13668             MagickBooleanType
13669               proceed;
13670
13671             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13672               (MagickSizeType) number_files);
13673             if (proceed == MagickFalse)
13674               break;
13675           }
13676       }
13677   }
13678   filelist=(char **) RelinquishMagickMemory(filelist);
13679   if (images == (Image *) NULL)
13680     {
13681       read_info=DestroyImageInfo(read_info);
13682       XSetCursorState(display,windows,MagickFalse);
13683       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13684       return((Image *) NULL);
13685     }
13686   /*
13687     Create the Visual Image Directory.
13688   */
13689   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13690   montage_info->pointsize=10;
13691   if (resource_info->font != (char *) NULL)
13692     (void) CloneString(&montage_info->font,resource_info->font);
13693   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13694   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13695     images),exception);
13696   images=DestroyImageList(images);
13697   montage_info=DestroyMontageInfo(montage_info);
13698   read_info=DestroyImageInfo(read_info);
13699   XSetCursorState(display,windows,MagickFalse);
13700   if (montage_image == (Image *) NULL)
13701     return(montage_image);
13702   XClientMessage(display,windows->image.id,windows->im_protocols,
13703     windows->im_next_image,CurrentTime);
13704   return(montage_image);
13705 }
13706 \f
13707 /*
13708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13709 %                                                                             %
13710 %                                                                             %
13711 %                                                                             %
13712 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13713 %                                                                             %
13714 %                                                                             %
13715 %                                                                             %
13716 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13717 %
13718 %  XDisplayBackgroundImage() displays an image in the background of a window.
13719 %
13720 %  The format of the XDisplayBackgroundImage method is:
13721 %
13722 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13723 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13724 %
13725 %  A description of each parameter follows:
13726 %
13727 %    o display: Specifies a connection to an X server;  returned from
13728 %      XOpenDisplay.
13729 %
13730 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13731 %
13732 %    o image: the image.
13733 %
13734 %    o exception: return any errors or warnings in this structure.
13735 %
13736 */
13737 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13738   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13739 {
13740   char
13741     geometry[MaxTextExtent],
13742     visual_type[MaxTextExtent];
13743
13744   int
13745     height,
13746     status,
13747     width;
13748
13749   RectangleInfo
13750     geometry_info;
13751
13752   static XPixelInfo
13753     pixel;
13754
13755   static XStandardColormap
13756     *map_info;
13757
13758   static XVisualInfo
13759     *visual_info = (XVisualInfo *) NULL;
13760
13761   static XWindowInfo
13762     window_info;
13763
13764   size_t
13765     delay;
13766
13767   Window
13768     root_window;
13769
13770   XGCValues
13771     context_values;
13772
13773   XResourceInfo
13774     resources;
13775
13776   XWindowAttributes
13777     window_attributes;
13778
13779   /*
13780     Determine target window.
13781   */
13782   assert(image != (Image *) NULL);
13783   assert(image->signature == MagickSignature);
13784   if (image->debug != MagickFalse)
13785     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13786   resources=(*resource_info);
13787   window_info.id=(Window) NULL;
13788   root_window=XRootWindow(display,XDefaultScreen(display));
13789   if (LocaleCompare(resources.window_id,"root") == 0)
13790     window_info.id=root_window;
13791   else
13792     {
13793       if (isdigit((unsigned char) *resources.window_id) != 0)
13794         window_info.id=XWindowByID(display,root_window,
13795           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13796       if (window_info.id == (Window) NULL)
13797         window_info.id=XWindowByName(display,root_window,resources.window_id);
13798     }
13799   if (window_info.id == (Window) NULL)
13800     {
13801       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13802         resources.window_id);
13803       return(MagickFalse);
13804     }
13805   /*
13806     Determine window visual id.
13807   */
13808   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13809   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13810   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13811   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13812   if (status != 0)
13813     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13814       XVisualIDFromVisual(window_attributes.visual));
13815   if (visual_info == (XVisualInfo *) NULL)
13816     {
13817       /*
13818         Allocate standard colormap.
13819       */
13820       map_info=XAllocStandardColormap();
13821       if (map_info == (XStandardColormap *) NULL)
13822         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13823           image->filename);
13824       map_info->colormap=(Colormap) NULL;
13825       pixel.pixels=(unsigned long *) NULL;
13826       /*
13827         Initialize visual info.
13828       */
13829       resources.map_type=(char *) NULL;
13830       resources.visual_type=visual_type;
13831       visual_info=XBestVisualInfo(display,map_info,&resources);
13832       if (visual_info == (XVisualInfo *) NULL)
13833         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13834           resources.visual_type);
13835       /*
13836         Initialize window info.
13837       */
13838       window_info.ximage=(XImage *) NULL;
13839       window_info.matte_image=(XImage *) NULL;
13840       window_info.pixmap=(Pixmap) NULL;
13841       window_info.matte_pixmap=(Pixmap) NULL;
13842     }
13843   /*
13844     Free previous root colors.
13845   */
13846   if (window_info.id == root_window)
13847     (void) XDestroyWindowColors(display,root_window);
13848   /*
13849     Initialize Standard Colormap.
13850   */
13851   resources.colormap=SharedColormap;
13852   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13853   /*
13854     Graphic context superclass.
13855   */
13856   context_values.background=pixel.background_color.pixel;
13857   context_values.foreground=pixel.foreground_color.pixel;
13858   pixel.annotate_context=XCreateGC(display,window_info.id,
13859     (size_t) (GCBackground | GCForeground),&context_values);
13860   if (pixel.annotate_context == (GC) NULL)
13861     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13862       image->filename);
13863   /*
13864     Initialize Image window attributes.
13865   */
13866   window_info.name=AcquireString("\0");
13867   window_info.icon_name=AcquireString("\0");
13868   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13869     &resources,&window_info);
13870   /*
13871     Create the X image.
13872   */
13873   window_info.width=(unsigned int) image->columns;
13874   window_info.height=(unsigned int) image->rows;
13875   if ((image->columns != window_info.width) ||
13876       (image->rows != window_info.height))
13877     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13878       image->filename);
13879   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13880     window_attributes.width,window_attributes.height);
13881   geometry_info.width=window_info.width;
13882   geometry_info.height=window_info.height;
13883   geometry_info.x=(ssize_t) window_info.x;
13884   geometry_info.y=(ssize_t) window_info.y;
13885   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13886     &geometry_info.width,&geometry_info.height);
13887   window_info.width=(unsigned int) geometry_info.width;
13888   window_info.height=(unsigned int) geometry_info.height;
13889   window_info.x=(int) geometry_info.x;
13890   window_info.y=(int) geometry_info.y;
13891   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13892     window_info.height,exception);
13893   if (status == MagickFalse)
13894     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13895       image->filename);
13896   window_info.x=0;
13897   window_info.y=0;
13898   if (image->debug != MagickFalse)
13899     {
13900       (void) LogMagickEvent(X11Event,GetMagickModule(),
13901         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13902         (double) image->columns,(double) image->rows);
13903       if (image->colors != 0)
13904         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13905           image->colors);
13906       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13907     }
13908   /*
13909     Adjust image dimensions as specified by backdrop or geometry options.
13910   */
13911   width=(int) window_info.width;
13912   height=(int) window_info.height;
13913   if (resources.backdrop != MagickFalse)
13914     {
13915       /*
13916         Center image on window.
13917       */
13918       window_info.x=(window_attributes.width/2)-
13919         (window_info.ximage->width/2);
13920       window_info.y=(window_attributes.height/2)-
13921         (window_info.ximage->height/2);
13922       width=window_attributes.width;
13923       height=window_attributes.height;
13924     }
13925   if ((resources.image_geometry != (char *) NULL) &&
13926       (*resources.image_geometry != '\0'))
13927     {
13928       char
13929         default_geometry[MaxTextExtent];
13930
13931       int
13932         flags,
13933         gravity;
13934
13935       XSizeHints
13936         *size_hints;
13937
13938       /*
13939         User specified geometry.
13940       */
13941       size_hints=XAllocSizeHints();
13942       if (size_hints == (XSizeHints *) NULL)
13943         ThrowXWindowFatalException(ResourceLimitFatalError,
13944           "MemoryAllocationFailed",image->filename);
13945       size_hints->flags=0L;
13946       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13947         width,height);
13948       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13949         default_geometry,window_info.border_width,size_hints,&window_info.x,
13950         &window_info.y,&width,&height,&gravity);
13951       if (flags & (XValue | YValue))
13952         {
13953           width=window_attributes.width;
13954           height=window_attributes.height;
13955         }
13956       (void) XFree((void *) size_hints);
13957     }
13958   /*
13959     Create the X pixmap.
13960   */
13961   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13962     (unsigned int) height,window_info.depth);
13963   if (window_info.pixmap == (Pixmap) NULL)
13964     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13965       image->filename);
13966   /*
13967     Display pixmap on the window.
13968   */
13969   if (((unsigned int) width > window_info.width) ||
13970       ((unsigned int) height > window_info.height))
13971     (void) XFillRectangle(display,window_info.pixmap,
13972       window_info.annotate_context,0,0,(unsigned int) width,
13973       (unsigned int) height);
13974   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13975     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13976     window_info.width,(unsigned int) window_info.height);
13977   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13978   (void) XClearWindow(display,window_info.id);
13979   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13980   XDelay(display,delay == 0UL ? 10UL : delay);
13981   (void) XSync(display,MagickFalse);
13982   return(window_info.id == root_window ? MagickTrue : MagickFalse);
13983 }
13984 \f
13985 /*
13986 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13987 %                                                                             %
13988 %                                                                             %
13989 %                                                                             %
13990 +   X D i s p l a y I m a g e                                                 %
13991 %                                                                             %
13992 %                                                                             %
13993 %                                                                             %
13994 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13995 %
13996 %  XDisplayImage() displays an image via X11.  A new image is created and
13997 %  returned if the user interactively transforms the displayed image.
13998 %
13999 %  The format of the XDisplayImage method is:
14000 %
14001 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14002 %        char **argv,int argc,Image **image,size_t *state,
14003 %        ExceptionInfo *exception)
14004 %
14005 %  A description of each parameter follows:
14006 %
14007 %    o nexus:  Method XDisplayImage returns an image when the
14008 %      user chooses 'Open Image' from the command menu or picks a tile
14009 %      from the image directory.  Otherwise a null image is returned.
14010 %
14011 %    o display: Specifies a connection to an X server;  returned from
14012 %      XOpenDisplay.
14013 %
14014 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14015 %
14016 %    o argv: Specifies the application's argument list.
14017 %
14018 %    o argc: Specifies the number of arguments.
14019 %
14020 %    o image: Specifies an address to an address of an Image structure;
14021 %
14022 %    o exception: return any errors or warnings in this structure.
14023 %
14024 */
14025 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14026   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14027 {
14028 #define MagnifySize  256  /* must be a power of 2 */
14029 #define MagickMenus  10
14030 #define MagickTitle  "Commands"
14031
14032   static const char
14033     *CommandMenu[] =
14034     {
14035       "File",
14036       "Edit",
14037       "View",
14038       "Transform",
14039       "Enhance",
14040       "Effects",
14041       "F/X",
14042       "Image Edit",
14043       "Miscellany",
14044       "Help",
14045       (char *) NULL
14046     },
14047     *FileMenu[] =
14048     {
14049       "Open...",
14050       "Next",
14051       "Former",
14052       "Select...",
14053       "Save...",
14054       "Print...",
14055       "Delete...",
14056       "New...",
14057       "Visual Directory...",
14058       "Quit",
14059       (char *) NULL
14060     },
14061     *EditMenu[] =
14062     {
14063       "Undo",
14064       "Redo",
14065       "Cut",
14066       "Copy",
14067       "Paste",
14068       (char *) NULL
14069     },
14070     *ViewMenu[] =
14071     {
14072       "Half Size",
14073       "Original Size",
14074       "Double Size",
14075       "Resize...",
14076       "Apply",
14077       "Refresh",
14078       "Restore",
14079       (char *) NULL
14080     },
14081     *TransformMenu[] =
14082     {
14083       "Crop",
14084       "Chop",
14085       "Flop",
14086       "Flip",
14087       "Rotate Right",
14088       "Rotate Left",
14089       "Rotate...",
14090       "Shear...",
14091       "Roll...",
14092       "Trim Edges",
14093       (char *) NULL
14094     },
14095     *EnhanceMenu[] =
14096     {
14097       "Hue...",
14098       "Saturation...",
14099       "Brightness...",
14100       "Gamma...",
14101       "Spiff",
14102       "Dull",
14103       "Contrast Stretch...",
14104       "Sigmoidal Contrast...",
14105       "Normalize",
14106       "Equalize",
14107       "Negate",
14108       "Grayscale",
14109       "Map...",
14110       "Quantize...",
14111       (char *) NULL
14112     },
14113     *EffectsMenu[] =
14114     {
14115       "Despeckle",
14116       "Emboss",
14117       "Reduce Noise",
14118       "Add Noise...",
14119       "Sharpen...",
14120       "Blur...",
14121       "Threshold...",
14122       "Edge Detect...",
14123       "Spread...",
14124       "Shade...",
14125       "Raise...",
14126       "Segment...",
14127       (char *) NULL
14128     },
14129     *FXMenu[] =
14130     {
14131       "Solarize...",
14132       "Sepia Tone...",
14133       "Swirl...",
14134       "Implode...",
14135       "Vignette...",
14136       "Wave...",
14137       "Oil Paint...",
14138       "Charcoal Draw...",
14139       (char *) NULL
14140     },
14141     *ImageEditMenu[] =
14142     {
14143       "Annotate...",
14144       "Draw...",
14145       "Color...",
14146       "Matte...",
14147       "Composite...",
14148       "Add Border...",
14149       "Add Frame...",
14150       "Comment...",
14151       "Launch...",
14152       "Region of Interest...",
14153       (char *) NULL
14154     },
14155     *MiscellanyMenu[] =
14156     {
14157       "Image Info",
14158       "Zoom Image",
14159       "Show Preview...",
14160       "Show Histogram",
14161       "Show Matte",
14162       "Background...",
14163       "Slide Show...",
14164       "Preferences...",
14165       (char *) NULL
14166     },
14167     *HelpMenu[] =
14168     {
14169       "Overview",
14170       "Browse Documentation",
14171       "About Display",
14172       (char *) NULL
14173     },
14174     *ShortCutsMenu[] =
14175     {
14176       "Next",
14177       "Former",
14178       "Open...",
14179       "Save...",
14180       "Print...",
14181       "Undo",
14182       "Restore",
14183       "Image Info",
14184       "Quit",
14185       (char *) NULL
14186     },
14187     *VirtualMenu[] =
14188     {
14189       "Image Info",
14190       "Print",
14191       "Next",
14192       "Quit",
14193       (char *) NULL
14194     };
14195
14196   static const char
14197     **Menus[MagickMenus] =
14198     {
14199       FileMenu,
14200       EditMenu,
14201       ViewMenu,
14202       TransformMenu,
14203       EnhanceMenu,
14204       EffectsMenu,
14205       FXMenu,
14206       ImageEditMenu,
14207       MiscellanyMenu,
14208       HelpMenu
14209     };
14210
14211   static CommandType
14212     CommandMenus[] =
14213     {
14214       NullCommand,
14215       NullCommand,
14216       NullCommand,
14217       NullCommand,
14218       NullCommand,
14219       NullCommand,
14220       NullCommand,
14221       NullCommand,
14222       NullCommand,
14223       NullCommand,
14224     },
14225     FileCommands[] =
14226     {
14227       OpenCommand,
14228       NextCommand,
14229       FormerCommand,
14230       SelectCommand,
14231       SaveCommand,
14232       PrintCommand,
14233       DeleteCommand,
14234       NewCommand,
14235       VisualDirectoryCommand,
14236       QuitCommand
14237     },
14238     EditCommands[] =
14239     {
14240       UndoCommand,
14241       RedoCommand,
14242       CutCommand,
14243       CopyCommand,
14244       PasteCommand
14245     },
14246     ViewCommands[] =
14247     {
14248       HalfSizeCommand,
14249       OriginalSizeCommand,
14250       DoubleSizeCommand,
14251       ResizeCommand,
14252       ApplyCommand,
14253       RefreshCommand,
14254       RestoreCommand
14255     },
14256     TransformCommands[] =
14257     {
14258       CropCommand,
14259       ChopCommand,
14260       FlopCommand,
14261       FlipCommand,
14262       RotateRightCommand,
14263       RotateLeftCommand,
14264       RotateCommand,
14265       ShearCommand,
14266       RollCommand,
14267       TrimCommand
14268     },
14269     EnhanceCommands[] =
14270     {
14271       HueCommand,
14272       SaturationCommand,
14273       BrightnessCommand,
14274       GammaCommand,
14275       SpiffCommand,
14276       DullCommand,
14277       ContrastStretchCommand,
14278       SigmoidalContrastCommand,
14279       NormalizeCommand,
14280       EqualizeCommand,
14281       NegateCommand,
14282       GrayscaleCommand,
14283       MapCommand,
14284       QuantizeCommand
14285     },
14286     EffectsCommands[] =
14287     {
14288       DespeckleCommand,
14289       EmbossCommand,
14290       ReduceNoiseCommand,
14291       AddNoiseCommand,
14292       SharpenCommand,
14293       BlurCommand,
14294       ThresholdCommand,
14295       EdgeDetectCommand,
14296       SpreadCommand,
14297       ShadeCommand,
14298       RaiseCommand,
14299       SegmentCommand
14300     },
14301     FXCommands[] =
14302     {
14303       SolarizeCommand,
14304       SepiaToneCommand,
14305       SwirlCommand,
14306       ImplodeCommand,
14307       VignetteCommand,
14308       WaveCommand,
14309       OilPaintCommand,
14310       CharcoalDrawCommand
14311     },
14312     ImageEditCommands[] =
14313     {
14314       AnnotateCommand,
14315       DrawCommand,
14316       ColorCommand,
14317       MatteCommand,
14318       CompositeCommand,
14319       AddBorderCommand,
14320       AddFrameCommand,
14321       CommentCommand,
14322       LaunchCommand,
14323       RegionofInterestCommand
14324     },
14325     MiscellanyCommands[] =
14326     {
14327       InfoCommand,
14328       ZoomCommand,
14329       ShowPreviewCommand,
14330       ShowHistogramCommand,
14331       ShowMatteCommand,
14332       BackgroundCommand,
14333       SlideShowCommand,
14334       PreferencesCommand
14335     },
14336     HelpCommands[] =
14337     {
14338       HelpCommand,
14339       BrowseDocumentationCommand,
14340       VersionCommand
14341     },
14342     ShortCutsCommands[] =
14343     {
14344       NextCommand,
14345       FormerCommand,
14346       OpenCommand,
14347       SaveCommand,
14348       PrintCommand,
14349       UndoCommand,
14350       RestoreCommand,
14351       InfoCommand,
14352       QuitCommand
14353     },
14354     VirtualCommands[] =
14355     {
14356       InfoCommand,
14357       PrintCommand,
14358       NextCommand,
14359       QuitCommand
14360     };
14361
14362   static CommandType
14363     *Commands[MagickMenus] =
14364     {
14365       FileCommands,
14366       EditCommands,
14367       ViewCommands,
14368       TransformCommands,
14369       EnhanceCommands,
14370       EffectsCommands,
14371       FXCommands,
14372       ImageEditCommands,
14373       MiscellanyCommands,
14374       HelpCommands
14375     };
14376
14377   char
14378     command[MaxTextExtent],
14379     *directory,
14380     geometry[MaxTextExtent],
14381     resource_name[MaxTextExtent];
14382
14383   CommandType
14384     command_type;
14385
14386   Image
14387     *display_image,
14388     *nexus;
14389
14390   int
14391     entry,
14392     id;
14393
14394   KeySym
14395     key_symbol;
14396
14397   MagickStatusType
14398     context_mask,
14399     status;
14400
14401   RectangleInfo
14402     geometry_info;
14403
14404   register int
14405     i;
14406
14407   static char
14408     working_directory[MaxTextExtent];
14409
14410   static XPoint
14411     vid_info;
14412
14413   static XWindowInfo
14414     *magick_windows[MaxXWindows];
14415
14416   static unsigned int
14417     number_windows;
14418
14419   struct stat
14420     attributes;
14421
14422   time_t
14423     timer,
14424     timestamp,
14425     update_time;
14426
14427   unsigned int
14428     height,
14429     width;
14430
14431   size_t
14432     delay;
14433
14434   WarningHandler
14435     warning_handler;
14436
14437   Window
14438     root_window;
14439
14440   XClassHint
14441     *class_hints;
14442
14443   XEvent
14444     event;
14445
14446   XFontStruct
14447     *font_info;
14448
14449   XGCValues
14450     context_values;
14451
14452   XPixelInfo
14453     *icon_pixel,
14454     *pixel;
14455
14456   XResourceInfo
14457     *icon_resources;
14458
14459   XStandardColormap
14460     *icon_map,
14461     *map_info;
14462
14463   XVisualInfo
14464     *icon_visual,
14465     *visual_info;
14466
14467   XWindowChanges
14468     window_changes;
14469
14470   XWindows
14471     *windows;
14472
14473   XWMHints
14474     *manager_hints;
14475
14476   assert(image != (Image **) NULL);
14477   assert((*image)->signature == MagickSignature);
14478   if ((*image)->debug != MagickFalse)
14479     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14480   display_image=(*image);
14481   warning_handler=(WarningHandler) NULL;
14482   windows=XSetWindows((XWindows *) ~0);
14483   if (windows != (XWindows *) NULL)
14484     {
14485       int
14486         status;
14487
14488       status=chdir(working_directory);
14489       if (status == -1)
14490         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14491           "UnableToOpenFile","%s",working_directory);
14492       warning_handler=resource_info->display_warnings ?
14493         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14494       warning_handler=resource_info->display_warnings ?
14495         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14496     }
14497   else
14498     {
14499       /*
14500         Allocate windows structure.
14501       */
14502       resource_info->colors=display_image->colors;
14503       windows=XSetWindows(XInitializeWindows(display,resource_info));
14504       if (windows == (XWindows *) NULL)
14505         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14506           (*image)->filename);
14507       /*
14508         Initialize window id's.
14509       */
14510       number_windows=0;
14511       magick_windows[number_windows++]=(&windows->icon);
14512       magick_windows[number_windows++]=(&windows->backdrop);
14513       magick_windows[number_windows++]=(&windows->image);
14514       magick_windows[number_windows++]=(&windows->info);
14515       magick_windows[number_windows++]=(&windows->command);
14516       magick_windows[number_windows++]=(&windows->widget);
14517       magick_windows[number_windows++]=(&windows->popup);
14518       magick_windows[number_windows++]=(&windows->magnify);
14519       magick_windows[number_windows++]=(&windows->pan);
14520       for (i=0; i < (int) number_windows; i++)
14521         magick_windows[i]->id=(Window) NULL;
14522       vid_info.x=0;
14523       vid_info.y=0;
14524     }
14525   /*
14526     Initialize font info.
14527   */
14528   if (windows->font_info != (XFontStruct *) NULL)
14529     (void) XFreeFont(display,windows->font_info);
14530   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14531   if (windows->font_info == (XFontStruct *) NULL)
14532     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14533       resource_info->font);
14534   /*
14535     Initialize Standard Colormap.
14536   */
14537   map_info=windows->map_info;
14538   icon_map=windows->icon_map;
14539   visual_info=windows->visual_info;
14540   icon_visual=windows->icon_visual;
14541   pixel=windows->pixel_info;
14542   icon_pixel=windows->icon_pixel;
14543   font_info=windows->font_info;
14544   icon_resources=windows->icon_resources;
14545   class_hints=windows->class_hints;
14546   manager_hints=windows->manager_hints;
14547   root_window=XRootWindow(display,visual_info->screen);
14548   nexus=NewImageList();
14549   if (display_image->debug != MagickFalse)
14550     {
14551       (void) LogMagickEvent(X11Event,GetMagickModule(),
14552         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14553         (double) display_image->scene,(double) display_image->columns,
14554         (double) display_image->rows);
14555       if (display_image->colors != 0)
14556         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14557           display_image->colors);
14558       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14559         display_image->magick);
14560     }
14561   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14562     map_info,pixel);
14563   display_image->taint=MagickFalse;
14564   /*
14565     Initialize graphic context.
14566   */
14567   windows->context.id=(Window) NULL;
14568   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14569     resource_info,&windows->context);
14570   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14571   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14572   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14573   manager_hints->flags=InputHint | StateHint;
14574   manager_hints->input=MagickFalse;
14575   manager_hints->initial_state=WithdrawnState;
14576   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14577     &windows->context);
14578   if (display_image->debug != MagickFalse)
14579     (void) LogMagickEvent(X11Event,GetMagickModule(),
14580       "Window id: 0x%lx (context)",windows->context.id);
14581   context_values.background=pixel->background_color.pixel;
14582   context_values.font=font_info->fid;
14583   context_values.foreground=pixel->foreground_color.pixel;
14584   context_values.graphics_exposures=MagickFalse;
14585   context_mask=(MagickStatusType)
14586     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14587   if (pixel->annotate_context != (GC) NULL)
14588     (void) XFreeGC(display,pixel->annotate_context);
14589   pixel->annotate_context=XCreateGC(display,windows->context.id,
14590     context_mask,&context_values);
14591   if (pixel->annotate_context == (GC) NULL)
14592     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14593       display_image->filename);
14594   context_values.background=pixel->depth_color.pixel;
14595   if (pixel->widget_context != (GC) NULL)
14596     (void) XFreeGC(display,pixel->widget_context);
14597   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14598     &context_values);
14599   if (pixel->widget_context == (GC) NULL)
14600     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14601       display_image->filename);
14602   context_values.background=pixel->foreground_color.pixel;
14603   context_values.foreground=pixel->background_color.pixel;
14604   context_values.plane_mask=context_values.background ^
14605     context_values.foreground;
14606   if (pixel->highlight_context != (GC) NULL)
14607     (void) XFreeGC(display,pixel->highlight_context);
14608   pixel->highlight_context=XCreateGC(display,windows->context.id,
14609     (size_t) (context_mask | GCPlaneMask),&context_values);
14610   if (pixel->highlight_context == (GC) NULL)
14611     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14612       display_image->filename);
14613   (void) XDestroyWindow(display,windows->context.id);
14614   /*
14615     Initialize icon window.
14616   */
14617   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14618     icon_resources,&windows->icon);
14619   windows->icon.geometry=resource_info->icon_geometry;
14620   XBestIconSize(display,&windows->icon,display_image);
14621   windows->icon.attributes.colormap=XDefaultColormap(display,
14622     icon_visual->screen);
14623   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14624   manager_hints->flags=InputHint | StateHint;
14625   manager_hints->input=MagickFalse;
14626   manager_hints->initial_state=IconicState;
14627   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14628     &windows->icon);
14629   if (display_image->debug != MagickFalse)
14630     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14631       windows->icon.id);
14632   /*
14633     Initialize graphic context for icon window.
14634   */
14635   if (icon_pixel->annotate_context != (GC) NULL)
14636     (void) XFreeGC(display,icon_pixel->annotate_context);
14637   context_values.background=icon_pixel->background_color.pixel;
14638   context_values.foreground=icon_pixel->foreground_color.pixel;
14639   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14640     (size_t) (GCBackground | GCForeground),&context_values);
14641   if (icon_pixel->annotate_context == (GC) NULL)
14642     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14643       display_image->filename);
14644   windows->icon.annotate_context=icon_pixel->annotate_context;
14645   /*
14646     Initialize Image window.
14647   */
14648   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14649     &windows->image);
14650   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14651   if (resource_info->use_shared_memory == MagickFalse)
14652     windows->image.shared_memory=MagickFalse;
14653   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14654     {
14655       char
14656         *title;
14657
14658       title=InterpretImageProperties(resource_info->image_info,display_image,
14659         resource_info->title,exception);
14660       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14661       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14662       title=DestroyString(title);
14663     }
14664   else
14665     {
14666       char
14667         filename[MaxTextExtent];
14668
14669       /*
14670         Window name is the base of the filename.
14671       */
14672       GetPathComponent(display_image->magick_filename,TailPath,filename);
14673       if (GetImageListLength(display_image) == 1)
14674         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14675           "%s: %s",MagickPackageName,filename);
14676       else
14677         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14678           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14679           (double) display_image->scene,(double) GetImageListLength(
14680           display_image));
14681       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14682     }
14683   if (resource_info->immutable)
14684     windows->image.immutable=MagickTrue;
14685   windows->image.use_pixmap=resource_info->use_pixmap;
14686   windows->image.geometry=resource_info->image_geometry;
14687   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14688     XDisplayWidth(display,visual_info->screen),
14689     XDisplayHeight(display,visual_info->screen));
14690   geometry_info.width=display_image->columns;
14691   geometry_info.height=display_image->rows;
14692   geometry_info.x=0;
14693   geometry_info.y=0;
14694   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14695     &geometry_info.width,&geometry_info.height);
14696   windows->image.width=(unsigned int) geometry_info.width;
14697   windows->image.height=(unsigned int) geometry_info.height;
14698   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14699     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14700     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14701     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14702   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14703     resource_info,&windows->backdrop);
14704   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14705     {
14706       /*
14707         Initialize backdrop window.
14708       */
14709       windows->backdrop.x=0;
14710       windows->backdrop.y=0;
14711       (void) CloneString(&windows->backdrop.name,"Backdrop");
14712       windows->backdrop.flags=(size_t) (USSize | USPosition);
14713       windows->backdrop.width=(unsigned int)
14714         XDisplayWidth(display,visual_info->screen);
14715       windows->backdrop.height=(unsigned int)
14716         XDisplayHeight(display,visual_info->screen);
14717       windows->backdrop.border_width=0;
14718       windows->backdrop.immutable=MagickTrue;
14719       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14720         ButtonReleaseMask;
14721       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14722         StructureNotifyMask;
14723       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14724       manager_hints->icon_window=windows->icon.id;
14725       manager_hints->input=MagickTrue;
14726       manager_hints->initial_state=resource_info->iconic ? IconicState :
14727         NormalState;
14728       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14729         &windows->backdrop);
14730       if (display_image->debug != MagickFalse)
14731         (void) LogMagickEvent(X11Event,GetMagickModule(),
14732           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14733       (void) XMapWindow(display,windows->backdrop.id);
14734       (void) XClearWindow(display,windows->backdrop.id);
14735       if (windows->image.id != (Window) NULL)
14736         {
14737           (void) XDestroyWindow(display,windows->image.id);
14738           windows->image.id=(Window) NULL;
14739         }
14740       /*
14741         Position image in the center the backdrop.
14742       */
14743       windows->image.flags|=USPosition;
14744       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14745         (windows->image.width/2);
14746       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14747         (windows->image.height/2);
14748     }
14749   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14750   manager_hints->icon_window=windows->icon.id;
14751   manager_hints->input=MagickTrue;
14752   manager_hints->initial_state=resource_info->iconic ? IconicState :
14753     NormalState;
14754   if (windows->group_leader.id != (Window) NULL)
14755     {
14756       /*
14757         Follow the leader.
14758       */
14759       manager_hints->flags|=WindowGroupHint;
14760       manager_hints->window_group=windows->group_leader.id;
14761       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14762       if (display_image->debug != MagickFalse)
14763         (void) LogMagickEvent(X11Event,GetMagickModule(),
14764           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14765     }
14766   XMakeWindow(display,
14767     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14768     argv,argc,class_hints,manager_hints,&windows->image);
14769   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14770     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14771   if (windows->group_leader.id != (Window) NULL)
14772     (void) XSetTransientForHint(display,windows->image.id,
14773       windows->group_leader.id);
14774   if (display_image->debug != MagickFalse)
14775     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14776       windows->image.id);
14777   /*
14778     Initialize Info widget.
14779   */
14780   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14781     &windows->info);
14782   (void) CloneString(&windows->info.name,"Info");
14783   (void) CloneString(&windows->info.icon_name,"Info");
14784   windows->info.border_width=1;
14785   windows->info.x=2;
14786   windows->info.y=2;
14787   windows->info.flags|=PPosition;
14788   windows->info.attributes.win_gravity=UnmapGravity;
14789   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14790     StructureNotifyMask;
14791   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14792   manager_hints->input=MagickFalse;
14793   manager_hints->initial_state=NormalState;
14794   manager_hints->window_group=windows->image.id;
14795   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14796     &windows->info);
14797   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14798     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14799   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14800     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14801   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14802   if (windows->image.mapped != MagickFalse)
14803     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14804   if (display_image->debug != MagickFalse)
14805     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14806       windows->info.id);
14807   /*
14808     Initialize Command widget.
14809   */
14810   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14811     resource_info,&windows->command);
14812   windows->command.data=MagickMenus;
14813   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14814   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14815     resource_info->client_name);
14816   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14817     resource_name,"geometry",(char *) NULL);
14818   (void) CloneString(&windows->command.name,MagickTitle);
14819   windows->command.border_width=0;
14820   windows->command.flags|=PPosition;
14821   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14822     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14823     OwnerGrabButtonMask | StructureNotifyMask;
14824   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14825   manager_hints->input=MagickTrue;
14826   manager_hints->initial_state=NormalState;
14827   manager_hints->window_group=windows->image.id;
14828   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14829     &windows->command);
14830   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14831     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14832     HighlightHeight);
14833   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14834     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14835   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14836   if (windows->command.mapped != MagickFalse)
14837     (void) XMapRaised(display,windows->command.id);
14838   if (display_image->debug != MagickFalse)
14839     (void) LogMagickEvent(X11Event,GetMagickModule(),
14840       "Window id: 0x%lx (command)",windows->command.id);
14841   /*
14842     Initialize Widget window.
14843   */
14844   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14845     resource_info,&windows->widget);
14846   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14847     resource_info->client_name);
14848   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14849     resource_name,"geometry",(char *) NULL);
14850   windows->widget.border_width=0;
14851   windows->widget.flags|=PPosition;
14852   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14853     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14854     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14855     StructureNotifyMask;
14856   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14857   manager_hints->input=MagickTrue;
14858   manager_hints->initial_state=NormalState;
14859   manager_hints->window_group=windows->image.id;
14860   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14861     &windows->widget);
14862   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14863     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14864   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14865     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14866   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14867   if (display_image->debug != MagickFalse)
14868     (void) LogMagickEvent(X11Event,GetMagickModule(),
14869       "Window id: 0x%lx (widget)",windows->widget.id);
14870   /*
14871     Initialize popup window.
14872   */
14873   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14874     resource_info,&windows->popup);
14875   windows->popup.border_width=0;
14876   windows->popup.flags|=PPosition;
14877   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14878     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14879     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14880   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14881   manager_hints->input=MagickTrue;
14882   manager_hints->initial_state=NormalState;
14883   manager_hints->window_group=windows->image.id;
14884   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14885     &windows->popup);
14886   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14887     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14888   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14889     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14890   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14891   if (display_image->debug != MagickFalse)
14892     (void) LogMagickEvent(X11Event,GetMagickModule(),
14893       "Window id: 0x%lx (pop up)",windows->popup.id);
14894   /*
14895     Initialize Magnify window and cursor.
14896   */
14897   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14898     resource_info,&windows->magnify);
14899   if (resource_info->use_shared_memory == MagickFalse)
14900     windows->magnify.shared_memory=MagickFalse;
14901   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14902     resource_info->client_name);
14903   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14904     resource_name,"geometry",(char *) NULL);
14905   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14906     resource_info->magnify);
14907   if (windows->magnify.cursor != (Cursor) NULL)
14908     (void) XFreeCursor(display,windows->magnify.cursor);
14909   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14910     map_info->colormap,resource_info->background_color,
14911     resource_info->foreground_color);
14912   if (windows->magnify.cursor == (Cursor) NULL)
14913     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14914       display_image->filename);
14915   windows->magnify.width=MagnifySize;
14916   windows->magnify.height=MagnifySize;
14917   windows->magnify.flags|=PPosition;
14918   windows->magnify.min_width=MagnifySize;
14919   windows->magnify.min_height=MagnifySize;
14920   windows->magnify.width_inc=MagnifySize;
14921   windows->magnify.height_inc=MagnifySize;
14922   windows->magnify.data=resource_info->magnify;
14923   windows->magnify.attributes.cursor=windows->magnify.cursor;
14924   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14925     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14926     StructureNotifyMask;
14927   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14928   manager_hints->input=MagickTrue;
14929   manager_hints->initial_state=NormalState;
14930   manager_hints->window_group=windows->image.id;
14931   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14932     &windows->magnify);
14933   if (display_image->debug != MagickFalse)
14934     (void) LogMagickEvent(X11Event,GetMagickModule(),
14935       "Window id: 0x%lx (magnify)",windows->magnify.id);
14936   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14937   /*
14938     Initialize panning window.
14939   */
14940   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14941     resource_info,&windows->pan);
14942   (void) CloneString(&windows->pan.name,"Pan Icon");
14943   windows->pan.width=windows->icon.width;
14944   windows->pan.height=windows->icon.height;
14945   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14946     resource_info->client_name);
14947   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14948     resource_name,"geometry",(char *) NULL);
14949   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14950     &windows->pan.width,&windows->pan.height);
14951   windows->pan.flags|=PPosition;
14952   windows->pan.immutable=MagickTrue;
14953   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14954     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14955     StructureNotifyMask;
14956   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14957   manager_hints->input=MagickFalse;
14958   manager_hints->initial_state=NormalState;
14959   manager_hints->window_group=windows->image.id;
14960   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14961     &windows->pan);
14962   if (display_image->debug != MagickFalse)
14963     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14964       windows->pan.id);
14965   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14966   if (windows->info.mapped != MagickFalse)
14967     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14968   if ((windows->image.mapped == MagickFalse) ||
14969       (windows->backdrop.id != (Window) NULL))
14970     (void) XMapWindow(display,windows->image.id);
14971   /*
14972     Set our progress monitor and warning handlers.
14973   */
14974   if (warning_handler == (WarningHandler) NULL)
14975     {
14976       warning_handler=resource_info->display_warnings ?
14977         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14978       warning_handler=resource_info->display_warnings ?
14979         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14980     }
14981   /*
14982     Initialize Image and Magnify X images.
14983   */
14984   windows->image.x=0;
14985   windows->image.y=0;
14986   windows->magnify.shape=MagickFalse;
14987   width=(unsigned int) display_image->columns;
14988   height=(unsigned int) display_image->rows;
14989   if ((display_image->columns != width) || (display_image->rows != height))
14990     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14991       display_image->filename);
14992   status=XMakeImage(display,resource_info,&windows->image,display_image,
14993     width,height,exception);
14994   if (status == MagickFalse)
14995     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14996       display_image->filename);
14997   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14998     windows->magnify.width,windows->magnify.height,exception);
14999   if (status == MagickFalse)
15000     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15001       display_image->filename);
15002   if (windows->magnify.mapped != MagickFalse)
15003     (void) XMapRaised(display,windows->magnify.id);
15004   if (windows->pan.mapped != MagickFalse)
15005     (void) XMapRaised(display,windows->pan.id);
15006   windows->image.window_changes.width=(int) display_image->columns;
15007   windows->image.window_changes.height=(int) display_image->rows;
15008   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15009   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15010   (void) XSync(display,MagickFalse);
15011   /*
15012     Respond to events.
15013   */
15014   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15015   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15016   update_time=0;
15017   if (resource_info->update != MagickFalse)
15018     {
15019       MagickBooleanType
15020         status;
15021
15022       /*
15023         Determine when file data was last modified.
15024       */
15025       status=GetPathAttributes(display_image->filename,&attributes);
15026       if (status != MagickFalse)
15027         update_time=attributes.st_mtime;
15028     }
15029   *state&=(~FormerImageState);
15030   *state&=(~MontageImageState);
15031   *state&=(~NextImageState);
15032   do
15033   {
15034     /*
15035       Handle a window event.
15036     */
15037     if (windows->image.mapped != MagickFalse)
15038       if ((display_image->delay != 0) || (resource_info->update != 0))
15039         {
15040           if (timer < time((time_t *) NULL))
15041             {
15042               if (resource_info->update == MagickFalse)
15043                 *state|=NextImageState | ExitState;
15044               else
15045                 {
15046                   MagickBooleanType
15047                     status;
15048
15049                   /*
15050                     Determine if image file was modified.
15051                   */
15052                   status=GetPathAttributes(display_image->filename,&attributes);
15053                   if (status != MagickFalse)
15054                     if (update_time != attributes.st_mtime)
15055                       {
15056                         /*
15057                           Redisplay image.
15058                         */
15059                         (void) FormatLocaleString(
15060                           resource_info->image_info->filename,MaxTextExtent,
15061                           "%s:%s",display_image->magick,
15062                           display_image->filename);
15063                         nexus=ReadImage(resource_info->image_info,
15064                           &display_image->exception);
15065                         if (nexus != (Image *) NULL)
15066                           {
15067                             nexus=DestroyImage(nexus);
15068                             *state|=NextImageState | ExitState;
15069                           }
15070                       }
15071                   delay=display_image->delay/MagickMax(
15072                     display_image->ticks_per_second,1L);
15073                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15074                 }
15075             }
15076           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15077             {
15078               /*
15079                 Do not block if delay > 0.
15080               */
15081               XDelay(display,SuspendTime << 2);
15082               continue;
15083             }
15084         }
15085     timestamp=time((time_t *) NULL);
15086     (void) XNextEvent(display,&event);
15087     if (windows->image.stasis == MagickFalse)
15088       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15089         MagickTrue : MagickFalse;
15090     if (windows->magnify.stasis == MagickFalse)
15091       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15092         MagickTrue : MagickFalse;
15093     if (event.xany.window == windows->command.id)
15094       {
15095         /*
15096           Select a command from the Command widget.
15097         */
15098         id=XCommandWidget(display,windows,CommandMenu,&event);
15099         if (id < 0)
15100           continue;
15101         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15102         command_type=CommandMenus[id];
15103         if (id < MagickMenus)
15104           {
15105             /*
15106               Select a command from a pop-up menu.
15107             */
15108             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15109               command);
15110             if (entry < 0)
15111               continue;
15112             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15113             command_type=Commands[id][entry];
15114           }
15115         if (command_type != NullCommand)
15116           nexus=XMagickCommand(display,resource_info,windows,command_type,
15117             &display_image,exception);
15118         continue;
15119       }
15120     switch (event.type)
15121     {
15122       case ButtonPress:
15123       {
15124         if (display_image->debug != MagickFalse)
15125           (void) LogMagickEvent(X11Event,GetMagickModule(),
15126             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15127             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15128         if ((event.xbutton.button == Button3) &&
15129             (event.xbutton.state & Mod1Mask))
15130           {
15131             /*
15132               Convert Alt-Button3 to Button2.
15133             */
15134             event.xbutton.button=Button2;
15135             event.xbutton.state&=(~Mod1Mask);
15136           }
15137         if (event.xbutton.window == windows->backdrop.id)
15138           {
15139             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15140               event.xbutton.time);
15141             break;
15142           }
15143         if (event.xbutton.window == windows->image.id)
15144           {
15145             switch (event.xbutton.button)
15146             {
15147               case Button1:
15148               {
15149                 if (resource_info->immutable)
15150                   {
15151                     /*
15152                       Select a command from the Virtual menu.
15153                     */
15154                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15155                       command);
15156                     if (entry >= 0)
15157                       nexus=XMagickCommand(display,resource_info,windows,
15158                         VirtualCommands[entry],&display_image,exception);
15159                     break;
15160                   }
15161                 /*
15162                   Map/unmap Command widget.
15163                 */
15164                 if (windows->command.mapped != MagickFalse)
15165                   (void) XWithdrawWindow(display,windows->command.id,
15166                     windows->command.screen);
15167                 else
15168                   {
15169                     (void) XCommandWidget(display,windows,CommandMenu,
15170                       (XEvent *) NULL);
15171                     (void) XMapRaised(display,windows->command.id);
15172                   }
15173                 break;
15174               }
15175               case Button2:
15176               {
15177                 /*
15178                   User pressed the image magnify button.
15179                 */
15180                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15181                   &display_image,exception);
15182                 XMagnifyImage(display,windows,&event);
15183                 break;
15184               }
15185               case Button3:
15186               {
15187                 if (resource_info->immutable)
15188                   {
15189                     /*
15190                       Select a command from the Virtual menu.
15191                     */
15192                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15193                       command);
15194                     if (entry >= 0)
15195                       nexus=XMagickCommand(display,resource_info,windows,
15196                         VirtualCommands[entry],&display_image,exception);
15197                     break;
15198                   }
15199                 if (display_image->montage != (char *) NULL)
15200                   {
15201                     /*
15202                       Open or delete a tile from a visual image directory.
15203                     */
15204                     nexus=XTileImage(display,resource_info,windows,
15205                       display_image,&event,exception);
15206                     if (nexus != (Image *) NULL)
15207                       *state|=MontageImageState | NextImageState | ExitState;
15208                     vid_info.x=(short int) windows->image.x;
15209                     vid_info.y=(short int) windows->image.y;
15210                     break;
15211                   }
15212                 /*
15213                   Select a command from the Short Cuts menu.
15214                 */
15215                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15216                   command);
15217                 if (entry >= 0)
15218                   nexus=XMagickCommand(display,resource_info,windows,
15219                     ShortCutsCommands[entry],&display_image,exception);
15220                 break;
15221               }
15222               case Button4:
15223               {
15224                 /*
15225                   Wheel up.
15226                 */
15227                 XTranslateImage(display,windows,*image,XK_Up);
15228                 break;
15229               }
15230               case Button5:
15231               {
15232                 /*
15233                   Wheel down.
15234                 */
15235                 XTranslateImage(display,windows,*image,XK_Down);
15236                 break;
15237               }
15238               default:
15239                 break;
15240             }
15241             break;
15242           }
15243         if (event.xbutton.window == windows->magnify.id)
15244           {
15245             int
15246               factor;
15247
15248             static const char
15249               *MagnifyMenu[] =
15250               {
15251                 "2",
15252                 "4",
15253                 "5",
15254                 "6",
15255                 "7",
15256                 "8",
15257                 "9",
15258                 "3",
15259                 (char *) NULL,
15260               };
15261
15262             static KeySym
15263               MagnifyCommands[] =
15264               {
15265                 XK_2,
15266                 XK_4,
15267                 XK_5,
15268                 XK_6,
15269                 XK_7,
15270                 XK_8,
15271                 XK_9,
15272                 XK_3
15273               };
15274
15275             /*
15276               Select a magnify factor from the pop-up menu.
15277             */
15278             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15279             if (factor >= 0)
15280               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15281             break;
15282           }
15283         if (event.xbutton.window == windows->pan.id)
15284           {
15285             switch (event.xbutton.button)
15286             {
15287               case Button4:
15288               {
15289                 /*
15290                   Wheel up.
15291                 */
15292                 XTranslateImage(display,windows,*image,XK_Up);
15293                 break;
15294               }
15295               case Button5:
15296               {
15297                 /*
15298                   Wheel down.
15299                 */
15300                 XTranslateImage(display,windows,*image,XK_Down);
15301                 break;
15302               }
15303               default:
15304               {
15305                 XPanImage(display,windows,&event);
15306                 break;
15307               }
15308             }
15309             break;
15310           }
15311         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15312           1L);
15313         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15314         break;
15315       }
15316       case ButtonRelease:
15317       {
15318         if (display_image->debug != MagickFalse)
15319           (void) LogMagickEvent(X11Event,GetMagickModule(),
15320             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15321             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15322         break;
15323       }
15324       case ClientMessage:
15325       {
15326         if (display_image->debug != MagickFalse)
15327           (void) LogMagickEvent(X11Event,GetMagickModule(),
15328             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15329             event.xclient.message_type,event.xclient.format,(unsigned long)
15330             event.xclient.data.l[0]);
15331         if (event.xclient.message_type == windows->im_protocols)
15332           {
15333             if (*event.xclient.data.l == (long) windows->im_update_widget)
15334               {
15335                 (void) CloneString(&windows->command.name,MagickTitle);
15336                 windows->command.data=MagickMenus;
15337                 (void) XCommandWidget(display,windows,CommandMenu,
15338                   (XEvent *) NULL);
15339                 break;
15340               }
15341             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15342               {
15343                 /*
15344                   Update graphic context and window colormap.
15345                 */
15346                 for (i=0; i < (int) number_windows; i++)
15347                 {
15348                   if (magick_windows[i]->id == windows->icon.id)
15349                     continue;
15350                   context_values.background=pixel->background_color.pixel;
15351                   context_values.foreground=pixel->foreground_color.pixel;
15352                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15353                     context_mask,&context_values);
15354                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15355                     context_mask,&context_values);
15356                   context_values.background=pixel->foreground_color.pixel;
15357                   context_values.foreground=pixel->background_color.pixel;
15358                   context_values.plane_mask=context_values.background ^
15359                     context_values.foreground;
15360                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15361                     (size_t) (context_mask | GCPlaneMask),
15362                     &context_values);
15363                   magick_windows[i]->attributes.background_pixel=
15364                     pixel->background_color.pixel;
15365                   magick_windows[i]->attributes.border_pixel=
15366                     pixel->border_color.pixel;
15367                   magick_windows[i]->attributes.colormap=map_info->colormap;
15368                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15369                     (unsigned long) magick_windows[i]->mask,
15370                     &magick_windows[i]->attributes);
15371                 }
15372                 if (windows->pan.mapped != MagickFalse)
15373                   {
15374                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15375                       windows->pan.pixmap);
15376                     (void) XClearWindow(display,windows->pan.id);
15377                     XDrawPanRectangle(display,windows);
15378                   }
15379                 if (windows->backdrop.id != (Window) NULL)
15380                   (void) XInstallColormap(display,map_info->colormap);
15381                 break;
15382               }
15383             if (*event.xclient.data.l == (long) windows->im_former_image)
15384               {
15385                 *state|=FormerImageState | ExitState;
15386                 break;
15387               }
15388             if (*event.xclient.data.l == (long) windows->im_next_image)
15389               {
15390                 *state|=NextImageState | ExitState;
15391                 break;
15392               }
15393             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15394               {
15395                 *state|=RetainColorsState;
15396                 break;
15397               }
15398             if (*event.xclient.data.l == (long) windows->im_exit)
15399               {
15400                 *state|=ExitState;
15401                 break;
15402               }
15403             break;
15404           }
15405         if (event.xclient.message_type == windows->dnd_protocols)
15406           {
15407             Atom
15408               selection,
15409               type;
15410
15411             int
15412               format,
15413               status;
15414
15415             unsigned char
15416               *data;
15417
15418             unsigned long
15419               after,
15420               length;
15421
15422             /*
15423               Display image named by the Drag-and-Drop selection.
15424             */
15425             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15426               break;
15427             selection=XInternAtom(display,"DndSelection",MagickFalse);
15428             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15429               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15430               &length,&after,&data);
15431             if ((status != Success) || (length == 0))
15432               break;
15433             if (*event.xclient.data.l == 2)
15434               {
15435                 /*
15436                   Offix DND.
15437                 */
15438                 (void) CopyMagickString(resource_info->image_info->filename,
15439                   (char *) data,MaxTextExtent);
15440               }
15441             else
15442               {
15443                 /*
15444                   XDND.
15445                 */
15446                 if (strncmp((char *) data, "file:", 5) != 0)
15447                   {
15448                     (void) XFree((void *) data);
15449                     break;
15450                   }
15451                 (void) CopyMagickString(resource_info->image_info->filename,
15452                   ((char *) data)+5,MaxTextExtent);
15453               }
15454             nexus=ReadImage(resource_info->image_info,
15455               &display_image->exception);
15456             CatchException(&display_image->exception);
15457             if (nexus != (Image *) NULL)
15458               *state|=NextImageState | ExitState;
15459             (void) XFree((void *) data);
15460             break;
15461           }
15462         /*
15463           If client window delete message, exit.
15464         */
15465         if (event.xclient.message_type != windows->wm_protocols)
15466           break;
15467         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15468           break;
15469         (void) XWithdrawWindow(display,event.xclient.window,
15470           visual_info->screen);
15471         if (event.xclient.window == windows->image.id)
15472           {
15473             *state|=ExitState;
15474             break;
15475           }
15476         if (event.xclient.window == windows->pan.id)
15477           {
15478             /*
15479               Restore original image size when pan window is deleted.
15480             */
15481             windows->image.window_changes.width=windows->image.ximage->width;
15482             windows->image.window_changes.height=windows->image.ximage->height;
15483             (void) XConfigureImage(display,resource_info,windows,
15484               display_image,exception);
15485           }
15486         break;
15487       }
15488       case ConfigureNotify:
15489       {
15490         if (display_image->debug != MagickFalse)
15491           (void) LogMagickEvent(X11Event,GetMagickModule(),
15492             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15493             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15494             event.xconfigure.y,event.xconfigure.send_event);
15495         if (event.xconfigure.window == windows->image.id)
15496           {
15497             /*
15498               Image window has a new configuration.
15499             */
15500             if (event.xconfigure.send_event != 0)
15501               {
15502                 XWindowChanges
15503                   window_changes;
15504
15505                 /*
15506                   Position the transient windows relative of the Image window.
15507                 */
15508                 if (windows->command.geometry == (char *) NULL)
15509                   if (windows->command.mapped == MagickFalse)
15510                     {
15511                       windows->command.x=event.xconfigure.x-
15512                         windows->command.width-25;
15513                       windows->command.y=event.xconfigure.y;
15514                       XConstrainWindowPosition(display,&windows->command);
15515                       window_changes.x=windows->command.x;
15516                       window_changes.y=windows->command.y;
15517                       (void) XReconfigureWMWindow(display,windows->command.id,
15518                         windows->command.screen,(unsigned int) (CWX | CWY),
15519                         &window_changes);
15520                     }
15521                 if (windows->widget.geometry == (char *) NULL)
15522                   if (windows->widget.mapped == MagickFalse)
15523                     {
15524                       windows->widget.x=event.xconfigure.x+
15525                         event.xconfigure.width/10;
15526                       windows->widget.y=event.xconfigure.y+
15527                         event.xconfigure.height/10;
15528                       XConstrainWindowPosition(display,&windows->widget);
15529                       window_changes.x=windows->widget.x;
15530                       window_changes.y=windows->widget.y;
15531                       (void) XReconfigureWMWindow(display,windows->widget.id,
15532                         windows->widget.screen,(unsigned int) (CWX | CWY),
15533                         &window_changes);
15534                     }
15535                 if (windows->magnify.geometry == (char *) NULL)
15536                   if (windows->magnify.mapped == MagickFalse)
15537                     {
15538                       windows->magnify.x=event.xconfigure.x+
15539                         event.xconfigure.width+25;
15540                       windows->magnify.y=event.xconfigure.y;
15541                       XConstrainWindowPosition(display,&windows->magnify);
15542                       window_changes.x=windows->magnify.x;
15543                       window_changes.y=windows->magnify.y;
15544                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15545                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15546                         &window_changes);
15547                     }
15548                 if (windows->pan.geometry == (char *) NULL)
15549                   if (windows->pan.mapped == MagickFalse)
15550                     {
15551                       windows->pan.x=event.xconfigure.x+
15552                         event.xconfigure.width+25;
15553                       windows->pan.y=event.xconfigure.y+
15554                         windows->magnify.height+50;
15555                       XConstrainWindowPosition(display,&windows->pan);
15556                       window_changes.x=windows->pan.x;
15557                       window_changes.y=windows->pan.y;
15558                       (void) XReconfigureWMWindow(display,windows->pan.id,
15559                         windows->pan.screen,(unsigned int) (CWX | CWY),
15560                         &window_changes);
15561                     }
15562               }
15563             if ((event.xconfigure.width == (int) windows->image.width) &&
15564                 (event.xconfigure.height == (int) windows->image.height))
15565               break;
15566             windows->image.width=(unsigned int) event.xconfigure.width;
15567             windows->image.height=(unsigned int) event.xconfigure.height;
15568             windows->image.x=0;
15569             windows->image.y=0;
15570             if (display_image->montage != (char *) NULL)
15571               {
15572                 windows->image.x=vid_info.x;
15573                 windows->image.y=vid_info.y;
15574               }
15575             if ((windows->image.mapped != MagickFalse) &&
15576                 (windows->image.stasis != MagickFalse))
15577               {
15578                 /*
15579                   Update image window configuration.
15580                 */
15581                 windows->image.window_changes.width=event.xconfigure.width;
15582                 windows->image.window_changes.height=event.xconfigure.height;
15583                 (void) XConfigureImage(display,resource_info,windows,
15584                   display_image,exception);
15585               }
15586             /*
15587               Update pan window configuration.
15588             */
15589             if ((event.xconfigure.width < windows->image.ximage->width) ||
15590                 (event.xconfigure.height < windows->image.ximage->height))
15591               {
15592                 (void) XMapRaised(display,windows->pan.id);
15593                 XDrawPanRectangle(display,windows);
15594               }
15595             else
15596               if (windows->pan.mapped != MagickFalse)
15597                 (void) XWithdrawWindow(display,windows->pan.id,
15598                   windows->pan.screen);
15599             break;
15600           }
15601         if (event.xconfigure.window == windows->magnify.id)
15602           {
15603             unsigned int
15604               magnify;
15605
15606             /*
15607               Magnify window has a new configuration.
15608             */
15609             windows->magnify.width=(unsigned int) event.xconfigure.width;
15610             windows->magnify.height=(unsigned int) event.xconfigure.height;
15611             if (windows->magnify.mapped == MagickFalse)
15612               break;
15613             magnify=1;
15614             while ((int) magnify <= event.xconfigure.width)
15615               magnify<<=1;
15616             while ((int) magnify <= event.xconfigure.height)
15617               magnify<<=1;
15618             magnify>>=1;
15619             if (((int) magnify != event.xconfigure.width) ||
15620                 ((int) magnify != event.xconfigure.height))
15621               {
15622                 window_changes.width=(int) magnify;
15623                 window_changes.height=(int) magnify;
15624                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15625                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15626                   &window_changes);
15627                 break;
15628               }
15629             if ((windows->magnify.mapped != MagickFalse) &&
15630                 (windows->magnify.stasis != MagickFalse))
15631               {
15632                 status=XMakeImage(display,resource_info,&windows->magnify,
15633                   display_image,windows->magnify.width,windows->magnify.height,
15634                   exception);
15635                 XMakeMagnifyImage(display,windows);
15636               }
15637             break;
15638           }
15639         if ((windows->magnify.mapped != MagickFalse) &&
15640             (event.xconfigure.window == windows->pan.id))
15641           {
15642             /*
15643               Pan icon window has a new configuration.
15644             */
15645             if (event.xconfigure.send_event != 0)
15646               {
15647                 windows->pan.x=event.xconfigure.x;
15648                 windows->pan.y=event.xconfigure.y;
15649               }
15650             windows->pan.width=(unsigned int) event.xconfigure.width;
15651             windows->pan.height=(unsigned int) event.xconfigure.height;
15652             break;
15653           }
15654         if (event.xconfigure.window == windows->icon.id)
15655           {
15656             /*
15657               Icon window has a new configuration.
15658             */
15659             windows->icon.width=(unsigned int) event.xconfigure.width;
15660             windows->icon.height=(unsigned int) event.xconfigure.height;
15661             break;
15662           }
15663         break;
15664       }
15665       case DestroyNotify:
15666       {
15667         /*
15668           Group leader has exited.
15669         */
15670         if (display_image->debug != MagickFalse)
15671           (void) LogMagickEvent(X11Event,GetMagickModule(),
15672             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15673         if (event.xdestroywindow.window == windows->group_leader.id)
15674           {
15675             *state|=ExitState;
15676             break;
15677           }
15678         break;
15679       }
15680       case EnterNotify:
15681       {
15682         /*
15683           Selectively install colormap.
15684         */
15685         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15686           if (event.xcrossing.mode != NotifyUngrab)
15687             XInstallColormap(display,map_info->colormap);
15688         break;
15689       }
15690       case Expose:
15691       {
15692         if (display_image->debug != MagickFalse)
15693           (void) LogMagickEvent(X11Event,GetMagickModule(),
15694             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15695             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15696             event.xexpose.y);
15697         /*
15698           Refresh windows that are now exposed.
15699         */
15700         if ((event.xexpose.window == windows->image.id) &&
15701             (windows->image.mapped != MagickFalse))
15702           {
15703             XRefreshWindow(display,&windows->image,&event);
15704             delay=display_image->delay/MagickMax(
15705               display_image->ticks_per_second,1L);
15706             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15707             break;
15708           }
15709         if ((event.xexpose.window == windows->magnify.id) &&
15710             (windows->magnify.mapped != MagickFalse))
15711           {
15712             XMakeMagnifyImage(display,windows);
15713             break;
15714           }
15715         if (event.xexpose.window == windows->pan.id)
15716           {
15717             XDrawPanRectangle(display,windows);
15718             break;
15719           }
15720         if (event.xexpose.window == windows->icon.id)
15721           {
15722             XRefreshWindow(display,&windows->icon,&event);
15723             break;
15724           }
15725         break;
15726       }
15727       case KeyPress:
15728       {
15729         int
15730           length;
15731
15732         /*
15733           Respond to a user key press.
15734         */
15735         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15736           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15737         *(command+length)='\0';
15738         if (display_image->debug != MagickFalse)
15739           (void) LogMagickEvent(X11Event,GetMagickModule(),
15740             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15741             key_symbol,command);
15742         if (event.xkey.window == windows->image.id)
15743           {
15744             command_type=XImageWindowCommand(display,resource_info,windows,
15745               event.xkey.state,key_symbol,&display_image,exception);
15746             if (command_type != NullCommand)
15747               nexus=XMagickCommand(display,resource_info,windows,command_type,
15748                 &display_image,exception);
15749           }
15750         if (event.xkey.window == windows->magnify.id)
15751           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15752         if (event.xkey.window == windows->pan.id)
15753           {
15754             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15755               (void) XWithdrawWindow(display,windows->pan.id,
15756                 windows->pan.screen);
15757             else
15758               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15759                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15760                   "Help Viewer - Image Pan",ImagePanHelp);
15761               else
15762                 XTranslateImage(display,windows,*image,key_symbol);
15763           }
15764         delay=display_image->delay/MagickMax(
15765           display_image->ticks_per_second,1L);
15766         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15767         break;
15768       }
15769       case KeyRelease:
15770       {
15771         /*
15772           Respond to a user key release.
15773         */
15774         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15775           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15776         if (display_image->debug != MagickFalse)
15777           (void) LogMagickEvent(X11Event,GetMagickModule(),
15778             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15779         break;
15780       }
15781       case LeaveNotify:
15782       {
15783         /*
15784           Selectively uninstall colormap.
15785         */
15786         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15787           if (event.xcrossing.mode != NotifyUngrab)
15788             XUninstallColormap(display,map_info->colormap);
15789         break;
15790       }
15791       case MapNotify:
15792       {
15793         if (display_image->debug != MagickFalse)
15794           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15795             event.xmap.window);
15796         if (event.xmap.window == windows->backdrop.id)
15797           {
15798             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15799               CurrentTime);
15800             windows->backdrop.mapped=MagickTrue;
15801             break;
15802           }
15803         if (event.xmap.window == windows->image.id)
15804           {
15805             if (windows->backdrop.id != (Window) NULL)
15806               (void) XInstallColormap(display,map_info->colormap);
15807             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15808               {
15809                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15810                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15811               }
15812             if (((int) windows->image.width < windows->image.ximage->width) ||
15813                 ((int) windows->image.height < windows->image.ximage->height))
15814               (void) XMapRaised(display,windows->pan.id);
15815             windows->image.mapped=MagickTrue;
15816             break;
15817           }
15818         if (event.xmap.window == windows->magnify.id)
15819           {
15820             XMakeMagnifyImage(display,windows);
15821             windows->magnify.mapped=MagickTrue;
15822             (void) XWithdrawWindow(display,windows->info.id,
15823               windows->info.screen);
15824             break;
15825           }
15826         if (event.xmap.window == windows->pan.id)
15827           {
15828             XMakePanImage(display,resource_info,windows,display_image,
15829               exception);
15830             windows->pan.mapped=MagickTrue;
15831             break;
15832           }
15833         if (event.xmap.window == windows->info.id)
15834           {
15835             windows->info.mapped=MagickTrue;
15836             break;
15837           }
15838         if (event.xmap.window == windows->icon.id)
15839           {
15840             MagickBooleanType
15841               taint;
15842
15843             /*
15844               Create an icon image.
15845             */
15846             taint=display_image->taint;
15847             XMakeStandardColormap(display,icon_visual,icon_resources,
15848               display_image,icon_map,icon_pixel);
15849             (void) XMakeImage(display,icon_resources,&windows->icon,
15850               display_image,windows->icon.width,windows->icon.height,
15851               exception);
15852             display_image->taint=taint;
15853             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15854               windows->icon.pixmap);
15855             (void) XClearWindow(display,windows->icon.id);
15856             (void) XWithdrawWindow(display,windows->info.id,
15857               windows->info.screen);
15858             windows->icon.mapped=MagickTrue;
15859             break;
15860           }
15861         if (event.xmap.window == windows->command.id)
15862           {
15863             windows->command.mapped=MagickTrue;
15864             break;
15865           }
15866         if (event.xmap.window == windows->popup.id)
15867           {
15868             windows->popup.mapped=MagickTrue;
15869             break;
15870           }
15871         if (event.xmap.window == windows->widget.id)
15872           {
15873             windows->widget.mapped=MagickTrue;
15874             break;
15875           }
15876         break;
15877       }
15878       case MappingNotify:
15879       {
15880         (void) XRefreshKeyboardMapping(&event.xmapping);
15881         break;
15882       }
15883       case NoExpose:
15884         break;
15885       case PropertyNotify:
15886       {
15887         Atom
15888           type;
15889
15890         int
15891           format,
15892           status;
15893
15894         unsigned char
15895           *data;
15896
15897         unsigned long
15898           after,
15899           length;
15900
15901         if (display_image->debug != MagickFalse)
15902           (void) LogMagickEvent(X11Event,GetMagickModule(),
15903             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15904             event.xproperty.atom,event.xproperty.state);
15905         if (event.xproperty.atom != windows->im_remote_command)
15906           break;
15907         /*
15908           Display image named by the remote command protocol.
15909         */
15910         status=XGetWindowProperty(display,event.xproperty.window,
15911           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15912           AnyPropertyType,&type,&format,&length,&after,&data);
15913         if ((status != Success) || (length == 0))
15914           break;
15915         if (LocaleCompare((char *) data,"-quit") == 0)
15916           {
15917             XClientMessage(display,windows->image.id,windows->im_protocols,
15918               windows->im_exit,CurrentTime);
15919             (void) XFree((void *) data);
15920             break;
15921           }
15922         (void) CopyMagickString(resource_info->image_info->filename,
15923           (char *) data,MaxTextExtent);
15924         (void) XFree((void *) data);
15925         nexus=ReadImage(resource_info->image_info,&display_image->exception);
15926         CatchException(&display_image->exception);
15927         if (nexus != (Image *) NULL)
15928           *state|=NextImageState | ExitState;
15929         break;
15930       }
15931       case ReparentNotify:
15932       {
15933         if (display_image->debug != MagickFalse)
15934           (void) LogMagickEvent(X11Event,GetMagickModule(),
15935             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15936             event.xreparent.window);
15937         break;
15938       }
15939       case UnmapNotify:
15940       {
15941         if (display_image->debug != MagickFalse)
15942           (void) LogMagickEvent(X11Event,GetMagickModule(),
15943             "Unmap Notify: 0x%lx",event.xunmap.window);
15944         if (event.xunmap.window == windows->backdrop.id)
15945           {
15946             windows->backdrop.mapped=MagickFalse;
15947             break;
15948           }
15949         if (event.xunmap.window == windows->image.id)
15950           {
15951             windows->image.mapped=MagickFalse;
15952             break;
15953           }
15954         if (event.xunmap.window == windows->magnify.id)
15955           {
15956             windows->magnify.mapped=MagickFalse;
15957             break;
15958           }
15959         if (event.xunmap.window == windows->pan.id)
15960           {
15961             windows->pan.mapped=MagickFalse;
15962             break;
15963           }
15964         if (event.xunmap.window == windows->info.id)
15965           {
15966             windows->info.mapped=MagickFalse;
15967             break;
15968           }
15969         if (event.xunmap.window == windows->icon.id)
15970           {
15971             if (map_info->colormap == icon_map->colormap)
15972               XConfigureImageColormap(display,resource_info,windows,
15973                 display_image);
15974             (void) XFreeStandardColormap(display,icon_visual,icon_map,
15975               icon_pixel);
15976             windows->icon.mapped=MagickFalse;
15977             break;
15978           }
15979         if (event.xunmap.window == windows->command.id)
15980           {
15981             windows->command.mapped=MagickFalse;
15982             break;
15983           }
15984         if (event.xunmap.window == windows->popup.id)
15985           {
15986             if (windows->backdrop.id != (Window) NULL)
15987               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15988                 CurrentTime);
15989             windows->popup.mapped=MagickFalse;
15990             break;
15991           }
15992         if (event.xunmap.window == windows->widget.id)
15993           {
15994             if (windows->backdrop.id != (Window) NULL)
15995               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15996                 CurrentTime);
15997             windows->widget.mapped=MagickFalse;
15998             break;
15999           }
16000         break;
16001       }
16002       default:
16003       {
16004         if (display_image->debug != MagickFalse)
16005           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16006             event.type);
16007         break;
16008       }
16009     }
16010   } while (!(*state & ExitState));
16011   if ((*state & ExitState) == 0)
16012     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16013       &display_image,exception);
16014   else
16015     if (resource_info->confirm_edit != MagickFalse)
16016       {
16017         /*
16018           Query user if image has changed.
16019         */
16020         if ((resource_info->immutable == MagickFalse) &&
16021             (display_image->taint != MagickFalse))
16022           {
16023             int
16024               status;
16025
16026             status=XConfirmWidget(display,windows,"Your image changed.",
16027               "Do you want to save it");
16028             if (status == 0)
16029               *state&=(~ExitState);
16030             else
16031               if (status > 0)
16032                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16033                   &display_image,exception);
16034           }
16035       }
16036   if ((windows->visual_info->klass == GrayScale) ||
16037       (windows->visual_info->klass == PseudoColor) ||
16038       (windows->visual_info->klass == DirectColor))
16039     {
16040       /*
16041         Withdraw pan and Magnify window.
16042       */
16043       if (windows->info.mapped != MagickFalse)
16044         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16045       if (windows->magnify.mapped != MagickFalse)
16046         (void) XWithdrawWindow(display,windows->magnify.id,
16047           windows->magnify.screen);
16048       if (windows->command.mapped != MagickFalse)
16049         (void) XWithdrawWindow(display,windows->command.id,
16050           windows->command.screen);
16051     }
16052   if (windows->pan.mapped != MagickFalse)
16053     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16054   if (resource_info->backdrop == MagickFalse)
16055     if (windows->backdrop.mapped)
16056       {
16057         (void) XWithdrawWindow(display,windows->backdrop.id,
16058           windows->backdrop.screen);
16059         (void) XDestroyWindow(display,windows->backdrop.id);
16060         windows->backdrop.id=(Window) NULL;
16061         (void) XWithdrawWindow(display,windows->image.id,
16062           windows->image.screen);
16063         (void) XDestroyWindow(display,windows->image.id);
16064         windows->image.id=(Window) NULL;
16065       }
16066   XSetCursorState(display,windows,MagickTrue);
16067   XCheckRefreshWindows(display,windows);
16068   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16069     *state&=(~ExitState);
16070   if (*state & ExitState)
16071     {
16072       /*
16073         Free Standard Colormap.
16074       */
16075       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16076       if (resource_info->map_type == (char *) NULL)
16077         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16078       /*
16079         Free X resources.
16080       */
16081       if (resource_info->copy_image != (Image *) NULL)
16082         {
16083           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16084           resource_info->copy_image=NewImageList();
16085         }
16086       DestroyXResources();
16087     }
16088   (void) XSync(display,MagickFalse);
16089   /*
16090     Restore our progress monitor and warning handlers.
16091   */
16092   (void) SetErrorHandler(warning_handler);
16093   (void) SetWarningHandler(warning_handler);
16094   /*
16095     Change to home directory.
16096   */
16097   directory=getcwd(working_directory,MaxTextExtent);
16098   (void) directory;
16099   {
16100     int
16101       status;
16102
16103     status=chdir(resource_info->home_directory);
16104     if (status == -1)
16105       (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16106         FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16107   }
16108   *image=display_image;
16109   return(nexus);
16110 }
16111 #else
16112 \f
16113 /*
16114 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16115 %                                                                             %
16116 %                                                                             %
16117 %                                                                             %
16118 +   D i s p l a y I m a g e s                                                 %
16119 %                                                                             %
16120 %                                                                             %
16121 %                                                                             %
16122 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16123 %
16124 %  DisplayImages() displays an image sequence to any X window screen.  It
16125 %  returns a value other than 0 if successful.  Check the exception member
16126 %  of image to determine the reason for any failure.
16127 %
16128 %  The format of the DisplayImages method is:
16129 %
16130 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16131 %        Image *images,ExceptionInfo *exception)
16132 %
16133 %  A description of each parameter follows:
16134 %
16135 %    o image_info: the image info.
16136 %
16137 %    o image: the image.
16138 %
16139 %    o exception: return any errors or warnings in this structure.
16140 %
16141 */
16142 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16143   Image *image,ExceptionInfo *exception)
16144 {
16145   assert(image_info != (const ImageInfo *) NULL);
16146   assert(image_info->signature == MagickSignature);
16147   assert(image != (Image *) NULL);
16148   assert(image->signature == MagickSignature);
16149   if (image->debug != MagickFalse)
16150     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16151   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16152     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16153   return(MagickFalse);
16154 }
16155 \f
16156 /*
16157 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16158 %                                                                             %
16159 %                                                                             %
16160 %                                                                             %
16161 +   R e m o t e D i s p l a y C o m m a n d                                   %
16162 %                                                                             %
16163 %                                                                             %
16164 %                                                                             %
16165 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16166 %
16167 %  RemoteDisplayCommand() encourages a remote display program to display the
16168 %  specified image filename.
16169 %
16170 %  The format of the RemoteDisplayCommand method is:
16171 %
16172 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16173 %        const char *window,const char *filename,ExceptionInfo *exception)
16174 %
16175 %  A description of each parameter follows:
16176 %
16177 %    o image_info: the image info.
16178 %
16179 %    o window: Specifies the name or id of an X window.
16180 %
16181 %    o filename: the name of the image filename to display.
16182 %
16183 %    o exception: return any errors or warnings in this structure.
16184 %
16185 */
16186 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16187   const char *window,const char *filename,ExceptionInfo *exception)
16188 {
16189   assert(image_info != (const ImageInfo *) NULL);
16190   assert(image_info->signature == MagickSignature);
16191   assert(filename != (char *) NULL);
16192   (void) window;
16193   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16194   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16195     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16196   return(MagickFalse);
16197 }
16198 #endif