]> 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,(*image)->compose,
8865         exception);
8866       if (border_image != (Image *) NULL)
8867         {
8868           *image=DestroyImage(*image);
8869           *image=border_image;
8870         }
8871       CatchException(exception);
8872       XSetCursorState(display,windows,MagickFalse);
8873       if (windows->image.orphan != MagickFalse)
8874         break;
8875       windows->image.window_changes.width=(int) (*image)->columns;
8876       windows->image.window_changes.height=(int) (*image)->rows;
8877       XConfigureImageColormap(display,resource_info,windows,*image);
8878       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8879       break;
8880     }
8881     case AddFrameCommand:
8882     {
8883       FrameInfo
8884         frame_info;
8885
8886       Image
8887         *frame_image;
8888
8889       static char
8890         geometry[MaxTextExtent] = "6x6";
8891
8892       /*
8893         Query user for frame color and geometry.
8894       */
8895       XColorBrowserWidget(display,windows,"Select",color);
8896       if (*color == '\0')
8897         break;
8898       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8899         geometry);
8900       if (*geometry == '\0')
8901         break;
8902       /*
8903         Surround image with an ornamental border.
8904       */
8905       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8906         exception);
8907       XSetCursorState(display,windows,MagickTrue);
8908       XCheckRefreshWindows(display,windows);
8909       (void) QueryColorDatabase(color,&(*image)->matte_color,
8910         exception);
8911       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8912         exception);
8913       frame_info.width=page_geometry.width;
8914       frame_info.height=page_geometry.height;
8915       frame_info.outer_bevel=page_geometry.x;
8916       frame_info.inner_bevel=page_geometry.y;
8917       frame_info.x=(ssize_t) frame_info.width;
8918       frame_info.y=(ssize_t) frame_info.height;
8919       frame_info.width=(*image)->columns+2*frame_info.width;
8920       frame_info.height=(*image)->rows+2*frame_info.height;
8921       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8922       if (frame_image != (Image *) NULL)
8923         {
8924           *image=DestroyImage(*image);
8925           *image=frame_image;
8926         }
8927       CatchException(exception);
8928       XSetCursorState(display,windows,MagickFalse);
8929       if (windows->image.orphan != MagickFalse)
8930         break;
8931       windows->image.window_changes.width=(int) (*image)->columns;
8932       windows->image.window_changes.height=(int) (*image)->rows;
8933       XConfigureImageColormap(display,resource_info,windows,*image);
8934       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8935       break;
8936     }
8937     case CommentCommand:
8938     {
8939       const char
8940         *value;
8941
8942       FILE
8943         *file;
8944
8945       int
8946         unique_file;
8947
8948       /*
8949         Edit image comment.
8950       */
8951       unique_file=AcquireUniqueFileResource(image_info->filename);
8952       if (unique_file == -1)
8953         XNoticeWidget(display,windows,"Unable to edit image comment",
8954           image_info->filename);
8955       value=GetImageProperty(*image,"comment");
8956       if (value == (char *) NULL)
8957         unique_file=close(unique_file)-1;
8958       else
8959         {
8960           register const char
8961             *p;
8962
8963           file=fdopen(unique_file,"w");
8964           if (file == (FILE *) NULL)
8965             {
8966               XNoticeWidget(display,windows,"Unable to edit image comment",
8967                 image_info->filename);
8968               break;
8969             }
8970           for (p=value; *p != '\0'; p++)
8971             (void) fputc((int) *p,file);
8972           (void) fputc('\n',file);
8973           (void) fclose(file);
8974         }
8975       XSetCursorState(display,windows,MagickTrue);
8976       XCheckRefreshWindows(display,windows);
8977       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8978         exception);
8979       if (status == MagickFalse)
8980         XNoticeWidget(display,windows,"Unable to edit image comment",
8981           (char *) NULL);
8982       else
8983         {
8984           char
8985             *comment;
8986
8987           comment=FileToString(image_info->filename,~0UL,exception);
8988           if (comment != (char *) NULL)
8989             {
8990               (void) SetImageProperty(*image,"comment",comment);
8991               (*image)->taint=MagickTrue;
8992             }
8993         }
8994       (void) RelinquishUniqueFileResource(image_info->filename);
8995       XSetCursorState(display,windows,MagickFalse);
8996       break;
8997     }
8998     case LaunchCommand:
8999     {
9000       /*
9001         Launch program.
9002       */
9003       XSetCursorState(display,windows,MagickTrue);
9004       XCheckRefreshWindows(display,windows);
9005       (void) AcquireUniqueFilename(filename);
9006       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9007         filename);
9008       status=WriteImage(image_info,*image,exception);
9009       if (status == MagickFalse)
9010         XNoticeWidget(display,windows,"Unable to launch image editor",
9011           (char *) NULL);
9012       else
9013         {
9014           nexus=ReadImage(resource_info->image_info,exception);
9015           CatchException(exception);
9016           XClientMessage(display,windows->image.id,windows->im_protocols,
9017             windows->im_next_image,CurrentTime);
9018         }
9019       (void) RelinquishUniqueFileResource(filename);
9020       XSetCursorState(display,windows,MagickFalse);
9021       break;
9022     }
9023     case RegionofInterestCommand:
9024     {
9025       /*
9026         Apply an image processing technique to a region of interest.
9027       */
9028       (void) XROIImage(display,resource_info,windows,image,exception);
9029       break;
9030     }
9031     case InfoCommand:
9032       break;
9033     case ZoomCommand:
9034     {
9035       /*
9036         Zoom image.
9037       */
9038       if (windows->magnify.mapped != MagickFalse)
9039         (void) XRaiseWindow(display,windows->magnify.id);
9040       else
9041         {
9042           /*
9043             Make magnify image.
9044           */
9045           XSetCursorState(display,windows,MagickTrue);
9046           (void) XMapRaised(display,windows->magnify.id);
9047           XSetCursorState(display,windows,MagickFalse);
9048         }
9049       break;
9050     }
9051     case ShowPreviewCommand:
9052     {
9053       char
9054         **previews;
9055
9056       Image
9057         *preview_image;
9058
9059       static char
9060         preview_type[MaxTextExtent] = "Gamma";
9061
9062       /*
9063         Select preview type from menu.
9064       */
9065       previews=GetCommandOptions(MagickPreviewOptions);
9066       if (previews == (char **) NULL)
9067         break;
9068       XListBrowserWidget(display,windows,&windows->widget,
9069         (const char **) previews,"Preview",
9070         "Select an enhancement, effect, or F/X:",preview_type);
9071       previews=DestroyStringList(previews);
9072       if (*preview_type == '\0')
9073         break;
9074       /*
9075         Show image preview.
9076       */
9077       XSetCursorState(display,windows,MagickTrue);
9078       XCheckRefreshWindows(display,windows);
9079       image_info->preview_type=(PreviewType)
9080         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9081       image_info->group=(ssize_t) windows->image.id;
9082       (void) DeleteImageProperty(*image,"label");
9083       (void) SetImageProperty(*image,"label","Preview");
9084       (void) AcquireUniqueFilename(filename);
9085       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9086         filename);
9087       status=WriteImage(image_info,*image,exception);
9088       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9089       preview_image=ReadImage(image_info,exception);
9090       (void) RelinquishUniqueFileResource(filename);
9091       if (preview_image == (Image *) NULL)
9092         break;
9093       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9094         filename);
9095       status=WriteImage(image_info,preview_image,exception);
9096       preview_image=DestroyImage(preview_image);
9097       if (status == MagickFalse)
9098         XNoticeWidget(display,windows,"Unable to show image preview",
9099           (*image)->filename);
9100       XDelay(display,1500);
9101       XSetCursorState(display,windows,MagickFalse);
9102       break;
9103     }
9104     case ShowHistogramCommand:
9105     {
9106       Image
9107         *histogram_image;
9108
9109       /*
9110         Show image histogram.
9111       */
9112       XSetCursorState(display,windows,MagickTrue);
9113       XCheckRefreshWindows(display,windows);
9114       image_info->group=(ssize_t) windows->image.id;
9115       (void) DeleteImageProperty(*image,"label");
9116       (void) SetImageProperty(*image,"label","Histogram");
9117       (void) AcquireUniqueFilename(filename);
9118       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9119         filename);
9120       status=WriteImage(image_info,*image,exception);
9121       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9122       histogram_image=ReadImage(image_info,exception);
9123       (void) RelinquishUniqueFileResource(filename);
9124       if (histogram_image == (Image *) NULL)
9125         break;
9126       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9127         "show:%s",filename);
9128       status=WriteImage(image_info,histogram_image,exception);
9129       histogram_image=DestroyImage(histogram_image);
9130       if (status == MagickFalse)
9131         XNoticeWidget(display,windows,"Unable to show histogram",
9132           (*image)->filename);
9133       XDelay(display,1500);
9134       XSetCursorState(display,windows,MagickFalse);
9135       break;
9136     }
9137     case ShowMatteCommand:
9138     {
9139       Image
9140         *matte_image;
9141
9142       if ((*image)->matte == MagickFalse)
9143         {
9144           XNoticeWidget(display,windows,
9145             "Image does not have any matte information",(*image)->filename);
9146           break;
9147         }
9148       /*
9149         Show image matte.
9150       */
9151       XSetCursorState(display,windows,MagickTrue);
9152       XCheckRefreshWindows(display,windows);
9153       image_info->group=(ssize_t) windows->image.id;
9154       (void) DeleteImageProperty(*image,"label");
9155       (void) SetImageProperty(*image,"label","Matte");
9156       (void) AcquireUniqueFilename(filename);
9157       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9158         filename);
9159       status=WriteImage(image_info,*image,exception);
9160       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9161       matte_image=ReadImage(image_info,exception);
9162       (void) RelinquishUniqueFileResource(filename);
9163       if (matte_image == (Image *) NULL)
9164         break;
9165       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9166         filename);
9167       status=WriteImage(image_info,matte_image,exception);
9168       matte_image=DestroyImage(matte_image);
9169       if (status == MagickFalse)
9170         XNoticeWidget(display,windows,"Unable to show matte",
9171           (*image)->filename);
9172       XDelay(display,1500);
9173       XSetCursorState(display,windows,MagickFalse);
9174       break;
9175     }
9176     case BackgroundCommand:
9177     {
9178       /*
9179         Background image.
9180       */
9181       status=XBackgroundImage(display,resource_info,windows,image,exception);
9182       if (status == MagickFalse)
9183         break;
9184       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9185       if (nexus != (Image *) NULL)
9186         XClientMessage(display,windows->image.id,windows->im_protocols,
9187           windows->im_next_image,CurrentTime);
9188       break;
9189     }
9190     case SlideShowCommand:
9191     {
9192       static char
9193         delay[MaxTextExtent] = "5";
9194
9195       /*
9196         Display next image after pausing.
9197       */
9198       (void) XDialogWidget(display,windows,"Slide Show",
9199         "Pause how many 1/100ths of a second between images:",delay);
9200       if (*delay == '\0')
9201         break;
9202       resource_info->delay=StringToUnsignedLong(delay);
9203       XClientMessage(display,windows->image.id,windows->im_protocols,
9204         windows->im_next_image,CurrentTime);
9205       break;
9206     }
9207     case PreferencesCommand:
9208     {
9209       /*
9210         Set user preferences.
9211       */
9212       status=XPreferencesWidget(display,resource_info,windows);
9213       if (status == MagickFalse)
9214         break;
9215       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9216       if (nexus != (Image *) NULL)
9217         XClientMessage(display,windows->image.id,windows->im_protocols,
9218           windows->im_next_image,CurrentTime);
9219       break;
9220     }
9221     case HelpCommand:
9222     {
9223       /*
9224         User requested help.
9225       */
9226       XTextViewWidget(display,resource_info,windows,MagickFalse,
9227         "Help Viewer - Display",DisplayHelp);
9228       break;
9229     }
9230     case BrowseDocumentationCommand:
9231     {
9232       Atom
9233         mozilla_atom;
9234
9235       Window
9236         mozilla_window,
9237         root_window;
9238
9239       /*
9240         Browse the ImageMagick documentation.
9241       */
9242       root_window=XRootWindow(display,XDefaultScreen(display));
9243       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9244       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9245       if (mozilla_window != (Window) NULL)
9246         {
9247           char
9248             command[MaxTextExtent],
9249             *url;
9250
9251           /*
9252             Display documentation using Netscape remote control.
9253           */
9254           url=GetMagickHomeURL();
9255           (void) FormatLocaleString(command,MaxTextExtent,
9256             "openurl(%s,new-tab)",url);
9257           url=DestroyString(url);
9258           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9259           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9260             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9261           XSetCursorState(display,windows,MagickFalse);
9262           break;
9263         }
9264       XSetCursorState(display,windows,MagickTrue);
9265       XCheckRefreshWindows(display,windows);
9266       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9267         exception);
9268       if (status == MagickFalse)
9269         XNoticeWidget(display,windows,"Unable to browse documentation",
9270           (char *) NULL);
9271       XDelay(display,1500);
9272       XSetCursorState(display,windows,MagickFalse);
9273       break;
9274     }
9275     case VersionCommand:
9276     {
9277       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9278         GetMagickCopyright());
9279       break;
9280     }
9281     case SaveToUndoBufferCommand:
9282       break;
9283     default:
9284     {
9285       (void) XBell(display,0);
9286       break;
9287     }
9288   }
9289   image_info=DestroyImageInfo(image_info);
9290   return(nexus);
9291 }
9292 \f
9293 /*
9294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9295 %                                                                             %
9296 %                                                                             %
9297 %                                                                             %
9298 +   X M a g n i f y I m a g e                                                 %
9299 %                                                                             %
9300 %                                                                             %
9301 %                                                                             %
9302 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9303 %
9304 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9305 %  The magnified portion is displayed in a separate window.
9306 %
9307 %  The format of the XMagnifyImage method is:
9308 %
9309 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9310 %
9311 %  A description of each parameter follows:
9312 %
9313 %    o display: Specifies a connection to an X server;  returned from
9314 %      XOpenDisplay.
9315 %
9316 %    o windows: Specifies a pointer to a XWindows structure.
9317 %
9318 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9319 %      the entire image is refreshed.
9320 %
9321 */
9322 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9323 {
9324   char
9325     text[MaxTextExtent];
9326
9327   register int
9328     x,
9329     y;
9330
9331   size_t
9332     state;
9333
9334   /*
9335     Update magnified image until the mouse button is released.
9336   */
9337   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9338   state=DefaultState;
9339   x=event->xbutton.x;
9340   y=event->xbutton.y;
9341   windows->magnify.x=(int) windows->image.x+x;
9342   windows->magnify.y=(int) windows->image.y+y;
9343   do
9344   {
9345     /*
9346       Map and unmap Info widget as text cursor crosses its boundaries.
9347     */
9348     if (windows->info.mapped != MagickFalse)
9349       {
9350         if ((x < (int) (windows->info.x+windows->info.width)) &&
9351             (y < (int) (windows->info.y+windows->info.height)))
9352           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9353       }
9354     else
9355       if ((x > (int) (windows->info.x+windows->info.width)) ||
9356           (y > (int) (windows->info.y+windows->info.height)))
9357         (void) XMapWindow(display,windows->info.id);
9358     if (windows->info.mapped != MagickFalse)
9359       {
9360         /*
9361           Display pointer position.
9362         */
9363         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9364           windows->magnify.x,windows->magnify.y);
9365         XInfoWidget(display,windows,text);
9366       }
9367     /*
9368       Wait for next event.
9369     */
9370     XScreenEvent(display,windows,event);
9371     switch (event->type)
9372     {
9373       case ButtonPress:
9374         break;
9375       case ButtonRelease:
9376       {
9377         /*
9378           User has finished magnifying image.
9379         */
9380         x=event->xbutton.x;
9381         y=event->xbutton.y;
9382         state|=ExitState;
9383         break;
9384       }
9385       case Expose:
9386         break;
9387       case MotionNotify:
9388       {
9389         x=event->xmotion.x;
9390         y=event->xmotion.y;
9391         break;
9392       }
9393       default:
9394         break;
9395     }
9396     /*
9397       Check boundary conditions.
9398     */
9399     if (x < 0)
9400       x=0;
9401     else
9402       if (x >= (int) windows->image.width)
9403         x=(int) windows->image.width-1;
9404     if (y < 0)
9405       y=0;
9406     else
9407      if (y >= (int) windows->image.height)
9408        y=(int) windows->image.height-1;
9409   } while ((state & ExitState) == 0);
9410   /*
9411     Display magnified image.
9412   */
9413   XSetCursorState(display,windows,MagickFalse);
9414 }
9415 \f
9416 /*
9417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9418 %                                                                             %
9419 %                                                                             %
9420 %                                                                             %
9421 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9422 %                                                                             %
9423 %                                                                             %
9424 %                                                                             %
9425 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9426 %
9427 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9428 %  pixel as specified by the key symbol.
9429 %
9430 %  The format of the XMagnifyWindowCommand method is:
9431 %
9432 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9433 %        const MagickStatusType state,const KeySym key_symbol)
9434 %
9435 %  A description of each parameter follows:
9436 %
9437 %    o display: Specifies a connection to an X server; returned from
9438 %      XOpenDisplay.
9439 %
9440 %    o windows: Specifies a pointer to a XWindows structure.
9441 %
9442 %    o state: key mask.
9443 %
9444 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9445 %      to trim.
9446 %
9447 */
9448 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9449   const MagickStatusType state,const KeySym key_symbol)
9450 {
9451   unsigned int
9452     quantum;
9453
9454   /*
9455     User specified a magnify factor or position.
9456   */
9457   quantum=1;
9458   if ((state & Mod1Mask) != 0)
9459     quantum=10;
9460   switch ((int) key_symbol)
9461   {
9462     case QuitCommand:
9463     {
9464       (void) XWithdrawWindow(display,windows->magnify.id,
9465         windows->magnify.screen);
9466       break;
9467     }
9468     case XK_Home:
9469     case XK_KP_Home:
9470     {
9471       windows->magnify.x=(int) windows->image.width/2;
9472       windows->magnify.y=(int) windows->image.height/2;
9473       break;
9474     }
9475     case XK_Left:
9476     case XK_KP_Left:
9477     {
9478       if (windows->magnify.x > 0)
9479         windows->magnify.x-=quantum;
9480       break;
9481     }
9482     case XK_Up:
9483     case XK_KP_Up:
9484     {
9485       if (windows->magnify.y > 0)
9486         windows->magnify.y-=quantum;
9487       break;
9488     }
9489     case XK_Right:
9490     case XK_KP_Right:
9491     {
9492       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9493         windows->magnify.x+=quantum;
9494       break;
9495     }
9496     case XK_Down:
9497     case XK_KP_Down:
9498     {
9499       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9500         windows->magnify.y+=quantum;
9501       break;
9502     }
9503     case XK_0:
9504     case XK_1:
9505     case XK_2:
9506     case XK_3:
9507     case XK_4:
9508     case XK_5:
9509     case XK_6:
9510     case XK_7:
9511     case XK_8:
9512     case XK_9:
9513     {
9514       windows->magnify.data=(key_symbol-XK_0);
9515       break;
9516     }
9517     case XK_KP_0:
9518     case XK_KP_1:
9519     case XK_KP_2:
9520     case XK_KP_3:
9521     case XK_KP_4:
9522     case XK_KP_5:
9523     case XK_KP_6:
9524     case XK_KP_7:
9525     case XK_KP_8:
9526     case XK_KP_9:
9527     {
9528       windows->magnify.data=(key_symbol-XK_KP_0);
9529       break;
9530     }
9531     default:
9532       break;
9533   }
9534   XMakeMagnifyImage(display,windows);
9535 }
9536 \f
9537 /*
9538 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9539 %                                                                             %
9540 %                                                                             %
9541 %                                                                             %
9542 +   X M a k e P a n I m a g e                                                 %
9543 %                                                                             %
9544 %                                                                             %
9545 %                                                                             %
9546 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9547 %
9548 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9549 %  icon window.
9550 %
9551 %  The format of the XMakePanImage method is:
9552 %
9553 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9554 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9555 %
9556 %  A description of each parameter follows:
9557 %
9558 %    o display: Specifies a connection to an X server;  returned from
9559 %      XOpenDisplay.
9560 %
9561 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9562 %
9563 %    o windows: Specifies a pointer to a XWindows structure.
9564 %
9565 %    o image: the image.
9566 %
9567 %    o exception: return any errors or warnings in this structure.
9568 %
9569 */
9570 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9571   XWindows *windows,Image *image,ExceptionInfo *exception)
9572 {
9573   MagickStatusType
9574     status;
9575
9576   /*
9577     Create and display image for panning icon.
9578   */
9579   XSetCursorState(display,windows,MagickTrue);
9580   XCheckRefreshWindows(display,windows);
9581   windows->pan.x=(int) windows->image.x;
9582   windows->pan.y=(int) windows->image.y;
9583   status=XMakeImage(display,resource_info,&windows->pan,image,
9584     windows->pan.width,windows->pan.height,exception);
9585   if (status == MagickFalse)
9586     ThrowXWindowFatalException(ResourceLimitError,
9587      "MemoryAllocationFailed",image->filename);
9588   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9589     windows->pan.pixmap);
9590   (void) XClearWindow(display,windows->pan.id);
9591   XDrawPanRectangle(display,windows);
9592   XSetCursorState(display,windows,MagickFalse);
9593 }
9594 \f
9595 /*
9596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9597 %                                                                             %
9598 %                                                                             %
9599 %                                                                             %
9600 +   X M a t t a E d i t I m a g e                                             %
9601 %                                                                             %
9602 %                                                                             %
9603 %                                                                             %
9604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9605 %
9606 %  XMatteEditImage() allows the user to interactively change the Matte channel
9607 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9608 %  before the matte information is stored.
9609 %
9610 %  The format of the XMatteEditImage method is:
9611 %
9612 %      MagickBooleanType XMatteEditImage(Display *display,
9613 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9614 %        ExceptionInfo *exception)
9615 %
9616 %  A description of each parameter follows:
9617 %
9618 %    o display: Specifies a connection to an X server;  returned from
9619 %      XOpenDisplay.
9620 %
9621 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9622 %
9623 %    o windows: Specifies a pointer to a XWindows structure.
9624 %
9625 %    o image: the image; returned from ReadImage.
9626 %
9627 %    o exception: return any errors or warnings in this structure.
9628 %
9629 */
9630 static MagickBooleanType XMatteEditImage(Display *display,
9631   XResourceInfo *resource_info,XWindows *windows,Image **image,
9632   ExceptionInfo *exception)
9633 {
9634   static char
9635     matte[MaxTextExtent] = "0";
9636
9637   static const char
9638     *MatteEditMenu[] =
9639     {
9640       "Method",
9641       "Border Color",
9642       "Fuzz",
9643       "Matte Value",
9644       "Undo",
9645       "Help",
9646       "Dismiss",
9647       (char *) NULL
9648     };
9649
9650   static const ModeType
9651     MatteEditCommands[] =
9652     {
9653       MatteEditMethod,
9654       MatteEditBorderCommand,
9655       MatteEditFuzzCommand,
9656       MatteEditValueCommand,
9657       MatteEditUndoCommand,
9658       MatteEditHelpCommand,
9659       MatteEditDismissCommand
9660     };
9661
9662   static PaintMethod
9663     method = PointMethod;
9664
9665   static XColor
9666     border_color = { 0, 0, 0, 0, 0, 0 };
9667
9668   char
9669     command[MaxTextExtent],
9670     text[MaxTextExtent];
9671
9672   Cursor
9673     cursor;
9674
9675   int
9676     entry,
9677     id,
9678     x,
9679     x_offset,
9680     y,
9681     y_offset;
9682
9683   register int
9684     i;
9685
9686   register Quantum
9687     *q;
9688
9689   unsigned int
9690     height,
9691     width;
9692
9693   size_t
9694     state;
9695
9696   XEvent
9697     event;
9698
9699   /*
9700     Map Command widget.
9701   */
9702   (void) CloneString(&windows->command.name,"Matte Edit");
9703   windows->command.data=4;
9704   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9705   (void) XMapRaised(display,windows->command.id);
9706   XClientMessage(display,windows->image.id,windows->im_protocols,
9707     windows->im_update_widget,CurrentTime);
9708   /*
9709     Make cursor.
9710   */
9711   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9712     resource_info->background_color,resource_info->foreground_color);
9713   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9714   /*
9715     Track pointer until button 1 is pressed.
9716   */
9717   XQueryPosition(display,windows->image.id,&x,&y);
9718   (void) XSelectInput(display,windows->image.id,
9719     windows->image.attributes.event_mask | PointerMotionMask);
9720   state=DefaultState;
9721   do
9722   {
9723     if (windows->info.mapped != MagickFalse)
9724       {
9725         /*
9726           Display pointer position.
9727         */
9728         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9729           x+windows->image.x,y+windows->image.y);
9730         XInfoWidget(display,windows,text);
9731       }
9732     /*
9733       Wait for next event.
9734     */
9735     XScreenEvent(display,windows,&event);
9736     if (event.xany.window == windows->command.id)
9737       {
9738         /*
9739           Select a command from the Command widget.
9740         */
9741         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9742         if (id < 0)
9743           {
9744             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9745             continue;
9746           }
9747         switch (MatteEditCommands[id])
9748         {
9749           case MatteEditMethod:
9750           {
9751             char
9752               **methods;
9753
9754             /*
9755               Select a method from the pop-up menu.
9756             */
9757             methods=GetCommandOptions(MagickMethodOptions);
9758             if (methods == (char **) NULL)
9759               break;
9760             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9761               (const char **) methods,command);
9762             if (entry >= 0)
9763               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9764                 MagickFalse,methods[entry]);
9765             methods=DestroyStringList(methods);
9766             break;
9767           }
9768           case MatteEditBorderCommand:
9769           {
9770             const char
9771               *ColorMenu[MaxNumberPens];
9772
9773             int
9774               pen_number;
9775
9776             /*
9777               Initialize menu selections.
9778             */
9779             for (i=0; i < (int) (MaxNumberPens-2); i++)
9780               ColorMenu[i]=resource_info->pen_colors[i];
9781             ColorMenu[MaxNumberPens-2]="Browser...";
9782             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9783             /*
9784               Select a pen color from the pop-up menu.
9785             */
9786             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9787               (const char **) ColorMenu,command);
9788             if (pen_number < 0)
9789               break;
9790             if (pen_number == (MaxNumberPens-2))
9791               {
9792                 static char
9793                   color_name[MaxTextExtent] = "gray";
9794
9795                 /*
9796                   Select a pen color from a dialog.
9797                 */
9798                 resource_info->pen_colors[pen_number]=color_name;
9799                 XColorBrowserWidget(display,windows,"Select",color_name);
9800                 if (*color_name == '\0')
9801                   break;
9802               }
9803             /*
9804               Set border color.
9805             */
9806             (void) XParseColor(display,windows->map_info->colormap,
9807               resource_info->pen_colors[pen_number],&border_color);
9808             break;
9809           }
9810           case MatteEditFuzzCommand:
9811           {
9812             static char
9813               fuzz[MaxTextExtent];
9814
9815             static const char
9816               *FuzzMenu[] =
9817               {
9818                 "0%",
9819                 "2%",
9820                 "5%",
9821                 "10%",
9822                 "15%",
9823                 "Dialog...",
9824                 (char *) NULL,
9825               };
9826
9827             /*
9828               Select a command from the pop-up menu.
9829             */
9830             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9831               command);
9832             if (entry < 0)
9833               break;
9834             if (entry != 5)
9835               {
9836                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
9837                   QuantumRange+1.0);
9838                 break;
9839               }
9840             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9841             (void) XDialogWidget(display,windows,"Ok",
9842               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9843             if (*fuzz == '\0')
9844               break;
9845             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9846             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9847             break;
9848           }
9849           case MatteEditValueCommand:
9850           {
9851             static char
9852               message[MaxTextExtent];
9853
9854             static const char
9855               *MatteMenu[] =
9856               {
9857                 "Opaque",
9858                 "Transparent",
9859                 "Dialog...",
9860                 (char *) NULL,
9861               };
9862
9863             /*
9864               Select a command from the pop-up menu.
9865             */
9866             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9867               command);
9868             if (entry < 0)
9869               break;
9870             if (entry != 2)
9871               {
9872                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9873                   OpaqueAlpha);
9874                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9875                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9876                     (Quantum) TransparentAlpha);
9877                 break;
9878               }
9879             (void) FormatLocaleString(message,MaxTextExtent,
9880               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9881               QuantumRange);
9882             (void) XDialogWidget(display,windows,"Matte",message,matte);
9883             if (*matte == '\0')
9884               break;
9885             break;
9886           }
9887           case MatteEditUndoCommand:
9888           {
9889             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9890               image,exception);
9891             break;
9892           }
9893           case MatteEditHelpCommand:
9894           {
9895             XTextViewWidget(display,resource_info,windows,MagickFalse,
9896               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9897             break;
9898           }
9899           case MatteEditDismissCommand:
9900           {
9901             /*
9902               Prematurely exit.
9903             */
9904             state|=EscapeState;
9905             state|=ExitState;
9906             break;
9907           }
9908           default:
9909             break;
9910         }
9911         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9912         continue;
9913       }
9914     switch (event.type)
9915     {
9916       case ButtonPress:
9917       {
9918         if (event.xbutton.button != Button1)
9919           break;
9920         if ((event.xbutton.window != windows->image.id) &&
9921             (event.xbutton.window != windows->magnify.id))
9922           break;
9923         /*
9924           Update matte data.
9925         */
9926         x=event.xbutton.x;
9927         y=event.xbutton.y;
9928         (void) XMagickCommand(display,resource_info,windows,
9929           SaveToUndoBufferCommand,image,exception);
9930         state|=UpdateConfigurationState;
9931         break;
9932       }
9933       case ButtonRelease:
9934       {
9935         if (event.xbutton.button != Button1)
9936           break;
9937         if ((event.xbutton.window != windows->image.id) &&
9938             (event.xbutton.window != windows->magnify.id))
9939           break;
9940         /*
9941           Update colormap information.
9942         */
9943         x=event.xbutton.x;
9944         y=event.xbutton.y;
9945         XConfigureImageColormap(display,resource_info,windows,*image);
9946         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9947         XInfoWidget(display,windows,text);
9948         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9949         state&=(~UpdateConfigurationState);
9950         break;
9951       }
9952       case Expose:
9953         break;
9954       case KeyPress:
9955       {
9956         char
9957           command[MaxTextExtent];
9958
9959         KeySym
9960           key_symbol;
9961
9962         if (event.xkey.window == windows->magnify.id)
9963           {
9964             Window
9965               window;
9966
9967             window=windows->magnify.id;
9968             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9969           }
9970         if (event.xkey.window != windows->image.id)
9971           break;
9972         /*
9973           Respond to a user key press.
9974         */
9975         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9976           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9977         switch ((int) key_symbol)
9978         {
9979           case XK_Escape:
9980           case XK_F20:
9981           {
9982             /*
9983               Prematurely exit.
9984             */
9985             state|=ExitState;
9986             break;
9987           }
9988           case XK_F1:
9989           case XK_Help:
9990           {
9991             XTextViewWidget(display,resource_info,windows,MagickFalse,
9992               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9993             break;
9994           }
9995           default:
9996           {
9997             (void) XBell(display,0);
9998             break;
9999           }
10000         }
10001         break;
10002       }
10003       case MotionNotify:
10004       {
10005         /*
10006           Map and unmap Info widget as cursor crosses its boundaries.
10007         */
10008         x=event.xmotion.x;
10009         y=event.xmotion.y;
10010         if (windows->info.mapped != MagickFalse)
10011           {
10012             if ((x < (int) (windows->info.x+windows->info.width)) &&
10013                 (y < (int) (windows->info.y+windows->info.height)))
10014               (void) XWithdrawWindow(display,windows->info.id,
10015                 windows->info.screen);
10016           }
10017         else
10018           if ((x > (int) (windows->info.x+windows->info.width)) ||
10019               (y > (int) (windows->info.y+windows->info.height)))
10020             (void) XMapWindow(display,windows->info.id);
10021         break;
10022       }
10023       default:
10024         break;
10025     }
10026     if (event.xany.window == windows->magnify.id)
10027       {
10028         x=windows->magnify.x-windows->image.x;
10029         y=windows->magnify.y-windows->image.y;
10030       }
10031     x_offset=x;
10032     y_offset=y;
10033     if ((state & UpdateConfigurationState) != 0)
10034       {
10035         CacheView
10036           *image_view;
10037
10038         int
10039           x,
10040           y;
10041
10042         /*
10043           Matte edit is relative to image configuration.
10044         */
10045         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10046           MagickTrue);
10047         XPutPixel(windows->image.ximage,x_offset,y_offset,
10048           windows->pixel_info->background_color.pixel);
10049         width=(unsigned int) (*image)->columns;
10050         height=(unsigned int) (*image)->rows;
10051         x=0;
10052         y=0;
10053         if (windows->image.crop_geometry != (char *) NULL)
10054           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10055             &height);
10056         x_offset=(int) (width*(windows->image.x+x_offset)/
10057           windows->image.ximage->width+x);
10058         y_offset=(int) (height*(windows->image.y+y_offset)/
10059           windows->image.ximage->height+y);
10060         if ((x_offset < 0) || (y_offset < 0))
10061           continue;
10062         if ((x_offset >= (int) (*image)->columns) ||
10063             (y_offset >= (int) (*image)->rows))
10064           continue;
10065         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10066           return(MagickFalse);
10067         (*image)->matte=MagickTrue;
10068         image_view=AcquireCacheView(*image);
10069         switch (method)
10070         {
10071           case PointMethod:
10072           default:
10073           {
10074             /*
10075               Update matte information using point algorithm.
10076             */
10077             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10078               (ssize_t) y_offset,1,1,exception);
10079             if (q == (Quantum *) NULL)
10080               break;
10081             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10082             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10083             break;
10084           }
10085           case ReplaceMethod:
10086           {
10087             PixelPacket
10088               pixel,
10089               target;
10090
10091             /*
10092               Update matte information using replace algorithm.
10093             */
10094             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10095               (ssize_t) y_offset,&target,exception);
10096             for (y=0; y < (int) (*image)->rows; y++)
10097             {
10098               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10099                 (*image)->columns,1,exception);
10100               if (q == (Quantum *) NULL)
10101                 break;
10102               for (x=0; x < (int) (*image)->columns; x++)
10103               {
10104                 GetPixelPacket(*image,q,&pixel);
10105                 if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
10106                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10107                 q+=GetPixelChannels(*image);
10108               }
10109               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10110                 break;
10111             }
10112             break;
10113           }
10114           case FloodfillMethod:
10115           case FillToBorderMethod:
10116           {
10117             ChannelType
10118               channel_mask;
10119
10120             DrawInfo
10121               *draw_info;
10122
10123             PixelInfo
10124               target;
10125
10126             /*
10127               Update matte information using floodfill algorithm.
10128             */
10129             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10130               (ssize_t) y_offset,&target,exception);
10131             if (method == FillToBorderMethod)
10132               {
10133                 target.red=(MagickRealType) ScaleShortToQuantum(
10134                   border_color.red);
10135                 target.green=(MagickRealType) ScaleShortToQuantum(
10136                   border_color.green);
10137                 target.blue=(MagickRealType) ScaleShortToQuantum(
10138                   border_color.blue);
10139               }
10140             draw_info=CloneDrawInfo(resource_info->image_info,
10141               (DrawInfo *) NULL);
10142             draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10143               (char **) NULL));
10144             channel_mask=SetPixelChannelMask(*image,AlphaChannel); 
10145             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10146               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10147               MagickFalse : MagickTrue,exception);
10148             (void) SetPixelChannelMap(*image,channel_mask);
10149             draw_info=DestroyDrawInfo(draw_info);
10150             break;
10151           }
10152           case ResetMethod:
10153           {
10154             /*
10155               Update matte information using reset algorithm.
10156             */
10157             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10158               return(MagickFalse);
10159             for (y=0; y < (int) (*image)->rows; y++)
10160             {
10161               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10162                 (*image)->columns,1,exception);
10163               if (q == (Quantum *) NULL)
10164                 break;
10165               for (x=0; x < (int) (*image)->columns; x++)
10166               {
10167                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10168                 q+=GetPixelChannels(*image);
10169               }
10170               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10171                 break;
10172             }
10173             if (StringToLong(matte) == (long) OpaqueAlpha)
10174               (*image)->matte=MagickFalse;
10175             break;
10176           }
10177         }
10178         image_view=DestroyCacheView(image_view);
10179         state&=(~UpdateConfigurationState);
10180       }
10181   } while ((state & ExitState) == 0);
10182   (void) XSelectInput(display,windows->image.id,
10183     windows->image.attributes.event_mask);
10184   XSetCursorState(display,windows,MagickFalse);
10185   (void) XFreeCursor(display,cursor);
10186   return(MagickTrue);
10187 }
10188 \f
10189 /*
10190 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10191 %                                                                             %
10192 %                                                                             %
10193 %                                                                             %
10194 +   X O p e n I m a g e                                                       %
10195 %                                                                             %
10196 %                                                                             %
10197 %                                                                             %
10198 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10199 %
10200 %  XOpenImage() loads an image from a file.
10201 %
10202 %  The format of the XOpenImage method is:
10203 %
10204 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10205 %       XWindows *windows,const unsigned int command)
10206 %
10207 %  A description of each parameter follows:
10208 %
10209 %    o display: Specifies a connection to an X server; returned from
10210 %      XOpenDisplay.
10211 %
10212 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10213 %
10214 %    o windows: Specifies a pointer to a XWindows structure.
10215 %
10216 %    o command: A value other than zero indicates that the file is selected
10217 %      from the command line argument list.
10218 %
10219 */
10220 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10221   XWindows *windows,const MagickBooleanType command)
10222 {
10223   const MagickInfo
10224     *magick_info;
10225
10226   ExceptionInfo
10227     *exception;
10228
10229   Image
10230     *nexus;
10231
10232   ImageInfo
10233     *image_info;
10234
10235   static char
10236     filename[MaxTextExtent] = "\0";
10237
10238   /*
10239     Request file name from user.
10240   */
10241   if (command == MagickFalse)
10242     XFileBrowserWidget(display,windows,"Open",filename);
10243   else
10244     {
10245       char
10246         **filelist,
10247         **files;
10248
10249       int
10250         count,
10251         status;
10252
10253       register int
10254         i,
10255         j;
10256
10257       /*
10258         Select next image from the command line.
10259       */
10260       status=XGetCommand(display,windows->image.id,&files,&count);
10261       if (status == 0)
10262         {
10263           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10264           return((Image *) NULL);
10265         }
10266       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10267       if (filelist == (char **) NULL)
10268         {
10269           ThrowXWindowFatalException(ResourceLimitError,
10270             "MemoryAllocationFailed","...");
10271           (void) XFreeStringList(files);
10272           return((Image *) NULL);
10273         }
10274       j=0;
10275       for (i=1; i < count; i++)
10276         if (*files[i] != '-')
10277           filelist[j++]=files[i];
10278       filelist[j]=(char *) NULL;
10279       XListBrowserWidget(display,windows,&windows->widget,
10280         (const char **) filelist,"Load","Select Image to Load:",filename);
10281       filelist=(char **) RelinquishMagickMemory(filelist);
10282       (void) XFreeStringList(files);
10283     }
10284   if (*filename == '\0')
10285     return((Image *) NULL);
10286   image_info=CloneImageInfo(resource_info->image_info);
10287   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10288     (void *) NULL);
10289   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10290   exception=AcquireExceptionInfo();
10291   (void) SetImageInfo(image_info,0,exception);
10292   if (LocaleCompare(image_info->magick,"X") == 0)
10293     {
10294       char
10295         seconds[MaxTextExtent];
10296
10297       /*
10298         User may want to delay the X server screen grab.
10299       */
10300       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10301       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10302         seconds);
10303       if (*seconds == '\0')
10304         return((Image *) NULL);
10305       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10306     }
10307   magick_info=GetMagickInfo(image_info->magick,exception);
10308   if ((magick_info != (const MagickInfo *) NULL) &&
10309       (magick_info->raw != MagickFalse))
10310     {
10311       char
10312         geometry[MaxTextExtent];
10313
10314       /*
10315         Request image size from the user.
10316       */
10317       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10318       if (image_info->size != (char *) NULL)
10319         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10320       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10321         geometry);
10322       (void) CloneString(&image_info->size,geometry);
10323     }
10324   /*
10325     Load the image.
10326   */
10327   XSetCursorState(display,windows,MagickTrue);
10328   XCheckRefreshWindows(display,windows);
10329   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10330   nexus=ReadImage(image_info,exception);
10331   CatchException(exception);
10332   XSetCursorState(display,windows,MagickFalse);
10333   if (nexus != (Image *) NULL)
10334     XClientMessage(display,windows->image.id,windows->im_protocols,
10335       windows->im_next_image,CurrentTime);
10336   else
10337     {
10338       char
10339         *text,
10340         **textlist;
10341
10342       /*
10343         Unknown image format.
10344       */
10345       text=FileToString(filename,~0,exception);
10346       if (text == (char *) NULL)
10347         return((Image *) NULL);
10348       textlist=StringToList(text);
10349       if (textlist != (char **) NULL)
10350         {
10351           char
10352             title[MaxTextExtent];
10353
10354           register int
10355             i;
10356
10357           (void) FormatLocaleString(title,MaxTextExtent,
10358             "Unknown format: %s",filename);
10359           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10360             (const char **) textlist);
10361           for (i=0; textlist[i] != (char *) NULL; i++)
10362             textlist[i]=DestroyString(textlist[i]);
10363           textlist=(char **) RelinquishMagickMemory(textlist);
10364         }
10365       text=DestroyString(text);
10366     }
10367   exception=DestroyExceptionInfo(exception);
10368   image_info=DestroyImageInfo(image_info);
10369   return(nexus);
10370 }
10371 \f
10372 /*
10373 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10374 %                                                                             %
10375 %                                                                             %
10376 %                                                                             %
10377 +   X P a n I m a g e                                                         %
10378 %                                                                             %
10379 %                                                                             %
10380 %                                                                             %
10381 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10382 %
10383 %  XPanImage() pans the image until the mouse button is released.
10384 %
10385 %  The format of the XPanImage method is:
10386 %
10387 %      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10388 %
10389 %  A description of each parameter follows:
10390 %
10391 %    o display: Specifies a connection to an X server;  returned from
10392 %      XOpenDisplay.
10393 %
10394 %    o windows: Specifies a pointer to a XWindows structure.
10395 %
10396 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10397 %      the entire image is refreshed.
10398 %
10399 */
10400 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10401 {
10402   char
10403     text[MaxTextExtent];
10404
10405   Cursor
10406     cursor;
10407
10408   MagickRealType
10409     x_factor,
10410     y_factor;
10411
10412   RectangleInfo
10413     pan_info;
10414
10415   size_t
10416     state;
10417
10418   /*
10419     Define cursor.
10420   */
10421   if ((windows->image.ximage->width > (int) windows->image.width) &&
10422       (windows->image.ximage->height > (int) windows->image.height))
10423     cursor=XCreateFontCursor(display,XC_fleur);
10424   else
10425     if (windows->image.ximage->width > (int) windows->image.width)
10426       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10427     else
10428       if (windows->image.ximage->height > (int) windows->image.height)
10429         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10430       else
10431         cursor=XCreateFontCursor(display,XC_arrow);
10432   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10433   /*
10434     Pan image as pointer moves until the mouse button is released.
10435   */
10436   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10437   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10438   pan_info.width=windows->pan.width*windows->image.width/
10439     windows->image.ximage->width;
10440   pan_info.height=windows->pan.height*windows->image.height/
10441     windows->image.ximage->height;
10442   pan_info.x=0;
10443   pan_info.y=0;
10444   state=UpdateConfigurationState;
10445   do
10446   {
10447     switch (event->type)
10448     {
10449       case ButtonPress:
10450       {
10451         /*
10452           User choose an initial pan location.
10453         */
10454         pan_info.x=(ssize_t) event->xbutton.x;
10455         pan_info.y=(ssize_t) event->xbutton.y;
10456         state|=UpdateConfigurationState;
10457         break;
10458       }
10459       case ButtonRelease:
10460       {
10461         /*
10462           User has finished panning the image.
10463         */
10464         pan_info.x=(ssize_t) event->xbutton.x;
10465         pan_info.y=(ssize_t) event->xbutton.y;
10466         state|=UpdateConfigurationState | ExitState;
10467         break;
10468       }
10469       case MotionNotify:
10470       {
10471         pan_info.x=(ssize_t) event->xmotion.x;
10472         pan_info.y=(ssize_t) event->xmotion.y;
10473         state|=UpdateConfigurationState;
10474       }
10475       default:
10476         break;
10477     }
10478     if ((state & UpdateConfigurationState) != 0)
10479       {
10480         /*
10481           Check boundary conditions.
10482         */
10483         if (pan_info.x < (ssize_t) (pan_info.width/2))
10484           pan_info.x=0;
10485         else
10486           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10487         if (pan_info.x < 0)
10488           pan_info.x=0;
10489         else
10490           if ((int) (pan_info.x+windows->image.width) >
10491               windows->image.ximage->width)
10492             pan_info.x=(ssize_t)
10493               (windows->image.ximage->width-windows->image.width);
10494         if (pan_info.y < (ssize_t) (pan_info.height/2))
10495           pan_info.y=0;
10496         else
10497           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10498         if (pan_info.y < 0)
10499           pan_info.y=0;
10500         else
10501           if ((int) (pan_info.y+windows->image.height) >
10502               windows->image.ximage->height)
10503             pan_info.y=(ssize_t)
10504               (windows->image.ximage->height-windows->image.height);
10505         if ((windows->image.x != (int) pan_info.x) ||
10506             (windows->image.y != (int) pan_info.y))
10507           {
10508             /*
10509               Display image pan offset.
10510             */
10511             windows->image.x=(int) pan_info.x;
10512             windows->image.y=(int) pan_info.y;
10513             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10514               windows->image.width,windows->image.height,windows->image.x,
10515               windows->image.y);
10516             XInfoWidget(display,windows,text);
10517             /*
10518               Refresh Image window.
10519             */
10520             XDrawPanRectangle(display,windows);
10521             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10522           }
10523         state&=(~UpdateConfigurationState);
10524       }
10525     /*
10526       Wait for next event.
10527     */
10528     if ((state & ExitState) == 0)
10529       XScreenEvent(display,windows,event);
10530   } while ((state & ExitState) == 0);
10531   /*
10532     Restore cursor.
10533   */
10534   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10535   (void) XFreeCursor(display,cursor);
10536   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10537 }
10538 \f
10539 /*
10540 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10541 %                                                                             %
10542 %                                                                             %
10543 %                                                                             %
10544 +   X P a s t e I m a g e                                                     %
10545 %                                                                             %
10546 %                                                                             %
10547 %                                                                             %
10548 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10549 %
10550 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10551 %  window image at a location the user chooses with the pointer.
10552 %
10553 %  The format of the XPasteImage method is:
10554 %
10555 %      MagickBooleanType XPasteImage(Display *display,
10556 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10557 %        ExceptionInfo *exception)
10558 %
10559 %  A description of each parameter follows:
10560 %
10561 %    o display: Specifies a connection to an X server;  returned from
10562 %      XOpenDisplay.
10563 %
10564 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10565 %
10566 %    o windows: Specifies a pointer to a XWindows structure.
10567 %
10568 %    o image: the image; returned from ReadImage.
10569 %
10570 %    o exception: return any errors or warnings in this structure.
10571 %
10572 */
10573 static MagickBooleanType XPasteImage(Display *display,
10574   XResourceInfo *resource_info,XWindows *windows,Image *image,
10575   ExceptionInfo *exception)
10576 {
10577   static const char
10578     *PasteMenu[] =
10579     {
10580       "Operator",
10581       "Help",
10582       "Dismiss",
10583       (char *) NULL
10584     };
10585
10586   static const ModeType
10587     PasteCommands[] =
10588     {
10589       PasteOperatorsCommand,
10590       PasteHelpCommand,
10591       PasteDismissCommand
10592     };
10593
10594   static CompositeOperator
10595     compose = CopyCompositeOp;
10596
10597   char
10598     text[MaxTextExtent];
10599
10600   Cursor
10601     cursor;
10602
10603   Image
10604     *paste_image;
10605
10606   int
10607     entry,
10608     id,
10609     x,
10610     y;
10611
10612   MagickRealType
10613     scale_factor;
10614
10615   RectangleInfo
10616     highlight_info,
10617     paste_info;
10618
10619   unsigned int
10620     height,
10621     width;
10622
10623   size_t
10624     state;
10625
10626   XEvent
10627     event;
10628
10629   /*
10630     Copy image.
10631   */
10632   if (resource_info->copy_image == (Image *) NULL)
10633     return(MagickFalse);
10634   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10635   /*
10636     Map Command widget.
10637   */
10638   (void) CloneString(&windows->command.name,"Paste");
10639   windows->command.data=1;
10640   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10641   (void) XMapRaised(display,windows->command.id);
10642   XClientMessage(display,windows->image.id,windows->im_protocols,
10643     windows->im_update_widget,CurrentTime);
10644   /*
10645     Track pointer until button 1 is pressed.
10646   */
10647   XSetCursorState(display,windows,MagickFalse);
10648   XQueryPosition(display,windows->image.id,&x,&y);
10649   (void) XSelectInput(display,windows->image.id,
10650     windows->image.attributes.event_mask | PointerMotionMask);
10651   paste_info.x=(ssize_t) windows->image.x+x;
10652   paste_info.y=(ssize_t) windows->image.y+y;
10653   paste_info.width=0;
10654   paste_info.height=0;
10655   cursor=XCreateFontCursor(display,XC_ul_angle);
10656   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10657   state=DefaultState;
10658   do
10659   {
10660     if (windows->info.mapped != MagickFalse)
10661       {
10662         /*
10663           Display pointer position.
10664         */
10665         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10666           (long) paste_info.x,(long) paste_info.y);
10667         XInfoWidget(display,windows,text);
10668       }
10669     highlight_info=paste_info;
10670     highlight_info.x=paste_info.x-windows->image.x;
10671     highlight_info.y=paste_info.y-windows->image.y;
10672     XHighlightRectangle(display,windows->image.id,
10673       windows->image.highlight_context,&highlight_info);
10674     /*
10675       Wait for next event.
10676     */
10677     XScreenEvent(display,windows,&event);
10678     XHighlightRectangle(display,windows->image.id,
10679       windows->image.highlight_context,&highlight_info);
10680     if (event.xany.window == windows->command.id)
10681       {
10682         /*
10683           Select a command from the Command widget.
10684         */
10685         id=XCommandWidget(display,windows,PasteMenu,&event);
10686         if (id < 0)
10687           continue;
10688         switch (PasteCommands[id])
10689         {
10690           case PasteOperatorsCommand:
10691           {
10692             char
10693               command[MaxTextExtent],
10694               **operators;
10695
10696             /*
10697               Select a command from the pop-up menu.
10698             */
10699             operators=GetCommandOptions(MagickComposeOptions);
10700             if (operators == (char **) NULL)
10701               break;
10702             entry=XMenuWidget(display,windows,PasteMenu[id],
10703               (const char **) operators,command);
10704             if (entry >= 0)
10705               compose=(CompositeOperator) ParseCommandOption(
10706                 MagickComposeOptions,MagickFalse,operators[entry]);
10707             operators=DestroyStringList(operators);
10708             break;
10709           }
10710           case PasteHelpCommand:
10711           {
10712             XTextViewWidget(display,resource_info,windows,MagickFalse,
10713               "Help Viewer - Image Composite",ImagePasteHelp);
10714             break;
10715           }
10716           case PasteDismissCommand:
10717           {
10718             /*
10719               Prematurely exit.
10720             */
10721             state|=EscapeState;
10722             state|=ExitState;
10723             break;
10724           }
10725           default:
10726             break;
10727         }
10728         continue;
10729       }
10730     switch (event.type)
10731     {
10732       case ButtonPress:
10733       {
10734         if (image->debug != MagickFalse)
10735           (void) LogMagickEvent(X11Event,GetMagickModule(),
10736             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10737             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10738         if (event.xbutton.button != Button1)
10739           break;
10740         if (event.xbutton.window != windows->image.id)
10741           break;
10742         /*
10743           Paste rectangle is relative to image configuration.
10744         */
10745         width=(unsigned int) image->columns;
10746         height=(unsigned int) image->rows;
10747         x=0;
10748         y=0;
10749         if (windows->image.crop_geometry != (char *) NULL)
10750           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10751             &width,&height);
10752         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10753         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10754         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10755         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10756         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10757         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10758         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10759         break;
10760       }
10761       case ButtonRelease:
10762       {
10763         if (image->debug != MagickFalse)
10764           (void) LogMagickEvent(X11Event,GetMagickModule(),
10765             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10766             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10767         if (event.xbutton.button != Button1)
10768           break;
10769         if (event.xbutton.window != windows->image.id)
10770           break;
10771         if ((paste_info.width != 0) && (paste_info.height != 0))
10772           {
10773             /*
10774               User has selected the location of the paste image.
10775             */
10776             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10777             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10778             state|=ExitState;
10779           }
10780         break;
10781       }
10782       case Expose:
10783         break;
10784       case KeyPress:
10785       {
10786         char
10787           command[MaxTextExtent];
10788
10789         KeySym
10790           key_symbol;
10791
10792         int
10793           length;
10794
10795         if (event.xkey.window != windows->image.id)
10796           break;
10797         /*
10798           Respond to a user key press.
10799         */
10800         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10801           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10802         *(command+length)='\0';
10803         if (image->debug != MagickFalse)
10804           (void) LogMagickEvent(X11Event,GetMagickModule(),
10805             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10806         switch ((int) key_symbol)
10807         {
10808           case XK_Escape:
10809           case XK_F20:
10810           {
10811             /*
10812               Prematurely exit.
10813             */
10814             paste_image=DestroyImage(paste_image);
10815             state|=EscapeState;
10816             state|=ExitState;
10817             break;
10818           }
10819           case XK_F1:
10820           case XK_Help:
10821           {
10822             (void) XSetFunction(display,windows->image.highlight_context,
10823               GXcopy);
10824             XTextViewWidget(display,resource_info,windows,MagickFalse,
10825               "Help Viewer - Image Composite",ImagePasteHelp);
10826             (void) XSetFunction(display,windows->image.highlight_context,
10827               GXinvert);
10828             break;
10829           }
10830           default:
10831           {
10832             (void) XBell(display,0);
10833             break;
10834           }
10835         }
10836         break;
10837       }
10838       case MotionNotify:
10839       {
10840         /*
10841           Map and unmap Info widget as text cursor crosses its boundaries.
10842         */
10843         x=event.xmotion.x;
10844         y=event.xmotion.y;
10845         if (windows->info.mapped != MagickFalse)
10846           {
10847             if ((x < (int) (windows->info.x+windows->info.width)) &&
10848                 (y < (int) (windows->info.y+windows->info.height)))
10849               (void) XWithdrawWindow(display,windows->info.id,
10850                 windows->info.screen);
10851           }
10852         else
10853           if ((x > (int) (windows->info.x+windows->info.width)) ||
10854               (y > (int) (windows->info.y+windows->info.height)))
10855             (void) XMapWindow(display,windows->info.id);
10856         paste_info.x=(ssize_t) windows->image.x+x;
10857         paste_info.y=(ssize_t) windows->image.y+y;
10858         break;
10859       }
10860       default:
10861       {
10862         if (image->debug != MagickFalse)
10863           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10864             event.type);
10865         break;
10866       }
10867     }
10868   } while ((state & ExitState) == 0);
10869   (void) XSelectInput(display,windows->image.id,
10870     windows->image.attributes.event_mask);
10871   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10872   XSetCursorState(display,windows,MagickFalse);
10873   (void) XFreeCursor(display,cursor);
10874   if ((state & EscapeState) != 0)
10875     return(MagickTrue);
10876   /*
10877     Image pasting is relative to image configuration.
10878   */
10879   XSetCursorState(display,windows,MagickTrue);
10880   XCheckRefreshWindows(display,windows);
10881   width=(unsigned int) image->columns;
10882   height=(unsigned int) image->rows;
10883   x=0;
10884   y=0;
10885   if (windows->image.crop_geometry != (char *) NULL)
10886     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10887   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10888   paste_info.x+=x;
10889   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10890   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10891   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10892   paste_info.y+=y;
10893   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10894   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10895   /*
10896     Paste image with X Image window.
10897   */
10898   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10899   paste_image=DestroyImage(paste_image);
10900   XSetCursorState(display,windows,MagickFalse);
10901   /*
10902     Update image colormap.
10903   */
10904   XConfigureImageColormap(display,resource_info,windows,image);
10905   (void) XConfigureImage(display,resource_info,windows,image,exception);
10906   return(MagickTrue);
10907 }
10908 \f
10909 /*
10910 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10911 %                                                                             %
10912 %                                                                             %
10913 %                                                                             %
10914 +   X P r i n t I m a g e                                                     %
10915 %                                                                             %
10916 %                                                                             %
10917 %                                                                             %
10918 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10919 %
10920 %  XPrintImage() prints an image to a Postscript printer.
10921 %
10922 %  The format of the XPrintImage method is:
10923 %
10924 %      MagickBooleanType XPrintImage(Display *display,
10925 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10926 %        ExceptionInfo *exception)
10927 %
10928 %  A description of each parameter follows:
10929 %
10930 %    o display: Specifies a connection to an X server; returned from
10931 %      XOpenDisplay.
10932 %
10933 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10934 %
10935 %    o windows: Specifies a pointer to a XWindows structure.
10936 %
10937 %    o image: the image.
10938 %
10939 %    o exception: return any errors or warnings in this structure.
10940 %
10941 */
10942 static MagickBooleanType XPrintImage(Display *display,
10943   XResourceInfo *resource_info,XWindows *windows,Image *image,
10944   ExceptionInfo *exception)
10945 {
10946   char
10947     filename[MaxTextExtent],
10948     geometry[MaxTextExtent];
10949
10950   Image
10951     *print_image;
10952
10953   ImageInfo
10954     *image_info;
10955
10956   MagickStatusType
10957     status;
10958
10959   /*
10960     Request Postscript page geometry from user.
10961   */
10962   image_info=CloneImageInfo(resource_info->image_info);
10963   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10964   if (image_info->page != (char *) NULL)
10965     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10966   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10967     "Select Postscript Page Geometry:",geometry);
10968   if (*geometry == '\0')
10969     return(MagickTrue);
10970   image_info->page=GetPageGeometry(geometry);
10971   /*
10972     Apply image transforms.
10973   */
10974   XSetCursorState(display,windows,MagickTrue);
10975   XCheckRefreshWindows(display,windows);
10976   print_image=CloneImage(image,0,0,MagickTrue,exception);
10977   if (print_image == (Image *) NULL)
10978     return(MagickFalse);
10979   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10980     windows->image.ximage->width,windows->image.ximage->height);
10981   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10982   /*
10983     Print image.
10984   */
10985   (void) AcquireUniqueFilename(filename);
10986   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10987     filename);
10988   status=WriteImage(image_info,print_image,exception);
10989   (void) RelinquishUniqueFileResource(filename);
10990   print_image=DestroyImage(print_image);
10991   image_info=DestroyImageInfo(image_info);
10992   XSetCursorState(display,windows,MagickFalse);
10993   return(status != 0 ? MagickTrue : MagickFalse);
10994 }
10995 \f
10996 /*
10997 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10998 %                                                                             %
10999 %                                                                             %
11000 %                                                                             %
11001 +   X R O I I m a g e                                                         %
11002 %                                                                             %
11003 %                                                                             %
11004 %                                                                             %
11005 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11006 %
11007 %  XROIImage() applies an image processing technique to a region of interest.
11008 %
11009 %  The format of the XROIImage method is:
11010 %
11011 %      MagickBooleanType XROIImage(Display *display,
11012 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11013 %        ExceptionInfo *exception)
11014 %
11015 %  A description of each parameter follows:
11016 %
11017 %    o display: Specifies a connection to an X server; returned from
11018 %      XOpenDisplay.
11019 %
11020 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11021 %
11022 %    o windows: Specifies a pointer to a XWindows structure.
11023 %
11024 %    o image: the image; returned from ReadImage.
11025 %
11026 %    o exception: return any errors or warnings in this structure.
11027 %
11028 */
11029 static MagickBooleanType XROIImage(Display *display,
11030   XResourceInfo *resource_info,XWindows *windows,Image **image,
11031   ExceptionInfo *exception)
11032 {
11033 #define ApplyMenus  7
11034
11035   static const char
11036     *ROIMenu[] =
11037     {
11038       "Help",
11039       "Dismiss",
11040       (char *) NULL
11041     },
11042     *ApplyMenu[] =
11043     {
11044       "File",
11045       "Edit",
11046       "Transform",
11047       "Enhance",
11048       "Effects",
11049       "F/X",
11050       "Miscellany",
11051       "Help",
11052       "Dismiss",
11053       (char *) NULL
11054     },
11055     *FileMenu[] =
11056     {
11057       "Save...",
11058       "Print...",
11059       (char *) NULL
11060     },
11061     *EditMenu[] =
11062     {
11063       "Undo",
11064       "Redo",
11065       (char *) NULL
11066     },
11067     *TransformMenu[] =
11068     {
11069       "Flop",
11070       "Flip",
11071       "Rotate Right",
11072       "Rotate Left",
11073       (char *) NULL
11074     },
11075     *EnhanceMenu[] =
11076     {
11077       "Hue...",
11078       "Saturation...",
11079       "Brightness...",
11080       "Gamma...",
11081       "Spiff",
11082       "Dull",
11083       "Contrast Stretch...",
11084       "Sigmoidal Contrast...",
11085       "Normalize",
11086       "Equalize",
11087       "Negate",
11088       "Grayscale",
11089       "Map...",
11090       "Quantize...",
11091       (char *) NULL
11092     },
11093     *EffectsMenu[] =
11094     {
11095       "Despeckle",
11096       "Emboss",
11097       "Reduce Noise",
11098       "Add Noise",
11099       "Sharpen...",
11100       "Blur...",
11101       "Threshold...",
11102       "Edge Detect...",
11103       "Spread...",
11104       "Shade...",
11105       "Raise...",
11106       "Segment...",
11107       (char *) NULL
11108     },
11109     *FXMenu[] =
11110     {
11111       "Solarize...",
11112       "Sepia Tone...",
11113       "Swirl...",
11114       "Implode...",
11115       "Vignette...",
11116       "Wave...",
11117       "Oil Paint...",
11118       "Charcoal Draw...",
11119       (char *) NULL
11120     },
11121     *MiscellanyMenu[] =
11122     {
11123       "Image Info",
11124       "Zoom Image",
11125       "Show Preview...",
11126       "Show Histogram",
11127       "Show Matte",
11128       (char *) NULL
11129     };
11130
11131   static const char
11132     **Menus[ApplyMenus] =
11133     {
11134       FileMenu,
11135       EditMenu,
11136       TransformMenu,
11137       EnhanceMenu,
11138       EffectsMenu,
11139       FXMenu,
11140       MiscellanyMenu
11141     };
11142
11143   static const CommandType
11144     ApplyCommands[] =
11145     {
11146       NullCommand,
11147       NullCommand,
11148       NullCommand,
11149       NullCommand,
11150       NullCommand,
11151       NullCommand,
11152       NullCommand,
11153       HelpCommand,
11154       QuitCommand
11155     },
11156     FileCommands[] =
11157     {
11158       SaveCommand,
11159       PrintCommand
11160     },
11161     EditCommands[] =
11162     {
11163       UndoCommand,
11164       RedoCommand
11165     },
11166     TransformCommands[] =
11167     {
11168       FlopCommand,
11169       FlipCommand,
11170       RotateRightCommand,
11171       RotateLeftCommand
11172     },
11173     EnhanceCommands[] =
11174     {
11175       HueCommand,
11176       SaturationCommand,
11177       BrightnessCommand,
11178       GammaCommand,
11179       SpiffCommand,
11180       DullCommand,
11181       ContrastStretchCommand,
11182       SigmoidalContrastCommand,
11183       NormalizeCommand,
11184       EqualizeCommand,
11185       NegateCommand,
11186       GrayscaleCommand,
11187       MapCommand,
11188       QuantizeCommand
11189     },
11190     EffectsCommands[] =
11191     {
11192       DespeckleCommand,
11193       EmbossCommand,
11194       ReduceNoiseCommand,
11195       AddNoiseCommand,
11196       SharpenCommand,
11197       BlurCommand,
11198       EdgeDetectCommand,
11199       SpreadCommand,
11200       ShadeCommand,
11201       RaiseCommand,
11202       SegmentCommand
11203     },
11204     FXCommands[] =
11205     {
11206       SolarizeCommand,
11207       SepiaToneCommand,
11208       SwirlCommand,
11209       ImplodeCommand,
11210       VignetteCommand,
11211       WaveCommand,
11212       OilPaintCommand,
11213       CharcoalDrawCommand
11214     },
11215     MiscellanyCommands[] =
11216     {
11217       InfoCommand,
11218       ZoomCommand,
11219       ShowPreviewCommand,
11220       ShowHistogramCommand,
11221       ShowMatteCommand
11222     },
11223     ROICommands[] =
11224     {
11225       ROIHelpCommand,
11226       ROIDismissCommand
11227     };
11228
11229   static const CommandType
11230     *Commands[ApplyMenus] =
11231     {
11232       FileCommands,
11233       EditCommands,
11234       TransformCommands,
11235       EnhanceCommands,
11236       EffectsCommands,
11237       FXCommands,
11238       MiscellanyCommands
11239     };
11240
11241   char
11242     command[MaxTextExtent],
11243     text[MaxTextExtent];
11244
11245   CommandType
11246     command_type;
11247
11248   Cursor
11249     cursor;
11250
11251   Image
11252     *roi_image;
11253
11254   int
11255     entry,
11256     id,
11257     x,
11258     y;
11259
11260   MagickRealType
11261     scale_factor;
11262
11263   MagickProgressMonitor
11264     progress_monitor;
11265
11266   RectangleInfo
11267     crop_info,
11268     highlight_info,
11269     roi_info;
11270
11271   unsigned int
11272     height,
11273     width;
11274
11275   size_t
11276     state;
11277
11278   XEvent
11279     event;
11280
11281   /*
11282     Map Command widget.
11283   */
11284   (void) CloneString(&windows->command.name,"ROI");
11285   windows->command.data=0;
11286   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11287   (void) XMapRaised(display,windows->command.id);
11288   XClientMessage(display,windows->image.id,windows->im_protocols,
11289     windows->im_update_widget,CurrentTime);
11290   /*
11291     Track pointer until button 1 is pressed.
11292   */
11293   XQueryPosition(display,windows->image.id,&x,&y);
11294   (void) XSelectInput(display,windows->image.id,
11295     windows->image.attributes.event_mask | PointerMotionMask);
11296   roi_info.x=(ssize_t) windows->image.x+x;
11297   roi_info.y=(ssize_t) windows->image.y+y;
11298   roi_info.width=0;
11299   roi_info.height=0;
11300   cursor=XCreateFontCursor(display,XC_fleur);
11301   state=DefaultState;
11302   do
11303   {
11304     if (windows->info.mapped != MagickFalse)
11305       {
11306         /*
11307           Display pointer position.
11308         */
11309         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11310           (long) roi_info.x,(long) roi_info.y);
11311         XInfoWidget(display,windows,text);
11312       }
11313     /*
11314       Wait for next event.
11315     */
11316     XScreenEvent(display,windows,&event);
11317     if (event.xany.window == windows->command.id)
11318       {
11319         /*
11320           Select a command from the Command widget.
11321         */
11322         id=XCommandWidget(display,windows,ROIMenu,&event);
11323         if (id < 0)
11324           continue;
11325         switch (ROICommands[id])
11326         {
11327           case ROIHelpCommand:
11328           {
11329             XTextViewWidget(display,resource_info,windows,MagickFalse,
11330               "Help Viewer - Region of Interest",ImageROIHelp);
11331             break;
11332           }
11333           case ROIDismissCommand:
11334           {
11335             /*
11336               Prematurely exit.
11337             */
11338             state|=EscapeState;
11339             state|=ExitState;
11340             break;
11341           }
11342           default:
11343             break;
11344         }
11345         continue;
11346       }
11347     switch (event.type)
11348     {
11349       case ButtonPress:
11350       {
11351         if (event.xbutton.button != Button1)
11352           break;
11353         if (event.xbutton.window != windows->image.id)
11354           break;
11355         /*
11356           Note first corner of region of interest rectangle-- exit loop.
11357         */
11358         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11359         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11360         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11361         state|=ExitState;
11362         break;
11363       }
11364       case ButtonRelease:
11365         break;
11366       case Expose:
11367         break;
11368       case KeyPress:
11369       {
11370         KeySym
11371           key_symbol;
11372
11373         if (event.xkey.window != windows->image.id)
11374           break;
11375         /*
11376           Respond to a user key press.
11377         */
11378         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11379           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11380         switch ((int) key_symbol)
11381         {
11382           case XK_Escape:
11383           case XK_F20:
11384           {
11385             /*
11386               Prematurely exit.
11387             */
11388             state|=EscapeState;
11389             state|=ExitState;
11390             break;
11391           }
11392           case XK_F1:
11393           case XK_Help:
11394           {
11395             XTextViewWidget(display,resource_info,windows,MagickFalse,
11396               "Help Viewer - Region of Interest",ImageROIHelp);
11397             break;
11398           }
11399           default:
11400           {
11401             (void) XBell(display,0);
11402             break;
11403           }
11404         }
11405         break;
11406       }
11407       case MotionNotify:
11408       {
11409         /*
11410           Map and unmap Info widget as text cursor crosses its boundaries.
11411         */
11412         x=event.xmotion.x;
11413         y=event.xmotion.y;
11414         if (windows->info.mapped != MagickFalse)
11415           {
11416             if ((x < (int) (windows->info.x+windows->info.width)) &&
11417                 (y < (int) (windows->info.y+windows->info.height)))
11418               (void) XWithdrawWindow(display,windows->info.id,
11419                 windows->info.screen);
11420           }
11421         else
11422           if ((x > (int) (windows->info.x+windows->info.width)) ||
11423               (y > (int) (windows->info.y+windows->info.height)))
11424             (void) XMapWindow(display,windows->info.id);
11425         roi_info.x=(ssize_t) windows->image.x+x;
11426         roi_info.y=(ssize_t) windows->image.y+y;
11427         break;
11428       }
11429       default:
11430         break;
11431     }
11432   } while ((state & ExitState) == 0);
11433   (void) XSelectInput(display,windows->image.id,
11434     windows->image.attributes.event_mask);
11435   if ((state & EscapeState) != 0)
11436     {
11437       /*
11438         User want to exit without region of interest.
11439       */
11440       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11441       (void) XFreeCursor(display,cursor);
11442       return(MagickTrue);
11443     }
11444   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11445   do
11446   {
11447     /*
11448       Size rectangle as pointer moves until the mouse button is released.
11449     */
11450     x=(int) roi_info.x;
11451     y=(int) roi_info.y;
11452     roi_info.width=0;
11453     roi_info.height=0;
11454     state=DefaultState;
11455     do
11456     {
11457       highlight_info=roi_info;
11458       highlight_info.x=roi_info.x-windows->image.x;
11459       highlight_info.y=roi_info.y-windows->image.y;
11460       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11461         {
11462           /*
11463             Display info and draw region of interest rectangle.
11464           */
11465           if (windows->info.mapped == MagickFalse)
11466             (void) XMapWindow(display,windows->info.id);
11467           (void) FormatLocaleString(text,MaxTextExtent,
11468             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11469             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11470           XInfoWidget(display,windows,text);
11471           XHighlightRectangle(display,windows->image.id,
11472             windows->image.highlight_context,&highlight_info);
11473         }
11474       else
11475         if (windows->info.mapped != MagickFalse)
11476           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11477       /*
11478         Wait for next event.
11479       */
11480       XScreenEvent(display,windows,&event);
11481       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11482         XHighlightRectangle(display,windows->image.id,
11483           windows->image.highlight_context,&highlight_info);
11484       switch (event.type)
11485       {
11486         case ButtonPress:
11487         {
11488           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11489           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11490           break;
11491         }
11492         case ButtonRelease:
11493         {
11494           /*
11495             User has committed to region of interest rectangle.
11496           */
11497           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11498           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11499           XSetCursorState(display,windows,MagickFalse);
11500           state|=ExitState;
11501           if (LocaleCompare(windows->command.name,"Apply") == 0)
11502             break;
11503           (void) CloneString(&windows->command.name,"Apply");
11504           windows->command.data=ApplyMenus;
11505           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11506           break;
11507         }
11508         case Expose:
11509           break;
11510         case MotionNotify:
11511         {
11512           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11513           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11514         }
11515         default:
11516           break;
11517       }
11518       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11519           ((state & ExitState) != 0))
11520         {
11521           /*
11522             Check boundary conditions.
11523           */
11524           if (roi_info.x < 0)
11525             roi_info.x=0;
11526           else
11527             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11528               roi_info.x=(ssize_t) windows->image.ximage->width;
11529           if ((int) roi_info.x < x)
11530             roi_info.width=(unsigned int) (x-roi_info.x);
11531           else
11532             {
11533               roi_info.width=(unsigned int) (roi_info.x-x);
11534               roi_info.x=(ssize_t) x;
11535             }
11536           if (roi_info.y < 0)
11537             roi_info.y=0;
11538           else
11539             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11540               roi_info.y=(ssize_t) windows->image.ximage->height;
11541           if ((int) roi_info.y < y)
11542             roi_info.height=(unsigned int) (y-roi_info.y);
11543           else
11544             {
11545               roi_info.height=(unsigned int) (roi_info.y-y);
11546               roi_info.y=(ssize_t) y;
11547             }
11548         }
11549     } while ((state & ExitState) == 0);
11550     /*
11551       Wait for user to grab a corner of the rectangle or press return.
11552     */
11553     state=DefaultState;
11554     command_type=NullCommand;
11555     (void) XMapWindow(display,windows->info.id);
11556     do
11557     {
11558       if (windows->info.mapped != MagickFalse)
11559         {
11560           /*
11561             Display pointer position.
11562           */
11563           (void) FormatLocaleString(text,MaxTextExtent,
11564             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11565             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11566           XInfoWidget(display,windows,text);
11567         }
11568       highlight_info=roi_info;
11569       highlight_info.x=roi_info.x-windows->image.x;
11570       highlight_info.y=roi_info.y-windows->image.y;
11571       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11572         {
11573           state|=EscapeState;
11574           state|=ExitState;
11575           break;
11576         }
11577       if ((state & UpdateRegionState) != 0)
11578         {
11579           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11580           switch (command_type)
11581           {
11582             case UndoCommand:
11583             case RedoCommand:
11584             {
11585               (void) XMagickCommand(display,resource_info,windows,command_type,
11586                 image,exception);
11587               break;
11588             }
11589             default:
11590             {
11591               /*
11592                 Region of interest is relative to image configuration.
11593               */
11594               progress_monitor=SetImageProgressMonitor(*image,
11595                 (MagickProgressMonitor) NULL,(*image)->client_data);
11596               crop_info=roi_info;
11597               width=(unsigned int) (*image)->columns;
11598               height=(unsigned int) (*image)->rows;
11599               x=0;
11600               y=0;
11601               if (windows->image.crop_geometry != (char *) NULL)
11602                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11603                   &width,&height);
11604               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11605               crop_info.x+=x;
11606               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11607               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11608               scale_factor=(MagickRealType)
11609                 height/windows->image.ximage->height;
11610               crop_info.y+=y;
11611               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11612               crop_info.height=(unsigned int)
11613                 (scale_factor*crop_info.height+0.5);
11614               roi_image=CropImage(*image,&crop_info,exception);
11615               (void) SetImageProgressMonitor(*image,progress_monitor,
11616                 (*image)->client_data);
11617               if (roi_image == (Image *) NULL)
11618                 continue;
11619               /*
11620                 Apply image processing technique to the region of interest.
11621               */
11622               windows->image.orphan=MagickTrue;
11623               (void) XMagickCommand(display,resource_info,windows,command_type,
11624                 &roi_image,exception);
11625               progress_monitor=SetImageProgressMonitor(*image,
11626                 (MagickProgressMonitor) NULL,(*image)->client_data);
11627               (void) XMagickCommand(display,resource_info,windows,
11628                 SaveToUndoBufferCommand,image,exception);
11629               windows->image.orphan=MagickFalse;
11630               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11631                 crop_info.x,crop_info.y);
11632               roi_image=DestroyImage(roi_image);
11633               (void) SetImageProgressMonitor(*image,progress_monitor,
11634                 (*image)->client_data);
11635               break;
11636             }
11637           }
11638           if (command_type != InfoCommand)
11639             {
11640               XConfigureImageColormap(display,resource_info,windows,*image);
11641               (void) XConfigureImage(display,resource_info,windows,*image,exception);
11642             }
11643           XCheckRefreshWindows(display,windows);
11644           XInfoWidget(display,windows,text);
11645           (void) XSetFunction(display,windows->image.highlight_context,
11646             GXinvert);
11647           state&=(~UpdateRegionState);
11648         }
11649       XHighlightRectangle(display,windows->image.id,
11650         windows->image.highlight_context,&highlight_info);
11651       XScreenEvent(display,windows,&event);
11652       if (event.xany.window == windows->command.id)
11653         {
11654           /*
11655             Select a command from the Command widget.
11656           */
11657           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11658           command_type=NullCommand;
11659           id=XCommandWidget(display,windows,ApplyMenu,&event);
11660           if (id >= 0)
11661             {
11662               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11663               command_type=ApplyCommands[id];
11664               if (id < ApplyMenus)
11665                 {
11666                   /*
11667                     Select a command from a pop-up menu.
11668                   */
11669                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11670                     (const char **) Menus[id],command);
11671                   if (entry >= 0)
11672                     {
11673                       (void) CopyMagickString(command,Menus[id][entry],
11674                         MaxTextExtent);
11675                       command_type=Commands[id][entry];
11676                     }
11677                 }
11678             }
11679           (void) XSetFunction(display,windows->image.highlight_context,
11680             GXinvert);
11681           XHighlightRectangle(display,windows->image.id,
11682             windows->image.highlight_context,&highlight_info);
11683           if (command_type == HelpCommand)
11684             {
11685               (void) XSetFunction(display,windows->image.highlight_context,
11686                 GXcopy);
11687               XTextViewWidget(display,resource_info,windows,MagickFalse,
11688                 "Help Viewer - Region of Interest",ImageROIHelp);
11689               (void) XSetFunction(display,windows->image.highlight_context,
11690                 GXinvert);
11691               continue;
11692             }
11693           if (command_type == QuitCommand)
11694             {
11695               /*
11696                 exit.
11697               */
11698               state|=EscapeState;
11699               state|=ExitState;
11700               continue;
11701             }
11702           if (command_type != NullCommand)
11703             state|=UpdateRegionState;
11704           continue;
11705         }
11706       XHighlightRectangle(display,windows->image.id,
11707         windows->image.highlight_context,&highlight_info);
11708       switch (event.type)
11709       {
11710         case ButtonPress:
11711         {
11712           x=windows->image.x;
11713           y=windows->image.y;
11714           if (event.xbutton.button != Button1)
11715             break;
11716           if (event.xbutton.window != windows->image.id)
11717             break;
11718           x=windows->image.x+event.xbutton.x;
11719           y=windows->image.y+event.xbutton.y;
11720           if ((x < (int) (roi_info.x+RoiDelta)) &&
11721               (x > (int) (roi_info.x-RoiDelta)) &&
11722               (y < (int) (roi_info.y+RoiDelta)) &&
11723               (y > (int) (roi_info.y-RoiDelta)))
11724             {
11725               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11726               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11727               state|=UpdateConfigurationState;
11728               break;
11729             }
11730           if ((x < (int) (roi_info.x+RoiDelta)) &&
11731               (x > (int) (roi_info.x-RoiDelta)) &&
11732               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11733               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11734             {
11735               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11736               state|=UpdateConfigurationState;
11737               break;
11738             }
11739           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11740               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11741               (y < (int) (roi_info.y+RoiDelta)) &&
11742               (y > (int) (roi_info.y-RoiDelta)))
11743             {
11744               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11745               state|=UpdateConfigurationState;
11746               break;
11747             }
11748           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11749               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11750               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11751               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11752             {
11753               state|=UpdateConfigurationState;
11754               break;
11755             }
11756         }
11757         case ButtonRelease:
11758         {
11759           if (event.xbutton.window == windows->pan.id)
11760             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11761                 (highlight_info.y != crop_info.y-windows->image.y))
11762               XHighlightRectangle(display,windows->image.id,
11763                 windows->image.highlight_context,&highlight_info);
11764           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11765             event.xbutton.time);
11766           break;
11767         }
11768         case Expose:
11769         {
11770           if (event.xexpose.window == windows->image.id)
11771             if (event.xexpose.count == 0)
11772               {
11773                 event.xexpose.x=(int) highlight_info.x;
11774                 event.xexpose.y=(int) highlight_info.y;
11775                 event.xexpose.width=(int) highlight_info.width;
11776                 event.xexpose.height=(int) highlight_info.height;
11777                 XRefreshWindow(display,&windows->image,&event);
11778               }
11779           if (event.xexpose.window == windows->info.id)
11780             if (event.xexpose.count == 0)
11781               XInfoWidget(display,windows,text);
11782           break;
11783         }
11784         case KeyPress:
11785         {
11786           KeySym
11787             key_symbol;
11788
11789           if (event.xkey.window != windows->image.id)
11790             break;
11791           /*
11792             Respond to a user key press.
11793           */
11794           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11795             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11796           switch ((int) key_symbol)
11797           {
11798             case XK_Shift_L:
11799             case XK_Shift_R:
11800               break;
11801             case XK_Escape:
11802             case XK_F20:
11803               state|=EscapeState;
11804             case XK_Return:
11805             {
11806               state|=ExitState;
11807               break;
11808             }
11809             case XK_Home:
11810             case XK_KP_Home:
11811             {
11812               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11813               roi_info.y=(ssize_t) (windows->image.height/2L-
11814                 roi_info.height/2L);
11815               break;
11816             }
11817             case XK_Left:
11818             case XK_KP_Left:
11819             {
11820               roi_info.x--;
11821               break;
11822             }
11823             case XK_Up:
11824             case XK_KP_Up:
11825             case XK_Next:
11826             {
11827               roi_info.y--;
11828               break;
11829             }
11830             case XK_Right:
11831             case XK_KP_Right:
11832             {
11833               roi_info.x++;
11834               break;
11835             }
11836             case XK_Prior:
11837             case XK_Down:
11838             case XK_KP_Down:
11839             {
11840               roi_info.y++;
11841               break;
11842             }
11843             case XK_F1:
11844             case XK_Help:
11845             {
11846               (void) XSetFunction(display,windows->image.highlight_context,
11847                 GXcopy);
11848               XTextViewWidget(display,resource_info,windows,MagickFalse,
11849                 "Help Viewer - Region of Interest",ImageROIHelp);
11850               (void) XSetFunction(display,windows->image.highlight_context,
11851                 GXinvert);
11852               break;
11853             }
11854             default:
11855             {
11856               command_type=XImageWindowCommand(display,resource_info,windows,
11857                 event.xkey.state,key_symbol,image,exception);
11858               if (command_type != NullCommand)
11859                 state|=UpdateRegionState;
11860               break;
11861             }
11862           }
11863           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11864             event.xkey.time);
11865           break;
11866         }
11867         case KeyRelease:
11868           break;
11869         case MotionNotify:
11870         {
11871           if (event.xbutton.window != windows->image.id)
11872             break;
11873           /*
11874             Map and unmap Info widget as text cursor crosses its boundaries.
11875           */
11876           x=event.xmotion.x;
11877           y=event.xmotion.y;
11878           if (windows->info.mapped != MagickFalse)
11879             {
11880               if ((x < (int) (windows->info.x+windows->info.width)) &&
11881                   (y < (int) (windows->info.y+windows->info.height)))
11882                 (void) XWithdrawWindow(display,windows->info.id,
11883                   windows->info.screen);
11884             }
11885           else
11886             if ((x > (int) (windows->info.x+windows->info.width)) ||
11887                 (y > (int) (windows->info.y+windows->info.height)))
11888               (void) XMapWindow(display,windows->info.id);
11889           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11890           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11891           break;
11892         }
11893         case SelectionRequest:
11894         {
11895           XSelectionEvent
11896             notify;
11897
11898           XSelectionRequestEvent
11899             *request;
11900
11901           /*
11902             Set primary selection.
11903           */
11904           (void) FormatLocaleString(text,MaxTextExtent,
11905             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11906             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11907           request=(&(event.xselectionrequest));
11908           (void) XChangeProperty(request->display,request->requestor,
11909             request->property,request->target,8,PropModeReplace,
11910             (unsigned char *) text,(int) strlen(text));
11911           notify.type=SelectionNotify;
11912           notify.display=request->display;
11913           notify.requestor=request->requestor;
11914           notify.selection=request->selection;
11915           notify.target=request->target;
11916           notify.time=request->time;
11917           if (request->property == None)
11918             notify.property=request->target;
11919           else
11920             notify.property=request->property;
11921           (void) XSendEvent(request->display,request->requestor,False,0,
11922             (XEvent *) &notify);
11923         }
11924         default:
11925           break;
11926       }
11927       if ((state & UpdateConfigurationState) != 0)
11928         {
11929           (void) XPutBackEvent(display,&event);
11930           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11931           break;
11932         }
11933     } while ((state & ExitState) == 0);
11934   } while ((state & ExitState) == 0);
11935   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11936   XSetCursorState(display,windows,MagickFalse);
11937   if ((state & EscapeState) != 0)
11938     return(MagickTrue);
11939   return(MagickTrue);
11940 }
11941 \f
11942 /*
11943 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11944 %                                                                             %
11945 %                                                                             %
11946 %                                                                             %
11947 +   X R o t a t e I m a g e                                                   %
11948 %                                                                             %
11949 %                                                                             %
11950 %                                                                             %
11951 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11952 %
11953 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11954 %  rotation angle is computed from the slope of a line drawn by the user.
11955 %
11956 %  The format of the XRotateImage method is:
11957 %
11958 %      MagickBooleanType XRotateImage(Display *display,
11959 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11960 %        Image **image,ExceptionInfo *exception)
11961 %
11962 %  A description of each parameter follows:
11963 %
11964 %    o display: Specifies a connection to an X server; returned from
11965 %      XOpenDisplay.
11966 %
11967 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11968 %
11969 %    o windows: Specifies a pointer to a XWindows structure.
11970 %
11971 %    o degrees: Specifies the number of degrees to rotate the image.
11972 %
11973 %    o image: the image.
11974 %
11975 %    o exception: return any errors or warnings in this structure.
11976 %
11977 */
11978 static MagickBooleanType XRotateImage(Display *display,
11979   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
11980   ExceptionInfo *exception)
11981 {
11982   static const char
11983     *RotateMenu[] =
11984     {
11985       "Pixel Color",
11986       "Direction",
11987       "Help",
11988       "Dismiss",
11989       (char *) NULL
11990     };
11991
11992   static ModeType
11993     direction = HorizontalRotateCommand;
11994
11995   static const ModeType
11996     DirectionCommands[] =
11997     {
11998       HorizontalRotateCommand,
11999       VerticalRotateCommand
12000     },
12001     RotateCommands[] =
12002     {
12003       RotateColorCommand,
12004       RotateDirectionCommand,
12005       RotateHelpCommand,
12006       RotateDismissCommand
12007     };
12008
12009   static unsigned int
12010     pen_id = 0;
12011
12012   char
12013     command[MaxTextExtent],
12014     text[MaxTextExtent];
12015
12016   Image
12017     *rotate_image;
12018
12019   int
12020     id,
12021     x,
12022     y;
12023
12024   MagickRealType
12025     normalized_degrees;
12026
12027   register int
12028     i;
12029
12030   unsigned int
12031     height,
12032     rotations,
12033     width;
12034
12035   if (degrees == 0.0)
12036     {
12037       unsigned int
12038         distance;
12039
12040       size_t
12041         state;
12042
12043       XEvent
12044         event;
12045
12046       XSegment
12047         rotate_info;
12048
12049       /*
12050         Map Command widget.
12051       */
12052       (void) CloneString(&windows->command.name,"Rotate");
12053       windows->command.data=2;
12054       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12055       (void) XMapRaised(display,windows->command.id);
12056       XClientMessage(display,windows->image.id,windows->im_protocols,
12057         windows->im_update_widget,CurrentTime);
12058       /*
12059         Wait for first button press.
12060       */
12061       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12062       XQueryPosition(display,windows->image.id,&x,&y);
12063       rotate_info.x1=x;
12064       rotate_info.y1=y;
12065       rotate_info.x2=x;
12066       rotate_info.y2=y;
12067       state=DefaultState;
12068       do
12069       {
12070         XHighlightLine(display,windows->image.id,
12071           windows->image.highlight_context,&rotate_info);
12072         /*
12073           Wait for next event.
12074         */
12075         XScreenEvent(display,windows,&event);
12076         XHighlightLine(display,windows->image.id,
12077           windows->image.highlight_context,&rotate_info);
12078         if (event.xany.window == windows->command.id)
12079           {
12080             /*
12081               Select a command from the Command widget.
12082             */
12083             id=XCommandWidget(display,windows,RotateMenu,&event);
12084             if (id < 0)
12085               continue;
12086             (void) XSetFunction(display,windows->image.highlight_context,
12087               GXcopy);
12088             switch (RotateCommands[id])
12089             {
12090               case RotateColorCommand:
12091               {
12092                 const char
12093                   *ColorMenu[MaxNumberPens];
12094
12095                 int
12096                   pen_number;
12097
12098                 XColor
12099                   color;
12100
12101                 /*
12102                   Initialize menu selections.
12103                 */
12104                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12105                   ColorMenu[i]=resource_info->pen_colors[i];
12106                 ColorMenu[MaxNumberPens-2]="Browser...";
12107                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12108                 /*
12109                   Select a pen color from the pop-up menu.
12110                 */
12111                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12112                   (const char **) ColorMenu,command);
12113                 if (pen_number < 0)
12114                   break;
12115                 if (pen_number == (MaxNumberPens-2))
12116                   {
12117                     static char
12118                       color_name[MaxTextExtent] = "gray";
12119
12120                     /*
12121                       Select a pen color from a dialog.
12122                     */
12123                     resource_info->pen_colors[pen_number]=color_name;
12124                     XColorBrowserWidget(display,windows,"Select",color_name);
12125                     if (*color_name == '\0')
12126                       break;
12127                   }
12128                 /*
12129                   Set pen color.
12130                 */
12131                 (void) XParseColor(display,windows->map_info->colormap,
12132                   resource_info->pen_colors[pen_number],&color);
12133                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12134                   (unsigned int) MaxColors,&color);
12135                 windows->pixel_info->pen_colors[pen_number]=color;
12136                 pen_id=(unsigned int) pen_number;
12137                 break;
12138               }
12139               case RotateDirectionCommand:
12140               {
12141                 static const char
12142                   *Directions[] =
12143                   {
12144                     "horizontal",
12145                     "vertical",
12146                     (char *) NULL,
12147                   };
12148
12149                 /*
12150                   Select a command from the pop-up menu.
12151                 */
12152                 id=XMenuWidget(display,windows,RotateMenu[id],
12153                   Directions,command);
12154                 if (id >= 0)
12155                   direction=DirectionCommands[id];
12156                 break;
12157               }
12158               case RotateHelpCommand:
12159               {
12160                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12161                   "Help Viewer - Image Rotation",ImageRotateHelp);
12162                 break;
12163               }
12164               case RotateDismissCommand:
12165               {
12166                 /*
12167                   Prematurely exit.
12168                 */
12169                 state|=EscapeState;
12170                 state|=ExitState;
12171                 break;
12172               }
12173               default:
12174                 break;
12175             }
12176             (void) XSetFunction(display,windows->image.highlight_context,
12177               GXinvert);
12178             continue;
12179           }
12180         switch (event.type)
12181         {
12182           case ButtonPress:
12183           {
12184             if (event.xbutton.button != Button1)
12185               break;
12186             if (event.xbutton.window != windows->image.id)
12187               break;
12188             /*
12189               exit loop.
12190             */
12191             (void) XSetFunction(display,windows->image.highlight_context,
12192               GXcopy);
12193             rotate_info.x1=event.xbutton.x;
12194             rotate_info.y1=event.xbutton.y;
12195             state|=ExitState;
12196             break;
12197           }
12198           case ButtonRelease:
12199             break;
12200           case Expose:
12201             break;
12202           case KeyPress:
12203           {
12204             char
12205               command[MaxTextExtent];
12206
12207             KeySym
12208               key_symbol;
12209
12210             if (event.xkey.window != windows->image.id)
12211               break;
12212             /*
12213               Respond to a user key press.
12214             */
12215             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12216               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12217             switch ((int) key_symbol)
12218             {
12219               case XK_Escape:
12220               case XK_F20:
12221               {
12222                 /*
12223                   Prematurely exit.
12224                 */
12225                 state|=EscapeState;
12226                 state|=ExitState;
12227                 break;
12228               }
12229               case XK_F1:
12230               case XK_Help:
12231               {
12232                 (void) XSetFunction(display,windows->image.highlight_context,
12233                   GXcopy);
12234                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12235                   "Help Viewer - Image Rotation",ImageRotateHelp);
12236                 (void) XSetFunction(display,windows->image.highlight_context,
12237                   GXinvert);
12238                 break;
12239               }
12240               default:
12241               {
12242                 (void) XBell(display,0);
12243                 break;
12244               }
12245             }
12246             break;
12247           }
12248           case MotionNotify:
12249           {
12250             rotate_info.x1=event.xmotion.x;
12251             rotate_info.y1=event.xmotion.y;
12252           }
12253         }
12254         rotate_info.x2=rotate_info.x1;
12255         rotate_info.y2=rotate_info.y1;
12256         if (direction == HorizontalRotateCommand)
12257           rotate_info.x2+=32;
12258         else
12259           rotate_info.y2-=32;
12260       } while ((state & ExitState) == 0);
12261       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12262       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12263       if ((state & EscapeState) != 0)
12264         return(MagickTrue);
12265       /*
12266         Draw line as pointer moves until the mouse button is released.
12267       */
12268       distance=0;
12269       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12270       state=DefaultState;
12271       do
12272       {
12273         if (distance > 9)
12274           {
12275             /*
12276               Display info and draw rotation line.
12277             */
12278             if (windows->info.mapped == MagickFalse)
12279               (void) XMapWindow(display,windows->info.id);
12280             (void) FormatLocaleString(text,MaxTextExtent," %g",
12281               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12282             XInfoWidget(display,windows,text);
12283             XHighlightLine(display,windows->image.id,
12284               windows->image.highlight_context,&rotate_info);
12285           }
12286         else
12287           if (windows->info.mapped != MagickFalse)
12288             (void) XWithdrawWindow(display,windows->info.id,
12289               windows->info.screen);
12290         /*
12291           Wait for next event.
12292         */
12293         XScreenEvent(display,windows,&event);
12294         if (distance > 9)
12295           XHighlightLine(display,windows->image.id,
12296             windows->image.highlight_context,&rotate_info);
12297         switch (event.type)
12298         {
12299           case ButtonPress:
12300             break;
12301           case ButtonRelease:
12302           {
12303             /*
12304               User has committed to rotation line.
12305             */
12306             rotate_info.x2=event.xbutton.x;
12307             rotate_info.y2=event.xbutton.y;
12308             state|=ExitState;
12309             break;
12310           }
12311           case Expose:
12312             break;
12313           case MotionNotify:
12314           {
12315             rotate_info.x2=event.xmotion.x;
12316             rotate_info.y2=event.xmotion.y;
12317           }
12318           default:
12319             break;
12320         }
12321         /*
12322           Check boundary conditions.
12323         */
12324         if (rotate_info.x2 < 0)
12325           rotate_info.x2=0;
12326         else
12327           if (rotate_info.x2 > (int) windows->image.width)
12328             rotate_info.x2=(short) windows->image.width;
12329         if (rotate_info.y2 < 0)
12330           rotate_info.y2=0;
12331         else
12332           if (rotate_info.y2 > (int) windows->image.height)
12333             rotate_info.y2=(short) windows->image.height;
12334         /*
12335           Compute rotation angle from the slope of the line.
12336         */
12337         degrees=0.0;
12338         distance=(unsigned int)
12339           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12340           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12341         if (distance > 9)
12342           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12343             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12344       } while ((state & ExitState) == 0);
12345       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12346       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12347       if (distance <= 9)
12348         return(MagickTrue);
12349     }
12350   if (direction == VerticalRotateCommand)
12351     degrees-=90.0;
12352   if (degrees == 0.0)
12353     return(MagickTrue);
12354   /*
12355     Rotate image.
12356   */
12357   normalized_degrees=degrees;
12358   while (normalized_degrees < -45.0)
12359     normalized_degrees+=360.0;
12360   for (rotations=0; normalized_degrees > 45.0; rotations++)
12361     normalized_degrees-=90.0;
12362   if (normalized_degrees != 0.0)
12363     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12364       exception);
12365   XSetCursorState(display,windows,MagickTrue);
12366   XCheckRefreshWindows(display,windows);
12367   (*image)->background_color.red=ScaleShortToQuantum(
12368     windows->pixel_info->pen_colors[pen_id].red);
12369   (*image)->background_color.green=ScaleShortToQuantum(
12370     windows->pixel_info->pen_colors[pen_id].green);
12371   (*image)->background_color.blue=ScaleShortToQuantum(
12372     windows->pixel_info->pen_colors[pen_id].blue);
12373   rotate_image=RotateImage(*image,degrees,exception);
12374   XSetCursorState(display,windows,MagickFalse);
12375   if (rotate_image == (Image *) NULL)
12376     return(MagickFalse);
12377   *image=DestroyImage(*image);
12378   *image=rotate_image;
12379   if (windows->image.crop_geometry != (char *) NULL)
12380     {
12381       /*
12382         Rotate crop geometry.
12383       */
12384       width=(unsigned int) (*image)->columns;
12385       height=(unsigned int) (*image)->rows;
12386       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12387       switch (rotations % 4)
12388       {
12389         default:
12390         case 0:
12391           break;
12392         case 1:
12393         {
12394           /*
12395             Rotate 90 degrees.
12396           */
12397           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12398             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12399             (int) height-y,x);
12400           break;
12401         }
12402         case 2:
12403         {
12404           /*
12405             Rotate 180 degrees.
12406           */
12407           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12408             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12409           break;
12410         }
12411         case 3:
12412         {
12413           /*
12414             Rotate 270 degrees.
12415           */
12416           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12417             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12418           break;
12419         }
12420       }
12421     }
12422   if (windows->image.orphan != MagickFalse)
12423     return(MagickTrue);
12424   if (normalized_degrees != 0.0)
12425     {
12426       /*
12427         Update image colormap.
12428       */
12429       windows->image.window_changes.width=(int) (*image)->columns;
12430       windows->image.window_changes.height=(int) (*image)->rows;
12431       if (windows->image.crop_geometry != (char *) NULL)
12432         {
12433           /*
12434             Obtain dimensions of image from crop geometry.
12435           */
12436           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12437             &width,&height);
12438           windows->image.window_changes.width=(int) width;
12439           windows->image.window_changes.height=(int) height;
12440         }
12441       XConfigureImageColormap(display,resource_info,windows,*image);
12442     }
12443   else
12444     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12445       {
12446         windows->image.window_changes.width=windows->image.ximage->height;
12447         windows->image.window_changes.height=windows->image.ximage->width;
12448       }
12449   /*
12450     Update image configuration.
12451   */
12452   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12453   return(MagickTrue);
12454 }
12455 \f
12456 /*
12457 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12458 %                                                                             %
12459 %                                                                             %
12460 %                                                                             %
12461 +   X S a v e I m a g e                                                       %
12462 %                                                                             %
12463 %                                                                             %
12464 %                                                                             %
12465 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12466 %
12467 %  XSaveImage() saves an image to a file.
12468 %
12469 %  The format of the XSaveImage method is:
12470 %
12471 %      MagickBooleanType XSaveImage(Display *display,
12472 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12473 %        ExceptionInfo *exception)
12474 %
12475 %  A description of each parameter follows:
12476 %
12477 %    o display: Specifies a connection to an X server; returned from
12478 %      XOpenDisplay.
12479 %
12480 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12481 %
12482 %    o windows: Specifies a pointer to a XWindows structure.
12483 %
12484 %    o image: the image.
12485 %
12486 %    o exception: return any errors or warnings in this structure.
12487 %
12488 */
12489 static MagickBooleanType XSaveImage(Display *display,
12490   XResourceInfo *resource_info,XWindows *windows,Image *image,
12491   ExceptionInfo *exception)
12492 {
12493   char
12494     filename[MaxTextExtent],
12495     geometry[MaxTextExtent];
12496
12497   Image
12498     *save_image;
12499
12500   ImageInfo
12501     *image_info;
12502
12503   MagickStatusType
12504     status;
12505
12506   /*
12507     Request file name from user.
12508   */
12509   if (resource_info->write_filename != (char *) NULL)
12510     (void) CopyMagickString(filename,resource_info->write_filename,
12511       MaxTextExtent);
12512   else
12513     {
12514       char
12515         path[MaxTextExtent];
12516
12517       int
12518         status;
12519
12520       GetPathComponent(image->filename,HeadPath,path);
12521       GetPathComponent(image->filename,TailPath,filename);
12522       if (*path != '\0')
12523         {
12524           status=chdir(path);
12525           if (status == -1)
12526             (void) ThrowMagickException(exception,GetMagickModule(),
12527               FileOpenError,"UnableToOpenFile","%s",path);
12528         }
12529     }
12530   XFileBrowserWidget(display,windows,"Save",filename);
12531   if (*filename == '\0')
12532     return(MagickTrue);
12533   if (IsPathAccessible(filename) != MagickFalse)
12534     {
12535       int
12536         status;
12537
12538       /*
12539         File exists-- seek user's permission before overwriting.
12540       */
12541       status=XConfirmWidget(display,windows,"Overwrite",filename);
12542       if (status <= 0)
12543         return(MagickTrue);
12544     }
12545   image_info=CloneImageInfo(resource_info->image_info);
12546   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12547   (void) SetImageInfo(image_info,1,exception);
12548   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12549       (LocaleCompare(image_info->magick,"JPG") == 0))
12550     {
12551       char
12552         quality[MaxTextExtent];
12553
12554       int
12555         status;
12556
12557       /*
12558         Request JPEG quality from user.
12559       */
12560       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12561         image->quality);
12562       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12563         quality);
12564       if (*quality == '\0')
12565         return(MagickTrue);
12566       image->quality=StringToUnsignedLong(quality);
12567       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12568     }
12569   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12570       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12571       (LocaleCompare(image_info->magick,"PS") == 0) ||
12572       (LocaleCompare(image_info->magick,"PS2") == 0))
12573     {
12574       char
12575         geometry[MaxTextExtent];
12576
12577       /*
12578         Request page geometry from user.
12579       */
12580       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12581       if (LocaleCompare(image_info->magick,"PDF") == 0)
12582         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12583       if (image_info->page != (char *) NULL)
12584         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12585       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12586         "Select page geometry:",geometry);
12587       if (*geometry != '\0')
12588         image_info->page=GetPageGeometry(geometry);
12589     }
12590   /*
12591     Apply image transforms.
12592   */
12593   XSetCursorState(display,windows,MagickTrue);
12594   XCheckRefreshWindows(display,windows);
12595   save_image=CloneImage(image,0,0,MagickTrue,exception);
12596   if (save_image == (Image *) NULL)
12597     return(MagickFalse);
12598   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12599     windows->image.ximage->width,windows->image.ximage->height);
12600   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12601   /*
12602     Write image.
12603   */
12604   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12605   status=WriteImage(image_info,save_image,exception);
12606   if (status != MagickFalse)
12607     image->taint=MagickFalse;
12608   save_image=DestroyImage(save_image);
12609   image_info=DestroyImageInfo(image_info);
12610   XSetCursorState(display,windows,MagickFalse);
12611   return(status != 0 ? MagickTrue : MagickFalse);
12612 }
12613 \f
12614 /*
12615 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12616 %                                                                             %
12617 %                                                                             %
12618 %                                                                             %
12619 +   X S c r e e n E v e n t                                                   %
12620 %                                                                             %
12621 %                                                                             %
12622 %                                                                             %
12623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12624 %
12625 %  XScreenEvent() handles global events associated with the Pan and Magnify
12626 %  windows.
12627 %
12628 %  The format of the XScreenEvent function is:
12629 %
12630 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12631 %
12632 %  A description of each parameter follows:
12633 %
12634 %    o display: Specifies a pointer to the Display structure;  returned from
12635 %      XOpenDisplay.
12636 %
12637 %    o windows: Specifies a pointer to a XWindows structure.
12638 %
12639 %    o event: Specifies a pointer to a X11 XEvent structure.
12640 %
12641 %
12642 */
12643
12644 #if defined(__cplusplus) || defined(c_plusplus)
12645 extern "C" {
12646 #endif
12647
12648 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12649 {
12650   register XWindows
12651     *windows;
12652
12653   windows=(XWindows *) data;
12654   if ((event->type == ClientMessage) &&
12655       (event->xclient.window == windows->image.id))
12656     return(MagickFalse);
12657   return(MagickTrue);
12658 }
12659
12660 #if defined(__cplusplus) || defined(c_plusplus)
12661 }
12662 #endif
12663
12664 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12665 {
12666   register int
12667     x,
12668     y;
12669
12670   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12671   if (event->xany.window == windows->command.id)
12672     return;
12673   switch (event->type)
12674   {
12675     case ButtonPress:
12676     case ButtonRelease:
12677     {
12678       if ((event->xbutton.button == Button3) &&
12679           (event->xbutton.state & Mod1Mask))
12680         {
12681           /*
12682             Convert Alt-Button3 to Button2.
12683           */
12684           event->xbutton.button=Button2;
12685           event->xbutton.state&=(~Mod1Mask);
12686         }
12687       if (event->xbutton.window == windows->backdrop.id)
12688         {
12689           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12690             event->xbutton.time);
12691           break;
12692         }
12693       if (event->xbutton.window == windows->pan.id)
12694         {
12695           XPanImage(display,windows,event);
12696           break;
12697         }
12698       if (event->xbutton.window == windows->image.id)
12699         if (event->xbutton.button == Button2)
12700           {
12701             /*
12702               Update magnified image.
12703             */
12704             x=event->xbutton.x;
12705             y=event->xbutton.y;
12706             if (x < 0)
12707               x=0;
12708             else
12709               if (x >= (int) windows->image.width)
12710                 x=(int) (windows->image.width-1);
12711             windows->magnify.x=(int) windows->image.x+x;
12712             if (y < 0)
12713               y=0;
12714             else
12715              if (y >= (int) windows->image.height)
12716                y=(int) (windows->image.height-1);
12717             windows->magnify.y=windows->image.y+y;
12718             if (windows->magnify.mapped == MagickFalse)
12719               (void) XMapRaised(display,windows->magnify.id);
12720             XMakeMagnifyImage(display,windows);
12721             if (event->type == ButtonRelease)
12722               (void) XWithdrawWindow(display,windows->info.id,
12723                 windows->info.screen);
12724             break;
12725           }
12726       break;
12727     }
12728     case ClientMessage:
12729     {
12730       /*
12731         If client window delete message, exit.
12732       */
12733       if (event->xclient.message_type != windows->wm_protocols)
12734         break;
12735       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12736         break;
12737       if (event->xclient.window == windows->magnify.id)
12738         {
12739           (void) XWithdrawWindow(display,windows->magnify.id,
12740             windows->magnify.screen);
12741           break;
12742         }
12743       break;
12744     }
12745     case ConfigureNotify:
12746     {
12747       if (event->xconfigure.window == windows->magnify.id)
12748         {
12749           unsigned int
12750             magnify;
12751
12752           /*
12753             Magnify window has a new configuration.
12754           */
12755           windows->magnify.width=(unsigned int) event->xconfigure.width;
12756           windows->magnify.height=(unsigned int) event->xconfigure.height;
12757           if (windows->magnify.mapped == MagickFalse)
12758             break;
12759           magnify=1;
12760           while ((int) magnify <= event->xconfigure.width)
12761             magnify<<=1;
12762           while ((int) magnify <= event->xconfigure.height)
12763             magnify<<=1;
12764           magnify>>=1;
12765           if (((int) magnify != event->xconfigure.width) ||
12766               ((int) magnify != event->xconfigure.height))
12767             {
12768               XWindowChanges
12769                 window_changes;
12770
12771               window_changes.width=(int) magnify;
12772               window_changes.height=(int) magnify;
12773               (void) XReconfigureWMWindow(display,windows->magnify.id,
12774                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12775                 &window_changes);
12776               break;
12777             }
12778           XMakeMagnifyImage(display,windows);
12779           break;
12780         }
12781       break;
12782     }
12783     case Expose:
12784     {
12785       if (event->xexpose.window == windows->image.id)
12786         {
12787           XRefreshWindow(display,&windows->image,event);
12788           break;
12789         }
12790       if (event->xexpose.window == windows->pan.id)
12791         if (event->xexpose.count == 0)
12792           {
12793             XDrawPanRectangle(display,windows);
12794             break;
12795           }
12796       if (event->xexpose.window == windows->magnify.id)
12797         if (event->xexpose.count == 0)
12798           {
12799             XMakeMagnifyImage(display,windows);
12800             break;
12801           }
12802       break;
12803     }
12804     case KeyPress:
12805     {
12806       char
12807         command[MaxTextExtent];
12808
12809       KeySym
12810         key_symbol;
12811
12812       if (event->xkey.window != windows->magnify.id)
12813         break;
12814       /*
12815         Respond to a user key press.
12816       */
12817       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12818         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12819       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12820       break;
12821     }
12822     case MapNotify:
12823     {
12824       if (event->xmap.window == windows->magnify.id)
12825         {
12826           windows->magnify.mapped=MagickTrue;
12827           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12828           break;
12829         }
12830       if (event->xmap.window == windows->info.id)
12831         {
12832           windows->info.mapped=MagickTrue;
12833           break;
12834         }
12835       break;
12836     }
12837     case MotionNotify:
12838     {
12839       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12840       if (event->xmotion.window == windows->image.id)
12841         if (windows->magnify.mapped != MagickFalse)
12842           {
12843             /*
12844               Update magnified image.
12845             */
12846             x=event->xmotion.x;
12847             y=event->xmotion.y;
12848             if (x < 0)
12849               x=0;
12850             else
12851               if (x >= (int) windows->image.width)
12852                 x=(int) (windows->image.width-1);
12853             windows->magnify.x=(int) windows->image.x+x;
12854             if (y < 0)
12855               y=0;
12856             else
12857              if (y >= (int) windows->image.height)
12858                y=(int) (windows->image.height-1);
12859             windows->magnify.y=windows->image.y+y;
12860             XMakeMagnifyImage(display,windows);
12861           }
12862       break;
12863     }
12864     case UnmapNotify:
12865     {
12866       if (event->xunmap.window == windows->magnify.id)
12867         {
12868           windows->magnify.mapped=MagickFalse;
12869           break;
12870         }
12871       if (event->xunmap.window == windows->info.id)
12872         {
12873           windows->info.mapped=MagickFalse;
12874           break;
12875         }
12876       break;
12877     }
12878     default:
12879       break;
12880   }
12881 }
12882 \f
12883 /*
12884 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12885 %                                                                             %
12886 %                                                                             %
12887 %                                                                             %
12888 +   X S e t C r o p G e o m e t r y                                           %
12889 %                                                                             %
12890 %                                                                             %
12891 %                                                                             %
12892 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12893 %
12894 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12895 %  and translates it to a cropping geometry relative to the image.
12896 %
12897 %  The format of the XSetCropGeometry method is:
12898 %
12899 %      void XSetCropGeometry(Display *display,XWindows *windows,
12900 %        RectangleInfo *crop_info,Image *image)
12901 %
12902 %  A description of each parameter follows:
12903 %
12904 %    o display: Specifies a connection to an X server; returned from
12905 %      XOpenDisplay.
12906 %
12907 %    o windows: Specifies a pointer to a XWindows structure.
12908 %
12909 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12910 %      Image window to crop.
12911 %
12912 %    o image: the image.
12913 %
12914 */
12915 static void XSetCropGeometry(Display *display,XWindows *windows,
12916   RectangleInfo *crop_info,Image *image)
12917 {
12918   char
12919     text[MaxTextExtent];
12920
12921   int
12922     x,
12923     y;
12924
12925   MagickRealType
12926     scale_factor;
12927
12928   unsigned int
12929     height,
12930     width;
12931
12932   if (windows->info.mapped != MagickFalse)
12933     {
12934       /*
12935         Display info on cropping rectangle.
12936       */
12937       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12938         (double) crop_info->width,(double) crop_info->height,(double)
12939         crop_info->x,(double) crop_info->y);
12940       XInfoWidget(display,windows,text);
12941     }
12942   /*
12943     Cropping geometry is relative to any previous crop geometry.
12944   */
12945   x=0;
12946   y=0;
12947   width=(unsigned int) image->columns;
12948   height=(unsigned int) image->rows;
12949   if (windows->image.crop_geometry != (char *) NULL)
12950     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12951   else
12952     windows->image.crop_geometry=AcquireString((char *) NULL);
12953   /*
12954     Define the crop geometry string from the cropping rectangle.
12955   */
12956   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12957   if (crop_info->x > 0)
12958     x+=(int) (scale_factor*crop_info->x+0.5);
12959   width=(unsigned int) (scale_factor*crop_info->width+0.5);
12960   if (width == 0)
12961     width=1;
12962   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12963   if (crop_info->y > 0)
12964     y+=(int) (scale_factor*crop_info->y+0.5);
12965   height=(unsigned int) (scale_factor*crop_info->height+0.5);
12966   if (height == 0)
12967     height=1;
12968   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12969     "%ux%u%+d%+d",width,height,x,y);
12970 }
12971 \f
12972 /*
12973 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12974 %                                                                             %
12975 %                                                                             %
12976 %                                                                             %
12977 +   X T i l e I m a g e                                                       %
12978 %                                                                             %
12979 %                                                                             %
12980 %                                                                             %
12981 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12982 %
12983 %  XTileImage() loads or deletes a selected tile from a visual image directory.
12984 %  The load or delete command is chosen from a menu.
12985 %
12986 %  The format of the XTileImage method is:
12987 %
12988 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
12989 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
12990 %
12991 %  A description of each parameter follows:
12992 %
12993 %    o tile_image:  XTileImage reads or deletes the tile image
12994 %      and returns it.  A null image is returned if an error occurs.
12995 %
12996 %    o display: Specifies a connection to an X server;  returned from
12997 %      XOpenDisplay.
12998 %
12999 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13000 %
13001 %    o windows: Specifies a pointer to a XWindows structure.
13002 %
13003 %    o image: the image; returned from ReadImage.
13004 %
13005 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13006 %      the entire image is refreshed.
13007 %
13008 %    o exception: return any errors or warnings in this structure.
13009 %
13010 */
13011 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13012   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13013 {
13014   static const char
13015     *VerbMenu[] =
13016     {
13017       "Load",
13018       "Next",
13019       "Former",
13020       "Delete",
13021       "Update",
13022       (char *) NULL,
13023     };
13024
13025   static const ModeType
13026     TileCommands[] =
13027     {
13028       TileLoadCommand,
13029       TileNextCommand,
13030       TileFormerCommand,
13031       TileDeleteCommand,
13032       TileUpdateCommand
13033     };
13034
13035   char
13036     command[MaxTextExtent],
13037     filename[MaxTextExtent];
13038
13039   Image
13040     *tile_image;
13041
13042   int
13043     id,
13044     status,
13045     tile,
13046     x,
13047     y;
13048
13049   MagickRealType
13050     scale_factor;
13051
13052   register char
13053     *p,
13054     *q;
13055
13056   register int
13057     i;
13058
13059   unsigned int
13060     height,
13061     width;
13062
13063   /*
13064     Tile image is relative to montage image configuration.
13065   */
13066   x=0;
13067   y=0;
13068   width=(unsigned int) image->columns;
13069   height=(unsigned int) image->rows;
13070   if (windows->image.crop_geometry != (char *) NULL)
13071     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13072   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13073   event->xbutton.x+=windows->image.x;
13074   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13075   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13076   event->xbutton.y+=windows->image.y;
13077   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13078   /*
13079     Determine size and location of each tile in the visual image directory.
13080   */
13081   width=(unsigned int) image->columns;
13082   height=(unsigned int) image->rows;
13083   x=0;
13084   y=0;
13085   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13086   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13087     (event->xbutton.x-x)/width;
13088   if (tile < 0)
13089     {
13090       /*
13091         Button press is outside any tile.
13092       */
13093       (void) XBell(display,0);
13094       return((Image *) NULL);
13095     }
13096   /*
13097     Determine file name from the tile directory.
13098   */
13099   p=image->directory;
13100   for (i=tile; (i != 0) && (*p != '\0'); )
13101   {
13102     if (*p == '\n')
13103       i--;
13104     p++;
13105   }
13106   if (*p == '\0')
13107     {
13108       /*
13109         Button press is outside any tile.
13110       */
13111       (void) XBell(display,0);
13112       return((Image *) NULL);
13113     }
13114   /*
13115     Select a command from the pop-up menu.
13116   */
13117   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13118   if (id < 0)
13119     return((Image *) NULL);
13120   q=p;
13121   while ((*q != '\n') && (*q != '\0'))
13122     q++;
13123   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13124   /*
13125     Perform command for the selected tile.
13126   */
13127   XSetCursorState(display,windows,MagickTrue);
13128   XCheckRefreshWindows(display,windows);
13129   tile_image=NewImageList();
13130   switch (TileCommands[id])
13131   {
13132     case TileLoadCommand:
13133     {
13134       /*
13135         Load tile image.
13136       */
13137       XCheckRefreshWindows(display,windows);
13138       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13139         MaxTextExtent);
13140       (void) CopyMagickString(resource_info->image_info->filename,filename,
13141         MaxTextExtent);
13142       tile_image=ReadImage(resource_info->image_info,exception);
13143       CatchException(exception);
13144       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13145       break;
13146     }
13147     case TileNextCommand:
13148     {
13149       /*
13150         Display next image.
13151       */
13152       XClientMessage(display,windows->image.id,windows->im_protocols,
13153         windows->im_next_image,CurrentTime);
13154       break;
13155     }
13156     case TileFormerCommand:
13157     {
13158       /*
13159         Display former image.
13160       */
13161       XClientMessage(display,windows->image.id,windows->im_protocols,
13162         windows->im_former_image,CurrentTime);
13163       break;
13164     }
13165     case TileDeleteCommand:
13166     {
13167       /*
13168         Delete tile image.
13169       */
13170       if (IsPathAccessible(filename) == MagickFalse)
13171         {
13172           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13173           break;
13174         }
13175       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13176       if (status <= 0)
13177         break;
13178       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13179       if (status != MagickFalse)
13180         {
13181           XNoticeWidget(display,windows,"Unable to delete image file:",
13182             filename);
13183           break;
13184         }
13185     }
13186     case TileUpdateCommand:
13187     {
13188       int
13189         x_offset,
13190         y_offset;
13191
13192       PixelPacket
13193         pixel;
13194
13195       register int
13196         j;
13197
13198       register Quantum
13199         *s;
13200
13201       /*
13202         Ensure all the images exist.
13203       */
13204       tile=0;
13205       for (p=image->directory; *p != '\0'; p++)
13206       {
13207         CacheView
13208           *image_view;
13209
13210         q=p;
13211         while ((*q != '\n') && (*q != '\0'))
13212           q++;
13213         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13214         p=q;
13215         if (IsPathAccessible(filename) != MagickFalse)
13216           {
13217             tile++;
13218             continue;
13219           }
13220         /*
13221           Overwrite tile with background color.
13222         */
13223         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13224         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13225         image_view=AcquireCacheView(image);
13226         (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13227         for (i=0; i < (int) height; i++)
13228         {
13229           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13230             y_offset+i,width,1,exception);
13231           if (s == (Quantum *) NULL)
13232             break;
13233           for (j=0; j < (int) width; j++)
13234           {
13235             SetPixelPacket(image,&pixel,s);
13236             s+=GetPixelChannels(image);
13237           }
13238           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13239             break;
13240         }
13241         image_view=DestroyCacheView(image_view);
13242         tile++;
13243       }
13244       windows->image.window_changes.width=(int) image->columns;
13245       windows->image.window_changes.height=(int) image->rows;
13246       XConfigureImageColormap(display,resource_info,windows,image);
13247       (void) XConfigureImage(display,resource_info,windows,image,exception);
13248       break;
13249     }
13250     default:
13251       break;
13252   }
13253   XSetCursorState(display,windows,MagickFalse);
13254   return(tile_image);
13255 }
13256 \f
13257 /*
13258 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13259 %                                                                             %
13260 %                                                                             %
13261 %                                                                             %
13262 +   X T r a n s l a t e I m a g e                                             %
13263 %                                                                             %
13264 %                                                                             %
13265 %                                                                             %
13266 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13267 %
13268 %  XTranslateImage() translates the image within an Image window by one pixel
13269 %  as specified by the key symbol.  If the image has a `montage string the
13270 %  translation is respect to the width and height contained within the string.
13271 %
13272 %  The format of the XTranslateImage method is:
13273 %
13274 %      void XTranslateImage(Display *display,XWindows *windows,
13275 %        Image *image,const KeySym key_symbol)
13276 %
13277 %  A description of each parameter follows:
13278 %
13279 %    o display: Specifies a connection to an X server; returned from
13280 %      XOpenDisplay.
13281 %
13282 %    o windows: Specifies a pointer to a XWindows structure.
13283 %
13284 %    o image: the image.
13285 %
13286 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13287 %      to trim.
13288 %
13289 */
13290 static void XTranslateImage(Display *display,XWindows *windows,
13291   Image *image,const KeySym key_symbol)
13292 {
13293   char
13294     text[MaxTextExtent];
13295
13296   int
13297     x,
13298     y;
13299
13300   unsigned int
13301     x_offset,
13302     y_offset;
13303
13304   /*
13305     User specified a pan position offset.
13306   */
13307   x_offset=windows->image.width;
13308   y_offset=windows->image.height;
13309   if (image->montage != (char *) NULL)
13310     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13311   switch ((int) key_symbol)
13312   {
13313     case XK_Home:
13314     case XK_KP_Home:
13315     {
13316       windows->image.x=(int) windows->image.width/2;
13317       windows->image.y=(int) windows->image.height/2;
13318       break;
13319     }
13320     case XK_Left:
13321     case XK_KP_Left:
13322     {
13323       windows->image.x-=x_offset;
13324       break;
13325     }
13326     case XK_Next:
13327     case XK_Up:
13328     case XK_KP_Up:
13329     {
13330       windows->image.y-=y_offset;
13331       break;
13332     }
13333     case XK_Right:
13334     case XK_KP_Right:
13335     {
13336       windows->image.x+=x_offset;
13337       break;
13338     }
13339     case XK_Prior:
13340     case XK_Down:
13341     case XK_KP_Down:
13342     {
13343       windows->image.y+=y_offset;
13344       break;
13345     }
13346     default:
13347       return;
13348   }
13349   /*
13350     Check boundary conditions.
13351   */
13352   if (windows->image.x < 0)
13353     windows->image.x=0;
13354   else
13355     if ((int) (windows->image.x+windows->image.width) >
13356         windows->image.ximage->width)
13357       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13358   if (windows->image.y < 0)
13359     windows->image.y=0;
13360   else
13361     if ((int) (windows->image.y+windows->image.height) >
13362         windows->image.ximage->height)
13363       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13364   /*
13365     Refresh Image window.
13366   */
13367   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13368     windows->image.width,windows->image.height,windows->image.x,
13369     windows->image.y);
13370   XInfoWidget(display,windows,text);
13371   XCheckRefreshWindows(display,windows);
13372   XDrawPanRectangle(display,windows);
13373   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13374   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13375 }
13376 \f
13377 /*
13378 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13379 %                                                                             %
13380 %                                                                             %
13381 %                                                                             %
13382 +   X T r i m I m a g e                                                       %
13383 %                                                                             %
13384 %                                                                             %
13385 %                                                                             %
13386 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13387 %
13388 %  XTrimImage() trims the edges from the Image window.
13389 %
13390 %  The format of the XTrimImage method is:
13391 %
13392 %      MagickBooleanType XTrimImage(Display *display,
13393 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13394 %        ExceptionInfo *exception)
13395 %
13396 %  A description of each parameter follows:
13397 %
13398 %    o display: Specifies a connection to an X server; returned from
13399 %      XOpenDisplay.
13400 %
13401 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13402 %
13403 %    o windows: Specifies a pointer to a XWindows structure.
13404 %
13405 %    o image: the image.
13406 %
13407 %    o exception: return any errors or warnings in this structure.
13408 %
13409 */
13410 static MagickBooleanType XTrimImage(Display *display,
13411   XResourceInfo *resource_info,XWindows *windows,Image *image,
13412   ExceptionInfo *exception)
13413 {
13414   RectangleInfo
13415     trim_info;
13416
13417   register int
13418     x,
13419     y;
13420
13421   size_t
13422     background,
13423     pixel;
13424
13425   /*
13426     Trim edges from image.
13427   */
13428   XSetCursorState(display,windows,MagickTrue);
13429   XCheckRefreshWindows(display,windows);
13430   /*
13431     Crop the left edge.
13432   */
13433   background=XGetPixel(windows->image.ximage,0,0);
13434   trim_info.width=(size_t) windows->image.ximage->width;
13435   for (x=0; x < windows->image.ximage->width; x++)
13436   {
13437     for (y=0; y < windows->image.ximage->height; y++)
13438     {
13439       pixel=XGetPixel(windows->image.ximage,x,y);
13440       if (pixel != background)
13441         break;
13442     }
13443     if (y < windows->image.ximage->height)
13444       break;
13445   }
13446   trim_info.x=(ssize_t) x;
13447   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13448     {
13449       XSetCursorState(display,windows,MagickFalse);
13450       return(MagickFalse);
13451     }
13452   /*
13453     Crop the right edge.
13454   */
13455   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13456   for (x=windows->image.ximage->width-1; x != 0; x--)
13457   {
13458     for (y=0; y < windows->image.ximage->height; y++)
13459     {
13460       pixel=XGetPixel(windows->image.ximage,x,y);
13461       if (pixel != background)
13462         break;
13463     }
13464     if (y < windows->image.ximage->height)
13465       break;
13466   }
13467   trim_info.width=(size_t) (x-trim_info.x+1);
13468   /*
13469     Crop the top edge.
13470   */
13471   background=XGetPixel(windows->image.ximage,0,0);
13472   trim_info.height=(size_t) windows->image.ximage->height;
13473   for (y=0; y < windows->image.ximage->height; y++)
13474   {
13475     for (x=0; x < windows->image.ximage->width; x++)
13476     {
13477       pixel=XGetPixel(windows->image.ximage,x,y);
13478       if (pixel != background)
13479         break;
13480     }
13481     if (x < windows->image.ximage->width)
13482       break;
13483   }
13484   trim_info.y=(ssize_t) y;
13485   /*
13486     Crop the bottom edge.
13487   */
13488   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13489   for (y=windows->image.ximage->height-1; y != 0; y--)
13490   {
13491     for (x=0; x < windows->image.ximage->width; x++)
13492     {
13493       pixel=XGetPixel(windows->image.ximage,x,y);
13494       if (pixel != background)
13495         break;
13496     }
13497     if (x < windows->image.ximage->width)
13498       break;
13499   }
13500   trim_info.height=(size_t) y-trim_info.y+1;
13501   if (((unsigned int) trim_info.width != windows->image.width) ||
13502       ((unsigned int) trim_info.height != windows->image.height))
13503     {
13504       /*
13505         Reconfigure Image window as defined by the trimming rectangle.
13506       */
13507       XSetCropGeometry(display,windows,&trim_info,image);
13508       windows->image.window_changes.width=(int) trim_info.width;
13509       windows->image.window_changes.height=(int) trim_info.height;
13510       (void) XConfigureImage(display,resource_info,windows,image,exception);
13511     }
13512   XSetCursorState(display,windows,MagickFalse);
13513   return(MagickTrue);
13514 }
13515 \f
13516 /*
13517 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13518 %                                                                             %
13519 %                                                                             %
13520 %                                                                             %
13521 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13522 %                                                                             %
13523 %                                                                             %
13524 %                                                                             %
13525 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13526 %
13527 %  XVisualDirectoryImage() creates a Visual Image Directory.
13528 %
13529 %  The format of the XVisualDirectoryImage method is:
13530 %
13531 %      Image *XVisualDirectoryImage(Display *display,
13532 %        XResourceInfo *resource_info,XWindows *windows,
13533 %        ExceptionInfo *exception)
13534 %
13535 %  A description of each parameter follows:
13536 %
13537 %    o display: Specifies a connection to an X server; returned from
13538 %      XOpenDisplay.
13539 %
13540 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13541 %
13542 %    o windows: Specifies a pointer to a XWindows structure.
13543 %
13544 %    o exception: return any errors or warnings in this structure.
13545 %
13546 */
13547 static Image *XVisualDirectoryImage(Display *display,
13548   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13549 {
13550 #define TileImageTag  "Scale/Image"
13551 #define XClientName  "montage"
13552
13553   char
13554     **filelist;
13555
13556   Image
13557     *images,
13558     *montage_image,
13559     *next_image,
13560     *thumbnail_image;
13561
13562   ImageInfo
13563     *read_info;
13564
13565   int
13566     number_files;
13567
13568   MagickBooleanType
13569     backdrop;
13570
13571   MagickStatusType
13572     status;
13573
13574   MontageInfo
13575     *montage_info;
13576
13577   RectangleInfo
13578     geometry;
13579
13580   register int
13581     i;
13582
13583   static char
13584     filename[MaxTextExtent] = "\0",
13585     filenames[MaxTextExtent] = "*";
13586
13587   XResourceInfo
13588     background_resources;
13589
13590   /*
13591     Request file name from user.
13592   */
13593   XFileBrowserWidget(display,windows,"Directory",filenames);
13594   if (*filenames == '\0')
13595     return((Image *) NULL);
13596   /*
13597     Expand the filenames.
13598   */
13599   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13600   if (filelist == (char **) NULL)
13601     {
13602       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13603         filenames);
13604       return((Image *) NULL);
13605     }
13606   number_files=1;
13607   filelist[0]=filenames;
13608   status=ExpandFilenames(&number_files,&filelist);
13609   if ((status == MagickFalse) || (number_files == 0))
13610     {
13611       if (number_files == 0)
13612         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13613       else
13614         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13615           filenames);
13616       return((Image *) NULL);
13617     }
13618   /*
13619     Set image background resources.
13620   */
13621   background_resources=(*resource_info);
13622   background_resources.window_id=AcquireString("");
13623   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13624     "0x%lx",windows->image.id);
13625   background_resources.backdrop=MagickTrue;
13626   /*
13627     Read each image and convert them to a tile.
13628   */
13629   backdrop=(windows->visual_info->klass == TrueColor) ||
13630     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13631   read_info=CloneImageInfo(resource_info->image_info);
13632   (void) SetImageOption(read_info,"jpeg:size","120x120");
13633   (void) CloneString(&read_info->size,DefaultTileGeometry);
13634   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13635     (void *) NULL);
13636   images=NewImageList();
13637   XSetCursorState(display,windows,MagickTrue);
13638   XCheckRefreshWindows(display,windows);
13639   for (i=0; i < (int) number_files; i++)
13640   {
13641     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13642     filelist[i]=DestroyString(filelist[i]);
13643     *read_info->magick='\0';
13644     next_image=ReadImage(read_info,exception);
13645     CatchException(exception);
13646     if (next_image != (Image *) NULL)
13647       {
13648         (void) DeleteImageProperty(next_image,"label");
13649         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13650           read_info,next_image,DefaultTileLabel,exception));
13651         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13652           exception);
13653         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13654           geometry.height,exception);
13655         if (thumbnail_image != (Image *) NULL)
13656           {
13657             next_image=DestroyImage(next_image);
13658             next_image=thumbnail_image;
13659           }
13660         if (backdrop)
13661           {
13662             (void) XDisplayBackgroundImage(display,&background_resources,
13663               next_image,exception);
13664             XSetCursorState(display,windows,MagickTrue);
13665           }
13666         AppendImageToList(&images,next_image);
13667         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13668           {
13669             MagickBooleanType
13670               proceed;
13671
13672             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13673               (MagickSizeType) number_files);
13674             if (proceed == MagickFalse)
13675               break;
13676           }
13677       }
13678   }
13679   filelist=(char **) RelinquishMagickMemory(filelist);
13680   if (images == (Image *) NULL)
13681     {
13682       read_info=DestroyImageInfo(read_info);
13683       XSetCursorState(display,windows,MagickFalse);
13684       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13685       return((Image *) NULL);
13686     }
13687   /*
13688     Create the Visual Image Directory.
13689   */
13690   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13691   montage_info->pointsize=10;
13692   if (resource_info->font != (char *) NULL)
13693     (void) CloneString(&montage_info->font,resource_info->font);
13694   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13695   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13696     images),exception);
13697   images=DestroyImageList(images);
13698   montage_info=DestroyMontageInfo(montage_info);
13699   read_info=DestroyImageInfo(read_info);
13700   XSetCursorState(display,windows,MagickFalse);
13701   if (montage_image == (Image *) NULL)
13702     return(montage_image);
13703   XClientMessage(display,windows->image.id,windows->im_protocols,
13704     windows->im_next_image,CurrentTime);
13705   return(montage_image);
13706 }
13707 \f
13708 /*
13709 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13710 %                                                                             %
13711 %                                                                             %
13712 %                                                                             %
13713 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13714 %                                                                             %
13715 %                                                                             %
13716 %                                                                             %
13717 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13718 %
13719 %  XDisplayBackgroundImage() displays an image in the background of a window.
13720 %
13721 %  The format of the XDisplayBackgroundImage method is:
13722 %
13723 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13724 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13725 %
13726 %  A description of each parameter follows:
13727 %
13728 %    o display: Specifies a connection to an X server;  returned from
13729 %      XOpenDisplay.
13730 %
13731 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13732 %
13733 %    o image: the image.
13734 %
13735 %    o exception: return any errors or warnings in this structure.
13736 %
13737 */
13738 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13739   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13740 {
13741   char
13742     geometry[MaxTextExtent],
13743     visual_type[MaxTextExtent];
13744
13745   int
13746     height,
13747     status,
13748     width;
13749
13750   RectangleInfo
13751     geometry_info;
13752
13753   static XPixelInfo
13754     pixel;
13755
13756   static XStandardColormap
13757     *map_info;
13758
13759   static XVisualInfo
13760     *visual_info = (XVisualInfo *) NULL;
13761
13762   static XWindowInfo
13763     window_info;
13764
13765   size_t
13766     delay;
13767
13768   Window
13769     root_window;
13770
13771   XGCValues
13772     context_values;
13773
13774   XResourceInfo
13775     resources;
13776
13777   XWindowAttributes
13778     window_attributes;
13779
13780   /*
13781     Determine target window.
13782   */
13783   assert(image != (Image *) NULL);
13784   assert(image->signature == MagickSignature);
13785   if (image->debug != MagickFalse)
13786     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13787   resources=(*resource_info);
13788   window_info.id=(Window) NULL;
13789   root_window=XRootWindow(display,XDefaultScreen(display));
13790   if (LocaleCompare(resources.window_id,"root") == 0)
13791     window_info.id=root_window;
13792   else
13793     {
13794       if (isdigit((unsigned char) *resources.window_id) != 0)
13795         window_info.id=XWindowByID(display,root_window,
13796           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13797       if (window_info.id == (Window) NULL)
13798         window_info.id=XWindowByName(display,root_window,resources.window_id);
13799     }
13800   if (window_info.id == (Window) NULL)
13801     {
13802       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13803         resources.window_id);
13804       return(MagickFalse);
13805     }
13806   /*
13807     Determine window visual id.
13808   */
13809   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13810   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13811   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13812   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13813   if (status != 0)
13814     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13815       XVisualIDFromVisual(window_attributes.visual));
13816   if (visual_info == (XVisualInfo *) NULL)
13817     {
13818       /*
13819         Allocate standard colormap.
13820       */
13821       map_info=XAllocStandardColormap();
13822       if (map_info == (XStandardColormap *) NULL)
13823         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13824           image->filename);
13825       map_info->colormap=(Colormap) NULL;
13826       pixel.pixels=(unsigned long *) NULL;
13827       /*
13828         Initialize visual info.
13829       */
13830       resources.map_type=(char *) NULL;
13831       resources.visual_type=visual_type;
13832       visual_info=XBestVisualInfo(display,map_info,&resources);
13833       if (visual_info == (XVisualInfo *) NULL)
13834         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13835           resources.visual_type);
13836       /*
13837         Initialize window info.
13838       */
13839       window_info.ximage=(XImage *) NULL;
13840       window_info.matte_image=(XImage *) NULL;
13841       window_info.pixmap=(Pixmap) NULL;
13842       window_info.matte_pixmap=(Pixmap) NULL;
13843     }
13844   /*
13845     Free previous root colors.
13846   */
13847   if (window_info.id == root_window)
13848     (void) XDestroyWindowColors(display,root_window);
13849   /*
13850     Initialize Standard Colormap.
13851   */
13852   resources.colormap=SharedColormap;
13853   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13854   /*
13855     Graphic context superclass.
13856   */
13857   context_values.background=pixel.background_color.pixel;
13858   context_values.foreground=pixel.foreground_color.pixel;
13859   pixel.annotate_context=XCreateGC(display,window_info.id,
13860     (size_t) (GCBackground | GCForeground),&context_values);
13861   if (pixel.annotate_context == (GC) NULL)
13862     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13863       image->filename);
13864   /*
13865     Initialize Image window attributes.
13866   */
13867   window_info.name=AcquireString("\0");
13868   window_info.icon_name=AcquireString("\0");
13869   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13870     &resources,&window_info);
13871   /*
13872     Create the X image.
13873   */
13874   window_info.width=(unsigned int) image->columns;
13875   window_info.height=(unsigned int) image->rows;
13876   if ((image->columns != window_info.width) ||
13877       (image->rows != window_info.height))
13878     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13879       image->filename);
13880   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13881     window_attributes.width,window_attributes.height);
13882   geometry_info.width=window_info.width;
13883   geometry_info.height=window_info.height;
13884   geometry_info.x=(ssize_t) window_info.x;
13885   geometry_info.y=(ssize_t) window_info.y;
13886   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13887     &geometry_info.width,&geometry_info.height);
13888   window_info.width=(unsigned int) geometry_info.width;
13889   window_info.height=(unsigned int) geometry_info.height;
13890   window_info.x=(int) geometry_info.x;
13891   window_info.y=(int) geometry_info.y;
13892   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13893     window_info.height,exception);
13894   if (status == MagickFalse)
13895     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13896       image->filename);
13897   window_info.x=0;
13898   window_info.y=0;
13899   if (image->debug != MagickFalse)
13900     {
13901       (void) LogMagickEvent(X11Event,GetMagickModule(),
13902         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13903         (double) image->columns,(double) image->rows);
13904       if (image->colors != 0)
13905         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13906           image->colors);
13907       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13908     }
13909   /*
13910     Adjust image dimensions as specified by backdrop or geometry options.
13911   */
13912   width=(int) window_info.width;
13913   height=(int) window_info.height;
13914   if (resources.backdrop != MagickFalse)
13915     {
13916       /*
13917         Center image on window.
13918       */
13919       window_info.x=(window_attributes.width/2)-
13920         (window_info.ximage->width/2);
13921       window_info.y=(window_attributes.height/2)-
13922         (window_info.ximage->height/2);
13923       width=window_attributes.width;
13924       height=window_attributes.height;
13925     }
13926   if ((resources.image_geometry != (char *) NULL) &&
13927       (*resources.image_geometry != '\0'))
13928     {
13929       char
13930         default_geometry[MaxTextExtent];
13931
13932       int
13933         flags,
13934         gravity;
13935
13936       XSizeHints
13937         *size_hints;
13938
13939       /*
13940         User specified geometry.
13941       */
13942       size_hints=XAllocSizeHints();
13943       if (size_hints == (XSizeHints *) NULL)
13944         ThrowXWindowFatalException(ResourceLimitFatalError,
13945           "MemoryAllocationFailed",image->filename);
13946       size_hints->flags=0L;
13947       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13948         width,height);
13949       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13950         default_geometry,window_info.border_width,size_hints,&window_info.x,
13951         &window_info.y,&width,&height,&gravity);
13952       if (flags & (XValue | YValue))
13953         {
13954           width=window_attributes.width;
13955           height=window_attributes.height;
13956         }
13957       (void) XFree((void *) size_hints);
13958     }
13959   /*
13960     Create the X pixmap.
13961   */
13962   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13963     (unsigned int) height,window_info.depth);
13964   if (window_info.pixmap == (Pixmap) NULL)
13965     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13966       image->filename);
13967   /*
13968     Display pixmap on the window.
13969   */
13970   if (((unsigned int) width > window_info.width) ||
13971       ((unsigned int) height > window_info.height))
13972     (void) XFillRectangle(display,window_info.pixmap,
13973       window_info.annotate_context,0,0,(unsigned int) width,
13974       (unsigned int) height);
13975   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13976     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13977     window_info.width,(unsigned int) window_info.height);
13978   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13979   (void) XClearWindow(display,window_info.id);
13980   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13981   XDelay(display,delay == 0UL ? 10UL : delay);
13982   (void) XSync(display,MagickFalse);
13983   return(window_info.id == root_window ? MagickTrue : MagickFalse);
13984 }
13985 \f
13986 /*
13987 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13988 %                                                                             %
13989 %                                                                             %
13990 %                                                                             %
13991 +   X D i s p l a y I m a g e                                                 %
13992 %                                                                             %
13993 %                                                                             %
13994 %                                                                             %
13995 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13996 %
13997 %  XDisplayImage() displays an image via X11.  A new image is created and
13998 %  returned if the user interactively transforms the displayed image.
13999 %
14000 %  The format of the XDisplayImage method is:
14001 %
14002 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14003 %        char **argv,int argc,Image **image,size_t *state,
14004 %        ExceptionInfo *exception)
14005 %
14006 %  A description of each parameter follows:
14007 %
14008 %    o nexus:  Method XDisplayImage returns an image when the
14009 %      user chooses 'Open Image' from the command menu or picks a tile
14010 %      from the image directory.  Otherwise a null image is returned.
14011 %
14012 %    o display: Specifies a connection to an X server;  returned from
14013 %      XOpenDisplay.
14014 %
14015 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14016 %
14017 %    o argv: Specifies the application's argument list.
14018 %
14019 %    o argc: Specifies the number of arguments.
14020 %
14021 %    o image: Specifies an address to an address of an Image structure;
14022 %
14023 %    o exception: return any errors or warnings in this structure.
14024 %
14025 */
14026 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14027   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14028 {
14029 #define MagnifySize  256  /* must be a power of 2 */
14030 #define MagickMenus  10
14031 #define MagickTitle  "Commands"
14032
14033   static const char
14034     *CommandMenu[] =
14035     {
14036       "File",
14037       "Edit",
14038       "View",
14039       "Transform",
14040       "Enhance",
14041       "Effects",
14042       "F/X",
14043       "Image Edit",
14044       "Miscellany",
14045       "Help",
14046       (char *) NULL
14047     },
14048     *FileMenu[] =
14049     {
14050       "Open...",
14051       "Next",
14052       "Former",
14053       "Select...",
14054       "Save...",
14055       "Print...",
14056       "Delete...",
14057       "New...",
14058       "Visual Directory...",
14059       "Quit",
14060       (char *) NULL
14061     },
14062     *EditMenu[] =
14063     {
14064       "Undo",
14065       "Redo",
14066       "Cut",
14067       "Copy",
14068       "Paste",
14069       (char *) NULL
14070     },
14071     *ViewMenu[] =
14072     {
14073       "Half Size",
14074       "Original Size",
14075       "Double Size",
14076       "Resize...",
14077       "Apply",
14078       "Refresh",
14079       "Restore",
14080       (char *) NULL
14081     },
14082     *TransformMenu[] =
14083     {
14084       "Crop",
14085       "Chop",
14086       "Flop",
14087       "Flip",
14088       "Rotate Right",
14089       "Rotate Left",
14090       "Rotate...",
14091       "Shear...",
14092       "Roll...",
14093       "Trim Edges",
14094       (char *) NULL
14095     },
14096     *EnhanceMenu[] =
14097     {
14098       "Hue...",
14099       "Saturation...",
14100       "Brightness...",
14101       "Gamma...",
14102       "Spiff",
14103       "Dull",
14104       "Contrast Stretch...",
14105       "Sigmoidal Contrast...",
14106       "Normalize",
14107       "Equalize",
14108       "Negate",
14109       "Grayscale",
14110       "Map...",
14111       "Quantize...",
14112       (char *) NULL
14113     },
14114     *EffectsMenu[] =
14115     {
14116       "Despeckle",
14117       "Emboss",
14118       "Reduce Noise",
14119       "Add Noise...",
14120       "Sharpen...",
14121       "Blur...",
14122       "Threshold...",
14123       "Edge Detect...",
14124       "Spread...",
14125       "Shade...",
14126       "Raise...",
14127       "Segment...",
14128       (char *) NULL
14129     },
14130     *FXMenu[] =
14131     {
14132       "Solarize...",
14133       "Sepia Tone...",
14134       "Swirl...",
14135       "Implode...",
14136       "Vignette...",
14137       "Wave...",
14138       "Oil Paint...",
14139       "Charcoal Draw...",
14140       (char *) NULL
14141     },
14142     *ImageEditMenu[] =
14143     {
14144       "Annotate...",
14145       "Draw...",
14146       "Color...",
14147       "Matte...",
14148       "Composite...",
14149       "Add Border...",
14150       "Add Frame...",
14151       "Comment...",
14152       "Launch...",
14153       "Region of Interest...",
14154       (char *) NULL
14155     },
14156     *MiscellanyMenu[] =
14157     {
14158       "Image Info",
14159       "Zoom Image",
14160       "Show Preview...",
14161       "Show Histogram",
14162       "Show Matte",
14163       "Background...",
14164       "Slide Show...",
14165       "Preferences...",
14166       (char *) NULL
14167     },
14168     *HelpMenu[] =
14169     {
14170       "Overview",
14171       "Browse Documentation",
14172       "About Display",
14173       (char *) NULL
14174     },
14175     *ShortCutsMenu[] =
14176     {
14177       "Next",
14178       "Former",
14179       "Open...",
14180       "Save...",
14181       "Print...",
14182       "Undo",
14183       "Restore",
14184       "Image Info",
14185       "Quit",
14186       (char *) NULL
14187     },
14188     *VirtualMenu[] =
14189     {
14190       "Image Info",
14191       "Print",
14192       "Next",
14193       "Quit",
14194       (char *) NULL
14195     };
14196
14197   static const char
14198     **Menus[MagickMenus] =
14199     {
14200       FileMenu,
14201       EditMenu,
14202       ViewMenu,
14203       TransformMenu,
14204       EnhanceMenu,
14205       EffectsMenu,
14206       FXMenu,
14207       ImageEditMenu,
14208       MiscellanyMenu,
14209       HelpMenu
14210     };
14211
14212   static CommandType
14213     CommandMenus[] =
14214     {
14215       NullCommand,
14216       NullCommand,
14217       NullCommand,
14218       NullCommand,
14219       NullCommand,
14220       NullCommand,
14221       NullCommand,
14222       NullCommand,
14223       NullCommand,
14224       NullCommand,
14225     },
14226     FileCommands[] =
14227     {
14228       OpenCommand,
14229       NextCommand,
14230       FormerCommand,
14231       SelectCommand,
14232       SaveCommand,
14233       PrintCommand,
14234       DeleteCommand,
14235       NewCommand,
14236       VisualDirectoryCommand,
14237       QuitCommand
14238     },
14239     EditCommands[] =
14240     {
14241       UndoCommand,
14242       RedoCommand,
14243       CutCommand,
14244       CopyCommand,
14245       PasteCommand
14246     },
14247     ViewCommands[] =
14248     {
14249       HalfSizeCommand,
14250       OriginalSizeCommand,
14251       DoubleSizeCommand,
14252       ResizeCommand,
14253       ApplyCommand,
14254       RefreshCommand,
14255       RestoreCommand
14256     },
14257     TransformCommands[] =
14258     {
14259       CropCommand,
14260       ChopCommand,
14261       FlopCommand,
14262       FlipCommand,
14263       RotateRightCommand,
14264       RotateLeftCommand,
14265       RotateCommand,
14266       ShearCommand,
14267       RollCommand,
14268       TrimCommand
14269     },
14270     EnhanceCommands[] =
14271     {
14272       HueCommand,
14273       SaturationCommand,
14274       BrightnessCommand,
14275       GammaCommand,
14276       SpiffCommand,
14277       DullCommand,
14278       ContrastStretchCommand,
14279       SigmoidalContrastCommand,
14280       NormalizeCommand,
14281       EqualizeCommand,
14282       NegateCommand,
14283       GrayscaleCommand,
14284       MapCommand,
14285       QuantizeCommand
14286     },
14287     EffectsCommands[] =
14288     {
14289       DespeckleCommand,
14290       EmbossCommand,
14291       ReduceNoiseCommand,
14292       AddNoiseCommand,
14293       SharpenCommand,
14294       BlurCommand,
14295       ThresholdCommand,
14296       EdgeDetectCommand,
14297       SpreadCommand,
14298       ShadeCommand,
14299       RaiseCommand,
14300       SegmentCommand
14301     },
14302     FXCommands[] =
14303     {
14304       SolarizeCommand,
14305       SepiaToneCommand,
14306       SwirlCommand,
14307       ImplodeCommand,
14308       VignetteCommand,
14309       WaveCommand,
14310       OilPaintCommand,
14311       CharcoalDrawCommand
14312     },
14313     ImageEditCommands[] =
14314     {
14315       AnnotateCommand,
14316       DrawCommand,
14317       ColorCommand,
14318       MatteCommand,
14319       CompositeCommand,
14320       AddBorderCommand,
14321       AddFrameCommand,
14322       CommentCommand,
14323       LaunchCommand,
14324       RegionofInterestCommand
14325     },
14326     MiscellanyCommands[] =
14327     {
14328       InfoCommand,
14329       ZoomCommand,
14330       ShowPreviewCommand,
14331       ShowHistogramCommand,
14332       ShowMatteCommand,
14333       BackgroundCommand,
14334       SlideShowCommand,
14335       PreferencesCommand
14336     },
14337     HelpCommands[] =
14338     {
14339       HelpCommand,
14340       BrowseDocumentationCommand,
14341       VersionCommand
14342     },
14343     ShortCutsCommands[] =
14344     {
14345       NextCommand,
14346       FormerCommand,
14347       OpenCommand,
14348       SaveCommand,
14349       PrintCommand,
14350       UndoCommand,
14351       RestoreCommand,
14352       InfoCommand,
14353       QuitCommand
14354     },
14355     VirtualCommands[] =
14356     {
14357       InfoCommand,
14358       PrintCommand,
14359       NextCommand,
14360       QuitCommand
14361     };
14362
14363   static CommandType
14364     *Commands[MagickMenus] =
14365     {
14366       FileCommands,
14367       EditCommands,
14368       ViewCommands,
14369       TransformCommands,
14370       EnhanceCommands,
14371       EffectsCommands,
14372       FXCommands,
14373       ImageEditCommands,
14374       MiscellanyCommands,
14375       HelpCommands
14376     };
14377
14378   char
14379     command[MaxTextExtent],
14380     *directory,
14381     geometry[MaxTextExtent],
14382     resource_name[MaxTextExtent];
14383
14384   CommandType
14385     command_type;
14386
14387   Image
14388     *display_image,
14389     *nexus;
14390
14391   int
14392     entry,
14393     id;
14394
14395   KeySym
14396     key_symbol;
14397
14398   MagickStatusType
14399     context_mask,
14400     status;
14401
14402   RectangleInfo
14403     geometry_info;
14404
14405   register int
14406     i;
14407
14408   static char
14409     working_directory[MaxTextExtent];
14410
14411   static XPoint
14412     vid_info;
14413
14414   static XWindowInfo
14415     *magick_windows[MaxXWindows];
14416
14417   static unsigned int
14418     number_windows;
14419
14420   struct stat
14421     attributes;
14422
14423   time_t
14424     timer,
14425     timestamp,
14426     update_time;
14427
14428   unsigned int
14429     height,
14430     width;
14431
14432   size_t
14433     delay;
14434
14435   WarningHandler
14436     warning_handler;
14437
14438   Window
14439     root_window;
14440
14441   XClassHint
14442     *class_hints;
14443
14444   XEvent
14445     event;
14446
14447   XFontStruct
14448     *font_info;
14449
14450   XGCValues
14451     context_values;
14452
14453   XPixelInfo
14454     *icon_pixel,
14455     *pixel;
14456
14457   XResourceInfo
14458     *icon_resources;
14459
14460   XStandardColormap
14461     *icon_map,
14462     *map_info;
14463
14464   XVisualInfo
14465     *icon_visual,
14466     *visual_info;
14467
14468   XWindowChanges
14469     window_changes;
14470
14471   XWindows
14472     *windows;
14473
14474   XWMHints
14475     *manager_hints;
14476
14477   assert(image != (Image **) NULL);
14478   assert((*image)->signature == MagickSignature);
14479   if ((*image)->debug != MagickFalse)
14480     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14481   display_image=(*image);
14482   warning_handler=(WarningHandler) NULL;
14483   windows=XSetWindows((XWindows *) ~0);
14484   if (windows != (XWindows *) NULL)
14485     {
14486       int
14487         status;
14488
14489       status=chdir(working_directory);
14490       if (status == -1)
14491         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14492           "UnableToOpenFile","%s",working_directory);
14493       warning_handler=resource_info->display_warnings ?
14494         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14495       warning_handler=resource_info->display_warnings ?
14496         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14497     }
14498   else
14499     {
14500       /*
14501         Allocate windows structure.
14502       */
14503       resource_info->colors=display_image->colors;
14504       windows=XSetWindows(XInitializeWindows(display,resource_info));
14505       if (windows == (XWindows *) NULL)
14506         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14507           (*image)->filename);
14508       /*
14509         Initialize window id's.
14510       */
14511       number_windows=0;
14512       magick_windows[number_windows++]=(&windows->icon);
14513       magick_windows[number_windows++]=(&windows->backdrop);
14514       magick_windows[number_windows++]=(&windows->image);
14515       magick_windows[number_windows++]=(&windows->info);
14516       magick_windows[number_windows++]=(&windows->command);
14517       magick_windows[number_windows++]=(&windows->widget);
14518       magick_windows[number_windows++]=(&windows->popup);
14519       magick_windows[number_windows++]=(&windows->magnify);
14520       magick_windows[number_windows++]=(&windows->pan);
14521       for (i=0; i < (int) number_windows; i++)
14522         magick_windows[i]->id=(Window) NULL;
14523       vid_info.x=0;
14524       vid_info.y=0;
14525     }
14526   /*
14527     Initialize font info.
14528   */
14529   if (windows->font_info != (XFontStruct *) NULL)
14530     (void) XFreeFont(display,windows->font_info);
14531   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14532   if (windows->font_info == (XFontStruct *) NULL)
14533     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14534       resource_info->font);
14535   /*
14536     Initialize Standard Colormap.
14537   */
14538   map_info=windows->map_info;
14539   icon_map=windows->icon_map;
14540   visual_info=windows->visual_info;
14541   icon_visual=windows->icon_visual;
14542   pixel=windows->pixel_info;
14543   icon_pixel=windows->icon_pixel;
14544   font_info=windows->font_info;
14545   icon_resources=windows->icon_resources;
14546   class_hints=windows->class_hints;
14547   manager_hints=windows->manager_hints;
14548   root_window=XRootWindow(display,visual_info->screen);
14549   nexus=NewImageList();
14550   if (display_image->debug != MagickFalse)
14551     {
14552       (void) LogMagickEvent(X11Event,GetMagickModule(),
14553         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14554         (double) display_image->scene,(double) display_image->columns,
14555         (double) display_image->rows);
14556       if (display_image->colors != 0)
14557         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14558           display_image->colors);
14559       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14560         display_image->magick);
14561     }
14562   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14563     map_info,pixel);
14564   display_image->taint=MagickFalse;
14565   /*
14566     Initialize graphic context.
14567   */
14568   windows->context.id=(Window) NULL;
14569   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14570     resource_info,&windows->context);
14571   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14572   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14573   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14574   manager_hints->flags=InputHint | StateHint;
14575   manager_hints->input=MagickFalse;
14576   manager_hints->initial_state=WithdrawnState;
14577   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14578     &windows->context);
14579   if (display_image->debug != MagickFalse)
14580     (void) LogMagickEvent(X11Event,GetMagickModule(),
14581       "Window id: 0x%lx (context)",windows->context.id);
14582   context_values.background=pixel->background_color.pixel;
14583   context_values.font=font_info->fid;
14584   context_values.foreground=pixel->foreground_color.pixel;
14585   context_values.graphics_exposures=MagickFalse;
14586   context_mask=(MagickStatusType)
14587     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14588   if (pixel->annotate_context != (GC) NULL)
14589     (void) XFreeGC(display,pixel->annotate_context);
14590   pixel->annotate_context=XCreateGC(display,windows->context.id,
14591     context_mask,&context_values);
14592   if (pixel->annotate_context == (GC) NULL)
14593     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14594       display_image->filename);
14595   context_values.background=pixel->depth_color.pixel;
14596   if (pixel->widget_context != (GC) NULL)
14597     (void) XFreeGC(display,pixel->widget_context);
14598   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14599     &context_values);
14600   if (pixel->widget_context == (GC) NULL)
14601     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14602       display_image->filename);
14603   context_values.background=pixel->foreground_color.pixel;
14604   context_values.foreground=pixel->background_color.pixel;
14605   context_values.plane_mask=context_values.background ^
14606     context_values.foreground;
14607   if (pixel->highlight_context != (GC) NULL)
14608     (void) XFreeGC(display,pixel->highlight_context);
14609   pixel->highlight_context=XCreateGC(display,windows->context.id,
14610     (size_t) (context_mask | GCPlaneMask),&context_values);
14611   if (pixel->highlight_context == (GC) NULL)
14612     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14613       display_image->filename);
14614   (void) XDestroyWindow(display,windows->context.id);
14615   /*
14616     Initialize icon window.
14617   */
14618   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14619     icon_resources,&windows->icon);
14620   windows->icon.geometry=resource_info->icon_geometry;
14621   XBestIconSize(display,&windows->icon,display_image);
14622   windows->icon.attributes.colormap=XDefaultColormap(display,
14623     icon_visual->screen);
14624   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14625   manager_hints->flags=InputHint | StateHint;
14626   manager_hints->input=MagickFalse;
14627   manager_hints->initial_state=IconicState;
14628   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14629     &windows->icon);
14630   if (display_image->debug != MagickFalse)
14631     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14632       windows->icon.id);
14633   /*
14634     Initialize graphic context for icon window.
14635   */
14636   if (icon_pixel->annotate_context != (GC) NULL)
14637     (void) XFreeGC(display,icon_pixel->annotate_context);
14638   context_values.background=icon_pixel->background_color.pixel;
14639   context_values.foreground=icon_pixel->foreground_color.pixel;
14640   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14641     (size_t) (GCBackground | GCForeground),&context_values);
14642   if (icon_pixel->annotate_context == (GC) NULL)
14643     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14644       display_image->filename);
14645   windows->icon.annotate_context=icon_pixel->annotate_context;
14646   /*
14647     Initialize Image window.
14648   */
14649   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14650     &windows->image);
14651   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14652   if (resource_info->use_shared_memory == MagickFalse)
14653     windows->image.shared_memory=MagickFalse;
14654   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14655     {
14656       char
14657         *title;
14658
14659       title=InterpretImageProperties(resource_info->image_info,display_image,
14660         resource_info->title,exception);
14661       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14662       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14663       title=DestroyString(title);
14664     }
14665   else
14666     {
14667       char
14668         filename[MaxTextExtent];
14669
14670       /*
14671         Window name is the base of the filename.
14672       */
14673       GetPathComponent(display_image->magick_filename,TailPath,filename);
14674       if (GetImageListLength(display_image) == 1)
14675         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14676           "%s: %s",MagickPackageName,filename);
14677       else
14678         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14679           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14680           (double) display_image->scene,(double) GetImageListLength(
14681           display_image));
14682       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14683     }
14684   if (resource_info->immutable)
14685     windows->image.immutable=MagickTrue;
14686   windows->image.use_pixmap=resource_info->use_pixmap;
14687   windows->image.geometry=resource_info->image_geometry;
14688   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14689     XDisplayWidth(display,visual_info->screen),
14690     XDisplayHeight(display,visual_info->screen));
14691   geometry_info.width=display_image->columns;
14692   geometry_info.height=display_image->rows;
14693   geometry_info.x=0;
14694   geometry_info.y=0;
14695   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14696     &geometry_info.width,&geometry_info.height);
14697   windows->image.width=(unsigned int) geometry_info.width;
14698   windows->image.height=(unsigned int) geometry_info.height;
14699   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14700     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14701     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14702     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14703   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14704     resource_info,&windows->backdrop);
14705   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14706     {
14707       /*
14708         Initialize backdrop window.
14709       */
14710       windows->backdrop.x=0;
14711       windows->backdrop.y=0;
14712       (void) CloneString(&windows->backdrop.name,"Backdrop");
14713       windows->backdrop.flags=(size_t) (USSize | USPosition);
14714       windows->backdrop.width=(unsigned int)
14715         XDisplayWidth(display,visual_info->screen);
14716       windows->backdrop.height=(unsigned int)
14717         XDisplayHeight(display,visual_info->screen);
14718       windows->backdrop.border_width=0;
14719       windows->backdrop.immutable=MagickTrue;
14720       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14721         ButtonReleaseMask;
14722       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14723         StructureNotifyMask;
14724       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14725       manager_hints->icon_window=windows->icon.id;
14726       manager_hints->input=MagickTrue;
14727       manager_hints->initial_state=resource_info->iconic ? IconicState :
14728         NormalState;
14729       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14730         &windows->backdrop);
14731       if (display_image->debug != MagickFalse)
14732         (void) LogMagickEvent(X11Event,GetMagickModule(),
14733           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14734       (void) XMapWindow(display,windows->backdrop.id);
14735       (void) XClearWindow(display,windows->backdrop.id);
14736       if (windows->image.id != (Window) NULL)
14737         {
14738           (void) XDestroyWindow(display,windows->image.id);
14739           windows->image.id=(Window) NULL;
14740         }
14741       /*
14742         Position image in the center the backdrop.
14743       */
14744       windows->image.flags|=USPosition;
14745       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14746         (windows->image.width/2);
14747       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14748         (windows->image.height/2);
14749     }
14750   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14751   manager_hints->icon_window=windows->icon.id;
14752   manager_hints->input=MagickTrue;
14753   manager_hints->initial_state=resource_info->iconic ? IconicState :
14754     NormalState;
14755   if (windows->group_leader.id != (Window) NULL)
14756     {
14757       /*
14758         Follow the leader.
14759       */
14760       manager_hints->flags|=WindowGroupHint;
14761       manager_hints->window_group=windows->group_leader.id;
14762       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14763       if (display_image->debug != MagickFalse)
14764         (void) LogMagickEvent(X11Event,GetMagickModule(),
14765           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14766     }
14767   XMakeWindow(display,
14768     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14769     argv,argc,class_hints,manager_hints,&windows->image);
14770   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14771     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14772   if (windows->group_leader.id != (Window) NULL)
14773     (void) XSetTransientForHint(display,windows->image.id,
14774       windows->group_leader.id);
14775   if (display_image->debug != MagickFalse)
14776     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14777       windows->image.id);
14778   /*
14779     Initialize Info widget.
14780   */
14781   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14782     &windows->info);
14783   (void) CloneString(&windows->info.name,"Info");
14784   (void) CloneString(&windows->info.icon_name,"Info");
14785   windows->info.border_width=1;
14786   windows->info.x=2;
14787   windows->info.y=2;
14788   windows->info.flags|=PPosition;
14789   windows->info.attributes.win_gravity=UnmapGravity;
14790   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14791     StructureNotifyMask;
14792   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14793   manager_hints->input=MagickFalse;
14794   manager_hints->initial_state=NormalState;
14795   manager_hints->window_group=windows->image.id;
14796   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14797     &windows->info);
14798   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14799     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14800   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14801     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14802   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14803   if (windows->image.mapped != MagickFalse)
14804     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14805   if (display_image->debug != MagickFalse)
14806     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14807       windows->info.id);
14808   /*
14809     Initialize Command widget.
14810   */
14811   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14812     resource_info,&windows->command);
14813   windows->command.data=MagickMenus;
14814   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14815   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14816     resource_info->client_name);
14817   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14818     resource_name,"geometry",(char *) NULL);
14819   (void) CloneString(&windows->command.name,MagickTitle);
14820   windows->command.border_width=0;
14821   windows->command.flags|=PPosition;
14822   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14823     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14824     OwnerGrabButtonMask | StructureNotifyMask;
14825   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14826   manager_hints->input=MagickTrue;
14827   manager_hints->initial_state=NormalState;
14828   manager_hints->window_group=windows->image.id;
14829   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14830     &windows->command);
14831   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14832     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14833     HighlightHeight);
14834   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14835     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14836   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14837   if (windows->command.mapped != MagickFalse)
14838     (void) XMapRaised(display,windows->command.id);
14839   if (display_image->debug != MagickFalse)
14840     (void) LogMagickEvent(X11Event,GetMagickModule(),
14841       "Window id: 0x%lx (command)",windows->command.id);
14842   /*
14843     Initialize Widget window.
14844   */
14845   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14846     resource_info,&windows->widget);
14847   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14848     resource_info->client_name);
14849   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14850     resource_name,"geometry",(char *) NULL);
14851   windows->widget.border_width=0;
14852   windows->widget.flags|=PPosition;
14853   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14854     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14855     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14856     StructureNotifyMask;
14857   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14858   manager_hints->input=MagickTrue;
14859   manager_hints->initial_state=NormalState;
14860   manager_hints->window_group=windows->image.id;
14861   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14862     &windows->widget);
14863   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14864     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14865   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14866     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14867   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14868   if (display_image->debug != MagickFalse)
14869     (void) LogMagickEvent(X11Event,GetMagickModule(),
14870       "Window id: 0x%lx (widget)",windows->widget.id);
14871   /*
14872     Initialize popup window.
14873   */
14874   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14875     resource_info,&windows->popup);
14876   windows->popup.border_width=0;
14877   windows->popup.flags|=PPosition;
14878   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14879     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14880     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14881   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14882   manager_hints->input=MagickTrue;
14883   manager_hints->initial_state=NormalState;
14884   manager_hints->window_group=windows->image.id;
14885   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14886     &windows->popup);
14887   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14888     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14889   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14890     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14891   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14892   if (display_image->debug != MagickFalse)
14893     (void) LogMagickEvent(X11Event,GetMagickModule(),
14894       "Window id: 0x%lx (pop up)",windows->popup.id);
14895   /*
14896     Initialize Magnify window and cursor.
14897   */
14898   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14899     resource_info,&windows->magnify);
14900   if (resource_info->use_shared_memory == MagickFalse)
14901     windows->magnify.shared_memory=MagickFalse;
14902   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14903     resource_info->client_name);
14904   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14905     resource_name,"geometry",(char *) NULL);
14906   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14907     resource_info->magnify);
14908   if (windows->magnify.cursor != (Cursor) NULL)
14909     (void) XFreeCursor(display,windows->magnify.cursor);
14910   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14911     map_info->colormap,resource_info->background_color,
14912     resource_info->foreground_color);
14913   if (windows->magnify.cursor == (Cursor) NULL)
14914     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14915       display_image->filename);
14916   windows->magnify.width=MagnifySize;
14917   windows->magnify.height=MagnifySize;
14918   windows->magnify.flags|=PPosition;
14919   windows->magnify.min_width=MagnifySize;
14920   windows->magnify.min_height=MagnifySize;
14921   windows->magnify.width_inc=MagnifySize;
14922   windows->magnify.height_inc=MagnifySize;
14923   windows->magnify.data=resource_info->magnify;
14924   windows->magnify.attributes.cursor=windows->magnify.cursor;
14925   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14926     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14927     StructureNotifyMask;
14928   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14929   manager_hints->input=MagickTrue;
14930   manager_hints->initial_state=NormalState;
14931   manager_hints->window_group=windows->image.id;
14932   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14933     &windows->magnify);
14934   if (display_image->debug != MagickFalse)
14935     (void) LogMagickEvent(X11Event,GetMagickModule(),
14936       "Window id: 0x%lx (magnify)",windows->magnify.id);
14937   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14938   /*
14939     Initialize panning window.
14940   */
14941   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14942     resource_info,&windows->pan);
14943   (void) CloneString(&windows->pan.name,"Pan Icon");
14944   windows->pan.width=windows->icon.width;
14945   windows->pan.height=windows->icon.height;
14946   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14947     resource_info->client_name);
14948   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14949     resource_name,"geometry",(char *) NULL);
14950   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14951     &windows->pan.width,&windows->pan.height);
14952   windows->pan.flags|=PPosition;
14953   windows->pan.immutable=MagickTrue;
14954   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14955     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14956     StructureNotifyMask;
14957   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14958   manager_hints->input=MagickFalse;
14959   manager_hints->initial_state=NormalState;
14960   manager_hints->window_group=windows->image.id;
14961   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14962     &windows->pan);
14963   if (display_image->debug != MagickFalse)
14964     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14965       windows->pan.id);
14966   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14967   if (windows->info.mapped != MagickFalse)
14968     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14969   if ((windows->image.mapped == MagickFalse) ||
14970       (windows->backdrop.id != (Window) NULL))
14971     (void) XMapWindow(display,windows->image.id);
14972   /*
14973     Set our progress monitor and warning handlers.
14974   */
14975   if (warning_handler == (WarningHandler) NULL)
14976     {
14977       warning_handler=resource_info->display_warnings ?
14978         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14979       warning_handler=resource_info->display_warnings ?
14980         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14981     }
14982   /*
14983     Initialize Image and Magnify X images.
14984   */
14985   windows->image.x=0;
14986   windows->image.y=0;
14987   windows->magnify.shape=MagickFalse;
14988   width=(unsigned int) display_image->columns;
14989   height=(unsigned int) display_image->rows;
14990   if ((display_image->columns != width) || (display_image->rows != height))
14991     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14992       display_image->filename);
14993   status=XMakeImage(display,resource_info,&windows->image,display_image,
14994     width,height,exception);
14995   if (status == MagickFalse)
14996     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14997       display_image->filename);
14998   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14999     windows->magnify.width,windows->magnify.height,exception);
15000   if (status == MagickFalse)
15001     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15002       display_image->filename);
15003   if (windows->magnify.mapped != MagickFalse)
15004     (void) XMapRaised(display,windows->magnify.id);
15005   if (windows->pan.mapped != MagickFalse)
15006     (void) XMapRaised(display,windows->pan.id);
15007   windows->image.window_changes.width=(int) display_image->columns;
15008   windows->image.window_changes.height=(int) display_image->rows;
15009   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15010   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15011   (void) XSync(display,MagickFalse);
15012   /*
15013     Respond to events.
15014   */
15015   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15016   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15017   update_time=0;
15018   if (resource_info->update != MagickFalse)
15019     {
15020       MagickBooleanType
15021         status;
15022
15023       /*
15024         Determine when file data was last modified.
15025       */
15026       status=GetPathAttributes(display_image->filename,&attributes);
15027       if (status != MagickFalse)
15028         update_time=attributes.st_mtime;
15029     }
15030   *state&=(~FormerImageState);
15031   *state&=(~MontageImageState);
15032   *state&=(~NextImageState);
15033   do
15034   {
15035     /*
15036       Handle a window event.
15037     */
15038     if (windows->image.mapped != MagickFalse)
15039       if ((display_image->delay != 0) || (resource_info->update != 0))
15040         {
15041           if (timer < time((time_t *) NULL))
15042             {
15043               if (resource_info->update == MagickFalse)
15044                 *state|=NextImageState | ExitState;
15045               else
15046                 {
15047                   MagickBooleanType
15048                     status;
15049
15050                   /*
15051                     Determine if image file was modified.
15052                   */
15053                   status=GetPathAttributes(display_image->filename,&attributes);
15054                   if (status != MagickFalse)
15055                     if (update_time != attributes.st_mtime)
15056                       {
15057                         /*
15058                           Redisplay image.
15059                         */
15060                         (void) FormatLocaleString(
15061                           resource_info->image_info->filename,MaxTextExtent,
15062                           "%s:%s",display_image->magick,
15063                           display_image->filename);
15064                         nexus=ReadImage(resource_info->image_info,
15065                           &display_image->exception);
15066                         if (nexus != (Image *) NULL)
15067                           {
15068                             nexus=DestroyImage(nexus);
15069                             *state|=NextImageState | ExitState;
15070                           }
15071                       }
15072                   delay=display_image->delay/MagickMax(
15073                     display_image->ticks_per_second,1L);
15074                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15075                 }
15076             }
15077           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15078             {
15079               /*
15080                 Do not block if delay > 0.
15081               */
15082               XDelay(display,SuspendTime << 2);
15083               continue;
15084             }
15085         }
15086     timestamp=time((time_t *) NULL);
15087     (void) XNextEvent(display,&event);
15088     if (windows->image.stasis == MagickFalse)
15089       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15090         MagickTrue : MagickFalse;
15091     if (windows->magnify.stasis == MagickFalse)
15092       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15093         MagickTrue : MagickFalse;
15094     if (event.xany.window == windows->command.id)
15095       {
15096         /*
15097           Select a command from the Command widget.
15098         */
15099         id=XCommandWidget(display,windows,CommandMenu,&event);
15100         if (id < 0)
15101           continue;
15102         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15103         command_type=CommandMenus[id];
15104         if (id < MagickMenus)
15105           {
15106             /*
15107               Select a command from a pop-up menu.
15108             */
15109             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15110               command);
15111             if (entry < 0)
15112               continue;
15113             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15114             command_type=Commands[id][entry];
15115           }
15116         if (command_type != NullCommand)
15117           nexus=XMagickCommand(display,resource_info,windows,command_type,
15118             &display_image,exception);
15119         continue;
15120       }
15121     switch (event.type)
15122     {
15123       case ButtonPress:
15124       {
15125         if (display_image->debug != MagickFalse)
15126           (void) LogMagickEvent(X11Event,GetMagickModule(),
15127             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15128             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15129         if ((event.xbutton.button == Button3) &&
15130             (event.xbutton.state & Mod1Mask))
15131           {
15132             /*
15133               Convert Alt-Button3 to Button2.
15134             */
15135             event.xbutton.button=Button2;
15136             event.xbutton.state&=(~Mod1Mask);
15137           }
15138         if (event.xbutton.window == windows->backdrop.id)
15139           {
15140             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15141               event.xbutton.time);
15142             break;
15143           }
15144         if (event.xbutton.window == windows->image.id)
15145           {
15146             switch (event.xbutton.button)
15147             {
15148               case Button1:
15149               {
15150                 if (resource_info->immutable)
15151                   {
15152                     /*
15153                       Select a command from the Virtual menu.
15154                     */
15155                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15156                       command);
15157                     if (entry >= 0)
15158                       nexus=XMagickCommand(display,resource_info,windows,
15159                         VirtualCommands[entry],&display_image,exception);
15160                     break;
15161                   }
15162                 /*
15163                   Map/unmap Command widget.
15164                 */
15165                 if (windows->command.mapped != MagickFalse)
15166                   (void) XWithdrawWindow(display,windows->command.id,
15167                     windows->command.screen);
15168                 else
15169                   {
15170                     (void) XCommandWidget(display,windows,CommandMenu,
15171                       (XEvent *) NULL);
15172                     (void) XMapRaised(display,windows->command.id);
15173                   }
15174                 break;
15175               }
15176               case Button2:
15177               {
15178                 /*
15179                   User pressed the image magnify button.
15180                 */
15181                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15182                   &display_image,exception);
15183                 XMagnifyImage(display,windows,&event);
15184                 break;
15185               }
15186               case Button3:
15187               {
15188                 if (resource_info->immutable)
15189                   {
15190                     /*
15191                       Select a command from the Virtual menu.
15192                     */
15193                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15194                       command);
15195                     if (entry >= 0)
15196                       nexus=XMagickCommand(display,resource_info,windows,
15197                         VirtualCommands[entry],&display_image,exception);
15198                     break;
15199                   }
15200                 if (display_image->montage != (char *) NULL)
15201                   {
15202                     /*
15203                       Open or delete a tile from a visual image directory.
15204                     */
15205                     nexus=XTileImage(display,resource_info,windows,
15206                       display_image,&event,exception);
15207                     if (nexus != (Image *) NULL)
15208                       *state|=MontageImageState | NextImageState | ExitState;
15209                     vid_info.x=(short int) windows->image.x;
15210                     vid_info.y=(short int) windows->image.y;
15211                     break;
15212                   }
15213                 /*
15214                   Select a command from the Short Cuts menu.
15215                 */
15216                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15217                   command);
15218                 if (entry >= 0)
15219                   nexus=XMagickCommand(display,resource_info,windows,
15220                     ShortCutsCommands[entry],&display_image,exception);
15221                 break;
15222               }
15223               case Button4:
15224               {
15225                 /*
15226                   Wheel up.
15227                 */
15228                 XTranslateImage(display,windows,*image,XK_Up);
15229                 break;
15230               }
15231               case Button5:
15232               {
15233                 /*
15234                   Wheel down.
15235                 */
15236                 XTranslateImage(display,windows,*image,XK_Down);
15237                 break;
15238               }
15239               default:
15240                 break;
15241             }
15242             break;
15243           }
15244         if (event.xbutton.window == windows->magnify.id)
15245           {
15246             int
15247               factor;
15248
15249             static const char
15250               *MagnifyMenu[] =
15251               {
15252                 "2",
15253                 "4",
15254                 "5",
15255                 "6",
15256                 "7",
15257                 "8",
15258                 "9",
15259                 "3",
15260                 (char *) NULL,
15261               };
15262
15263             static KeySym
15264               MagnifyCommands[] =
15265               {
15266                 XK_2,
15267                 XK_4,
15268                 XK_5,
15269                 XK_6,
15270                 XK_7,
15271                 XK_8,
15272                 XK_9,
15273                 XK_3
15274               };
15275
15276             /*
15277               Select a magnify factor from the pop-up menu.
15278             */
15279             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15280             if (factor >= 0)
15281               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15282             break;
15283           }
15284         if (event.xbutton.window == windows->pan.id)
15285           {
15286             switch (event.xbutton.button)
15287             {
15288               case Button4:
15289               {
15290                 /*
15291                   Wheel up.
15292                 */
15293                 XTranslateImage(display,windows,*image,XK_Up);
15294                 break;
15295               }
15296               case Button5:
15297               {
15298                 /*
15299                   Wheel down.
15300                 */
15301                 XTranslateImage(display,windows,*image,XK_Down);
15302                 break;
15303               }
15304               default:
15305               {
15306                 XPanImage(display,windows,&event);
15307                 break;
15308               }
15309             }
15310             break;
15311           }
15312         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15313           1L);
15314         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15315         break;
15316       }
15317       case ButtonRelease:
15318       {
15319         if (display_image->debug != MagickFalse)
15320           (void) LogMagickEvent(X11Event,GetMagickModule(),
15321             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15322             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15323         break;
15324       }
15325       case ClientMessage:
15326       {
15327         if (display_image->debug != MagickFalse)
15328           (void) LogMagickEvent(X11Event,GetMagickModule(),
15329             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15330             event.xclient.message_type,event.xclient.format,(unsigned long)
15331             event.xclient.data.l[0]);
15332         if (event.xclient.message_type == windows->im_protocols)
15333           {
15334             if (*event.xclient.data.l == (long) windows->im_update_widget)
15335               {
15336                 (void) CloneString(&windows->command.name,MagickTitle);
15337                 windows->command.data=MagickMenus;
15338                 (void) XCommandWidget(display,windows,CommandMenu,
15339                   (XEvent *) NULL);
15340                 break;
15341               }
15342             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15343               {
15344                 /*
15345                   Update graphic context and window colormap.
15346                 */
15347                 for (i=0; i < (int) number_windows; i++)
15348                 {
15349                   if (magick_windows[i]->id == windows->icon.id)
15350                     continue;
15351                   context_values.background=pixel->background_color.pixel;
15352                   context_values.foreground=pixel->foreground_color.pixel;
15353                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15354                     context_mask,&context_values);
15355                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15356                     context_mask,&context_values);
15357                   context_values.background=pixel->foreground_color.pixel;
15358                   context_values.foreground=pixel->background_color.pixel;
15359                   context_values.plane_mask=context_values.background ^
15360                     context_values.foreground;
15361                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15362                     (size_t) (context_mask | GCPlaneMask),
15363                     &context_values);
15364                   magick_windows[i]->attributes.background_pixel=
15365                     pixel->background_color.pixel;
15366                   magick_windows[i]->attributes.border_pixel=
15367                     pixel->border_color.pixel;
15368                   magick_windows[i]->attributes.colormap=map_info->colormap;
15369                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15370                     (unsigned long) magick_windows[i]->mask,
15371                     &magick_windows[i]->attributes);
15372                 }
15373                 if (windows->pan.mapped != MagickFalse)
15374                   {
15375                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15376                       windows->pan.pixmap);
15377                     (void) XClearWindow(display,windows->pan.id);
15378                     XDrawPanRectangle(display,windows);
15379                   }
15380                 if (windows->backdrop.id != (Window) NULL)
15381                   (void) XInstallColormap(display,map_info->colormap);
15382                 break;
15383               }
15384             if (*event.xclient.data.l == (long) windows->im_former_image)
15385               {
15386                 *state|=FormerImageState | ExitState;
15387                 break;
15388               }
15389             if (*event.xclient.data.l == (long) windows->im_next_image)
15390               {
15391                 *state|=NextImageState | ExitState;
15392                 break;
15393               }
15394             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15395               {
15396                 *state|=RetainColorsState;
15397                 break;
15398               }
15399             if (*event.xclient.data.l == (long) windows->im_exit)
15400               {
15401                 *state|=ExitState;
15402                 break;
15403               }
15404             break;
15405           }
15406         if (event.xclient.message_type == windows->dnd_protocols)
15407           {
15408             Atom
15409               selection,
15410               type;
15411
15412             int
15413               format,
15414               status;
15415
15416             unsigned char
15417               *data;
15418
15419             unsigned long
15420               after,
15421               length;
15422
15423             /*
15424               Display image named by the Drag-and-Drop selection.
15425             */
15426             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15427               break;
15428             selection=XInternAtom(display,"DndSelection",MagickFalse);
15429             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15430               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15431               &length,&after,&data);
15432             if ((status != Success) || (length == 0))
15433               break;
15434             if (*event.xclient.data.l == 2)
15435               {
15436                 /*
15437                   Offix DND.
15438                 */
15439                 (void) CopyMagickString(resource_info->image_info->filename,
15440                   (char *) data,MaxTextExtent);
15441               }
15442             else
15443               {
15444                 /*
15445                   XDND.
15446                 */
15447                 if (strncmp((char *) data, "file:", 5) != 0)
15448                   {
15449                     (void) XFree((void *) data);
15450                     break;
15451                   }
15452                 (void) CopyMagickString(resource_info->image_info->filename,
15453                   ((char *) data)+5,MaxTextExtent);
15454               }
15455             nexus=ReadImage(resource_info->image_info,
15456               &display_image->exception);
15457             CatchException(&display_image->exception);
15458             if (nexus != (Image *) NULL)
15459               *state|=NextImageState | ExitState;
15460             (void) XFree((void *) data);
15461             break;
15462           }
15463         /*
15464           If client window delete message, exit.
15465         */
15466         if (event.xclient.message_type != windows->wm_protocols)
15467           break;
15468         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15469           break;
15470         (void) XWithdrawWindow(display,event.xclient.window,
15471           visual_info->screen);
15472         if (event.xclient.window == windows->image.id)
15473           {
15474             *state|=ExitState;
15475             break;
15476           }
15477         if (event.xclient.window == windows->pan.id)
15478           {
15479             /*
15480               Restore original image size when pan window is deleted.
15481             */
15482             windows->image.window_changes.width=windows->image.ximage->width;
15483             windows->image.window_changes.height=windows->image.ximage->height;
15484             (void) XConfigureImage(display,resource_info,windows,
15485               display_image,exception);
15486           }
15487         break;
15488       }
15489       case ConfigureNotify:
15490       {
15491         if (display_image->debug != MagickFalse)
15492           (void) LogMagickEvent(X11Event,GetMagickModule(),
15493             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15494             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15495             event.xconfigure.y,event.xconfigure.send_event);
15496         if (event.xconfigure.window == windows->image.id)
15497           {
15498             /*
15499               Image window has a new configuration.
15500             */
15501             if (event.xconfigure.send_event != 0)
15502               {
15503                 XWindowChanges
15504                   window_changes;
15505
15506                 /*
15507                   Position the transient windows relative of the Image window.
15508                 */
15509                 if (windows->command.geometry == (char *) NULL)
15510                   if (windows->command.mapped == MagickFalse)
15511                     {
15512                       windows->command.x=event.xconfigure.x-
15513                         windows->command.width-25;
15514                       windows->command.y=event.xconfigure.y;
15515                       XConstrainWindowPosition(display,&windows->command);
15516                       window_changes.x=windows->command.x;
15517                       window_changes.y=windows->command.y;
15518                       (void) XReconfigureWMWindow(display,windows->command.id,
15519                         windows->command.screen,(unsigned int) (CWX | CWY),
15520                         &window_changes);
15521                     }
15522                 if (windows->widget.geometry == (char *) NULL)
15523                   if (windows->widget.mapped == MagickFalse)
15524                     {
15525                       windows->widget.x=event.xconfigure.x+
15526                         event.xconfigure.width/10;
15527                       windows->widget.y=event.xconfigure.y+
15528                         event.xconfigure.height/10;
15529                       XConstrainWindowPosition(display,&windows->widget);
15530                       window_changes.x=windows->widget.x;
15531                       window_changes.y=windows->widget.y;
15532                       (void) XReconfigureWMWindow(display,windows->widget.id,
15533                         windows->widget.screen,(unsigned int) (CWX | CWY),
15534                         &window_changes);
15535                     }
15536                 if (windows->magnify.geometry == (char *) NULL)
15537                   if (windows->magnify.mapped == MagickFalse)
15538                     {
15539                       windows->magnify.x=event.xconfigure.x+
15540                         event.xconfigure.width+25;
15541                       windows->magnify.y=event.xconfigure.y;
15542                       XConstrainWindowPosition(display,&windows->magnify);
15543                       window_changes.x=windows->magnify.x;
15544                       window_changes.y=windows->magnify.y;
15545                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15546                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15547                         &window_changes);
15548                     }
15549                 if (windows->pan.geometry == (char *) NULL)
15550                   if (windows->pan.mapped == MagickFalse)
15551                     {
15552                       windows->pan.x=event.xconfigure.x+
15553                         event.xconfigure.width+25;
15554                       windows->pan.y=event.xconfigure.y+
15555                         windows->magnify.height+50;
15556                       XConstrainWindowPosition(display,&windows->pan);
15557                       window_changes.x=windows->pan.x;
15558                       window_changes.y=windows->pan.y;
15559                       (void) XReconfigureWMWindow(display,windows->pan.id,
15560                         windows->pan.screen,(unsigned int) (CWX | CWY),
15561                         &window_changes);
15562                     }
15563               }
15564             if ((event.xconfigure.width == (int) windows->image.width) &&
15565                 (event.xconfigure.height == (int) windows->image.height))
15566               break;
15567             windows->image.width=(unsigned int) event.xconfigure.width;
15568             windows->image.height=(unsigned int) event.xconfigure.height;
15569             windows->image.x=0;
15570             windows->image.y=0;
15571             if (display_image->montage != (char *) NULL)
15572               {
15573                 windows->image.x=vid_info.x;
15574                 windows->image.y=vid_info.y;
15575               }
15576             if ((windows->image.mapped != MagickFalse) &&
15577                 (windows->image.stasis != MagickFalse))
15578               {
15579                 /*
15580                   Update image window configuration.
15581                 */
15582                 windows->image.window_changes.width=event.xconfigure.width;
15583                 windows->image.window_changes.height=event.xconfigure.height;
15584                 (void) XConfigureImage(display,resource_info,windows,
15585                   display_image,exception);
15586               }
15587             /*
15588               Update pan window configuration.
15589             */
15590             if ((event.xconfigure.width < windows->image.ximage->width) ||
15591                 (event.xconfigure.height < windows->image.ximage->height))
15592               {
15593                 (void) XMapRaised(display,windows->pan.id);
15594                 XDrawPanRectangle(display,windows);
15595               }
15596             else
15597               if (windows->pan.mapped != MagickFalse)
15598                 (void) XWithdrawWindow(display,windows->pan.id,
15599                   windows->pan.screen);
15600             break;
15601           }
15602         if (event.xconfigure.window == windows->magnify.id)
15603           {
15604             unsigned int
15605               magnify;
15606
15607             /*
15608               Magnify window has a new configuration.
15609             */
15610             windows->magnify.width=(unsigned int) event.xconfigure.width;
15611             windows->magnify.height=(unsigned int) event.xconfigure.height;
15612             if (windows->magnify.mapped == MagickFalse)
15613               break;
15614             magnify=1;
15615             while ((int) magnify <= event.xconfigure.width)
15616               magnify<<=1;
15617             while ((int) magnify <= event.xconfigure.height)
15618               magnify<<=1;
15619             magnify>>=1;
15620             if (((int) magnify != event.xconfigure.width) ||
15621                 ((int) magnify != event.xconfigure.height))
15622               {
15623                 window_changes.width=(int) magnify;
15624                 window_changes.height=(int) magnify;
15625                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15626                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15627                   &window_changes);
15628                 break;
15629               }
15630             if ((windows->magnify.mapped != MagickFalse) &&
15631                 (windows->magnify.stasis != MagickFalse))
15632               {
15633                 status=XMakeImage(display,resource_info,&windows->magnify,
15634                   display_image,windows->magnify.width,windows->magnify.height,
15635                   exception);
15636                 XMakeMagnifyImage(display,windows);
15637               }
15638             break;
15639           }
15640         if ((windows->magnify.mapped != MagickFalse) &&
15641             (event.xconfigure.window == windows->pan.id))
15642           {
15643             /*
15644               Pan icon window has a new configuration.
15645             */
15646             if (event.xconfigure.send_event != 0)
15647               {
15648                 windows->pan.x=event.xconfigure.x;
15649                 windows->pan.y=event.xconfigure.y;
15650               }
15651             windows->pan.width=(unsigned int) event.xconfigure.width;
15652             windows->pan.height=(unsigned int) event.xconfigure.height;
15653             break;
15654           }
15655         if (event.xconfigure.window == windows->icon.id)
15656           {
15657             /*
15658               Icon window has a new configuration.
15659             */
15660             windows->icon.width=(unsigned int) event.xconfigure.width;
15661             windows->icon.height=(unsigned int) event.xconfigure.height;
15662             break;
15663           }
15664         break;
15665       }
15666       case DestroyNotify:
15667       {
15668         /*
15669           Group leader has exited.
15670         */
15671         if (display_image->debug != MagickFalse)
15672           (void) LogMagickEvent(X11Event,GetMagickModule(),
15673             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15674         if (event.xdestroywindow.window == windows->group_leader.id)
15675           {
15676             *state|=ExitState;
15677             break;
15678           }
15679         break;
15680       }
15681       case EnterNotify:
15682       {
15683         /*
15684           Selectively install colormap.
15685         */
15686         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15687           if (event.xcrossing.mode != NotifyUngrab)
15688             XInstallColormap(display,map_info->colormap);
15689         break;
15690       }
15691       case Expose:
15692       {
15693         if (display_image->debug != MagickFalse)
15694           (void) LogMagickEvent(X11Event,GetMagickModule(),
15695             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15696             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15697             event.xexpose.y);
15698         /*
15699           Refresh windows that are now exposed.
15700         */
15701         if ((event.xexpose.window == windows->image.id) &&
15702             (windows->image.mapped != MagickFalse))
15703           {
15704             XRefreshWindow(display,&windows->image,&event);
15705             delay=display_image->delay/MagickMax(
15706               display_image->ticks_per_second,1L);
15707             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15708             break;
15709           }
15710         if ((event.xexpose.window == windows->magnify.id) &&
15711             (windows->magnify.mapped != MagickFalse))
15712           {
15713             XMakeMagnifyImage(display,windows);
15714             break;
15715           }
15716         if (event.xexpose.window == windows->pan.id)
15717           {
15718             XDrawPanRectangle(display,windows);
15719             break;
15720           }
15721         if (event.xexpose.window == windows->icon.id)
15722           {
15723             XRefreshWindow(display,&windows->icon,&event);
15724             break;
15725           }
15726         break;
15727       }
15728       case KeyPress:
15729       {
15730         int
15731           length;
15732
15733         /*
15734           Respond to a user key press.
15735         */
15736         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15737           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15738         *(command+length)='\0';
15739         if (display_image->debug != MagickFalse)
15740           (void) LogMagickEvent(X11Event,GetMagickModule(),
15741             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15742             key_symbol,command);
15743         if (event.xkey.window == windows->image.id)
15744           {
15745             command_type=XImageWindowCommand(display,resource_info,windows,
15746               event.xkey.state,key_symbol,&display_image,exception);
15747             if (command_type != NullCommand)
15748               nexus=XMagickCommand(display,resource_info,windows,command_type,
15749                 &display_image,exception);
15750           }
15751         if (event.xkey.window == windows->magnify.id)
15752           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15753         if (event.xkey.window == windows->pan.id)
15754           {
15755             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15756               (void) XWithdrawWindow(display,windows->pan.id,
15757                 windows->pan.screen);
15758             else
15759               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15760                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15761                   "Help Viewer - Image Pan",ImagePanHelp);
15762               else
15763                 XTranslateImage(display,windows,*image,key_symbol);
15764           }
15765         delay=display_image->delay/MagickMax(
15766           display_image->ticks_per_second,1L);
15767         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15768         break;
15769       }
15770       case KeyRelease:
15771       {
15772         /*
15773           Respond to a user key release.
15774         */
15775         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15776           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15777         if (display_image->debug != MagickFalse)
15778           (void) LogMagickEvent(X11Event,GetMagickModule(),
15779             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15780         break;
15781       }
15782       case LeaveNotify:
15783       {
15784         /*
15785           Selectively uninstall colormap.
15786         */
15787         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15788           if (event.xcrossing.mode != NotifyUngrab)
15789             XUninstallColormap(display,map_info->colormap);
15790         break;
15791       }
15792       case MapNotify:
15793       {
15794         if (display_image->debug != MagickFalse)
15795           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15796             event.xmap.window);
15797         if (event.xmap.window == windows->backdrop.id)
15798           {
15799             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15800               CurrentTime);
15801             windows->backdrop.mapped=MagickTrue;
15802             break;
15803           }
15804         if (event.xmap.window == windows->image.id)
15805           {
15806             if (windows->backdrop.id != (Window) NULL)
15807               (void) XInstallColormap(display,map_info->colormap);
15808             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15809               {
15810                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15811                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15812               }
15813             if (((int) windows->image.width < windows->image.ximage->width) ||
15814                 ((int) windows->image.height < windows->image.ximage->height))
15815               (void) XMapRaised(display,windows->pan.id);
15816             windows->image.mapped=MagickTrue;
15817             break;
15818           }
15819         if (event.xmap.window == windows->magnify.id)
15820           {
15821             XMakeMagnifyImage(display,windows);
15822             windows->magnify.mapped=MagickTrue;
15823             (void) XWithdrawWindow(display,windows->info.id,
15824               windows->info.screen);
15825             break;
15826           }
15827         if (event.xmap.window == windows->pan.id)
15828           {
15829             XMakePanImage(display,resource_info,windows,display_image,
15830               exception);
15831             windows->pan.mapped=MagickTrue;
15832             break;
15833           }
15834         if (event.xmap.window == windows->info.id)
15835           {
15836             windows->info.mapped=MagickTrue;
15837             break;
15838           }
15839         if (event.xmap.window == windows->icon.id)
15840           {
15841             MagickBooleanType
15842               taint;
15843
15844             /*
15845               Create an icon image.
15846             */
15847             taint=display_image->taint;
15848             XMakeStandardColormap(display,icon_visual,icon_resources,
15849               display_image,icon_map,icon_pixel);
15850             (void) XMakeImage(display,icon_resources,&windows->icon,
15851               display_image,windows->icon.width,windows->icon.height,
15852               exception);
15853             display_image->taint=taint;
15854             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15855               windows->icon.pixmap);
15856             (void) XClearWindow(display,windows->icon.id);
15857             (void) XWithdrawWindow(display,windows->info.id,
15858               windows->info.screen);
15859             windows->icon.mapped=MagickTrue;
15860             break;
15861           }
15862         if (event.xmap.window == windows->command.id)
15863           {
15864             windows->command.mapped=MagickTrue;
15865             break;
15866           }
15867         if (event.xmap.window == windows->popup.id)
15868           {
15869             windows->popup.mapped=MagickTrue;
15870             break;
15871           }
15872         if (event.xmap.window == windows->widget.id)
15873           {
15874             windows->widget.mapped=MagickTrue;
15875             break;
15876           }
15877         break;
15878       }
15879       case MappingNotify:
15880       {
15881         (void) XRefreshKeyboardMapping(&event.xmapping);
15882         break;
15883       }
15884       case NoExpose:
15885         break;
15886       case PropertyNotify:
15887       {
15888         Atom
15889           type;
15890
15891         int
15892           format,
15893           status;
15894
15895         unsigned char
15896           *data;
15897
15898         unsigned long
15899           after,
15900           length;
15901
15902         if (display_image->debug != MagickFalse)
15903           (void) LogMagickEvent(X11Event,GetMagickModule(),
15904             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15905             event.xproperty.atom,event.xproperty.state);
15906         if (event.xproperty.atom != windows->im_remote_command)
15907           break;
15908         /*
15909           Display image named by the remote command protocol.
15910         */
15911         status=XGetWindowProperty(display,event.xproperty.window,
15912           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15913           AnyPropertyType,&type,&format,&length,&after,&data);
15914         if ((status != Success) || (length == 0))
15915           break;
15916         if (LocaleCompare((char *) data,"-quit") == 0)
15917           {
15918             XClientMessage(display,windows->image.id,windows->im_protocols,
15919               windows->im_exit,CurrentTime);
15920             (void) XFree((void *) data);
15921             break;
15922           }
15923         (void) CopyMagickString(resource_info->image_info->filename,
15924           (char *) data,MaxTextExtent);
15925         (void) XFree((void *) data);
15926         nexus=ReadImage(resource_info->image_info,&display_image->exception);
15927         CatchException(&display_image->exception);
15928         if (nexus != (Image *) NULL)
15929           *state|=NextImageState | ExitState;
15930         break;
15931       }
15932       case ReparentNotify:
15933       {
15934         if (display_image->debug != MagickFalse)
15935           (void) LogMagickEvent(X11Event,GetMagickModule(),
15936             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15937             event.xreparent.window);
15938         break;
15939       }
15940       case UnmapNotify:
15941       {
15942         if (display_image->debug != MagickFalse)
15943           (void) LogMagickEvent(X11Event,GetMagickModule(),
15944             "Unmap Notify: 0x%lx",event.xunmap.window);
15945         if (event.xunmap.window == windows->backdrop.id)
15946           {
15947             windows->backdrop.mapped=MagickFalse;
15948             break;
15949           }
15950         if (event.xunmap.window == windows->image.id)
15951           {
15952             windows->image.mapped=MagickFalse;
15953             break;
15954           }
15955         if (event.xunmap.window == windows->magnify.id)
15956           {
15957             windows->magnify.mapped=MagickFalse;
15958             break;
15959           }
15960         if (event.xunmap.window == windows->pan.id)
15961           {
15962             windows->pan.mapped=MagickFalse;
15963             break;
15964           }
15965         if (event.xunmap.window == windows->info.id)
15966           {
15967             windows->info.mapped=MagickFalse;
15968             break;
15969           }
15970         if (event.xunmap.window == windows->icon.id)
15971           {
15972             if (map_info->colormap == icon_map->colormap)
15973               XConfigureImageColormap(display,resource_info,windows,
15974                 display_image);
15975             (void) XFreeStandardColormap(display,icon_visual,icon_map,
15976               icon_pixel);
15977             windows->icon.mapped=MagickFalse;
15978             break;
15979           }
15980         if (event.xunmap.window == windows->command.id)
15981           {
15982             windows->command.mapped=MagickFalse;
15983             break;
15984           }
15985         if (event.xunmap.window == windows->popup.id)
15986           {
15987             if (windows->backdrop.id != (Window) NULL)
15988               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15989                 CurrentTime);
15990             windows->popup.mapped=MagickFalse;
15991             break;
15992           }
15993         if (event.xunmap.window == windows->widget.id)
15994           {
15995             if (windows->backdrop.id != (Window) NULL)
15996               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15997                 CurrentTime);
15998             windows->widget.mapped=MagickFalse;
15999             break;
16000           }
16001         break;
16002       }
16003       default:
16004       {
16005         if (display_image->debug != MagickFalse)
16006           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16007             event.type);
16008         break;
16009       }
16010     }
16011   } while (!(*state & ExitState));
16012   if ((*state & ExitState) == 0)
16013     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16014       &display_image,exception);
16015   else
16016     if (resource_info->confirm_edit != MagickFalse)
16017       {
16018         /*
16019           Query user if image has changed.
16020         */
16021         if ((resource_info->immutable == MagickFalse) &&
16022             (display_image->taint != MagickFalse))
16023           {
16024             int
16025               status;
16026
16027             status=XConfirmWidget(display,windows,"Your image changed.",
16028               "Do you want to save it");
16029             if (status == 0)
16030               *state&=(~ExitState);
16031             else
16032               if (status > 0)
16033                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16034                   &display_image,exception);
16035           }
16036       }
16037   if ((windows->visual_info->klass == GrayScale) ||
16038       (windows->visual_info->klass == PseudoColor) ||
16039       (windows->visual_info->klass == DirectColor))
16040     {
16041       /*
16042         Withdraw pan and Magnify window.
16043       */
16044       if (windows->info.mapped != MagickFalse)
16045         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16046       if (windows->magnify.mapped != MagickFalse)
16047         (void) XWithdrawWindow(display,windows->magnify.id,
16048           windows->magnify.screen);
16049       if (windows->command.mapped != MagickFalse)
16050         (void) XWithdrawWindow(display,windows->command.id,
16051           windows->command.screen);
16052     }
16053   if (windows->pan.mapped != MagickFalse)
16054     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16055   if (resource_info->backdrop == MagickFalse)
16056     if (windows->backdrop.mapped)
16057       {
16058         (void) XWithdrawWindow(display,windows->backdrop.id,
16059           windows->backdrop.screen);
16060         (void) XDestroyWindow(display,windows->backdrop.id);
16061         windows->backdrop.id=(Window) NULL;
16062         (void) XWithdrawWindow(display,windows->image.id,
16063           windows->image.screen);
16064         (void) XDestroyWindow(display,windows->image.id);
16065         windows->image.id=(Window) NULL;
16066       }
16067   XSetCursorState(display,windows,MagickTrue);
16068   XCheckRefreshWindows(display,windows);
16069   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16070     *state&=(~ExitState);
16071   if (*state & ExitState)
16072     {
16073       /*
16074         Free Standard Colormap.
16075       */
16076       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16077       if (resource_info->map_type == (char *) NULL)
16078         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16079       /*
16080         Free X resources.
16081       */
16082       if (resource_info->copy_image != (Image *) NULL)
16083         {
16084           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16085           resource_info->copy_image=NewImageList();
16086         }
16087       DestroyXResources();
16088     }
16089   (void) XSync(display,MagickFalse);
16090   /*
16091     Restore our progress monitor and warning handlers.
16092   */
16093   (void) SetErrorHandler(warning_handler);
16094   (void) SetWarningHandler(warning_handler);
16095   /*
16096     Change to home directory.
16097   */
16098   directory=getcwd(working_directory,MaxTextExtent);
16099   (void) directory;
16100   {
16101     int
16102       status;
16103
16104     status=chdir(resource_info->home_directory);
16105     if (status == -1)
16106       (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16107         FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16108   }
16109   *image=display_image;
16110   return(nexus);
16111 }
16112 #else
16113 \f
16114 /*
16115 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16116 %                                                                             %
16117 %                                                                             %
16118 %                                                                             %
16119 +   D i s p l a y I m a g e s                                                 %
16120 %                                                                             %
16121 %                                                                             %
16122 %                                                                             %
16123 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16124 %
16125 %  DisplayImages() displays an image sequence to any X window screen.  It
16126 %  returns a value other than 0 if successful.  Check the exception member
16127 %  of image to determine the reason for any failure.
16128 %
16129 %  The format of the DisplayImages method is:
16130 %
16131 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16132 %        Image *images,ExceptionInfo *exception)
16133 %
16134 %  A description of each parameter follows:
16135 %
16136 %    o image_info: the image info.
16137 %
16138 %    o image: the image.
16139 %
16140 %    o exception: return any errors or warnings in this structure.
16141 %
16142 */
16143 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16144   Image *image,ExceptionInfo *exception)
16145 {
16146   assert(image_info != (const ImageInfo *) NULL);
16147   assert(image_info->signature == MagickSignature);
16148   assert(image != (Image *) NULL);
16149   assert(image->signature == MagickSignature);
16150   if (image->debug != MagickFalse)
16151     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16152   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16153     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16154   return(MagickFalse);
16155 }
16156 \f
16157 /*
16158 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16159 %                                                                             %
16160 %                                                                             %
16161 %                                                                             %
16162 +   R e m o t e D i s p l a y C o m m a n d                                   %
16163 %                                                                             %
16164 %                                                                             %
16165 %                                                                             %
16166 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16167 %
16168 %  RemoteDisplayCommand() encourages a remote display program to display the
16169 %  specified image filename.
16170 %
16171 %  The format of the RemoteDisplayCommand method is:
16172 %
16173 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16174 %        const char *window,const char *filename,ExceptionInfo *exception)
16175 %
16176 %  A description of each parameter follows:
16177 %
16178 %    o image_info: the image info.
16179 %
16180 %    o window: Specifies the name or id of an X window.
16181 %
16182 %    o filename: the name of the image filename to display.
16183 %
16184 %    o exception: return any errors or warnings in this structure.
16185 %
16186 */
16187 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16188   const char *window,const char *filename,ExceptionInfo *exception)
16189 {
16190   assert(image_info != (const ImageInfo *) NULL);
16191   assert(image_info->signature == MagickSignature);
16192   assert(filename != (char *) NULL);
16193   (void) window;
16194   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16195   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16196     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16197   return(MagickFalse);
16198 }
16199 #endif