]> 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/version.h"
89 #include "MagickCore/widget.h"
90 #include "MagickCore/xwindow-private.h"
91 \f
92 #if defined(MAGICKCORE_X11_DELEGATE)
93 /*
94   Define declarations.
95 */
96 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
97 \f
98 /*
99   Constant declarations.
100 */
101 static const unsigned char
102   HighlightBitmap[8] =
103   {
104     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
105   },
106   OpaqueBitmap[8] =
107   {
108     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
109   },
110   ShadowBitmap[8] =
111   {
112     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
113   };
114
115 static const char
116   *PageSizes[] =
117   {
118     "Letter",
119     "Tabloid",
120     "Ledger",
121     "Legal",
122     "Statement",
123     "Executive",
124     "A3",
125     "A4",
126     "A5",
127     "B4",
128     "B5",
129     "Folio",
130     "Quarto",
131     "10x14",
132     (char *) NULL
133   };
134 \f
135 /*
136   Help widget declarations.
137 */
138 static const char
139   *ImageAnnotateHelp[] =
140   {
141     "In annotate mode, the Command widget has these options:",
142     "",
143     "    Font Name",
144     "      fixed",
145     "      variable",
146     "      5x8",
147     "      6x10",
148     "      7x13bold",
149     "      8x13bold",
150     "      9x15bold",
151     "      10x20",
152     "      12x24",
153     "      Browser...",
154     "    Font Color",
155     "      black",
156     "      blue",
157     "      cyan",
158     "      green",
159     "      gray",
160     "      red",
161     "      magenta",
162     "      yellow",
163     "      white",
164     "      transparent",
165     "      Browser...",
166     "    Font Color",
167     "      black",
168     "      blue",
169     "      cyan",
170     "      green",
171     "      gray",
172     "      red",
173     "      magenta",
174     "      yellow",
175     "      white",
176     "      transparent",
177     "      Browser...",
178     "    Rotate Text",
179     "      -90",
180     "      -45",
181     "      -30",
182     "      0",
183     "      30",
184     "      45",
185     "      90",
186     "      180",
187     "      Dialog...",
188     "    Help",
189     "    Dismiss",
190     "",
191     "Choose a font name from the Font Name sub-menu.  Additional",
192     "font names can be specified with the font browser.  You can",
193     "change the menu names by setting the X resources font1",
194     "through font9.",
195     "",
196     "Choose a font color from the Font Color sub-menu.",
197     "Additional font colors can be specified with the color",
198     "browser.  You can change the menu colors by setting the X",
199     "resources pen1 through pen9.",
200     "",
201     "If you select the color browser and press Grab, you can",
202     "choose the font color by moving the pointer to the desired",
203     "color on the screen and press any button.",
204     "",
205     "If you choose to rotate the text, choose Rotate Text from the",
206     "menu and select an angle.  Typically you will only want to",
207     "rotate one line of text at a time.  Depending on the angle you",
208     "choose, subsequent lines may end up overwriting each other.",
209     "",
210     "Choosing a font and its color is optional.  The default font",
211     "is fixed and the default color is black.  However, you must",
212     "choose a location to begin entering text and press button 1.",
213     "An underscore character will appear at the location of the",
214     "pointer.  The cursor changes to a pencil to indicate you are",
215     "in text mode.  To exit immediately, press Dismiss.",
216     "",
217     "In text mode, any key presses will display the character at",
218     "the location of the underscore and advance the underscore",
219     "cursor.  Enter your text and once completed press Apply to",
220     "finish your image annotation.  To correct errors press BACK",
221     "SPACE.  To delete an entire line of text, press DELETE.  Any",
222     "text that exceeds the boundaries of the image window is",
223     "automagically continued onto the next line.",
224     "",
225     "The actual color you request for the font is saved in the",
226     "image.  However, the color that appears in your image window",
227     "may be different.  For example, on a monochrome screen the",
228     "text will appear black or white even if you choose the color",
229     "red as the font color.  However, the image saved to a file",
230     "with -write is written with red lettering.  To assure the",
231     "correct color text in the final image, any PseudoClass image",
232     "is promoted to DirectClass (see miff(5)).  To force a",
233     "PseudoClass image to remain PseudoClass, use -colors.",
234     (char *) NULL,
235   },
236   *ImageChopHelp[] =
237   {
238     "In chop mode, the Command widget has these options:",
239     "",
240     "    Direction",
241     "      horizontal",
242     "      vertical",
243     "    Help",
244     "    Dismiss",
245     "",
246     "If the you choose the horizontal direction (this the",
247     "default), the area of the image between the two horizontal",
248     "endpoints of the chop line is removed.  Otherwise, the area",
249     "of the image between the two vertical endpoints of the chop",
250     "line is removed.",
251     "",
252     "Select a location within the image window to begin your chop,",
253     "press and hold any button.  Next, move the pointer to",
254     "another location in the image.  As you move a line will",
255     "connect the initial location and the pointer.  When you",
256     "release the button, the area within the image to chop is",
257     "determined by which direction you choose from the Command",
258     "widget.",
259     "",
260     "To cancel the image chopping, move the pointer back to the",
261     "starting point of the line and release the button.",
262     (char *) NULL,
263   },
264   *ImageColorEditHelp[] =
265   {
266     "In color edit mode, the Command widget has these options:",
267     "",
268     "    Method",
269     "      point",
270     "      replace",
271     "      floodfill",
272     "      filltoborder",
273     "      reset",
274     "    Pixel Color",
275     "      black",
276     "      blue",
277     "      cyan",
278     "      green",
279     "      gray",
280     "      red",
281     "      magenta",
282     "      yellow",
283     "      white",
284     "      Browser...",
285     "    Border Color",
286     "      black",
287     "      blue",
288     "      cyan",
289     "      green",
290     "      gray",
291     "      red",
292     "      magenta",
293     "      yellow",
294     "      white",
295     "      Browser...",
296     "    Fuzz",
297     "      0%",
298     "      2%",
299     "      5%",
300     "      10%",
301     "      15%",
302     "      Dialog...",
303     "    Undo",
304     "    Help",
305     "    Dismiss",
306     "",
307     "Choose a color editing method from the Method sub-menu",
308     "of the Command widget.  The point method recolors any pixel",
309     "selected with the pointer until the button is released.  The",
310     "replace method recolors any pixel that matches the color of",
311     "the pixel you select with a button press.  Floodfill recolors",
312     "any pixel that matches the color of the pixel you select with",
313     "a button press and is a neighbor.  Whereas filltoborder recolors",
314     "any neighbor pixel that is not the border color.  Finally reset",
315     "changes the entire image to the designated color.",
316     "",
317     "Next, choose a pixel color from the Pixel Color sub-menu.",
318     "Additional pixel colors can be specified with the color",
319     "browser.  You can change the menu colors by setting the X",
320     "resources pen1 through pen9.",
321     "",
322     "Now press button 1 to select a pixel within the image window",
323     "to change its color.  Additional pixels may be recolored as",
324     "prescribed by the method you choose.",
325     "",
326     "If the Magnify widget is mapped, it can be helpful in positioning",
327     "your pointer within the image (refer to button 2).",
328     "",
329     "The actual color you request for the pixels is saved in the",
330     "image.  However, the color that appears in your image window",
331     "may be different.  For example, on a monochrome screen the",
332     "pixel will appear black or white even if you choose the",
333     "color red as the pixel color.  However, the image saved to a",
334     "file with -write is written with red pixels.  To assure the",
335     "correct color text in the final image, any PseudoClass image",
336     "is promoted to DirectClass (see miff(5)).  To force a",
337     "PseudoClass image to remain PseudoClass, use -colors.",
338     (char *) NULL,
339   },
340   *ImageCompositeHelp[] =
341   {
342     "First a widget window is displayed requesting you to enter an",
343     "image name. Press Composite, Grab or type a file name.",
344     "Press Cancel if you choose not to create a composite image.",
345     "When you choose Grab, move the pointer to the desired window",
346     "and press any button.",
347     "",
348     "If the Composite image does not have any matte information,",
349     "you are informed and the file browser is displayed again.",
350     "Enter the name of a mask image.  The image is typically",
351     "grayscale and the same size as the composite image.  If the",
352     "image is not grayscale, it is converted to grayscale and the",
353     "resulting intensities are used as matte information.",
354     "",
355     "A small window appears showing the location of the cursor in",
356     "the image window. You are now in composite mode.  To exit",
357     "immediately, press Dismiss.  In composite mode, the Command",
358     "widget has these options:",
359     "",
360     "    Operators",
361     "      Over",
362     "      In",
363     "      Out",
364     "      Atop",
365     "      Xor",
366     "      Plus",
367     "      Minus",
368     "      Add",
369     "      Subtract",
370     "      Difference",
371     "      Multiply",
372     "      Bumpmap",
373     "      Copy",
374     "      CopyRed",
375     "      CopyGreen",
376     "      CopyBlue",
377     "      CopyOpacity",
378     "      Clear",
379     "    Dissolve",
380     "    Displace",
381     "    Help",
382     "    Dismiss",
383     "",
384     "Choose a composite operation from the Operators sub-menu of",
385     "the Command widget.  How each operator behaves is described",
386     "below.  Image window is the image currently displayed on",
387     "your X server and image is the image obtained with the File",
388     "Browser widget.",
389     "",
390     "Over     The result is the union of the two image shapes,",
391     "         with image obscuring image window in the region of",
392     "         overlap.",
393     "",
394     "In       The result is simply image cut by the shape of",
395     "         image window.  None of the image data of image",
396     "         window is in the result.",
397     "",
398     "Out      The resulting image is image with the shape of",
399     "         image window cut out.",
400     "",
401     "Atop     The result is the same shape as image image window,",
402     "         with image obscuring image window where the image",
403     "         shapes overlap.  Note this differs from over",
404     "         because the portion of image outside image window's",
405     "         shape does not appear in the result.",
406     "",
407     "Xor      The result is the image data from both image and",
408     "         image window that is outside the overlap region.",
409     "         The overlap region is blank.",
410     "",
411     "Plus     The result is just the sum of the image data.",
412     "         Output values are cropped to QuantumRange (no overflow).",
413     "",
414     "Minus    The result of image - image window, with underflow",
415     "         cropped to zero.",
416     "",
417     "Add      The result of image + image window, with overflow",
418     "         wrapping around (mod 256).",
419     "",
420     "Subtract The result of image - image window, with underflow",
421     "         wrapping around (mod 256).  The add and subtract",
422     "         operators can be used to perform reversible",
423     "         transformations.",
424     "",
425     "Difference",
426     "         The result of abs(image - image window).  This",
427     "         useful for comparing two very similar images.",
428     "",
429     "Multiply",
430     "         The result of image * image window.  This",
431     "         useful for the creation of drop-shadows.",
432     "",
433     "Bumpmap  The result of surface normals from image * image",
434     "         window.",
435     "",
436     "Copy     The resulting image is image window replaced with",
437     "         image.  Here the matte information is ignored.",
438     "",
439     "CopyRed  The red layer of the image window is replace with",
440     "         the red layer of the image.  The other layers are",
441     "         untouched.",
442     "",
443     "CopyGreen",
444     "         The green layer of the image window is replace with",
445     "         the green layer of the image.  The other layers are",
446     "         untouched.",
447     "",
448     "CopyBlue The blue layer of the image window is replace with",
449     "         the blue layer of the image.  The other layers are",
450     "         untouched.",
451     "",
452     "CopyOpacity",
453     "         The matte layer of the image window is replace with",
454     "         the matte layer of the image.  The other layers are",
455     "         untouched.",
456     "",
457     "The image compositor requires a matte, or alpha channel in",
458     "the image for some operations.  This extra channel usually",
459     "defines a mask which represents a sort of a cookie-cutter",
460     "for the image.  This the case when matte is opaque (full",
461     "coverage) for pixels inside the shape, zero outside, and",
462     "between 0 and QuantumRange on the boundary.  If image does not",
463     "have a matte channel, it is initialized with 0 for any pixel",
464     "matching in color to pixel location (0,0), otherwise QuantumRange.",
465     "",
466     "If you choose Dissolve, the composite operator becomes Over.  The",
467     "image matte channel percent transparency is initialized to factor.",
468     "The image window is initialized to (100-factor). Where factor is the",
469     "value you specify in the Dialog widget.",
470     "",
471     "Displace shifts the image pixels as defined by a displacement",
472     "map.  With this option, image is used as a displacement map.",
473     "Black, within the displacement map, is a maximum positive",
474     "displacement.  White is a maximum negative displacement and",
475     "middle gray is neutral.  The displacement is scaled to determine",
476     "the pixel shift.  By default, the displacement applies in both the",
477     "horizontal and vertical directions.  However, if you specify a mask,",
478     "image is the horizontal X displacement and mask the vertical Y",
479     "displacement.",
480     "",
481     "Note that matte information for image window is not retained",
482     "for colormapped X server visuals (e.g. StaticColor,",
483     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
484     "behavior may require a TrueColor or DirectColor visual or a",
485     "Standard Colormap.",
486     "",
487     "Choosing a composite operator is optional.  The default",
488     "operator is replace.  However, you must choose a location to",
489     "composite your image and press button 1.  Press and hold the",
490     "button before releasing and an outline of the image will",
491     "appear to help you identify your location.",
492     "",
493     "The actual colors of the composite image is saved.  However,",
494     "the color that appears in image window may be different.",
495     "For example, on a monochrome screen image window will appear",
496     "black or white even though your composited image may have",
497     "many colors.  If the image is saved to a file it is written",
498     "with the correct colors.  To assure the correct colors are",
499     "saved in the final image, any PseudoClass image is promoted",
500     "to DirectClass (see miff(5)).  To force a PseudoClass image",
501     "to remain PseudoClass, use -colors.",
502     (char *) NULL,
503   },
504   *ImageCutHelp[] =
505   {
506     "In cut mode, the Command widget has these options:",
507     "",
508     "    Help",
509     "    Dismiss",
510     "",
511     "To define a cut region, press button 1 and drag.  The",
512     "cut region is defined by a highlighted rectangle that",
513     "expands or contracts as it follows the pointer.  Once you",
514     "are satisfied with the cut region, release the button.",
515     "You are now in rectify mode.  In rectify mode, the Command",
516     "widget has these options:",
517     "",
518     "    Cut",
519     "    Help",
520     "    Dismiss",
521     "",
522     "You can make adjustments by moving the pointer to one of the",
523     "cut rectangle corners, pressing a button, and dragging.",
524     "Finally, press Cut to commit your copy region.  To",
525     "exit without cutting the image, press Dismiss.",
526     (char *) NULL,
527   },
528   *ImageCopyHelp[] =
529   {
530     "In copy mode, the Command widget has these options:",
531     "",
532     "    Help",
533     "    Dismiss",
534     "",
535     "To define a copy region, press button 1 and drag.  The",
536     "copy region is defined by a highlighted rectangle that",
537     "expands or contracts as it follows the pointer.  Once you",
538     "are satisfied with the copy region, release the button.",
539     "You are now in rectify mode.  In rectify mode, the Command",
540     "widget has these options:",
541     "",
542     "    Copy",
543     "    Help",
544     "    Dismiss",
545     "",
546     "You can make adjustments by moving the pointer to one of the",
547     "copy rectangle corners, pressing a button, and dragging.",
548     "Finally, press Copy to commit your copy region.  To",
549     "exit without copying the image, press Dismiss.",
550     (char *) NULL,
551   },
552   *ImageCropHelp[] =
553   {
554     "In crop mode, the Command widget has these options:",
555     "",
556     "    Help",
557     "    Dismiss",
558     "",
559     "To define a cropping region, press button 1 and drag.  The",
560     "cropping region is defined by a highlighted rectangle that",
561     "expands or contracts as it follows the pointer.  Once you",
562     "are satisfied with the cropping region, release the button.",
563     "You are now in rectify mode.  In rectify mode, the Command",
564     "widget has these options:",
565     "",
566     "    Crop",
567     "    Help",
568     "    Dismiss",
569     "",
570     "You can make adjustments by moving the pointer to one of the",
571     "cropping rectangle corners, pressing a button, and dragging.",
572     "Finally, press Crop to commit your cropping region.  To",
573     "exit without cropping the image, press Dismiss.",
574     (char *) NULL,
575   },
576   *ImageDrawHelp[] =
577   {
578     "The cursor changes to a crosshair to indicate you are in",
579     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
580     "the Command widget has these options:",
581     "",
582     "    Element",
583     "      point",
584     "      line",
585     "      rectangle",
586     "      fill rectangle",
587     "      circle",
588     "      fill circle",
589     "      ellipse",
590     "      fill ellipse",
591     "      polygon",
592     "      fill polygon",
593     "    Color",
594     "      black",
595     "      blue",
596     "      cyan",
597     "      green",
598     "      gray",
599     "      red",
600     "      magenta",
601     "      yellow",
602     "      white",
603     "      transparent",
604     "      Browser...",
605     "    Stipple",
606     "      Brick",
607     "      Diagonal",
608     "      Scales",
609     "      Vertical",
610     "      Wavy",
611     "      Translucent",
612     "      Opaque",
613     "      Open...",
614     "    Width",
615     "      1",
616     "      2",
617     "      4",
618     "      8",
619     "      16",
620     "      Dialog...",
621     "    Undo",
622     "    Help",
623     "    Dismiss",
624     "",
625     "Choose a drawing primitive from the Element sub-menu.",
626     "",
627     "Choose a color from the Color sub-menu.  Additional",
628     "colors can be specified with the color browser.",
629     "",
630     "If you choose the color browser and press Grab, you can",
631     "select the color by moving the pointer to the desired",
632     "color on the screen and press any button.  The transparent",
633     "color updates the image matte channel and is useful for",
634     "image compositing.",
635     "",
636     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
637     "Additional stipples can be specified with the file browser.",
638     "Stipples obtained from the file browser must be on disk in the",
639     "X11 bitmap format.",
640     "",
641     "Choose a width, if appropriate, from the Width sub-menu.  To",
642     "choose a specific width select the Dialog widget.",
643     "",
644     "Choose a point in the Image window and press button 1 and",
645     "hold.  Next, move the pointer to another location in the",
646     "image.  As you move, a line connects the initial location and",
647     "the pointer.  When you release the button, the image is",
648     "updated with the primitive you just drew.  For polygons, the",
649     "image is updated when you press and release the button without",
650     "moving the pointer.",
651     "",
652     "To cancel image drawing, move the pointer back to the",
653     "starting point of the line and release the button.",
654     (char *) NULL,
655   },
656   *DisplayHelp[] =
657   {
658     "BUTTONS",
659     "  The effects of each button press is described below.  Three",
660     "  buttons are required.  If you have a two button mouse,",
661     "  button 1 and 3 are returned.  Press ALT and button 3 to",
662     "  simulate button 2.",
663     "",
664     "  1    Press this button to map or unmap the Command widget.",
665     "",
666     "  2    Press and drag to define a region of the image to",
667     "       magnify.",
668     "",
669     "  3    Press and drag to choose from a select set of commands.",
670     "       This button behaves differently if the image being",
671     "       displayed is a visual image directory.  Here, choose a",
672     "       particular tile of the directory and press this button and",
673     "       drag to select a command from a pop-up menu.  Choose from",
674     "       these menu items:",
675     "",
676     "           Open",
677     "           Next",
678     "           Former",
679     "           Delete",
680     "           Update",
681     "",
682     "       If you choose Open, the image represented by the tile is",
683     "       displayed.  To return to the visual image directory, choose",
684     "       Next from the Command widget.  Next and Former moves to the",
685     "       next or former image respectively.  Choose Delete to delete",
686     "       a particular image tile.  Finally, choose Update to",
687     "       synchronize all the image tiles with their respective",
688     "       images.",
689     "",
690     "COMMAND WIDGET",
691     "  The Command widget lists a number of sub-menus and commands.",
692     "  They are",
693     "",
694     "      File",
695     "        Open...",
696     "        Next",
697     "        Former",
698     "        Select...",
699     "        Save...",
700     "        Print...",
701     "        Delete...",
702     "        New...",
703     "        Visual Directory...",
704     "        Quit",
705     "      Edit",
706     "        Undo",
707     "        Redo",
708     "        Cut",
709     "        Copy",
710     "        Paste",
711     "      View",
712     "        Half Size",
713     "        Original Size",
714     "        Double Size",
715     "        Resize...",
716     "        Apply",
717     "        Refresh",
718     "        Restore",
719     "      Transform",
720     "        Crop",
721     "        Chop",
722     "        Flop",
723     "        Flip",
724     "        Rotate Right",
725     "        Rotate Left",
726     "        Rotate...",
727     "        Shear...",
728     "        Roll...",
729     "        Trim Edges",
730     "      Enhance",
731     "        Brightness...",
732     "        Saturation...",
733     "        Hue...",
734     "        Gamma...",
735     "        Sharpen...",
736     "        Dull",
737     "        Contrast Stretch...",
738     "        Sigmoidal Contrast...",
739     "        Normalize",
740     "        Equalize",
741     "        Negate",
742     "        Grayscale",
743     "        Map...",
744     "        Quantize...",
745     "      Effects",
746     "        Despeckle",
747     "        Emboss",
748     "        Reduce Noise",
749     "        Add Noise",
750     "        Sharpen...",
751     "        Blur...",
752     "        Threshold...",
753     "        Edge Detect...",
754     "        Spread...",
755     "        Shade...",
756     "        Painting...",
757     "        Segment...",
758     "      F/X",
759     "        Solarize...",
760     "        Sepia Tone...",
761     "        Swirl...",
762     "        Implode...",
763     "        Vignette...",
764     "        Wave...",
765     "        Oil Painting...",
766     "        Charcoal Drawing...",
767     "      Image Edit",
768     "        Annotate...",
769     "        Draw...",
770     "        Color...",
771     "        Matte...",
772     "        Composite...",
773     "        Add Border...",
774     "        Add Frame...",
775     "        Comment...",
776     "        Launch...",
777     "        Region of Interest...",
778     "      Miscellany",
779     "        Image Info",
780     "        Zoom Image",
781     "        Show Preview...",
782     "        Show Histogram",
783     "        Show Matte",
784     "        Background...",
785     "        Slide Show",
786     "        Preferences...",
787     "      Help",
788     "        Overview",
789     "        Browse Documentation",
790     "        About Display",
791     "",
792     "  Menu items with a indented triangle have a sub-menu.  They",
793     "  are represented above as the indented items.  To access a",
794     "  sub-menu item, move the pointer to the appropriate menu and",
795     "  press a button and drag.  When you find the desired sub-menu",
796     "  item, release the button and the command is executed.  Move",
797     "  the pointer away from the sub-menu if you decide not to",
798     "  execute a particular command.",
799     "",
800     "KEYBOARD ACCELERATORS",
801     "  Accelerators are one or two key presses that effect a",
802     "  particular command.  The keyboard accelerators that",
803     "  display(1) understands is:",
804     "",
805     "  Ctl+O     Press to open an image from a file.",
806     "",
807     "  space     Press to display the next image.",
808     "",
809     "            If the image is a multi-paged document such as a Postscript",
810     "            document, you can skip ahead several pages by preceding",
811     "            this command with a number.  For example to display the",
812     "            third page beyond the current page, press 3<space>.",
813     "",
814     "  backspace Press to display the former image.",
815     "",
816     "            If the image is a multi-paged document such as a Postscript",
817     "            document, you can skip behind several pages by preceding",
818     "            this command with a number.  For example to display the",
819     "            third page preceding the current page, press 3<backspace>.",
820     "",
821     "  Ctl+S     Press to write the image to a file.",
822     "",
823     "  Ctl+P     Press to print the image to a Postscript printer.",
824     "",
825     "  Ctl+D     Press to delete an image file.",
826     "",
827     "  Ctl+N     Press to create a blank canvas.",
828     "",
829     "  Ctl+Q     Press to discard all images and exit program.",
830     "",
831     "  Ctl+Z     Press to undo last image transformation.",
832     "",
833     "  Ctl+R     Press to redo last image transformation.",
834     "",
835     "  Ctl+X     Press to cut a region of the image.",
836     "",
837     "  Ctl+C     Press to copy a region of the image.",
838     "",
839     "  Ctl+V     Press to paste a region to the image.",
840     "",
841     "  <         Press to half the image size.",
842     "",
843     "  -         Press to return to the original image size.",
844     "",
845     "  >         Press to double the image size.",
846     "",
847     "  %         Press to resize the image to a width and height you",
848     "            specify.",
849     "",
850     "Cmd-A       Press to make any image transformations permanent."
851     "",
852     "            By default, any image size transformations are applied",
853     "            to the original image to create the image displayed on",
854     "            the X server.  However, the transformations are not",
855     "            permanent (i.e. the original image does not change",
856     "            size only the X image does).  For example, if you",
857     "            press > the X image will appear to double in size,",
858     "            but the original image will in fact remain the same size.",
859     "            To force the original image to double in size, press >",
860     "            followed by Cmd-A.",
861     "",
862     "  @         Press to refresh the image window.",
863     "",
864     "  C         Press to cut out a rectangular region of the image.",
865     "",
866     "  [         Press to chop the image.",
867     "",
868     "  H         Press to flop image in the horizontal direction.",
869     "",
870     "  V         Press to flip image in the vertical direction.",
871     "",
872     "  /         Press to rotate the image 90 degrees clockwise.",
873     "",
874     " \\         Press to rotate the image 90 degrees counter-clockwise.",
875     "",
876     "  *         Press to rotate the image the number of degrees you",
877     "            specify.",
878     "",
879     "  S         Press to shear the image the number of degrees you",
880     "            specify.",
881     "",
882     "  R         Press to roll the image.",
883     "",
884     "  T         Press to trim the image edges.",
885     "",
886     "  Shft-H    Press to vary the image hue.",
887     "",
888     "  Shft-S    Press to vary the color saturation.",
889     "",
890     "  Shft-L    Press to vary the color brightness.",
891     "",
892     "  Shft-G    Press to gamma correct the image.",
893     "",
894     "  Shft-C    Press to sharpen the image contrast.",
895     "",
896     "  Shft-Z    Press to dull the image contrast.",
897     "",
898     "  =         Press to perform histogram equalization on the image.",
899     "",
900     "  Shft-N    Press to perform histogram normalization on the image.",
901     "",
902     "  Shft-~    Press to negate the colors of the image.",
903     "",
904     "  .         Press to convert the image colors to gray.",
905     "",
906     "  Shft-#    Press to set the maximum number of unique colors in the",
907     "            image.",
908     "",
909     "  F2        Press to reduce the speckles in an image.",
910     "",
911     "  F3        Press to eliminate peak noise from an image.",
912     "",
913     "  F4        Press to add noise to an image.",
914     "",
915     "  F5        Press to sharpen an image.",
916     "",
917     "  F6        Press to delete an image file.",
918     "",
919     "  F7        Press to threshold the image.",
920     "",
921     "  F8        Press to detect edges within an image.",
922     "",
923     "  F9        Press to emboss an image.",
924     "",
925     "  F10       Press to displace pixels by a random amount.",
926     "",
927     "  F11       Press to negate all pixels above the threshold level.",
928     "",
929     "  F12       Press to shade the image using a distant light source.",
930     "",
931     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
932     "",
933     "  F14       Press to segment the image by color.",
934     "",
935     "  Meta-S    Press to swirl image pixels about the center.",
936     "",
937     "  Meta-I    Press to implode image pixels about the center.",
938     "",
939     "  Meta-W    Press to alter an image along a sine wave.",
940     "",
941     "  Meta-P    Press to simulate an oil painting.",
942     "",
943     "  Meta-C    Press to simulate a charcoal drawing.",
944     "",
945     "  Alt-A     Press to annotate the image with text.",
946     "",
947     "  Alt-D     Press to draw on an image.",
948     "",
949     "  Alt-P     Press to edit an image pixel color.",
950     "",
951     "  Alt-M     Press to edit the image matte information.",
952     "",
953     "  Alt-V     Press to composite the image with another.",
954     "",
955     "  Alt-B     Press to add a border to the image.",
956     "",
957     "  Alt-F     Press to add an ornamental border to the image.",
958     "",
959     "  Alt-Shft-!",
960     "            Press to add an image comment.",
961     "",
962     "  Ctl-A     Press to apply image processing techniques to a region",
963     "            of interest.",
964     "",
965     "  Shft-?    Press to display information about the image.",
966     "",
967     "  Shft-+    Press to map the zoom image window.",
968     "",
969     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
970     "",
971     "  F1        Press to display helpful information about display(1).",
972     "",
973     "  Find      Press to browse documentation about ImageMagick.",
974     "",
975     "  1-9       Press to change the level of magnification.",
976     "",
977     "  Use the arrow keys to move the image one pixel up, down,",
978     "  left, or right within the magnify window.  Be sure to first",
979     "  map the magnify window by pressing button 2.",
980     "",
981     "  Press ALT and one of the arrow keys to trim off one pixel",
982     "  from any side of the image.",
983     (char *) NULL,
984   },
985   *ImageMatteEditHelp[] =
986   {
987     "Matte information within an image is useful for some",
988     "operations such as image compositing (See IMAGE",
989     "COMPOSITING).  This extra channel usually defines a mask",
990     "which represents a sort of a cookie-cutter for the image.",
991     "This the case when matte is opaque (full coverage) for",
992     "pixels inside the shape, zero outside, and between 0 and",
993     "QuantumRange on the boundary.",
994     "",
995     "A small window appears showing the location of the cursor in",
996     "the image window. You are now in matte edit mode.  To exit",
997     "immediately, press Dismiss.  In matte edit mode, the Command",
998     "widget has these options:",
999     "",
1000     "    Method",
1001     "      point",
1002     "      replace",
1003     "      floodfill",
1004     "      filltoborder",
1005     "      reset",
1006     "    Border Color",
1007     "      black",
1008     "      blue",
1009     "      cyan",
1010     "      green",
1011     "      gray",
1012     "      red",
1013     "      magenta",
1014     "      yellow",
1015     "      white",
1016     "      Browser...",
1017     "    Fuzz",
1018     "      0%",
1019     "      2%",
1020     "      5%",
1021     "      10%",
1022     "      15%",
1023     "      Dialog...",
1024     "    Matte",
1025     "      Opaque",
1026     "      Transparent",
1027     "      Dialog...",
1028     "    Undo",
1029     "    Help",
1030     "    Dismiss",
1031     "",
1032     "Choose a matte editing method from the Method sub-menu of",
1033     "the Command widget.  The point method changes the matte value",
1034     "of any pixel selected with the pointer until the button is",
1035     "is released.  The replace method changes the matte value of",
1036     "any pixel that matches the color of the pixel you select with",
1037     "a button press.  Floodfill changes the matte value of any pixel",
1038     "that matches the color of the pixel you select with a button",
1039     "press and is a neighbor.  Whereas filltoborder changes the matte",
1040     "value any neighbor pixel that is not the border color.  Finally",
1041     "reset changes the entire image to the designated matte value.",
1042     "",
1043     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1044     "select the Dialog entry.  Here a dialog appears requesting a matte",
1045     "value.  The value you select is assigned as the opacity value of the",
1046     "selected pixel or pixels.",
1047     "",
1048     "Now, press any button to select a pixel within the image",
1049     "window to change its matte value.",
1050     "",
1051     "If the Magnify widget is mapped, it can be helpful in positioning",
1052     "your pointer within the image (refer to button 2).",
1053     "",
1054     "Matte information is only valid in a DirectClass image.",
1055     "Therefore, any PseudoClass image is promoted to DirectClass",
1056     "(see miff(5)).  Note that matte information for PseudoClass",
1057     "is not retained for colormapped X server visuals (e.g.",
1058     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1059     "immediately save your image to a file (refer to Write).",
1060     "Correct matte editing behavior may require a TrueColor or",
1061     "DirectColor visual or a Standard Colormap.",
1062     (char *) NULL,
1063   },
1064   *ImagePanHelp[] =
1065   {
1066     "When an image exceeds the width or height of the X server",
1067     "screen, display maps a small panning icon.  The rectangle",
1068     "within the panning icon shows the area that is currently",
1069     "displayed in the image window.  To pan about the image,",
1070     "press any button and drag the pointer within the panning",
1071     "icon.  The pan rectangle moves with the pointer and the",
1072     "image window is updated to reflect the location of the",
1073     "rectangle within the panning icon.  When you have selected",
1074     "the area of the image you wish to view, release the button.",
1075     "",
1076     "Use the arrow keys to pan the image one pixel up, down,",
1077     "left, or right within the image window.",
1078     "",
1079     "The panning icon is withdrawn if the image becomes smaller",
1080     "than the dimensions of the X server screen.",
1081     (char *) NULL,
1082   },
1083   *ImagePasteHelp[] =
1084   {
1085     "A small window appears showing the location of the cursor in",
1086     "the image window. You are now in paste mode.  To exit",
1087     "immediately, press Dismiss.  In paste mode, the Command",
1088     "widget has these options:",
1089     "",
1090     "    Operators",
1091     "      over",
1092     "      in",
1093     "      out",
1094     "      atop",
1095     "      xor",
1096     "      plus",
1097     "      minus",
1098     "      add",
1099     "      subtract",
1100     "      difference",
1101     "      replace",
1102     "    Help",
1103     "    Dismiss",
1104     "",
1105     "Choose a composite operation from the Operators sub-menu of",
1106     "the Command widget.  How each operator behaves is described",
1107     "below.  Image window is the image currently displayed on",
1108     "your X server and image is the image obtained with the File",
1109     "Browser widget.",
1110     "",
1111     "Over     The result is the union of the two image shapes,",
1112     "         with image obscuring image window in the region of",
1113     "         overlap.",
1114     "",
1115     "In       The result is simply image cut by the shape of",
1116     "         image window.  None of the image data of image",
1117     "         window is in the result.",
1118     "",
1119     "Out      The resulting image is image with the shape of",
1120     "         image window cut out.",
1121     "",
1122     "Atop     The result is the same shape as image image window,",
1123     "         with image obscuring image window where the image",
1124     "         shapes overlap.  Note this differs from over",
1125     "         because the portion of image outside image window's",
1126     "         shape does not appear in the result.",
1127     "",
1128     "Xor      The result is the image data from both image and",
1129     "         image window that is outside the overlap region.",
1130     "         The overlap region is blank.",
1131     "",
1132     "Plus     The result is just the sum of the image data.",
1133     "         Output values are cropped to QuantumRange (no overflow).",
1134     "         This operation is independent of the matte",
1135     "         channels.",
1136     "",
1137     "Minus    The result of image - image window, with underflow",
1138     "         cropped to zero.",
1139     "",
1140     "Add      The result of image + image window, with overflow",
1141     "         wrapping around (mod 256).",
1142     "",
1143     "Subtract The result of image - image window, with underflow",
1144     "         wrapping around (mod 256).  The add and subtract",
1145     "         operators can be used to perform reversible",
1146     "         transformations.",
1147     "",
1148     "Difference",
1149     "         The result of abs(image - image window).  This",
1150     "         useful for comparing two very similar images.",
1151     "",
1152     "Copy     The resulting image is image window replaced with",
1153     "         image.  Here the matte information is ignored.",
1154     "",
1155     "CopyRed  The red layer of the image window is replace with",
1156     "         the red layer of the image.  The other layers are",
1157     "         untouched.",
1158     "",
1159     "CopyGreen",
1160     "         The green layer of the image window is replace with",
1161     "         the green layer of the image.  The other layers are",
1162     "         untouched.",
1163     "",
1164     "CopyBlue The blue layer of the image window is replace with",
1165     "         the blue layer of the image.  The other layers are",
1166     "         untouched.",
1167     "",
1168     "CopyOpacity",
1169     "         The matte layer of the image window is replace with",
1170     "         the matte layer of the image.  The other layers are",
1171     "         untouched.",
1172     "",
1173     "The image compositor requires a matte, or alpha channel in",
1174     "the image for some operations.  This extra channel usually",
1175     "defines a mask which represents a sort of a cookie-cutter",
1176     "for the image.  This the case when matte is opaque (full",
1177     "coverage) for pixels inside the shape, zero outside, and",
1178     "between 0 and QuantumRange on the boundary.  If image does not",
1179     "have a matte channel, it is initialized with 0 for any pixel",
1180     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1181     "",
1182     "Note that matte information for image window is not retained",
1183     "for colormapped X server visuals (e.g. StaticColor,",
1184     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1185     "behavior may require a TrueColor or DirectColor visual or a",
1186     "Standard Colormap.",
1187     "",
1188     "Choosing a composite operator is optional.  The default",
1189     "operator is replace.  However, you must choose a location to",
1190     "paste your image and press button 1.  Press and hold the",
1191     "button before releasing and an outline of the image will",
1192     "appear to help you identify your location.",
1193     "",
1194     "The actual colors of the pasted image is saved.  However,",
1195     "the color that appears in image window may be different.",
1196     "For example, on a monochrome screen image window will appear",
1197     "black or white even though your pasted image may have",
1198     "many colors.  If the image is saved to a file it is written",
1199     "with the correct colors.  To assure the correct colors are",
1200     "saved in the final image, any PseudoClass image is promoted",
1201     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1202     "to remain PseudoClass, use -colors.",
1203     (char *) NULL,
1204   },
1205   *ImageROIHelp[] =
1206   {
1207     "In region of interest mode, the Command widget has these",
1208     "options:",
1209     "",
1210     "    Help",
1211     "    Dismiss",
1212     "",
1213     "To define a region of interest, press button 1 and drag.",
1214     "The region of interest is defined by a highlighted rectangle",
1215     "that expands or contracts as it follows the pointer.  Once",
1216     "you are satisfied with the region of interest, release the",
1217     "button.  You are now in apply mode.  In apply mode the",
1218     "Command widget has these options:",
1219     "",
1220     "      File",
1221     "        Save...",
1222     "        Print...",
1223     "      Edit",
1224     "        Undo",
1225     "        Redo",
1226     "      Transform",
1227     "        Flop",
1228     "        Flip",
1229     "        Rotate Right",
1230     "        Rotate Left",
1231     "      Enhance",
1232     "        Hue...",
1233     "        Saturation...",
1234     "        Brightness...",
1235     "        Gamma...",
1236     "        Spiff",
1237     "        Dull",
1238     "        Contrast Stretch",
1239     "        Sigmoidal Contrast...",
1240     "        Normalize",
1241     "        Equalize",
1242     "        Negate",
1243     "        Grayscale",
1244     "        Map...",
1245     "        Quantize...",
1246     "      Effects",
1247     "        Despeckle",
1248     "        Emboss",
1249     "        Reduce Noise",
1250     "        Sharpen...",
1251     "        Blur...",
1252     "        Threshold...",
1253     "        Edge Detect...",
1254     "        Spread...",
1255     "        Shade...",
1256     "        Raise...",
1257     "        Segment...",
1258     "      F/X",
1259     "        Solarize...",
1260     "        Sepia Tone...",
1261     "        Swirl...",
1262     "        Implode...",
1263     "        Vignette...",
1264     "        Wave...",
1265     "        Oil Painting...",
1266     "        Charcoal Drawing...",
1267     "      Miscellany",
1268     "        Image Info",
1269     "        Zoom Image",
1270     "        Show Preview...",
1271     "        Show Histogram",
1272     "        Show Matte",
1273     "      Help",
1274     "      Dismiss",
1275     "",
1276     "You can make adjustments to the region of interest by moving",
1277     "the pointer to one of the rectangle corners, pressing a",
1278     "button, and dragging.  Finally, choose an image processing",
1279     "technique from the Command widget.  You can choose more than",
1280     "one image processing technique to apply to an area.",
1281     "Alternatively, you can move the region of interest before",
1282     "applying another image processing technique.  To exit, press",
1283     "Dismiss.",
1284     (char *) NULL,
1285   },
1286   *ImageRotateHelp[] =
1287   {
1288     "In rotate mode, the Command widget has these options:",
1289     "",
1290     "    Pixel Color",
1291     "      black",
1292     "      blue",
1293     "      cyan",
1294     "      green",
1295     "      gray",
1296     "      red",
1297     "      magenta",
1298     "      yellow",
1299     "      white",
1300     "      Browser...",
1301     "    Direction",
1302     "      horizontal",
1303     "      vertical",
1304     "    Help",
1305     "    Dismiss",
1306     "",
1307     "Choose a background color from the Pixel Color sub-menu.",
1308     "Additional background colors can be specified with the color",
1309     "browser.  You can change the menu colors by setting the X",
1310     "resources pen1 through pen9.",
1311     "",
1312     "If you choose the color browser and press Grab, you can",
1313     "select the background color by moving the pointer to the",
1314     "desired color on the screen and press any button.",
1315     "",
1316     "Choose a point in the image window and press this button and",
1317     "hold.  Next, move the pointer to another location in the",
1318     "image.  As you move a line connects the initial location and",
1319     "the pointer.  When you release the button, the degree of",
1320     "image rotation is determined by the slope of the line you",
1321     "just drew.  The slope is relative to the direction you",
1322     "choose from the Direction sub-menu of the Command widget.",
1323     "",
1324     "To cancel the image rotation, move the pointer back to the",
1325     "starting point of the line and release the button.",
1326     (char *) NULL,
1327   };
1328 \f
1329 /*
1330   Enumeration declarations.
1331 */
1332 typedef enum
1333 {
1334   CopyMode,
1335   CropMode,
1336   CutMode
1337 } ClipboardMode;
1338
1339 typedef enum
1340 {
1341   OpenCommand,
1342   NextCommand,
1343   FormerCommand,
1344   SelectCommand,
1345   SaveCommand,
1346   PrintCommand,
1347   DeleteCommand,
1348   NewCommand,
1349   VisualDirectoryCommand,
1350   QuitCommand,
1351   UndoCommand,
1352   RedoCommand,
1353   CutCommand,
1354   CopyCommand,
1355   PasteCommand,
1356   HalfSizeCommand,
1357   OriginalSizeCommand,
1358   DoubleSizeCommand,
1359   ResizeCommand,
1360   ApplyCommand,
1361   RefreshCommand,
1362   RestoreCommand,
1363   CropCommand,
1364   ChopCommand,
1365   FlopCommand,
1366   FlipCommand,
1367   RotateRightCommand,
1368   RotateLeftCommand,
1369   RotateCommand,
1370   ShearCommand,
1371   RollCommand,
1372   TrimCommand,
1373   HueCommand,
1374   SaturationCommand,
1375   BrightnessCommand,
1376   GammaCommand,
1377   SpiffCommand,
1378   DullCommand,
1379   ContrastStretchCommand,
1380   SigmoidalContrastCommand,
1381   NormalizeCommand,
1382   EqualizeCommand,
1383   NegateCommand,
1384   GrayscaleCommand,
1385   MapCommand,
1386   QuantizeCommand,
1387   DespeckleCommand,
1388   EmbossCommand,
1389   ReduceNoiseCommand,
1390   AddNoiseCommand,
1391   SharpenCommand,
1392   BlurCommand,
1393   ThresholdCommand,
1394   EdgeDetectCommand,
1395   SpreadCommand,
1396   ShadeCommand,
1397   RaiseCommand,
1398   SegmentCommand,
1399   SolarizeCommand,
1400   SepiaToneCommand,
1401   SwirlCommand,
1402   ImplodeCommand,
1403   VignetteCommand,
1404   WaveCommand,
1405   OilPaintCommand,
1406   CharcoalDrawCommand,
1407   AnnotateCommand,
1408   DrawCommand,
1409   ColorCommand,
1410   MatteCommand,
1411   CompositeCommand,
1412   AddBorderCommand,
1413   AddFrameCommand,
1414   CommentCommand,
1415   LaunchCommand,
1416   RegionofInterestCommand,
1417   ROIHelpCommand,
1418   ROIDismissCommand,
1419   InfoCommand,
1420   ZoomCommand,
1421   ShowPreviewCommand,
1422   ShowHistogramCommand,
1423   ShowMatteCommand,
1424   BackgroundCommand,
1425   SlideShowCommand,
1426   PreferencesCommand,
1427   HelpCommand,
1428   BrowseDocumentationCommand,
1429   VersionCommand,
1430   SaveToUndoBufferCommand,
1431   FreeBuffersCommand,
1432   NullCommand
1433 } CommandType;
1434
1435 typedef enum
1436 {
1437   AnnotateNameCommand,
1438   AnnotateFontColorCommand,
1439   AnnotateBackgroundColorCommand,
1440   AnnotateRotateCommand,
1441   AnnotateHelpCommand,
1442   AnnotateDismissCommand,
1443   TextHelpCommand,
1444   TextApplyCommand,
1445   ChopDirectionCommand,
1446   ChopHelpCommand,
1447   ChopDismissCommand,
1448   HorizontalChopCommand,
1449   VerticalChopCommand,
1450   ColorEditMethodCommand,
1451   ColorEditColorCommand,
1452   ColorEditBorderCommand,
1453   ColorEditFuzzCommand,
1454   ColorEditUndoCommand,
1455   ColorEditHelpCommand,
1456   ColorEditDismissCommand,
1457   CompositeOperatorsCommand,
1458   CompositeDissolveCommand,
1459   CompositeDisplaceCommand,
1460   CompositeHelpCommand,
1461   CompositeDismissCommand,
1462   CropHelpCommand,
1463   CropDismissCommand,
1464   RectifyCopyCommand,
1465   RectifyHelpCommand,
1466   RectifyDismissCommand,
1467   DrawElementCommand,
1468   DrawColorCommand,
1469   DrawStippleCommand,
1470   DrawWidthCommand,
1471   DrawUndoCommand,
1472   DrawHelpCommand,
1473   DrawDismissCommand,
1474   MatteEditMethod,
1475   MatteEditBorderCommand,
1476   MatteEditFuzzCommand,
1477   MatteEditValueCommand,
1478   MatteEditUndoCommand,
1479   MatteEditHelpCommand,
1480   MatteEditDismissCommand,
1481   PasteOperatorsCommand,
1482   PasteHelpCommand,
1483   PasteDismissCommand,
1484   RotateColorCommand,
1485   RotateDirectionCommand,
1486   RotateCropCommand,
1487   RotateSharpenCommand,
1488   RotateHelpCommand,
1489   RotateDismissCommand,
1490   HorizontalRotateCommand,
1491   VerticalRotateCommand,
1492   TileLoadCommand,
1493   TileNextCommand,
1494   TileFormerCommand,
1495   TileDeleteCommand,
1496   TileUpdateCommand
1497 } ModeType;
1498 \f
1499 /*
1500   Stipples.
1501 */
1502 #define BricksWidth  20
1503 #define BricksHeight  20
1504 #define DiagonalWidth  16
1505 #define DiagonalHeight  16
1506 #define HighlightWidth  8
1507 #define HighlightHeight  8
1508 #define OpaqueWidth  8
1509 #define OpaqueHeight  8
1510 #define ScalesWidth  16
1511 #define ScalesHeight  16
1512 #define ShadowWidth  8
1513 #define ShadowHeight  8
1514 #define VerticalWidth  16
1515 #define VerticalHeight  16
1516 #define WavyWidth  16
1517 #define WavyHeight  16
1518 \f
1519 /*
1520   Constant declaration.
1521 */
1522 static const int
1523   RoiDelta = 8;
1524
1525 static const unsigned char
1526   BricksBitmap[] =
1527   {
1528     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1529     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1530     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1531     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1532     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1533   },
1534   DiagonalBitmap[] =
1535   {
1536     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1537     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1538     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1539   },
1540   ScalesBitmap[] =
1541   {
1542     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1543     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1544     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1545   },
1546   VerticalBitmap[] =
1547   {
1548     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1549     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1550     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1551   },
1552   WavyBitmap[] =
1553   {
1554     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1555     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1556     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1557   };
1558 \f
1559 /*
1560   Function prototypes.
1561 */
1562 static CommandType
1563   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1564     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1565
1566 static Image
1567   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1568     Image **,ExceptionInfo *),
1569   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1570   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1571     ExceptionInfo *),
1572   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1573     ExceptionInfo *);
1574
1575 static MagickBooleanType
1576   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1577     ExceptionInfo *),
1578   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1579     ExceptionInfo *),
1580   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1581     ExceptionInfo *),
1582   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1583     ExceptionInfo *),
1584   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1585     ExceptionInfo *),
1586   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1587     ExceptionInfo *),
1588   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1589   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1590     ExceptionInfo *),
1591   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1592     ExceptionInfo *),
1593   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1594   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1595   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1596     ExceptionInfo *),
1597   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1598   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1599   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1600
1601 static void
1602   XDrawPanRectangle(Display *,XWindows *),
1603   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1604     ExceptionInfo *),
1605   XMagnifyImage(Display *,XWindows *,XEvent *),
1606   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1607   XPanImage(Display *,XWindows *,XEvent *),
1608   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1609     const KeySym),
1610   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1611   XScreenEvent(Display *,XWindows *,XEvent *),
1612   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1613 \f
1614 /*
1615 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1616 %                                                                             %
1617 %                                                                             %
1618 %                                                                             %
1619 %   D i s p l a y I m a g e s                                                 %
1620 %                                                                             %
1621 %                                                                             %
1622 %                                                                             %
1623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1624 %
1625 %  DisplayImages() displays an image sequence to any X window screen.  It
1626 %  returns a value other than 0 if successful.  Check the exception member
1627 %  of image to determine the reason for any failure.
1628 %
1629 %  The format of the DisplayImages method is:
1630 %
1631 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1632 %        Image *images,ExceptionInfo *exception)
1633 %
1634 %  A description of each parameter follows:
1635 %
1636 %    o image_info: the image info.
1637 %
1638 %    o image: the image.
1639 %
1640 %    o exception: return any errors or warnings in this structure.
1641 %
1642 */
1643 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1644   Image *images,ExceptionInfo *exception)
1645 {
1646   char
1647     *argv[1];
1648
1649   Display
1650     *display;
1651
1652   Image
1653     *image;
1654
1655   register ssize_t
1656     i;
1657
1658   size_t
1659     state;
1660
1661   XrmDatabase
1662     resource_database;
1663
1664   XResourceInfo
1665     resource_info;
1666
1667   assert(image_info != (const ImageInfo *) NULL);
1668   assert(image_info->signature == MagickSignature);
1669   assert(images != (Image *) NULL);
1670   assert(images->signature == MagickSignature);
1671   if (images->debug != MagickFalse)
1672     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1673   display=XOpenDisplay(image_info->server_name);
1674   if (display == (Display *) NULL)
1675     {
1676       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1677         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1678       return(MagickFalse);
1679     }
1680   if (exception->severity != UndefinedException)
1681     CatchException(exception);
1682   (void) XSetErrorHandler(XError);
1683   resource_database=XGetResourceDatabase(display,GetClientName());
1684   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1685   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1686   if (image_info->page != (char *) NULL)
1687     resource_info.image_geometry=AcquireString(image_info->page);
1688   resource_info.immutable=MagickTrue;
1689   argv[0]=AcquireString(GetClientName());
1690   state=DefaultState;
1691   for (i=0; (state & ExitState) == 0; i++)
1692   {
1693     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1694       break;
1695     image=GetImageFromList(images,i % GetImageListLength(images));
1696     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1697   }
1698   argv[0]=DestroyString(argv[0]);
1699   (void) XCloseDisplay(display);
1700   XDestroyResourceInfo(&resource_info);
1701   if (exception->severity != UndefinedException)
1702     return(MagickFalse);
1703   return(MagickTrue);
1704 }
1705 \f
1706 /*
1707 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1708 %                                                                             %
1709 %                                                                             %
1710 %                                                                             %
1711 %   R e m o t e D i s p l a y C o m m a n d                                   %
1712 %                                                                             %
1713 %                                                                             %
1714 %                                                                             %
1715 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1716 %
1717 %  RemoteDisplayCommand() encourages a remote display program to display the
1718 %  specified image filename.
1719 %
1720 %  The format of the RemoteDisplayCommand method is:
1721 %
1722 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1723 %        const char *window,const char *filename,ExceptionInfo *exception)
1724 %
1725 %  A description of each parameter follows:
1726 %
1727 %    o image_info: the image info.
1728 %
1729 %    o window: Specifies the name or id of an X window.
1730 %
1731 %    o filename: the name of the image filename to display.
1732 %
1733 %    o exception: return any errors or warnings in this structure.
1734 %
1735 */
1736 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1737   const char *window,const char *filename,ExceptionInfo *exception)
1738 {
1739   Display
1740     *display;
1741
1742   MagickStatusType
1743     status;
1744
1745   assert(image_info != (const ImageInfo *) NULL);
1746   assert(image_info->signature == MagickSignature);
1747   assert(filename != (char *) NULL);
1748   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1749   display=XOpenDisplay(image_info->server_name);
1750   if (display == (Display *) NULL)
1751     {
1752       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1753         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1754       return(MagickFalse);
1755     }
1756   (void) XSetErrorHandler(XError);
1757   status=XRemoteCommand(display,window,filename);
1758   (void) XCloseDisplay(display);
1759   return(status != 0 ? MagickTrue : MagickFalse);
1760 }
1761 \f
1762 /*
1763 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1764 %                                                                             %
1765 %                                                                             %
1766 %                                                                             %
1767 +   X A n n o t a t e E d i t I m a g e                                       %
1768 %                                                                             %
1769 %                                                                             %
1770 %                                                                             %
1771 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1772 %
1773 %  XAnnotateEditImage() annotates the image with text.
1774 %
1775 %  The format of the XAnnotateEditImage method is:
1776 %
1777 %      MagickBooleanType XAnnotateEditImage(Display *display,
1778 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1779 %        ExceptionInfo *exception)
1780 %
1781 %  A description of each parameter follows:
1782 %
1783 %    o display: Specifies a connection to an X server;  returned from
1784 %      XOpenDisplay.
1785 %
1786 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1787 %
1788 %    o windows: Specifies a pointer to a XWindows structure.
1789 %
1790 %    o image: the image; returned from ReadImage.
1791 %
1792 */
1793
1794 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1795 {
1796   if (x > y)
1797     return(x);
1798   return(y);
1799 }
1800
1801 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1802 {
1803   if (x < y)
1804     return(x);
1805   return(y);
1806 }
1807
1808 static MagickBooleanType XAnnotateEditImage(Display *display,
1809   XResourceInfo *resource_info,XWindows *windows,Image *image,
1810   ExceptionInfo *exception)
1811 {
1812   static const char
1813     *AnnotateMenu[] =
1814     {
1815       "Font Name",
1816       "Font Color",
1817       "Box Color",
1818       "Rotate Text",
1819       "Help",
1820       "Dismiss",
1821       (char *) NULL
1822     },
1823     *TextMenu[] =
1824     {
1825       "Help",
1826       "Apply",
1827       (char *) NULL
1828     };
1829
1830   static const ModeType
1831     AnnotateCommands[] =
1832     {
1833       AnnotateNameCommand,
1834       AnnotateFontColorCommand,
1835       AnnotateBackgroundColorCommand,
1836       AnnotateRotateCommand,
1837       AnnotateHelpCommand,
1838       AnnotateDismissCommand
1839     },
1840     TextCommands[] =
1841     {
1842       TextHelpCommand,
1843       TextApplyCommand
1844     };
1845
1846   static MagickBooleanType
1847     transparent_box = MagickTrue,
1848     transparent_pen = MagickFalse;
1849
1850   static MagickRealType
1851     degrees = 0.0;
1852
1853   static unsigned int
1854     box_id = MaxNumberPens-2,
1855     font_id = 0,
1856     pen_id = 0;
1857
1858   char
1859     command[MaxTextExtent],
1860     text[MaxTextExtent];
1861
1862   const char
1863     *ColorMenu[MaxNumberPens+1];
1864
1865   Cursor
1866     cursor;
1867
1868   GC
1869     annotate_context;
1870
1871   int
1872     id,
1873     pen_number,
1874     status,
1875     x,
1876     y;
1877
1878   KeySym
1879     key_symbol;
1880
1881   register char
1882     *p;
1883
1884   register ssize_t
1885     i;
1886
1887   unsigned int
1888     height,
1889     width;
1890
1891   size_t
1892     state;
1893
1894   XAnnotateInfo
1895     *annotate_info,
1896     *previous_info;
1897
1898   XColor
1899     color;
1900
1901   XFontStruct
1902     *font_info;
1903
1904   XEvent
1905     event,
1906     text_event;
1907
1908   /*
1909     Map Command widget.
1910   */
1911   (void) CloneString(&windows->command.name,"Annotate");
1912   windows->command.data=4;
1913   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1914   (void) XMapRaised(display,windows->command.id);
1915   XClientMessage(display,windows->image.id,windows->im_protocols,
1916     windows->im_update_widget,CurrentTime);
1917   /*
1918     Track pointer until button 1 is pressed.
1919   */
1920   XQueryPosition(display,windows->image.id,&x,&y);
1921   (void) XSelectInput(display,windows->image.id,
1922     windows->image.attributes.event_mask | PointerMotionMask);
1923   cursor=XCreateFontCursor(display,XC_left_side);
1924   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1925   state=DefaultState;
1926   do
1927   {
1928     if (windows->info.mapped != MagickFalse)
1929       {
1930         /*
1931           Display pointer position.
1932         */
1933         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1934           x+windows->image.x,y+windows->image.y);
1935         XInfoWidget(display,windows,text);
1936       }
1937     /*
1938       Wait for next event.
1939     */
1940     XScreenEvent(display,windows,&event);
1941     if (event.xany.window == windows->command.id)
1942       {
1943         /*
1944           Select a command from the Command widget.
1945         */
1946         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1947         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1948         if (id < 0)
1949           continue;
1950         switch (AnnotateCommands[id])
1951         {
1952           case AnnotateNameCommand:
1953           {
1954             const char
1955               *FontMenu[MaxNumberFonts];
1956
1957             int
1958               font_number;
1959
1960             /*
1961               Initialize menu selections.
1962             */
1963             for (i=0; i < MaxNumberFonts; i++)
1964               FontMenu[i]=resource_info->font_name[i];
1965             FontMenu[MaxNumberFonts-2]="Browser...";
1966             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1967             /*
1968               Select a font name from the pop-up menu.
1969             */
1970             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1971               (const char **) FontMenu,command);
1972             if (font_number < 0)
1973               break;
1974             if (font_number == (MaxNumberFonts-2))
1975               {
1976                 static char
1977                   font_name[MaxTextExtent] = "fixed";
1978
1979                 /*
1980                   Select a font name from a browser.
1981                 */
1982                 resource_info->font_name[font_number]=font_name;
1983                 XFontBrowserWidget(display,windows,"Select",font_name);
1984                 if (*font_name == '\0')
1985                   break;
1986               }
1987             /*
1988               Initialize font info.
1989             */
1990             font_info=XLoadQueryFont(display,resource_info->font_name[
1991               font_number]);
1992             if (font_info == (XFontStruct *) NULL)
1993               {
1994                 XNoticeWidget(display,windows,"Unable to load font:",
1995                   resource_info->font_name[font_number]);
1996                 break;
1997               }
1998             font_id=(unsigned int) font_number;
1999             (void) XFreeFont(display,font_info);
2000             break;
2001           }
2002           case AnnotateFontColorCommand:
2003           {
2004             /*
2005               Initialize menu selections.
2006             */
2007             for (i=0; i < (int) (MaxNumberPens-2); i++)
2008               ColorMenu[i]=resource_info->pen_colors[i];
2009             ColorMenu[MaxNumberPens-2]="transparent";
2010             ColorMenu[MaxNumberPens-1]="Browser...";
2011             ColorMenu[MaxNumberPens]=(const char *) NULL;
2012             /*
2013               Select a pen color from the pop-up menu.
2014             */
2015             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2016               (const char **) ColorMenu,command);
2017             if (pen_number < 0)
2018               break;
2019             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2020               MagickFalse;
2021             if (transparent_pen != MagickFalse)
2022               break;
2023             if (pen_number == (MaxNumberPens-1))
2024               {
2025                 static char
2026                   color_name[MaxTextExtent] = "gray";
2027
2028                 /*
2029                   Select a pen color from a dialog.
2030                 */
2031                 resource_info->pen_colors[pen_number]=color_name;
2032                 XColorBrowserWidget(display,windows,"Select",color_name);
2033                 if (*color_name == '\0')
2034                   break;
2035               }
2036             /*
2037               Set pen color.
2038             */
2039             (void) XParseColor(display,windows->map_info->colormap,
2040               resource_info->pen_colors[pen_number],&color);
2041             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2042               (unsigned int) MaxColors,&color);
2043             windows->pixel_info->pen_colors[pen_number]=color;
2044             pen_id=(unsigned int) pen_number;
2045             break;
2046           }
2047           case AnnotateBackgroundColorCommand:
2048           {
2049             /*
2050               Initialize menu selections.
2051             */
2052             for (i=0; i < (int) (MaxNumberPens-2); i++)
2053               ColorMenu[i]=resource_info->pen_colors[i];
2054             ColorMenu[MaxNumberPens-2]="transparent";
2055             ColorMenu[MaxNumberPens-1]="Browser...";
2056             ColorMenu[MaxNumberPens]=(const char *) NULL;
2057             /*
2058               Select a pen color from the pop-up menu.
2059             */
2060             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2061               (const char **) ColorMenu,command);
2062             if (pen_number < 0)
2063               break;
2064             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2065               MagickFalse;
2066             if (transparent_box != MagickFalse)
2067               break;
2068             if (pen_number == (MaxNumberPens-1))
2069               {
2070                 static char
2071                   color_name[MaxTextExtent] = "gray";
2072
2073                 /*
2074                   Select a pen color from a dialog.
2075                 */
2076                 resource_info->pen_colors[pen_number]=color_name;
2077                 XColorBrowserWidget(display,windows,"Select",color_name);
2078                 if (*color_name == '\0')
2079                   break;
2080               }
2081             /*
2082               Set pen color.
2083             */
2084             (void) XParseColor(display,windows->map_info->colormap,
2085               resource_info->pen_colors[pen_number],&color);
2086             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2087               (unsigned int) MaxColors,&color);
2088             windows->pixel_info->pen_colors[pen_number]=color;
2089             box_id=(unsigned int) pen_number;
2090             break;
2091           }
2092           case AnnotateRotateCommand:
2093           {
2094             int
2095               entry;
2096
2097             static char
2098               angle[MaxTextExtent] = "30.0";
2099
2100             static const char
2101               *RotateMenu[] =
2102               {
2103                 "-90",
2104                 "-45",
2105                 "-30",
2106                 "0",
2107                 "30",
2108                 "45",
2109                 "90",
2110                 "180",
2111                 "Dialog...",
2112                 (char *) NULL,
2113               };
2114
2115             /*
2116               Select a command from the pop-up menu.
2117             */
2118             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2119               command);
2120             if (entry < 0)
2121               break;
2122             if (entry != 8)
2123               {
2124                 degrees=InterpretLocaleValue(RotateMenu[entry],(char **) NULL);
2125                 break;
2126               }
2127             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2128               angle);
2129             if (*angle == '\0')
2130               break;
2131             degrees=InterpretLocaleValue(angle,(char **) NULL);
2132             break;
2133           }
2134           case AnnotateHelpCommand:
2135           {
2136             XTextViewWidget(display,resource_info,windows,MagickFalse,
2137               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2138             break;
2139           }
2140           case AnnotateDismissCommand:
2141           {
2142             /*
2143               Prematurely exit.
2144             */
2145             state|=EscapeState;
2146             state|=ExitState;
2147             break;
2148           }
2149           default:
2150             break;
2151         }
2152         continue;
2153       }
2154     switch (event.type)
2155     {
2156       case ButtonPress:
2157       {
2158         if (event.xbutton.button != Button1)
2159           break;
2160         if (event.xbutton.window != windows->image.id)
2161           break;
2162         /*
2163           Change to text entering mode.
2164         */
2165         x=event.xbutton.x;
2166         y=event.xbutton.y;
2167         state|=ExitState;
2168         break;
2169       }
2170       case ButtonRelease:
2171         break;
2172       case Expose:
2173         break;
2174       case KeyPress:
2175       {
2176         if (event.xkey.window != windows->image.id)
2177           break;
2178         /*
2179           Respond to a user key press.
2180         */
2181         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2182           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2183         switch ((int) key_symbol)
2184         {
2185           case XK_Escape:
2186           case XK_F20:
2187           {
2188             /*
2189               Prematurely exit.
2190             */
2191             state|=EscapeState;
2192             state|=ExitState;
2193             break;
2194           }
2195           case XK_F1:
2196           case XK_Help:
2197           {
2198             XTextViewWidget(display,resource_info,windows,MagickFalse,
2199               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2200             break;
2201           }
2202           default:
2203           {
2204             (void) XBell(display,0);
2205             break;
2206           }
2207         }
2208         break;
2209       }
2210       case MotionNotify:
2211       {
2212         /*
2213           Map and unmap Info widget as cursor crosses its boundaries.
2214         */
2215         x=event.xmotion.x;
2216         y=event.xmotion.y;
2217         if (windows->info.mapped != MagickFalse)
2218           {
2219             if ((x < (int) (windows->info.x+windows->info.width)) &&
2220                 (y < (int) (windows->info.y+windows->info.height)))
2221               (void) XWithdrawWindow(display,windows->info.id,
2222                 windows->info.screen);
2223           }
2224         else
2225           if ((x > (int) (windows->info.x+windows->info.width)) ||
2226               (y > (int) (windows->info.y+windows->info.height)))
2227             (void) XMapWindow(display,windows->info.id);
2228         break;
2229       }
2230       default:
2231         break;
2232     }
2233   } while ((state & ExitState) == 0);
2234   (void) XSelectInput(display,windows->image.id,
2235     windows->image.attributes.event_mask);
2236   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2237   if ((state & EscapeState) != 0)
2238     return(MagickTrue);
2239   /*
2240     Set font info and check boundary conditions.
2241   */
2242   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2243   if (font_info == (XFontStruct *) NULL)
2244     {
2245       XNoticeWidget(display,windows,"Unable to load font:",
2246         resource_info->font_name[font_id]);
2247       font_info=windows->font_info;
2248     }
2249   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2250     x=(int) windows->image.width-font_info->max_bounds.width;
2251   if (y < (int) (font_info->ascent+font_info->descent))
2252     y=(int) font_info->ascent+font_info->descent;
2253   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2254       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2255     return(MagickFalse);
2256   /*
2257     Initialize annotate structure.
2258   */
2259   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2260   if (annotate_info == (XAnnotateInfo *) NULL)
2261     return(MagickFalse);
2262   XGetAnnotateInfo(annotate_info);
2263   annotate_info->x=x;
2264   annotate_info->y=y;
2265   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2266     annotate_info->stencil=OpaqueStencil;
2267   else
2268     if (transparent_box == MagickFalse)
2269       annotate_info->stencil=BackgroundStencil;
2270     else
2271       annotate_info->stencil=ForegroundStencil;
2272   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2273   annotate_info->degrees=degrees;
2274   annotate_info->font_info=font_info;
2275   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2276     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2277     sizeof(*annotate_info->text));
2278   if (annotate_info->text == (char *) NULL)
2279     return(MagickFalse);
2280   /*
2281     Create cursor and set graphic context.
2282   */
2283   cursor=XCreateFontCursor(display,XC_pencil);
2284   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2285   annotate_context=windows->image.annotate_context;
2286   (void) XSetFont(display,annotate_context,font_info->fid);
2287   (void) XSetBackground(display,annotate_context,
2288     windows->pixel_info->pen_colors[box_id].pixel);
2289   (void) XSetForeground(display,annotate_context,
2290     windows->pixel_info->pen_colors[pen_id].pixel);
2291   /*
2292     Begin annotating the image with text.
2293   */
2294   (void) CloneString(&windows->command.name,"Text");
2295   windows->command.data=0;
2296   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2297   state=DefaultState;
2298   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2299   text_event.xexpose.width=(int) font_info->max_bounds.width;
2300   text_event.xexpose.height=font_info->max_bounds.ascent+
2301     font_info->max_bounds.descent;
2302   p=annotate_info->text;
2303   do
2304   {
2305     /*
2306       Display text cursor.
2307     */
2308     *p='\0';
2309     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2310     /*
2311       Wait for next event.
2312     */
2313     XScreenEvent(display,windows,&event);
2314     if (event.xany.window == windows->command.id)
2315       {
2316         /*
2317           Select a command from the Command widget.
2318         */
2319         (void) XSetBackground(display,annotate_context,
2320           windows->pixel_info->background_color.pixel);
2321         (void) XSetForeground(display,annotate_context,
2322           windows->pixel_info->foreground_color.pixel);
2323         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2324         (void) XSetBackground(display,annotate_context,
2325           windows->pixel_info->pen_colors[box_id].pixel);
2326         (void) XSetForeground(display,annotate_context,
2327           windows->pixel_info->pen_colors[pen_id].pixel);
2328         if (id < 0)
2329           continue;
2330         switch (TextCommands[id])
2331         {
2332           case TextHelpCommand:
2333           {
2334             XTextViewWidget(display,resource_info,windows,MagickFalse,
2335               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2336             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2337             break;
2338           }
2339           case TextApplyCommand:
2340           {
2341             /*
2342               Finished annotating.
2343             */
2344             annotate_info->width=(unsigned int) XTextWidth(font_info,
2345               annotate_info->text,(int) strlen(annotate_info->text));
2346             XRefreshWindow(display,&windows->image,&text_event);
2347             state|=ExitState;
2348             break;
2349           }
2350           default:
2351             break;
2352         }
2353         continue;
2354       }
2355     /*
2356       Erase text cursor.
2357     */
2358     text_event.xexpose.x=x;
2359     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2360     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2361       (unsigned int) text_event.xexpose.width,(unsigned int)
2362       text_event.xexpose.height,MagickFalse);
2363     XRefreshWindow(display,&windows->image,&text_event);
2364     switch (event.type)
2365     {
2366       case ButtonPress:
2367       {
2368         if (event.xbutton.window != windows->image.id)
2369           break;
2370         if (event.xbutton.button == Button2)
2371           {
2372             /*
2373               Request primary selection.
2374             */
2375             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2376               windows->image.id,CurrentTime);
2377             break;
2378           }
2379         break;
2380       }
2381       case Expose:
2382       {
2383         if (event.xexpose.count == 0)
2384           {
2385             XAnnotateInfo
2386               *text_info;
2387
2388             /*
2389               Refresh Image window.
2390             */
2391             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2392             text_info=annotate_info;
2393             while (text_info != (XAnnotateInfo *) NULL)
2394             {
2395               if (annotate_info->stencil == ForegroundStencil)
2396                 (void) XDrawString(display,windows->image.id,annotate_context,
2397                   text_info->x,text_info->y,text_info->text,
2398                   (int) strlen(text_info->text));
2399               else
2400                 (void) XDrawImageString(display,windows->image.id,
2401                   annotate_context,text_info->x,text_info->y,text_info->text,
2402                   (int) strlen(text_info->text));
2403               text_info=text_info->previous;
2404             }
2405             (void) XDrawString(display,windows->image.id,annotate_context,
2406               x,y,"_",1);
2407           }
2408         break;
2409       }
2410       case KeyPress:
2411       {
2412         int
2413           length;
2414
2415         if (event.xkey.window != windows->image.id)
2416           break;
2417         /*
2418           Respond to a user key press.
2419         */
2420         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2421           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2422         *(command+length)='\0';
2423         if (((event.xkey.state & ControlMask) != 0) ||
2424             ((event.xkey.state & Mod1Mask) != 0))
2425           state|=ModifierState;
2426         if ((state & ModifierState) != 0)
2427           switch ((int) key_symbol)
2428           {
2429             case XK_u:
2430             case XK_U:
2431             {
2432               key_symbol=DeleteCommand;
2433               break;
2434             }
2435             default:
2436               break;
2437           }
2438         switch ((int) key_symbol)
2439         {
2440           case XK_BackSpace:
2441           {
2442             /*
2443               Erase one character.
2444             */
2445             if (p == annotate_info->text)
2446               {
2447                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2448                   break;
2449                 else
2450                   {
2451                     /*
2452                       Go to end of the previous line of text.
2453                     */
2454                     annotate_info=annotate_info->previous;
2455                     p=annotate_info->text;
2456                     x=annotate_info->x+annotate_info->width;
2457                     y=annotate_info->y;
2458                     if (annotate_info->width != 0)
2459                       p+=strlen(annotate_info->text);
2460                     break;
2461                   }
2462               }
2463             p--;
2464             x-=XTextWidth(font_info,p,1);
2465             text_event.xexpose.x=x;
2466             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2467             XRefreshWindow(display,&windows->image,&text_event);
2468             break;
2469           }
2470           case XK_bracketleft:
2471           {
2472             key_symbol=XK_Escape;
2473             break;
2474           }
2475           case DeleteCommand:
2476           {
2477             /*
2478               Erase the entire line of text.
2479             */
2480             while (p != annotate_info->text)
2481             {
2482               p--;
2483               x-=XTextWidth(font_info,p,1);
2484               text_event.xexpose.x=x;
2485               XRefreshWindow(display,&windows->image,&text_event);
2486             }
2487             break;
2488           }
2489           case XK_Escape:
2490           case XK_F20:
2491           {
2492             /*
2493               Finished annotating.
2494             */
2495             annotate_info->width=(unsigned int) XTextWidth(font_info,
2496               annotate_info->text,(int) strlen(annotate_info->text));
2497             XRefreshWindow(display,&windows->image,&text_event);
2498             state|=ExitState;
2499             break;
2500           }
2501           default:
2502           {
2503             /*
2504               Draw a single character on the Image window.
2505             */
2506             if ((state & ModifierState) != 0)
2507               break;
2508             if (*command == '\0')
2509               break;
2510             *p=(*command);
2511             if (annotate_info->stencil == ForegroundStencil)
2512               (void) XDrawString(display,windows->image.id,annotate_context,
2513                 x,y,p,1);
2514             else
2515               (void) XDrawImageString(display,windows->image.id,
2516                 annotate_context,x,y,p,1);
2517             x+=XTextWidth(font_info,p,1);
2518             p++;
2519             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2520               break;
2521           }
2522           case XK_Return:
2523           case XK_KP_Enter:
2524           {
2525             /*
2526               Advance to the next line of text.
2527             */
2528             *p='\0';
2529             annotate_info->width=(unsigned int) XTextWidth(font_info,
2530               annotate_info->text,(int) strlen(annotate_info->text));
2531             if (annotate_info->next != (XAnnotateInfo *) NULL)
2532               {
2533                 /*
2534                   Line of text already exists.
2535                 */
2536                 annotate_info=annotate_info->next;
2537                 x=annotate_info->x;
2538                 y=annotate_info->y;
2539                 p=annotate_info->text;
2540                 break;
2541               }
2542             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2543               sizeof(*annotate_info->next));
2544             if (annotate_info->next == (XAnnotateInfo *) NULL)
2545               return(MagickFalse);
2546             *annotate_info->next=(*annotate_info);
2547             annotate_info->next->previous=annotate_info;
2548             annotate_info=annotate_info->next;
2549             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2550               windows->image.width/MagickMax((ssize_t)
2551               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2552             if (annotate_info->text == (char *) NULL)
2553               return(MagickFalse);
2554             annotate_info->y+=annotate_info->height;
2555             if (annotate_info->y > (int) windows->image.height)
2556               annotate_info->y=(int) annotate_info->height;
2557             annotate_info->next=(XAnnotateInfo *) NULL;
2558             x=annotate_info->x;
2559             y=annotate_info->y;
2560             p=annotate_info->text;
2561             break;
2562           }
2563         }
2564         break;
2565       }
2566       case KeyRelease:
2567       {
2568         /*
2569           Respond to a user key release.
2570         */
2571         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2572           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2573         state&=(~ModifierState);
2574         break;
2575       }
2576       case SelectionNotify:
2577       {
2578         Atom
2579           type;
2580
2581         int
2582           format;
2583
2584         unsigned char
2585           *data;
2586
2587         unsigned long
2588           after,
2589           length;
2590
2591         /*
2592           Obtain response from primary selection.
2593         */
2594         if (event.xselection.property == (Atom) None)
2595           break;
2596         status=XGetWindowProperty(display,event.xselection.requestor,
2597           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2598           &type,&format,&length,&after,&data);
2599         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2600             (length == 0))
2601           break;
2602         /*
2603           Annotate Image window with primary selection.
2604         */
2605         for (i=0; i < (ssize_t) length; i++)
2606         {
2607           if ((char) data[i] != '\n')
2608             {
2609               /*
2610                 Draw a single character on the Image window.
2611               */
2612               *p=(char) data[i];
2613               (void) XDrawString(display,windows->image.id,annotate_context,
2614                 x,y,p,1);
2615               x+=XTextWidth(font_info,p,1);
2616               p++;
2617               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2618                 continue;
2619             }
2620           /*
2621             Advance to the next line of text.
2622           */
2623           *p='\0';
2624           annotate_info->width=(unsigned int) XTextWidth(font_info,
2625             annotate_info->text,(int) strlen(annotate_info->text));
2626           if (annotate_info->next != (XAnnotateInfo *) NULL)
2627             {
2628               /*
2629                 Line of text already exists.
2630               */
2631               annotate_info=annotate_info->next;
2632               x=annotate_info->x;
2633               y=annotate_info->y;
2634               p=annotate_info->text;
2635               continue;
2636             }
2637           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2638             sizeof(*annotate_info->next));
2639           if (annotate_info->next == (XAnnotateInfo *) NULL)
2640             return(MagickFalse);
2641           *annotate_info->next=(*annotate_info);
2642           annotate_info->next->previous=annotate_info;
2643           annotate_info=annotate_info->next;
2644           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2645             windows->image.width/MagickMax((ssize_t)
2646             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2647           if (annotate_info->text == (char *) NULL)
2648             return(MagickFalse);
2649           annotate_info->y+=annotate_info->height;
2650           if (annotate_info->y > (int) windows->image.height)
2651             annotate_info->y=(int) annotate_info->height;
2652           annotate_info->next=(XAnnotateInfo *) NULL;
2653           x=annotate_info->x;
2654           y=annotate_info->y;
2655           p=annotate_info->text;
2656         }
2657         (void) XFree((void *) data);
2658         break;
2659       }
2660       default:
2661         break;
2662     }
2663   } while ((state & ExitState) == 0);
2664   (void) XFreeCursor(display,cursor);
2665   /*
2666     Annotation is relative to image configuration.
2667   */
2668   width=(unsigned int) image->columns;
2669   height=(unsigned int) image->rows;
2670   x=0;
2671   y=0;
2672   if (windows->image.crop_geometry != (char *) NULL)
2673     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2674   /*
2675     Initialize annotated image.
2676   */
2677   XSetCursorState(display,windows,MagickTrue);
2678   XCheckRefreshWindows(display,windows);
2679   while (annotate_info != (XAnnotateInfo *) NULL)
2680   {
2681     if (annotate_info->width == 0)
2682       {
2683         /*
2684           No text on this line--  go to the next line of text.
2685         */
2686         previous_info=annotate_info->previous;
2687         annotate_info->text=(char *)
2688           RelinquishMagickMemory(annotate_info->text);
2689         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2690         annotate_info=previous_info;
2691         continue;
2692       }
2693     /*
2694       Determine pixel index for box and pen color.
2695     */
2696     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2697     if (windows->pixel_info->colors != 0)
2698       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2699         if (windows->pixel_info->pixels[i] ==
2700             windows->pixel_info->pen_colors[box_id].pixel)
2701           {
2702             windows->pixel_info->box_index=(unsigned short) i;
2703             break;
2704           }
2705     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2706     if (windows->pixel_info->colors != 0)
2707       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2708         if (windows->pixel_info->pixels[i] ==
2709             windows->pixel_info->pen_colors[pen_id].pixel)
2710           {
2711             windows->pixel_info->pen_index=(unsigned short) i;
2712             break;
2713           }
2714     /*
2715       Define the annotate geometry string.
2716     */
2717     annotate_info->x=(int)
2718       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2719     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2720       windows->image.y)/windows->image.ximage->height;
2721     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2722       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2723       height*annotate_info->height/windows->image.ximage->height,
2724       annotate_info->x+x,annotate_info->y+y);
2725     /*
2726       Annotate image with text.
2727     */
2728     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2729     if (status == 0)
2730       return(MagickFalse);
2731     /*
2732       Free up memory.
2733     */
2734     previous_info=annotate_info->previous;
2735     annotate_info->text=DestroyString(annotate_info->text);
2736     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2737     annotate_info=previous_info;
2738   }
2739   (void) XSetForeground(display,annotate_context,
2740     windows->pixel_info->foreground_color.pixel);
2741   (void) XSetBackground(display,annotate_context,
2742     windows->pixel_info->background_color.pixel);
2743   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2744   XSetCursorState(display,windows,MagickFalse);
2745   (void) XFreeFont(display,font_info);
2746   /*
2747     Update image configuration.
2748   */
2749   XConfigureImageColormap(display,resource_info,windows,image);
2750   (void) XConfigureImage(display,resource_info,windows,image,exception);
2751   return(MagickTrue);
2752 }
2753 \f
2754 /*
2755 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2756 %                                                                             %
2757 %                                                                             %
2758 %                                                                             %
2759 +   X B a c k g r o u n d I m a g e                                           %
2760 %                                                                             %
2761 %                                                                             %
2762 %                                                                             %
2763 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2764 %
2765 %  XBackgroundImage() displays the image in the background of a window.
2766 %
2767 %  The format of the XBackgroundImage method is:
2768 %
2769 %      MagickBooleanType XBackgroundImage(Display *display,
2770 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2771 %        ExceptionInfo *exception)
2772 %
2773 %  A description of each parameter follows:
2774 %
2775 %    o display: Specifies a connection to an X server; returned from
2776 %      XOpenDisplay.
2777 %
2778 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2779 %
2780 %    o windows: Specifies a pointer to a XWindows structure.
2781 %
2782 %    o image: the image.
2783 %
2784 %    o exception: return any errors or warnings in this structure.
2785 %
2786 */
2787 static MagickBooleanType XBackgroundImage(Display *display,
2788   XResourceInfo *resource_info,XWindows *windows,Image **image,
2789   ExceptionInfo *exception)
2790 {
2791 #define BackgroundImageTag  "Background/Image"
2792
2793   int
2794     status;
2795
2796   static char
2797     window_id[MaxTextExtent] = "root";
2798
2799   XResourceInfo
2800     background_resources;
2801
2802   /*
2803     Put image in background.
2804   */
2805   status=XDialogWidget(display,windows,"Background",
2806     "Enter window id (id 0x00 selects window with pointer):",window_id);
2807   if (*window_id == '\0')
2808     return(MagickFalse);
2809   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2810     exception);
2811   XInfoWidget(display,windows,BackgroundImageTag);
2812   XSetCursorState(display,windows,MagickTrue);
2813   XCheckRefreshWindows(display,windows);
2814   background_resources=(*resource_info);
2815   background_resources.window_id=window_id;
2816   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2817   status=XDisplayBackgroundImage(display,&background_resources,*image,
2818     exception);
2819   if (status != MagickFalse)
2820     XClientMessage(display,windows->image.id,windows->im_protocols,
2821       windows->im_retain_colors,CurrentTime);
2822   XSetCursorState(display,windows,MagickFalse);
2823   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2824     exception);
2825   return(MagickTrue);
2826 }
2827 \f
2828 /*
2829 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2830 %                                                                             %
2831 %                                                                             %
2832 %                                                                             %
2833 +   X C h o p I m a g e                                                       %
2834 %                                                                             %
2835 %                                                                             %
2836 %                                                                             %
2837 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2838 %
2839 %  XChopImage() chops the X image.
2840 %
2841 %  The format of the XChopImage method is:
2842 %
2843 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2844 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2845 %
2846 %  A description of each parameter follows:
2847 %
2848 %    o display: Specifies a connection to an X server; returned from
2849 %      XOpenDisplay.
2850 %
2851 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2852 %
2853 %    o windows: Specifies a pointer to a XWindows structure.
2854 %
2855 %    o image: the image.
2856 %
2857 %    o exception: return any errors or warnings in this structure.
2858 %
2859 */
2860 static MagickBooleanType XChopImage(Display *display,
2861   XResourceInfo *resource_info,XWindows *windows,Image **image,
2862   ExceptionInfo *exception)
2863 {
2864   static const char
2865     *ChopMenu[] =
2866     {
2867       "Direction",
2868       "Help",
2869       "Dismiss",
2870       (char *) NULL
2871     };
2872
2873   static ModeType
2874     direction = HorizontalChopCommand;
2875
2876   static const ModeType
2877     ChopCommands[] =
2878     {
2879       ChopDirectionCommand,
2880       ChopHelpCommand,
2881       ChopDismissCommand
2882     },
2883     DirectionCommands[] =
2884     {
2885       HorizontalChopCommand,
2886       VerticalChopCommand
2887     };
2888
2889   char
2890     text[MaxTextExtent];
2891
2892   Image
2893     *chop_image;
2894
2895   int
2896     id,
2897     x,
2898     y;
2899
2900   MagickRealType
2901     scale_factor;
2902
2903   RectangleInfo
2904     chop_info;
2905
2906   unsigned int
2907     distance,
2908     height,
2909     width;
2910
2911   size_t
2912     state;
2913
2914   XEvent
2915     event;
2916
2917   XSegment
2918     segment_info;
2919
2920   /*
2921     Map Command widget.
2922   */
2923   (void) CloneString(&windows->command.name,"Chop");
2924   windows->command.data=1;
2925   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2926   (void) XMapRaised(display,windows->command.id);
2927   XClientMessage(display,windows->image.id,windows->im_protocols,
2928     windows->im_update_widget,CurrentTime);
2929   /*
2930     Track pointer until button 1 is pressed.
2931   */
2932   XQueryPosition(display,windows->image.id,&x,&y);
2933   (void) XSelectInput(display,windows->image.id,
2934     windows->image.attributes.event_mask | PointerMotionMask);
2935   state=DefaultState;
2936   do
2937   {
2938     if (windows->info.mapped != MagickFalse)
2939       {
2940         /*
2941           Display pointer position.
2942         */
2943         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2944           x+windows->image.x,y+windows->image.y);
2945         XInfoWidget(display,windows,text);
2946       }
2947     /*
2948       Wait for next event.
2949     */
2950     XScreenEvent(display,windows,&event);
2951     if (event.xany.window == windows->command.id)
2952       {
2953         /*
2954           Select a command from the Command widget.
2955         */
2956         id=XCommandWidget(display,windows,ChopMenu,&event);
2957         if (id < 0)
2958           continue;
2959         switch (ChopCommands[id])
2960         {
2961           case ChopDirectionCommand:
2962           {
2963             char
2964               command[MaxTextExtent];
2965
2966             static const char
2967               *Directions[] =
2968               {
2969                 "horizontal",
2970                 "vertical",
2971                 (char *) NULL,
2972               };
2973
2974             /*
2975               Select a command from the pop-up menu.
2976             */
2977             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2978             if (id >= 0)
2979               direction=DirectionCommands[id];
2980             break;
2981           }
2982           case ChopHelpCommand:
2983           {
2984             XTextViewWidget(display,resource_info,windows,MagickFalse,
2985               "Help Viewer - Image Chop",ImageChopHelp);
2986             break;
2987           }
2988           case ChopDismissCommand:
2989           {
2990             /*
2991               Prematurely exit.
2992             */
2993             state|=EscapeState;
2994             state|=ExitState;
2995             break;
2996           }
2997           default:
2998             break;
2999         }
3000         continue;
3001       }
3002     switch (event.type)
3003     {
3004       case ButtonPress:
3005       {
3006         if (event.xbutton.button != Button1)
3007           break;
3008         if (event.xbutton.window != windows->image.id)
3009           break;
3010         /*
3011           User has committed to start point of chopping line.
3012         */
3013         segment_info.x1=(short int) event.xbutton.x;
3014         segment_info.x2=(short int) event.xbutton.x;
3015         segment_info.y1=(short int) event.xbutton.y;
3016         segment_info.y2=(short int) event.xbutton.y;
3017         state|=ExitState;
3018         break;
3019       }
3020       case ButtonRelease:
3021         break;
3022       case Expose:
3023         break;
3024       case KeyPress:
3025       {
3026         char
3027           command[MaxTextExtent];
3028
3029         KeySym
3030           key_symbol;
3031
3032         if (event.xkey.window != windows->image.id)
3033           break;
3034         /*
3035           Respond to a user key press.
3036         */
3037         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3038           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3039         switch ((int) key_symbol)
3040         {
3041           case XK_Escape:
3042           case XK_F20:
3043           {
3044             /*
3045               Prematurely exit.
3046             */
3047             state|=EscapeState;
3048             state|=ExitState;
3049             break;
3050           }
3051           case XK_F1:
3052           case XK_Help:
3053           {
3054             (void) XSetFunction(display,windows->image.highlight_context,
3055               GXcopy);
3056             XTextViewWidget(display,resource_info,windows,MagickFalse,
3057               "Help Viewer - Image Chop",ImageChopHelp);
3058             (void) XSetFunction(display,windows->image.highlight_context,
3059               GXinvert);
3060             break;
3061           }
3062           default:
3063           {
3064             (void) XBell(display,0);
3065             break;
3066           }
3067         }
3068         break;
3069       }
3070       case MotionNotify:
3071       {
3072         /*
3073           Map and unmap Info widget as text cursor crosses its boundaries.
3074         */
3075         x=event.xmotion.x;
3076         y=event.xmotion.y;
3077         if (windows->info.mapped != MagickFalse)
3078           {
3079             if ((x < (int) (windows->info.x+windows->info.width)) &&
3080                 (y < (int) (windows->info.y+windows->info.height)))
3081               (void) XWithdrawWindow(display,windows->info.id,
3082                 windows->info.screen);
3083           }
3084         else
3085           if ((x > (int) (windows->info.x+windows->info.width)) ||
3086               (y > (int) (windows->info.y+windows->info.height)))
3087             (void) XMapWindow(display,windows->info.id);
3088       }
3089     }
3090   } while ((state & ExitState) == 0);
3091   (void) XSelectInput(display,windows->image.id,
3092     windows->image.attributes.event_mask);
3093   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3094   if ((state & EscapeState) != 0)
3095     return(MagickTrue);
3096   /*
3097     Draw line as pointer moves until the mouse button is released.
3098   */
3099   chop_info.width=0;
3100   chop_info.height=0;
3101   chop_info.x=0;
3102   chop_info.y=0;
3103   distance=0;
3104   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3105   state=DefaultState;
3106   do
3107   {
3108     if (distance > 9)
3109       {
3110         /*
3111           Display info and draw chopping line.
3112         */
3113         if (windows->info.mapped == MagickFalse)
3114           (void) XMapWindow(display,windows->info.id);
3115         (void) FormatLocaleString(text,MaxTextExtent,
3116           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3117           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3118         XInfoWidget(display,windows,text);
3119         XHighlightLine(display,windows->image.id,
3120           windows->image.highlight_context,&segment_info);
3121       }
3122     else
3123       if (windows->info.mapped != MagickFalse)
3124         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3125     /*
3126       Wait for next event.
3127     */
3128     XScreenEvent(display,windows,&event);
3129     if (distance > 9)
3130       XHighlightLine(display,windows->image.id,
3131         windows->image.highlight_context,&segment_info);
3132     switch (event.type)
3133     {
3134       case ButtonPress:
3135       {
3136         segment_info.x2=(short int) event.xmotion.x;
3137         segment_info.y2=(short int) event.xmotion.y;
3138         break;
3139       }
3140       case ButtonRelease:
3141       {
3142         /*
3143           User has committed to chopping line.
3144         */
3145         segment_info.x2=(short int) event.xbutton.x;
3146         segment_info.y2=(short int) event.xbutton.y;
3147         state|=ExitState;
3148         break;
3149       }
3150       case Expose:
3151         break;
3152       case MotionNotify:
3153       {
3154         segment_info.x2=(short int) event.xmotion.x;
3155         segment_info.y2=(short int) event.xmotion.y;
3156       }
3157       default:
3158         break;
3159     }
3160     /*
3161       Check boundary conditions.
3162     */
3163     if (segment_info.x2 < 0)
3164       segment_info.x2=0;
3165     else
3166       if (segment_info.x2 > windows->image.ximage->width)
3167         segment_info.x2=windows->image.ximage->width;
3168     if (segment_info.y2 < 0)
3169       segment_info.y2=0;
3170     else
3171       if (segment_info.y2 > windows->image.ximage->height)
3172         segment_info.y2=windows->image.ximage->height;
3173     distance=(unsigned int)
3174       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3175        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3176     /*
3177       Compute chopping geometry.
3178     */
3179     if (direction == HorizontalChopCommand)
3180       {
3181         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3182         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3183         chop_info.height=0;
3184         chop_info.y=0;
3185         if (segment_info.x1 > (int) segment_info.x2)
3186           {
3187             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3188             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3189           }
3190       }
3191     else
3192       {
3193         chop_info.width=0;
3194         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3195         chop_info.x=0;
3196         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3197         if (segment_info.y1 > segment_info.y2)
3198           {
3199             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3200             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3201           }
3202       }
3203   } while ((state & ExitState) == 0);
3204   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3205   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3206   if (distance <= 9)
3207     return(MagickTrue);
3208   /*
3209     Image chopping is relative to image configuration.
3210   */
3211   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3212     exception);
3213   XSetCursorState(display,windows,MagickTrue);
3214   XCheckRefreshWindows(display,windows);
3215   windows->image.window_changes.width=windows->image.ximage->width-
3216     (unsigned int) chop_info.width;
3217   windows->image.window_changes.height=windows->image.ximage->height-
3218     (unsigned int) chop_info.height;
3219   width=(unsigned int) (*image)->columns;
3220   height=(unsigned int) (*image)->rows;
3221   x=0;
3222   y=0;
3223   if (windows->image.crop_geometry != (char *) NULL)
3224     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3225   scale_factor=(MagickRealType) width/windows->image.ximage->width;
3226   chop_info.x+=x;
3227   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3228   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3229   scale_factor=(MagickRealType) height/windows->image.ximage->height;
3230   chop_info.y+=y;
3231   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3232   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3233   /*
3234     Chop image.
3235   */
3236   chop_image=ChopImage(*image,&chop_info,exception);
3237   XSetCursorState(display,windows,MagickFalse);
3238   if (chop_image == (Image *) NULL)
3239     return(MagickFalse);
3240   *image=DestroyImage(*image);
3241   *image=chop_image;
3242   /*
3243     Update image configuration.
3244   */
3245   XConfigureImageColormap(display,resource_info,windows,*image);
3246   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3247   return(MagickTrue);
3248 }
3249 \f
3250 /*
3251 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3252 %                                                                             %
3253 %                                                                             %
3254 %                                                                             %
3255 +   X C o l o r E d i t I m a g e                                             %
3256 %                                                                             %
3257 %                                                                             %
3258 %                                                                             %
3259 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3260 %
3261 %  XColorEditImage() allows the user to interactively change the color of one
3262 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3263 %
3264 %  The format of the XColorEditImage method is:
3265 %
3266 %      MagickBooleanType XColorEditImage(Display *display,
3267 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3268 %          ExceptionInfo *exception)
3269 %
3270 %  A description of each parameter follows:
3271 %
3272 %    o display: Specifies a connection to an X server;  returned from
3273 %      XOpenDisplay.
3274 %
3275 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3276 %
3277 %    o windows: Specifies a pointer to a XWindows structure.
3278 %
3279 %    o image: the image; returned from ReadImage.
3280 %
3281 %    o exception: return any errors or warnings in this structure.
3282 %
3283 */
3284 static MagickBooleanType XColorEditImage(Display *display,
3285   XResourceInfo *resource_info,XWindows *windows,Image **image,
3286   ExceptionInfo *exception)
3287 {
3288   static const char
3289     *ColorEditMenu[] =
3290     {
3291       "Method",
3292       "Pixel Color",
3293       "Border Color",
3294       "Fuzz",
3295       "Undo",
3296       "Help",
3297       "Dismiss",
3298       (char *) NULL
3299     };
3300
3301   static const ModeType
3302     ColorEditCommands[] =
3303     {
3304       ColorEditMethodCommand,
3305       ColorEditColorCommand,
3306       ColorEditBorderCommand,
3307       ColorEditFuzzCommand,
3308       ColorEditUndoCommand,
3309       ColorEditHelpCommand,
3310       ColorEditDismissCommand
3311     };
3312
3313   static PaintMethod
3314     method = PointMethod;
3315
3316   static unsigned int
3317     pen_id = 0;
3318
3319   static XColor
3320     border_color = { 0, 0, 0, 0, 0, 0 };
3321
3322   char
3323     command[MaxTextExtent],
3324     text[MaxTextExtent];
3325
3326   Cursor
3327     cursor;
3328
3329   int
3330     entry,
3331     id,
3332     x,
3333     x_offset,
3334     y,
3335     y_offset;
3336
3337   register Quantum
3338     *q;
3339
3340   register ssize_t
3341     i;
3342
3343   unsigned int
3344     height,
3345     width;
3346
3347   size_t
3348     state;
3349
3350   XColor
3351     color;
3352
3353   XEvent
3354     event;
3355
3356   /*
3357     Map Command widget.
3358   */
3359   (void) CloneString(&windows->command.name,"Color Edit");
3360   windows->command.data=4;
3361   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3362   (void) XMapRaised(display,windows->command.id);
3363   XClientMessage(display,windows->image.id,windows->im_protocols,
3364     windows->im_update_widget,CurrentTime);
3365   /*
3366     Make cursor.
3367   */
3368   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3369     resource_info->background_color,resource_info->foreground_color);
3370   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3371   /*
3372     Track pointer until button 1 is pressed.
3373   */
3374   XQueryPosition(display,windows->image.id,&x,&y);
3375   (void) XSelectInput(display,windows->image.id,
3376     windows->image.attributes.event_mask | PointerMotionMask);
3377   state=DefaultState;
3378   do
3379   {
3380     if (windows->info.mapped != MagickFalse)
3381       {
3382         /*
3383           Display pointer position.
3384         */
3385         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3386           x+windows->image.x,y+windows->image.y);
3387         XInfoWidget(display,windows,text);
3388       }
3389     /*
3390       Wait for next event.
3391     */
3392     XScreenEvent(display,windows,&event);
3393     if (event.xany.window == windows->command.id)
3394       {
3395         /*
3396           Select a command from the Command widget.
3397         */
3398         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3399         if (id < 0)
3400           {
3401             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3402             continue;
3403           }
3404         switch (ColorEditCommands[id])
3405         {
3406           case ColorEditMethodCommand:
3407           {
3408             char
3409               **methods;
3410
3411             /*
3412               Select a method from the pop-up menu.
3413             */
3414             methods=(char **) GetCommandOptions(MagickMethodOptions);
3415             if (methods == (char **) NULL)
3416               break;
3417             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3418               (const char **) methods,command);
3419             if (entry >= 0)
3420               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3421                 MagickFalse,methods[entry]);
3422             methods=DestroyStringList(methods);
3423             break;
3424           }
3425           case ColorEditColorCommand:
3426           {
3427             const char
3428               *ColorMenu[MaxNumberPens];
3429
3430             int
3431               pen_number;
3432
3433             /*
3434               Initialize menu selections.
3435             */
3436             for (i=0; i < (int) (MaxNumberPens-2); i++)
3437               ColorMenu[i]=resource_info->pen_colors[i];
3438             ColorMenu[MaxNumberPens-2]="Browser...";
3439             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3440             /*
3441               Select a pen color from the pop-up menu.
3442             */
3443             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3444               (const char **) ColorMenu,command);
3445             if (pen_number < 0)
3446               break;
3447             if (pen_number == (MaxNumberPens-2))
3448               {
3449                 static char
3450                   color_name[MaxTextExtent] = "gray";
3451
3452                 /*
3453                   Select a pen color from a dialog.
3454                 */
3455                 resource_info->pen_colors[pen_number]=color_name;
3456                 XColorBrowserWidget(display,windows,"Select",color_name);
3457                 if (*color_name == '\0')
3458                   break;
3459               }
3460             /*
3461               Set pen color.
3462             */
3463             (void) XParseColor(display,windows->map_info->colormap,
3464               resource_info->pen_colors[pen_number],&color);
3465             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3466               (unsigned int) MaxColors,&color);
3467             windows->pixel_info->pen_colors[pen_number]=color;
3468             pen_id=(unsigned int) pen_number;
3469             break;
3470           }
3471           case ColorEditBorderCommand:
3472           {
3473             const char
3474               *ColorMenu[MaxNumberPens];
3475
3476             int
3477               pen_number;
3478
3479             /*
3480               Initialize menu selections.
3481             */
3482             for (i=0; i < (int) (MaxNumberPens-2); i++)
3483               ColorMenu[i]=resource_info->pen_colors[i];
3484             ColorMenu[MaxNumberPens-2]="Browser...";
3485             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3486             /*
3487               Select a pen color from the pop-up menu.
3488             */
3489             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3490               (const char **) ColorMenu,command);
3491             if (pen_number < 0)
3492               break;
3493             if (pen_number == (MaxNumberPens-2))
3494               {
3495                 static char
3496                   color_name[MaxTextExtent] = "gray";
3497
3498                 /*
3499                   Select a pen color from a dialog.
3500                 */
3501                 resource_info->pen_colors[pen_number]=color_name;
3502                 XColorBrowserWidget(display,windows,"Select",color_name);
3503                 if (*color_name == '\0')
3504                   break;
3505               }
3506             /*
3507               Set border color.
3508             */
3509             (void) XParseColor(display,windows->map_info->colormap,
3510               resource_info->pen_colors[pen_number],&border_color);
3511             break;
3512           }
3513           case ColorEditFuzzCommand:
3514           {
3515             static char
3516               fuzz[MaxTextExtent];
3517
3518             static const char
3519               *FuzzMenu[] =
3520               {
3521                 "0%",
3522                 "2%",
3523                 "5%",
3524                 "10%",
3525                 "15%",
3526                 "Dialog...",
3527                 (char *) NULL,
3528               };
3529
3530             /*
3531               Select a command from the pop-up menu.
3532             */
3533             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3534               command);
3535             if (entry < 0)
3536               break;
3537             if (entry != 5)
3538               {
3539                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
3540                   QuantumRange+1.0);
3541                 break;
3542               }
3543             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3544             (void) XDialogWidget(display,windows,"Ok",
3545               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3546             if (*fuzz == '\0')
3547               break;
3548             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3549             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
3550             break;
3551           }
3552           case ColorEditUndoCommand:
3553           {
3554             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3555               image,exception);
3556             break;
3557           }
3558           case ColorEditHelpCommand:
3559           default:
3560           {
3561             XTextViewWidget(display,resource_info,windows,MagickFalse,
3562               "Help Viewer - Image Annotation",ImageColorEditHelp);
3563             break;
3564           }
3565           case ColorEditDismissCommand:
3566           {
3567             /*
3568               Prematurely exit.
3569             */
3570             state|=EscapeState;
3571             state|=ExitState;
3572             break;
3573           }
3574         }
3575         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3576         continue;
3577       }
3578     switch (event.type)
3579     {
3580       case ButtonPress:
3581       {
3582         if (event.xbutton.button != Button1)
3583           break;
3584         if ((event.xbutton.window != windows->image.id) &&
3585             (event.xbutton.window != windows->magnify.id))
3586           break;
3587         /*
3588           exit loop.
3589         */
3590         x=event.xbutton.x;
3591         y=event.xbutton.y;
3592         (void) XMagickCommand(display,resource_info,windows,
3593           SaveToUndoBufferCommand,image,exception);
3594         state|=UpdateConfigurationState;
3595         break;
3596       }
3597       case ButtonRelease:
3598       {
3599         if (event.xbutton.button != Button1)
3600           break;
3601         if ((event.xbutton.window != windows->image.id) &&
3602             (event.xbutton.window != windows->magnify.id))
3603           break;
3604         /*
3605           Update colormap information.
3606         */
3607         x=event.xbutton.x;
3608         y=event.xbutton.y;
3609         XConfigureImageColormap(display,resource_info,windows,*image);
3610         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3611         XInfoWidget(display,windows,text);
3612         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3613         state&=(~UpdateConfigurationState);
3614         break;
3615       }
3616       case Expose:
3617         break;
3618       case KeyPress:
3619       {
3620         KeySym
3621           key_symbol;
3622
3623         if (event.xkey.window == windows->magnify.id)
3624           {
3625             Window
3626               window;
3627
3628             window=windows->magnify.id;
3629             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3630           }
3631         if (event.xkey.window != windows->image.id)
3632           break;
3633         /*
3634           Respond to a user key press.
3635         */
3636         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3637           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3638         switch ((int) key_symbol)
3639         {
3640           case XK_Escape:
3641           case XK_F20:
3642           {
3643             /*
3644               Prematurely exit.
3645             */
3646             state|=ExitState;
3647             break;
3648           }
3649           case XK_F1:
3650           case XK_Help:
3651           {
3652             XTextViewWidget(display,resource_info,windows,MagickFalse,
3653               "Help Viewer - Image Annotation",ImageColorEditHelp);
3654             break;
3655           }
3656           default:
3657           {
3658             (void) XBell(display,0);
3659             break;
3660           }
3661         }
3662         break;
3663       }
3664       case MotionNotify:
3665       {
3666         /*
3667           Map and unmap Info widget as cursor crosses its boundaries.
3668         */
3669         x=event.xmotion.x;
3670         y=event.xmotion.y;
3671         if (windows->info.mapped != MagickFalse)
3672           {
3673             if ((x < (int) (windows->info.x+windows->info.width)) &&
3674                 (y < (int) (windows->info.y+windows->info.height)))
3675               (void) XWithdrawWindow(display,windows->info.id,
3676                 windows->info.screen);
3677           }
3678         else
3679           if ((x > (int) (windows->info.x+windows->info.width)) ||
3680               (y > (int) (windows->info.y+windows->info.height)))
3681             (void) XMapWindow(display,windows->info.id);
3682         break;
3683       }
3684       default:
3685         break;
3686     }
3687     if (event.xany.window == windows->magnify.id)
3688       {
3689         x=windows->magnify.x-windows->image.x;
3690         y=windows->magnify.y-windows->image.y;
3691       }
3692     x_offset=x;
3693     y_offset=y;
3694     if ((state & UpdateConfigurationState) != 0)
3695       {
3696         CacheView
3697           *image_view;
3698
3699         int
3700           x,
3701           y;
3702
3703         /*
3704           Pixel edit is relative to image configuration.
3705         */
3706         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3707           MagickTrue);
3708         color=windows->pixel_info->pen_colors[pen_id];
3709         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3710         width=(unsigned int) (*image)->columns;
3711         height=(unsigned int) (*image)->rows;
3712         x=0;
3713         y=0;
3714         if (windows->image.crop_geometry != (char *) NULL)
3715           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3716             &width,&height);
3717         x_offset=(int)
3718           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3719         y_offset=(int)
3720           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3721         if ((x_offset < 0) || (y_offset < 0))
3722           continue;
3723         if ((x_offset >= (int) (*image)->columns) ||
3724             (y_offset >= (int) (*image)->rows))
3725           continue;
3726         image_view=AcquireCacheView(*image);
3727         switch (method)
3728         {
3729           case PointMethod:
3730           default:
3731           {
3732             /*
3733               Update color information using point algorithm.
3734             */
3735             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3736               return(MagickFalse);
3737             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3738               (ssize_t) y_offset,1,1,exception);
3739             if (q == (Quantum *) NULL)
3740               break;
3741             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3742             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3743             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3744             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3745             break;
3746           }
3747           case ReplaceMethod:
3748           {
3749             PixelPacket
3750               pixel,
3751               target;
3752
3753             /*
3754               Update color information using replace algorithm.
3755             */
3756             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3757               (ssize_t) y_offset,&target,exception);
3758             if ((*image)->storage_class == DirectClass)
3759               {
3760                 for (y=0; y < (int) (*image)->rows; y++)
3761                 {
3762                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3763                     (*image)->columns,1,exception);
3764                   if (q == (Quantum *) NULL)
3765                     break;
3766                   for (x=0; x < (int) (*image)->columns; x++)
3767                   {
3768                     GetPixelPacket(*image,q,&pixel);
3769                     if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
3770                       {
3771                         SetPixelRed(*image,ScaleShortToQuantum(
3772                           color.red),q);
3773                         SetPixelGreen(*image,ScaleShortToQuantum(
3774                           color.green),q);
3775                         SetPixelBlue(*image,ScaleShortToQuantum(
3776                           color.blue),q);
3777                       }
3778                     q+=GetPixelChannels(*image);
3779                   }
3780                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3781                     break;
3782                 }
3783               }
3784             else
3785               {
3786                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3787                   if (IsFuzzyEquivalencePixelPacket(*image,(*image)->colormap+i,&target))
3788                     {
3789                       (*image)->colormap[i].red=ScaleShortToQuantum(
3790                         color.red);
3791                       (*image)->colormap[i].green=ScaleShortToQuantum(
3792                         color.green);
3793                       (*image)->colormap[i].blue=ScaleShortToQuantum(
3794                         color.blue);
3795                     }
3796                 (void) SyncImage(*image);
3797               }
3798             break;
3799           }
3800           case FloodfillMethod:
3801           case FillToBorderMethod:
3802           {
3803             DrawInfo
3804               *draw_info;
3805
3806             PixelInfo
3807               target;
3808
3809             /*
3810               Update color information using floodfill algorithm.
3811             */
3812             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3813               (ssize_t) y_offset,&target,exception);
3814             if (method == FillToBorderMethod)
3815               {
3816                 target.red=(MagickRealType)
3817                   ScaleShortToQuantum(border_color.red);
3818                 target.green=(MagickRealType)
3819                   ScaleShortToQuantum(border_color.green);
3820                 target.blue=(MagickRealType)
3821                   ScaleShortToQuantum(border_color.blue);
3822               }
3823             draw_info=CloneDrawInfo(resource_info->image_info,
3824               (DrawInfo *) NULL);
3825             (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3826               &draw_info->fill,exception);
3827             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3828               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3829               MagickFalse : MagickTrue,exception);
3830             draw_info=DestroyDrawInfo(draw_info);
3831             break;
3832           }
3833           case ResetMethod:
3834           {
3835             /*
3836               Update color information using reset algorithm.
3837             */
3838             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3839               return(MagickFalse);
3840             for (y=0; y < (int) (*image)->rows; y++)
3841             {
3842               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3843                 (*image)->columns,1,exception);
3844               if (q == (Quantum *) NULL)
3845                 break;
3846               for (x=0; x < (int) (*image)->columns; x++)
3847               {
3848                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3849                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3850                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3851                 q+=GetPixelChannels(*image);
3852               }
3853               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3854                 break;
3855             }
3856             break;
3857           }
3858         }
3859         image_view=DestroyCacheView(image_view);
3860         state&=(~UpdateConfigurationState);
3861       }
3862   } while ((state & ExitState) == 0);
3863   (void) XSelectInput(display,windows->image.id,
3864     windows->image.attributes.event_mask);
3865   XSetCursorState(display,windows,MagickFalse);
3866   (void) XFreeCursor(display,cursor);
3867   return(MagickTrue);
3868 }
3869 \f
3870 /*
3871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3872 %                                                                             %
3873 %                                                                             %
3874 %                                                                             %
3875 +   X C o m p o s i t e I m a g e                                             %
3876 %                                                                             %
3877 %                                                                             %
3878 %                                                                             %
3879 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3880 %
3881 %  XCompositeImage() requests an image name from the user, reads the image and
3882 %  composites it with the X window image at a location the user chooses with
3883 %  the pointer.
3884 %
3885 %  The format of the XCompositeImage method is:
3886 %
3887 %      MagickBooleanType XCompositeImage(Display *display,
3888 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3889 %        ExceptionInfo *exception)
3890 %
3891 %  A description of each parameter follows:
3892 %
3893 %    o display: Specifies a connection to an X server;  returned from
3894 %      XOpenDisplay.
3895 %
3896 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3897 %
3898 %    o windows: Specifies a pointer to a XWindows structure.
3899 %
3900 %    o image: the image; returned from ReadImage.
3901 %
3902 %    o exception: return any errors or warnings in this structure.
3903 %
3904 */
3905 static MagickBooleanType XCompositeImage(Display *display,
3906   XResourceInfo *resource_info,XWindows *windows,Image *image,
3907   ExceptionInfo *exception)
3908 {
3909   static char
3910     displacement_geometry[MaxTextExtent] = "30x30",
3911     filename[MaxTextExtent] = "\0";
3912
3913   static const char
3914     *CompositeMenu[] =
3915     {
3916       "Operators",
3917       "Dissolve",
3918       "Displace",
3919       "Help",
3920       "Dismiss",
3921       (char *) NULL
3922     };
3923
3924   static CompositeOperator
3925     compose = CopyCompositeOp;
3926
3927   static const ModeType
3928     CompositeCommands[] =
3929     {
3930       CompositeOperatorsCommand,
3931       CompositeDissolveCommand,
3932       CompositeDisplaceCommand,
3933       CompositeHelpCommand,
3934       CompositeDismissCommand
3935     };
3936
3937   char
3938     text[MaxTextExtent];
3939
3940   Cursor
3941     cursor;
3942
3943   Image
3944     *composite_image;
3945
3946   int
3947     entry,
3948     id,
3949     x,
3950     y;
3951
3952   MagickRealType
3953     blend,
3954     scale_factor;
3955
3956   RectangleInfo
3957     highlight_info,
3958     composite_info;
3959
3960   unsigned int
3961     height,
3962     width;
3963
3964   size_t
3965     state;
3966
3967   XEvent
3968     event;
3969
3970   /*
3971     Request image file name from user.
3972   */
3973   XFileBrowserWidget(display,windows,"Composite",filename);
3974   if (*filename == '\0')
3975     return(MagickTrue);
3976   /*
3977     Read image.
3978   */
3979   XSetCursorState(display,windows,MagickTrue);
3980   XCheckRefreshWindows(display,windows);
3981   (void) CopyMagickString(resource_info->image_info->filename,filename,
3982     MaxTextExtent);
3983   composite_image=ReadImage(resource_info->image_info,exception);
3984   CatchException(exception);
3985   XSetCursorState(display,windows,MagickFalse);
3986   if (composite_image == (Image *) NULL)
3987     return(MagickFalse);
3988   /*
3989     Map Command widget.
3990   */
3991   (void) CloneString(&windows->command.name,"Composite");
3992   windows->command.data=1;
3993   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3994   (void) XMapRaised(display,windows->command.id);
3995   XClientMessage(display,windows->image.id,windows->im_protocols,
3996     windows->im_update_widget,CurrentTime);
3997   /*
3998     Track pointer until button 1 is pressed.
3999   */
4000   XQueryPosition(display,windows->image.id,&x,&y);
4001   (void) XSelectInput(display,windows->image.id,
4002     windows->image.attributes.event_mask | PointerMotionMask);
4003   composite_info.x=(ssize_t) windows->image.x+x;
4004   composite_info.y=(ssize_t) windows->image.y+y;
4005   composite_info.width=0;
4006   composite_info.height=0;
4007   cursor=XCreateFontCursor(display,XC_ul_angle);
4008   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4009   blend=0.0;
4010   state=DefaultState;
4011   do
4012   {
4013     if (windows->info.mapped != MagickFalse)
4014       {
4015         /*
4016           Display pointer position.
4017         */
4018         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4019           (long) composite_info.x,(long) composite_info.y);
4020         XInfoWidget(display,windows,text);
4021       }
4022     highlight_info=composite_info;
4023     highlight_info.x=composite_info.x-windows->image.x;
4024     highlight_info.y=composite_info.y-windows->image.y;
4025     XHighlightRectangle(display,windows->image.id,
4026       windows->image.highlight_context,&highlight_info);
4027     /*
4028       Wait for next event.
4029     */
4030     XScreenEvent(display,windows,&event);
4031     XHighlightRectangle(display,windows->image.id,
4032       windows->image.highlight_context,&highlight_info);
4033     if (event.xany.window == windows->command.id)
4034       {
4035         /*
4036           Select a command from the Command widget.
4037         */
4038         id=XCommandWidget(display,windows,CompositeMenu,&event);
4039         if (id < 0)
4040           continue;
4041         switch (CompositeCommands[id])
4042         {
4043           case CompositeOperatorsCommand:
4044           {
4045             char
4046               command[MaxTextExtent],
4047               **operators;
4048
4049             /*
4050               Select a command from the pop-up menu.
4051             */
4052             operators=GetCommandOptions(MagickComposeOptions);
4053             if (operators == (char **) NULL)
4054               break;
4055             entry=XMenuWidget(display,windows,CompositeMenu[id],
4056               (const char **) operators,command);
4057             if (entry >= 0)
4058               compose=(CompositeOperator) ParseCommandOption(
4059                 MagickComposeOptions,MagickFalse,operators[entry]);
4060             operators=DestroyStringList(operators);
4061             break;
4062           }
4063           case CompositeDissolveCommand:
4064           {
4065             static char
4066               factor[MaxTextExtent] = "20.0";
4067
4068             /*
4069               Dissolve the two images a given percent.
4070             */
4071             (void) XSetFunction(display,windows->image.highlight_context,
4072               GXcopy);
4073             (void) XDialogWidget(display,windows,"Dissolve",
4074               "Enter the blend factor (0.0 - 99.9%):",factor);
4075             (void) XSetFunction(display,windows->image.highlight_context,
4076               GXinvert);
4077             if (*factor == '\0')
4078               break;
4079             blend=InterpretLocaleValue(factor,(char **) NULL);
4080             compose=DissolveCompositeOp;
4081             break;
4082           }
4083           case CompositeDisplaceCommand:
4084           {
4085             /*
4086               Get horizontal and vertical scale displacement geometry.
4087             */
4088             (void) XSetFunction(display,windows->image.highlight_context,
4089               GXcopy);
4090             (void) XDialogWidget(display,windows,"Displace",
4091               "Enter the horizontal and vertical scale:",displacement_geometry);
4092             (void) XSetFunction(display,windows->image.highlight_context,
4093               GXinvert);
4094             if (*displacement_geometry == '\0')
4095               break;
4096             compose=DisplaceCompositeOp;
4097             break;
4098           }
4099           case CompositeHelpCommand:
4100           {
4101             (void) XSetFunction(display,windows->image.highlight_context,
4102               GXcopy);
4103             XTextViewWidget(display,resource_info,windows,MagickFalse,
4104               "Help Viewer - Image Composite",ImageCompositeHelp);
4105             (void) XSetFunction(display,windows->image.highlight_context,
4106               GXinvert);
4107             break;
4108           }
4109           case CompositeDismissCommand:
4110           {
4111             /*
4112               Prematurely exit.
4113             */
4114             state|=EscapeState;
4115             state|=ExitState;
4116             break;
4117           }
4118           default:
4119             break;
4120         }
4121         continue;
4122       }
4123     switch (event.type)
4124     {
4125       case ButtonPress:
4126       {
4127         if (image->debug != MagickFalse)
4128           (void) LogMagickEvent(X11Event,GetMagickModule(),
4129             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4130             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4131         if (event.xbutton.button != Button1)
4132           break;
4133         if (event.xbutton.window != windows->image.id)
4134           break;
4135         /*
4136           Change cursor.
4137         */
4138         composite_info.width=composite_image->columns;
4139         composite_info.height=composite_image->rows;
4140         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4141         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4142         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4143         break;
4144       }
4145       case ButtonRelease:
4146       {
4147         if (image->debug != MagickFalse)
4148           (void) LogMagickEvent(X11Event,GetMagickModule(),
4149             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4150             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4151         if (event.xbutton.button != Button1)
4152           break;
4153         if (event.xbutton.window != windows->image.id)
4154           break;
4155         if ((composite_info.width != 0) && (composite_info.height != 0))
4156           {
4157             /*
4158               User has selected the location of the composite image.
4159             */
4160             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4161             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4162             state|=ExitState;
4163           }
4164         break;
4165       }
4166       case Expose:
4167         break;
4168       case KeyPress:
4169       {
4170         char
4171           command[MaxTextExtent];
4172
4173         KeySym
4174           key_symbol;
4175
4176         int
4177           length;
4178
4179         if (event.xkey.window != windows->image.id)
4180           break;
4181         /*
4182           Respond to a user key press.
4183         */
4184         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4185           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4186         *(command+length)='\0';
4187         if (image->debug != MagickFalse)
4188           (void) LogMagickEvent(X11Event,GetMagickModule(),
4189             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4190         switch ((int) key_symbol)
4191         {
4192           case XK_Escape:
4193           case XK_F20:
4194           {
4195             /*
4196               Prematurely exit.
4197             */
4198             composite_image=DestroyImage(composite_image);
4199             state|=EscapeState;
4200             state|=ExitState;
4201             break;
4202           }
4203           case XK_F1:
4204           case XK_Help:
4205           {
4206             (void) XSetFunction(display,windows->image.highlight_context,
4207               GXcopy);
4208             XTextViewWidget(display,resource_info,windows,MagickFalse,
4209               "Help Viewer - Image Composite",ImageCompositeHelp);
4210             (void) XSetFunction(display,windows->image.highlight_context,
4211               GXinvert);
4212             break;
4213           }
4214           default:
4215           {
4216             (void) XBell(display,0);
4217             break;
4218           }
4219         }
4220         break;
4221       }
4222       case MotionNotify:
4223       {
4224         /*
4225           Map and unmap Info widget as text cursor crosses its boundaries.
4226         */
4227         x=event.xmotion.x;
4228         y=event.xmotion.y;
4229         if (windows->info.mapped != MagickFalse)
4230           {
4231             if ((x < (int) (windows->info.x+windows->info.width)) &&
4232                 (y < (int) (windows->info.y+windows->info.height)))
4233               (void) XWithdrawWindow(display,windows->info.id,
4234                 windows->info.screen);
4235           }
4236         else
4237           if ((x > (int) (windows->info.x+windows->info.width)) ||
4238               (y > (int) (windows->info.y+windows->info.height)))
4239             (void) XMapWindow(display,windows->info.id);
4240         composite_info.x=(ssize_t) windows->image.x+x;
4241         composite_info.y=(ssize_t) windows->image.y+y;
4242         break;
4243       }
4244       default:
4245       {
4246         if (image->debug != MagickFalse)
4247           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4248             event.type);
4249         break;
4250       }
4251     }
4252   } while ((state & ExitState) == 0);
4253   (void) XSelectInput(display,windows->image.id,
4254     windows->image.attributes.event_mask);
4255   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4256   XSetCursorState(display,windows,MagickFalse);
4257   (void) XFreeCursor(display,cursor);
4258   if ((state & EscapeState) != 0)
4259     return(MagickTrue);
4260   /*
4261     Image compositing is relative to image configuration.
4262   */
4263   XSetCursorState(display,windows,MagickTrue);
4264   XCheckRefreshWindows(display,windows);
4265   width=(unsigned int) image->columns;
4266   height=(unsigned int) image->rows;
4267   x=0;
4268   y=0;
4269   if (windows->image.crop_geometry != (char *) NULL)
4270     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4271   scale_factor=(MagickRealType) width/windows->image.ximage->width;
4272   composite_info.x+=x;
4273   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4274   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4275   scale_factor=(MagickRealType) height/windows->image.ximage->height;
4276   composite_info.y+=y;
4277   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4278   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4279   if ((composite_info.width != composite_image->columns) ||
4280       (composite_info.height != composite_image->rows))
4281     {
4282       Image
4283         *resize_image;
4284
4285       /*
4286         Scale composite image.
4287       */
4288       resize_image=ResizeImage(composite_image,composite_info.width,
4289         composite_info.height,composite_image->filter,composite_image->blur,
4290         exception);
4291       composite_image=DestroyImage(composite_image);
4292       if (resize_image == (Image *) NULL)
4293         {
4294           XSetCursorState(display,windows,MagickFalse);
4295           return(MagickFalse);
4296         }
4297       composite_image=resize_image;
4298     }
4299   if (compose == DisplaceCompositeOp)
4300     (void) SetImageArtifact(composite_image,"compose:args",
4301       displacement_geometry);
4302   if (blend != 0.0)
4303     {
4304       CacheView
4305         *image_view;
4306
4307       int
4308         y;
4309
4310       Quantum
4311         opacity;
4312
4313       register int
4314         x;
4315
4316       register Quantum
4317         *q;
4318
4319       /*
4320         Create mattes for blending.
4321       */
4322       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4323       opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4324         ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4325       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4326         return(MagickFalse);
4327       image->matte=MagickTrue;
4328       image_view=AcquireCacheView(image);
4329       for (y=0; y < (int) image->rows; y++)
4330       {
4331         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4332           exception);
4333         if (q == (Quantum *) NULL)
4334           break;
4335         for (x=0; x < (int) image->columns; x++)
4336         {
4337           SetPixelAlpha(image,opacity,q);
4338           q+=GetPixelChannels(image);
4339         }
4340         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4341           break;
4342       }
4343       image_view=DestroyCacheView(image_view);
4344     }
4345   /*
4346     Composite image with X Image window.
4347   */
4348   (void) CompositeImage(image,compose,composite_image,composite_info.x,
4349     composite_info.y);
4350   composite_image=DestroyImage(composite_image);
4351   XSetCursorState(display,windows,MagickFalse);
4352   /*
4353     Update image configuration.
4354   */
4355   XConfigureImageColormap(display,resource_info,windows,image);
4356   (void) XConfigureImage(display,resource_info,windows,image,exception);
4357   return(MagickTrue);
4358 }
4359 \f
4360 /*
4361 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4362 %                                                                             %
4363 %                                                                             %
4364 %                                                                             %
4365 +   X C o n f i g u r e I m a g e                                             %
4366 %                                                                             %
4367 %                                                                             %
4368 %                                                                             %
4369 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4370 %
4371 %  XConfigureImage() creates a new X image.  It also notifies the window
4372 %  manager of the new image size and configures the transient widows.
4373 %
4374 %  The format of the XConfigureImage method is:
4375 %
4376 %      MagickBooleanType XConfigureImage(Display *display,
4377 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4378 %        ExceptionInfo *exception)
4379 %
4380 %  A description of each parameter follows:
4381 %
4382 %    o display: Specifies a connection to an X server; returned from
4383 %      XOpenDisplay.
4384 %
4385 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4386 %
4387 %    o windows: Specifies a pointer to a XWindows structure.
4388 %
4389 %    o image: the image.
4390 %
4391 %    o exception: return any errors or warnings in this structure.
4392 %
4393 %    o exception: return any errors or warnings in this structure.
4394 %
4395 */
4396 static MagickBooleanType XConfigureImage(Display *display,
4397   XResourceInfo *resource_info,XWindows *windows,Image *image,
4398   ExceptionInfo *exception)
4399 {
4400   char
4401     geometry[MaxTextExtent];
4402
4403   MagickStatusType
4404     status;
4405
4406   size_t
4407     mask,
4408     height,
4409     width;
4410
4411   ssize_t
4412     x,
4413     y;
4414
4415   XSizeHints
4416     *size_hints;
4417
4418   XWindowChanges
4419     window_changes;
4420
4421   /*
4422     Dismiss if window dimensions are zero.
4423   */
4424   width=(unsigned int) windows->image.window_changes.width;
4425   height=(unsigned int) windows->image.window_changes.height;
4426   if (image->debug != MagickFalse)
4427     (void) LogMagickEvent(X11Event,GetMagickModule(),
4428       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4429       windows->image.ximage->height,(double) width,(double) height);
4430   if ((width*height) == 0)
4431     return(MagickTrue);
4432   x=0;
4433   y=0;
4434   /*
4435     Resize image to fit Image window dimensions.
4436   */
4437   XSetCursorState(display,windows,MagickTrue);
4438   (void) XFlush(display);
4439   if (((int) width != windows->image.ximage->width) ||
4440       ((int) height != windows->image.ximage->height))
4441     image->taint=MagickTrue;
4442   windows->magnify.x=(int)
4443     width*windows->magnify.x/windows->image.ximage->width;
4444   windows->magnify.y=(int)
4445     height*windows->magnify.y/windows->image.ximage->height;
4446   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4447   windows->image.y=(int)
4448     (height*windows->image.y/windows->image.ximage->height);
4449   status=XMakeImage(display,resource_info,&windows->image,image,
4450     (unsigned int) width,(unsigned int) height,exception);
4451   if (status == MagickFalse)
4452     XNoticeWidget(display,windows,"Unable to configure X image:",
4453       windows->image.name);
4454   /*
4455     Notify window manager of the new configuration.
4456   */
4457   if (resource_info->image_geometry != (char *) NULL)
4458     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4459       resource_info->image_geometry);
4460   else
4461     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4462       XDisplayWidth(display,windows->image.screen),
4463       XDisplayHeight(display,windows->image.screen));
4464   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4465   window_changes.width=(int) width;
4466   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4467     window_changes.width=XDisplayWidth(display,windows->image.screen);
4468   window_changes.height=(int) height;
4469   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4470     window_changes.height=XDisplayHeight(display,windows->image.screen);
4471   mask=(size_t) (CWWidth | CWHeight);
4472   if (resource_info->backdrop)
4473     {
4474       mask|=CWX | CWY;
4475       window_changes.x=(int)
4476         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4477       window_changes.y=(int)
4478         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4479     }
4480   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4481     (unsigned int) mask,&window_changes);
4482   (void) XClearWindow(display,windows->image.id);
4483   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4484   /*
4485     Update Magnify window configuration.
4486   */
4487   if (windows->magnify.mapped != MagickFalse)
4488     XMakeMagnifyImage(display,windows);
4489   windows->pan.crop_geometry=windows->image.crop_geometry;
4490   XBestIconSize(display,&windows->pan,image);
4491   while (((windows->pan.width << 1) < MaxIconSize) &&
4492          ((windows->pan.height << 1) < MaxIconSize))
4493   {
4494     windows->pan.width<<=1;
4495     windows->pan.height<<=1;
4496   }
4497   if (windows->pan.geometry != (char *) NULL)
4498     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4499       &windows->pan.width,&windows->pan.height);
4500   window_changes.width=(int) windows->pan.width;
4501   window_changes.height=(int) windows->pan.height;
4502   size_hints=XAllocSizeHints();
4503   if (size_hints != (XSizeHints *) NULL)
4504     {
4505       /*
4506         Set new size hints.
4507       */
4508       size_hints->flags=PSize | PMinSize | PMaxSize;
4509       size_hints->width=window_changes.width;
4510       size_hints->height=window_changes.height;
4511       size_hints->min_width=size_hints->width;
4512       size_hints->min_height=size_hints->height;
4513       size_hints->max_width=size_hints->width;
4514       size_hints->max_height=size_hints->height;
4515       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4516       (void) XFree((void *) size_hints);
4517     }
4518   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4519     (unsigned int) (CWWidth | CWHeight),&window_changes);
4520   /*
4521     Update icon window configuration.
4522   */
4523   windows->icon.crop_geometry=windows->image.crop_geometry;
4524   XBestIconSize(display,&windows->icon,image);
4525   window_changes.width=(int) windows->icon.width;
4526   window_changes.height=(int) windows->icon.height;
4527   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4528     (unsigned int) (CWWidth | CWHeight),&window_changes);
4529   XSetCursorState(display,windows,MagickFalse);
4530   return(status != 0 ? MagickTrue : MagickFalse);
4531 }
4532 \f
4533 /*
4534 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4535 %                                                                             %
4536 %                                                                             %
4537 %                                                                             %
4538 +   X C r o p I m a g e                                                       %
4539 %                                                                             %
4540 %                                                                             %
4541 %                                                                             %
4542 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4543 %
4544 %  XCropImage() allows the user to select a region of the image and crop, copy,
4545 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4546 %  the image with XPasteImage.
4547 %
4548 %  The format of the XCropImage method is:
4549 %
4550 %      MagickBooleanType XCropImage(Display *display,
4551 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4552 %        const ClipboardMode mode,ExceptionInfo *exception)
4553 %
4554 %  A description of each parameter follows:
4555 %
4556 %    o display: Specifies a connection to an X server; returned from
4557 %      XOpenDisplay.
4558 %
4559 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4560 %
4561 %    o windows: Specifies a pointer to a XWindows structure.
4562 %
4563 %    o image: the image; returned from ReadImage.
4564 %
4565 %    o mode: This unsigned value specified whether the image should be
4566 %      cropped, copied, or cut.
4567 %
4568 %    o exception: return any errors or warnings in this structure.
4569 %
4570 */
4571 static MagickBooleanType XCropImage(Display *display,
4572   XResourceInfo *resource_info,XWindows *windows,Image *image,
4573   const ClipboardMode mode,ExceptionInfo *exception)
4574 {
4575   static const char
4576     *CropModeMenu[] =
4577     {
4578       "Help",
4579       "Dismiss",
4580       (char *) NULL
4581     },
4582     *RectifyModeMenu[] =
4583     {
4584       "Crop",
4585       "Help",
4586       "Dismiss",
4587       (char *) NULL
4588     };
4589
4590   static const ModeType
4591     CropCommands[] =
4592     {
4593       CropHelpCommand,
4594       CropDismissCommand
4595     },
4596     RectifyCommands[] =
4597     {
4598       RectifyCopyCommand,
4599       RectifyHelpCommand,
4600       RectifyDismissCommand
4601     };
4602
4603   CacheView
4604     *image_view;
4605
4606   char
4607     command[MaxTextExtent],
4608     text[MaxTextExtent];
4609
4610   Cursor
4611     cursor;
4612
4613   int
4614     id,
4615     x,
4616     y;
4617
4618   KeySym
4619     key_symbol;
4620
4621   Image
4622     *crop_image;
4623
4624   MagickRealType
4625     scale_factor;
4626
4627   RectangleInfo
4628     crop_info,
4629     highlight_info;
4630
4631   register Quantum
4632     *q;
4633
4634   unsigned int
4635     height,
4636     width;
4637
4638   size_t
4639     state;
4640
4641   XEvent
4642     event;
4643
4644   /*
4645     Map Command widget.
4646   */
4647   switch (mode)
4648   {
4649     case CopyMode:
4650     {
4651       (void) CloneString(&windows->command.name,"Copy");
4652       break;
4653     }
4654     case CropMode:
4655     {
4656       (void) CloneString(&windows->command.name,"Crop");
4657       break;
4658     }
4659     case CutMode:
4660     {
4661       (void) CloneString(&windows->command.name,"Cut");
4662       break;
4663     }
4664   }
4665   RectifyModeMenu[0]=windows->command.name;
4666   windows->command.data=0;
4667   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4668   (void) XMapRaised(display,windows->command.id);
4669   XClientMessage(display,windows->image.id,windows->im_protocols,
4670     windows->im_update_widget,CurrentTime);
4671   /*
4672     Track pointer until button 1 is pressed.
4673   */
4674   XQueryPosition(display,windows->image.id,&x,&y);
4675   (void) XSelectInput(display,windows->image.id,
4676     windows->image.attributes.event_mask | PointerMotionMask);
4677   crop_info.x=(ssize_t) windows->image.x+x;
4678   crop_info.y=(ssize_t) windows->image.y+y;
4679   crop_info.width=0;
4680   crop_info.height=0;
4681   cursor=XCreateFontCursor(display,XC_fleur);
4682   state=DefaultState;
4683   do
4684   {
4685     if (windows->info.mapped != MagickFalse)
4686       {
4687         /*
4688           Display pointer position.
4689         */
4690         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4691           (long) crop_info.x,(long) crop_info.y);
4692         XInfoWidget(display,windows,text);
4693       }
4694     /*
4695       Wait for next event.
4696     */
4697     XScreenEvent(display,windows,&event);
4698     if (event.xany.window == windows->command.id)
4699       {
4700         /*
4701           Select a command from the Command widget.
4702         */
4703         id=XCommandWidget(display,windows,CropModeMenu,&event);
4704         if (id < 0)
4705           continue;
4706         switch (CropCommands[id])
4707         {
4708           case CropHelpCommand:
4709           {
4710             switch (mode)
4711             {
4712               case CopyMode:
4713               {
4714                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4715                   "Help Viewer - Image Copy",ImageCopyHelp);
4716                 break;
4717               }
4718               case CropMode:
4719               {
4720                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4721                   "Help Viewer - Image Crop",ImageCropHelp);
4722                 break;
4723               }
4724               case CutMode:
4725               {
4726                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4727                   "Help Viewer - Image Cut",ImageCutHelp);
4728                 break;
4729               }
4730             }
4731             break;
4732           }
4733           case CropDismissCommand:
4734           {
4735             /*
4736               Prematurely exit.
4737             */
4738             state|=EscapeState;
4739             state|=ExitState;
4740             break;
4741           }
4742           default:
4743             break;
4744         }
4745         continue;
4746       }
4747     switch (event.type)
4748     {
4749       case ButtonPress:
4750       {
4751         if (event.xbutton.button != Button1)
4752           break;
4753         if (event.xbutton.window != windows->image.id)
4754           break;
4755         /*
4756           Note first corner of cropping rectangle-- exit loop.
4757         */
4758         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4759         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4760         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4761         state|=ExitState;
4762         break;
4763       }
4764       case ButtonRelease:
4765         break;
4766       case Expose:
4767         break;
4768       case KeyPress:
4769       {
4770         if (event.xkey.window != windows->image.id)
4771           break;
4772         /*
4773           Respond to a user key press.
4774         */
4775         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4776           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4777         switch ((int) key_symbol)
4778         {
4779           case XK_Escape:
4780           case XK_F20:
4781           {
4782             /*
4783               Prematurely exit.
4784             */
4785             state|=EscapeState;
4786             state|=ExitState;
4787             break;
4788           }
4789           case XK_F1:
4790           case XK_Help:
4791           {
4792             switch (mode)
4793             {
4794               case CopyMode:
4795               {
4796                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4797                   "Help Viewer - Image Copy",ImageCopyHelp);
4798                 break;
4799               }
4800               case CropMode:
4801               {
4802                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4803                   "Help Viewer - Image Crop",ImageCropHelp);
4804                 break;
4805               }
4806               case CutMode:
4807               {
4808                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4809                   "Help Viewer - Image Cut",ImageCutHelp);
4810                 break;
4811               }
4812             }
4813             break;
4814           }
4815           default:
4816           {
4817             (void) XBell(display,0);
4818             break;
4819           }
4820         }
4821         break;
4822       }
4823       case MotionNotify:
4824       {
4825         if (event.xmotion.window != windows->image.id)
4826           break;
4827         /*
4828           Map and unmap Info widget as text cursor crosses its boundaries.
4829         */
4830         x=event.xmotion.x;
4831         y=event.xmotion.y;
4832         if (windows->info.mapped != MagickFalse)
4833           {
4834             if ((x < (int) (windows->info.x+windows->info.width)) &&
4835                 (y < (int) (windows->info.y+windows->info.height)))
4836               (void) XWithdrawWindow(display,windows->info.id,
4837                 windows->info.screen);
4838           }
4839         else
4840           if ((x > (int) (windows->info.x+windows->info.width)) ||
4841               (y > (int) (windows->info.y+windows->info.height)))
4842             (void) XMapWindow(display,windows->info.id);
4843         crop_info.x=(ssize_t) windows->image.x+x;
4844         crop_info.y=(ssize_t) windows->image.y+y;
4845         break;
4846       }
4847       default:
4848         break;
4849     }
4850   } while ((state & ExitState) == 0);
4851   (void) XSelectInput(display,windows->image.id,
4852     windows->image.attributes.event_mask);
4853   if ((state & EscapeState) != 0)
4854     {
4855       /*
4856         User want to exit without cropping.
4857       */
4858       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4859       (void) XFreeCursor(display,cursor);
4860       return(MagickTrue);
4861     }
4862   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4863   do
4864   {
4865     /*
4866       Size rectangle as pointer moves until the mouse button is released.
4867     */
4868     x=(int) crop_info.x;
4869     y=(int) crop_info.y;
4870     crop_info.width=0;
4871     crop_info.height=0;
4872     state=DefaultState;
4873     do
4874     {
4875       highlight_info=crop_info;
4876       highlight_info.x=crop_info.x-windows->image.x;
4877       highlight_info.y=crop_info.y-windows->image.y;
4878       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4879         {
4880           /*
4881             Display info and draw cropping rectangle.
4882           */
4883           if (windows->info.mapped == MagickFalse)
4884             (void) XMapWindow(display,windows->info.id);
4885           (void) FormatLocaleString(text,MaxTextExtent,
4886             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4887             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4888           XInfoWidget(display,windows,text);
4889           XHighlightRectangle(display,windows->image.id,
4890             windows->image.highlight_context,&highlight_info);
4891         }
4892       else
4893         if (windows->info.mapped != MagickFalse)
4894           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4895       /*
4896         Wait for next event.
4897       */
4898       XScreenEvent(display,windows,&event);
4899       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4900         XHighlightRectangle(display,windows->image.id,
4901           windows->image.highlight_context,&highlight_info);
4902       switch (event.type)
4903       {
4904         case ButtonPress:
4905         {
4906           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4907           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4908           break;
4909         }
4910         case ButtonRelease:
4911         {
4912           /*
4913             User has committed to cropping rectangle.
4914           */
4915           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4916           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4917           XSetCursorState(display,windows,MagickFalse);
4918           state|=ExitState;
4919           windows->command.data=0;
4920           (void) XCommandWidget(display,windows,RectifyModeMenu,
4921             (XEvent *) NULL);
4922           break;
4923         }
4924         case Expose:
4925           break;
4926         case MotionNotify:
4927         {
4928           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4929           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4930         }
4931         default:
4932           break;
4933       }
4934       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4935           ((state & ExitState) != 0))
4936         {
4937           /*
4938             Check boundary conditions.
4939           */
4940           if (crop_info.x < 0)
4941             crop_info.x=0;
4942           else
4943             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4944               crop_info.x=(ssize_t) windows->image.ximage->width;
4945           if ((int) crop_info.x < x)
4946             crop_info.width=(unsigned int) (x-crop_info.x);
4947           else
4948             {
4949               crop_info.width=(unsigned int) (crop_info.x-x);
4950               crop_info.x=(ssize_t) x;
4951             }
4952           if (crop_info.y < 0)
4953             crop_info.y=0;
4954           else
4955             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4956               crop_info.y=(ssize_t) windows->image.ximage->height;
4957           if ((int) crop_info.y < y)
4958             crop_info.height=(unsigned int) (y-crop_info.y);
4959           else
4960             {
4961               crop_info.height=(unsigned int) (crop_info.y-y);
4962               crop_info.y=(ssize_t) y;
4963             }
4964         }
4965     } while ((state & ExitState) == 0);
4966     /*
4967       Wait for user to grab a corner of the rectangle or press return.
4968     */
4969     state=DefaultState;
4970     (void) XMapWindow(display,windows->info.id);
4971     do
4972     {
4973       if (windows->info.mapped != MagickFalse)
4974         {
4975           /*
4976             Display pointer position.
4977           */
4978           (void) FormatLocaleString(text,MaxTextExtent,
4979             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4980             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4981           XInfoWidget(display,windows,text);
4982         }
4983       highlight_info=crop_info;
4984       highlight_info.x=crop_info.x-windows->image.x;
4985       highlight_info.y=crop_info.y-windows->image.y;
4986       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4987         {
4988           state|=EscapeState;
4989           state|=ExitState;
4990           break;
4991         }
4992       XHighlightRectangle(display,windows->image.id,
4993         windows->image.highlight_context,&highlight_info);
4994       XScreenEvent(display,windows,&event);
4995       if (event.xany.window == windows->command.id)
4996         {
4997           /*
4998             Select a command from the Command widget.
4999           */
5000           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5001           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5002           (void) XSetFunction(display,windows->image.highlight_context,
5003             GXinvert);
5004           XHighlightRectangle(display,windows->image.id,
5005             windows->image.highlight_context,&highlight_info);
5006           if (id >= 0)
5007             switch (RectifyCommands[id])
5008             {
5009               case RectifyCopyCommand:
5010               {
5011                 state|=ExitState;
5012                 break;
5013               }
5014               case RectifyHelpCommand:
5015               {
5016                 (void) XSetFunction(display,windows->image.highlight_context,
5017                   GXcopy);
5018                 switch (mode)
5019                 {
5020                   case CopyMode:
5021                   {
5022                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5023                       "Help Viewer - Image Copy",ImageCopyHelp);
5024                     break;
5025                   }
5026                   case CropMode:
5027                   {
5028                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5029                       "Help Viewer - Image Crop",ImageCropHelp);
5030                     break;
5031                   }
5032                   case CutMode:
5033                   {
5034                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5035                       "Help Viewer - Image Cut",ImageCutHelp);
5036                     break;
5037                   }
5038                 }
5039                 (void) XSetFunction(display,windows->image.highlight_context,
5040                   GXinvert);
5041                 break;
5042               }
5043               case RectifyDismissCommand:
5044               {
5045                 /*
5046                   Prematurely exit.
5047                 */
5048                 state|=EscapeState;
5049                 state|=ExitState;
5050                 break;
5051               }
5052               default:
5053                 break;
5054             }
5055           continue;
5056         }
5057       XHighlightRectangle(display,windows->image.id,
5058         windows->image.highlight_context,&highlight_info);
5059       switch (event.type)
5060       {
5061         case ButtonPress:
5062         {
5063           if (event.xbutton.button != Button1)
5064             break;
5065           if (event.xbutton.window != windows->image.id)
5066             break;
5067           x=windows->image.x+event.xbutton.x;
5068           y=windows->image.y+event.xbutton.y;
5069           if ((x < (int) (crop_info.x+RoiDelta)) &&
5070               (x > (int) (crop_info.x-RoiDelta)) &&
5071               (y < (int) (crop_info.y+RoiDelta)) &&
5072               (y > (int) (crop_info.y-RoiDelta)))
5073             {
5074               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5075               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5076               state|=UpdateConfigurationState;
5077               break;
5078             }
5079           if ((x < (int) (crop_info.x+RoiDelta)) &&
5080               (x > (int) (crop_info.x-RoiDelta)) &&
5081               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5082               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5083             {
5084               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5085               state|=UpdateConfigurationState;
5086               break;
5087             }
5088           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5089               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5090               (y < (int) (crop_info.y+RoiDelta)) &&
5091               (y > (int) (crop_info.y-RoiDelta)))
5092             {
5093               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5094               state|=UpdateConfigurationState;
5095               break;
5096             }
5097           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5098               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5099               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5100               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5101             {
5102               state|=UpdateConfigurationState;
5103               break;
5104             }
5105         }
5106         case ButtonRelease:
5107         {
5108           if (event.xbutton.window == windows->pan.id)
5109             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5110                 (highlight_info.y != crop_info.y-windows->image.y))
5111               XHighlightRectangle(display,windows->image.id,
5112                 windows->image.highlight_context,&highlight_info);
5113           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5114             event.xbutton.time);
5115           break;
5116         }
5117         case Expose:
5118         {
5119           if (event.xexpose.window == windows->image.id)
5120             if (event.xexpose.count == 0)
5121               {
5122                 event.xexpose.x=(int) highlight_info.x;
5123                 event.xexpose.y=(int) highlight_info.y;
5124                 event.xexpose.width=(int) highlight_info.width;
5125                 event.xexpose.height=(int) highlight_info.height;
5126                 XRefreshWindow(display,&windows->image,&event);
5127               }
5128           if (event.xexpose.window == windows->info.id)
5129             if (event.xexpose.count == 0)
5130               XInfoWidget(display,windows,text);
5131           break;
5132         }
5133         case KeyPress:
5134         {
5135           if (event.xkey.window != windows->image.id)
5136             break;
5137           /*
5138             Respond to a user key press.
5139           */
5140           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5141             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5142           switch ((int) key_symbol)
5143           {
5144             case XK_Escape:
5145             case XK_F20:
5146               state|=EscapeState;
5147             case XK_Return:
5148             {
5149               state|=ExitState;
5150               break;
5151             }
5152             case XK_Home:
5153             case XK_KP_Home:
5154             {
5155               crop_info.x=(ssize_t) (windows->image.width/2L-
5156                 crop_info.width/2L);
5157               crop_info.y=(ssize_t) (windows->image.height/2L-
5158                 crop_info.height/2L);
5159               break;
5160             }
5161             case XK_Left:
5162             case XK_KP_Left:
5163             {
5164               crop_info.x--;
5165               break;
5166             }
5167             case XK_Up:
5168             case XK_KP_Up:
5169             case XK_Next:
5170             {
5171               crop_info.y--;
5172               break;
5173             }
5174             case XK_Right:
5175             case XK_KP_Right:
5176             {
5177               crop_info.x++;
5178               break;
5179             }
5180             case XK_Prior:
5181             case XK_Down:
5182             case XK_KP_Down:
5183             {
5184               crop_info.y++;
5185               break;
5186             }
5187             case XK_F1:
5188             case XK_Help:
5189             {
5190               (void) XSetFunction(display,windows->image.highlight_context,
5191                 GXcopy);
5192               switch (mode)
5193               {
5194                 case CopyMode:
5195                 {
5196                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5197                     "Help Viewer - Image Copy",ImageCopyHelp);
5198                   break;
5199                 }
5200                 case CropMode:
5201                 {
5202                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5203                     "Help Viewer - Image Cropg",ImageCropHelp);
5204                   break;
5205                 }
5206                 case CutMode:
5207                 {
5208                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5209                     "Help Viewer - Image Cutg",ImageCutHelp);
5210                   break;
5211                 }
5212               }
5213               (void) XSetFunction(display,windows->image.highlight_context,
5214                 GXinvert);
5215               break;
5216             }
5217             default:
5218             {
5219               (void) XBell(display,0);
5220               break;
5221             }
5222           }
5223           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5224             event.xkey.time);
5225           break;
5226         }
5227         case KeyRelease:
5228           break;
5229         case MotionNotify:
5230         {
5231           if (event.xmotion.window != windows->image.id)
5232             break;
5233           /*
5234             Map and unmap Info widget as text cursor crosses its boundaries.
5235           */
5236           x=event.xmotion.x;
5237           y=event.xmotion.y;
5238           if (windows->info.mapped != MagickFalse)
5239             {
5240               if ((x < (int) (windows->info.x+windows->info.width)) &&
5241                   (y < (int) (windows->info.y+windows->info.height)))
5242                 (void) XWithdrawWindow(display,windows->info.id,
5243                   windows->info.screen);
5244             }
5245           else
5246             if ((x > (int) (windows->info.x+windows->info.width)) ||
5247                 (y > (int) (windows->info.y+windows->info.height)))
5248               (void) XMapWindow(display,windows->info.id);
5249           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5250           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5251           break;
5252         }
5253         case SelectionRequest:
5254         {
5255           XSelectionEvent
5256             notify;
5257
5258           XSelectionRequestEvent
5259             *request;
5260
5261           /*
5262             Set primary selection.
5263           */
5264           (void) FormatLocaleString(text,MaxTextExtent,
5265             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5266             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5267           request=(&(event.xselectionrequest));
5268           (void) XChangeProperty(request->display,request->requestor,
5269             request->property,request->target,8,PropModeReplace,
5270             (unsigned char *) text,(int) strlen(text));
5271           notify.type=SelectionNotify;
5272           notify.display=request->display;
5273           notify.requestor=request->requestor;
5274           notify.selection=request->selection;
5275           notify.target=request->target;
5276           notify.time=request->time;
5277           if (request->property == None)
5278             notify.property=request->target;
5279           else
5280             notify.property=request->property;
5281           (void) XSendEvent(request->display,request->requestor,False,0,
5282             (XEvent *) &notify);
5283         }
5284         default:
5285           break;
5286       }
5287       if ((state & UpdateConfigurationState) != 0)
5288         {
5289           (void) XPutBackEvent(display,&event);
5290           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5291           break;
5292         }
5293     } while ((state & ExitState) == 0);
5294   } while ((state & ExitState) == 0);
5295   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5296   XSetCursorState(display,windows,MagickFalse);
5297   if ((state & EscapeState) != 0)
5298     return(MagickTrue);
5299   if (mode == CropMode)
5300     if (((int) crop_info.width != windows->image.ximage->width) ||
5301         ((int) crop_info.height != windows->image.ximage->height))
5302       {
5303         /*
5304           Reconfigure Image window as defined by cropping rectangle.
5305         */
5306         XSetCropGeometry(display,windows,&crop_info,image);
5307         windows->image.window_changes.width=(int) crop_info.width;
5308         windows->image.window_changes.height=(int) crop_info.height;
5309         (void) XConfigureImage(display,resource_info,windows,image,exception);
5310         return(MagickTrue);
5311       }
5312   /*
5313     Copy image before applying image transforms.
5314   */
5315   XSetCursorState(display,windows,MagickTrue);
5316   XCheckRefreshWindows(display,windows);
5317   width=(unsigned int) image->columns;
5318   height=(unsigned int) image->rows;
5319   x=0;
5320   y=0;
5321   if (windows->image.crop_geometry != (char *) NULL)
5322     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5323   scale_factor=(MagickRealType) width/windows->image.ximage->width;
5324   crop_info.x+=x;
5325   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5326   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5327   scale_factor=(MagickRealType) height/windows->image.ximage->height;
5328   crop_info.y+=y;
5329   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5330   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5331   crop_image=CropImage(image,&crop_info,exception);
5332   XSetCursorState(display,windows,MagickFalse);
5333   if (crop_image == (Image *) NULL)
5334     return(MagickFalse);
5335   if (resource_info->copy_image != (Image *) NULL)
5336     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5337   resource_info->copy_image=crop_image;
5338   if (mode == CopyMode)
5339     {
5340       (void) XConfigureImage(display,resource_info,windows,image,exception);
5341       return(MagickTrue);
5342     }
5343   /*
5344     Cut image.
5345   */
5346   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5347     return(MagickFalse);
5348   image->matte=MagickTrue;
5349   image_view=AcquireCacheView(image);
5350   for (y=0; y < (int) crop_info.height; y++)
5351   {
5352     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5353       crop_info.width,1,exception);
5354     if (q == (Quantum *) NULL)
5355       break;
5356     for (x=0; x < (int) crop_info.width; x++)
5357     {
5358       SetPixelAlpha(image,TransparentAlpha,q);
5359       q+=GetPixelChannels(image);
5360     }
5361     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5362       break;
5363   }
5364   image_view=DestroyCacheView(image_view);
5365   /*
5366     Update image configuration.
5367   */
5368   XConfigureImageColormap(display,resource_info,windows,image);
5369   (void) XConfigureImage(display,resource_info,windows,image,exception);
5370   return(MagickTrue);
5371 }
5372 \f
5373 /*
5374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5375 %                                                                             %
5376 %                                                                             %
5377 %                                                                             %
5378 +   X D r a w I m a g e                                                       %
5379 %                                                                             %
5380 %                                                                             %
5381 %                                                                             %
5382 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5383 %
5384 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5385 %  the image.
5386 %
5387 %  The format of the XDrawEditImage method is:
5388 %
5389 %      MagickBooleanType XDrawEditImage(Display *display,
5390 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5391 %        ExceptionInfo *exception)
5392 %
5393 %  A description of each parameter follows:
5394 %
5395 %    o display: Specifies a connection to an X server; returned from
5396 %      XOpenDisplay.
5397 %
5398 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5399 %
5400 %    o windows: Specifies a pointer to a XWindows structure.
5401 %
5402 %    o image: the image.
5403 %
5404 %    o exception: return any errors or warnings in this structure.
5405 %
5406 */
5407 static MagickBooleanType XDrawEditImage(Display *display,
5408   XResourceInfo *resource_info,XWindows *windows,Image **image,
5409   ExceptionInfo *exception)
5410 {
5411   static const char
5412     *DrawMenu[] =
5413     {
5414       "Element",
5415       "Color",
5416       "Stipple",
5417       "Width",
5418       "Undo",
5419       "Help",
5420       "Dismiss",
5421       (char *) NULL
5422     };
5423
5424   static ElementType
5425     element = PointElement;
5426
5427   static const ModeType
5428     DrawCommands[] =
5429     {
5430       DrawElementCommand,
5431       DrawColorCommand,
5432       DrawStippleCommand,
5433       DrawWidthCommand,
5434       DrawUndoCommand,
5435       DrawHelpCommand,
5436       DrawDismissCommand
5437     };
5438
5439   static Pixmap
5440     stipple = (Pixmap) NULL;
5441
5442   static unsigned int
5443     pen_id = 0,
5444     line_width = 1;
5445
5446   char
5447     command[MaxTextExtent],
5448     text[MaxTextExtent];
5449
5450   Cursor
5451     cursor;
5452
5453   int
5454     entry,
5455     id,
5456     number_coordinates,
5457     x,
5458     y;
5459
5460   MagickRealType
5461     degrees;
5462
5463   MagickStatusType
5464     status;
5465
5466   RectangleInfo
5467     rectangle_info;
5468
5469   register int
5470     i;
5471
5472   unsigned int
5473     distance,
5474     height,
5475     max_coordinates,
5476     width;
5477
5478   size_t
5479     state;
5480
5481   Window
5482     root_window;
5483
5484   XDrawInfo
5485     draw_info;
5486
5487   XEvent
5488     event;
5489
5490   XPoint
5491     *coordinate_info;
5492
5493   XSegment
5494     line_info;
5495
5496   /*
5497     Allocate polygon info.
5498   */
5499   max_coordinates=2048;
5500   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5501     sizeof(*coordinate_info));
5502   if (coordinate_info == (XPoint *) NULL)
5503     {
5504       (void) ThrowMagickException(exception,GetMagickModule(),
5505         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5506       return(MagickFalse);
5507     }
5508   /*
5509     Map Command widget.
5510   */
5511   (void) CloneString(&windows->command.name,"Draw");
5512   windows->command.data=4;
5513   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5514   (void) XMapRaised(display,windows->command.id);
5515   XClientMessage(display,windows->image.id,windows->im_protocols,
5516     windows->im_update_widget,CurrentTime);
5517   /*
5518     Wait for first button press.
5519   */
5520   root_window=XRootWindow(display,XDefaultScreen(display));
5521   draw_info.stencil=OpaqueStencil;
5522   status=MagickTrue;
5523   cursor=XCreateFontCursor(display,XC_tcross);
5524   for ( ; ; )
5525   {
5526     XQueryPosition(display,windows->image.id,&x,&y);
5527     (void) XSelectInput(display,windows->image.id,
5528       windows->image.attributes.event_mask | PointerMotionMask);
5529     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5530     state=DefaultState;
5531     do
5532     {
5533       if (windows->info.mapped != MagickFalse)
5534         {
5535           /*
5536             Display pointer position.
5537           */
5538           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5539             x+windows->image.x,y+windows->image.y);
5540           XInfoWidget(display,windows,text);
5541         }
5542       /*
5543         Wait for next event.
5544       */
5545       XScreenEvent(display,windows,&event);
5546       if (event.xany.window == windows->command.id)
5547         {
5548           /*
5549             Select a command from the Command widget.
5550           */
5551           id=XCommandWidget(display,windows,DrawMenu,&event);
5552           if (id < 0)
5553             continue;
5554           switch (DrawCommands[id])
5555           {
5556             case DrawElementCommand:
5557             {
5558               static const char
5559                 *Elements[] =
5560                 {
5561                   "point",
5562                   "line",
5563                   "rectangle",
5564                   "fill rectangle",
5565                   "circle",
5566                   "fill circle",
5567                   "ellipse",
5568                   "fill ellipse",
5569                   "polygon",
5570                   "fill polygon",
5571                   (char *) NULL,
5572                 };
5573
5574               /*
5575                 Select a command from the pop-up menu.
5576               */
5577               element=(ElementType) (XMenuWidget(display,windows,
5578                 DrawMenu[id],Elements,command)+1);
5579               break;
5580             }
5581             case DrawColorCommand:
5582             {
5583               const char
5584                 *ColorMenu[MaxNumberPens+1];
5585
5586               int
5587                 pen_number;
5588
5589               MagickBooleanType
5590                 transparent;
5591
5592               XColor
5593                 color;
5594
5595               /*
5596                 Initialize menu selections.
5597               */
5598               for (i=0; i < (int) (MaxNumberPens-2); i++)
5599                 ColorMenu[i]=resource_info->pen_colors[i];
5600               ColorMenu[MaxNumberPens-2]="transparent";
5601               ColorMenu[MaxNumberPens-1]="Browser...";
5602               ColorMenu[MaxNumberPens]=(char *) NULL;
5603               /*
5604                 Select a pen color from the pop-up menu.
5605               */
5606               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5607                 (const char **) ColorMenu,command);
5608               if (pen_number < 0)
5609                 break;
5610               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5611                 MagickFalse;
5612               if (transparent != MagickFalse)
5613                 {
5614                   draw_info.stencil=TransparentStencil;
5615                   break;
5616                 }
5617               if (pen_number == (MaxNumberPens-1))
5618                 {
5619                   static char
5620                     color_name[MaxTextExtent] = "gray";
5621
5622                   /*
5623                     Select a pen color from a dialog.
5624                   */
5625                   resource_info->pen_colors[pen_number]=color_name;
5626                   XColorBrowserWidget(display,windows,"Select",color_name);
5627                   if (*color_name == '\0')
5628                     break;
5629                 }
5630               /*
5631                 Set pen color.
5632               */
5633               (void) XParseColor(display,windows->map_info->colormap,
5634                 resource_info->pen_colors[pen_number],&color);
5635               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5636                 (unsigned int) MaxColors,&color);
5637               windows->pixel_info->pen_colors[pen_number]=color;
5638               pen_id=(unsigned int) pen_number;
5639               draw_info.stencil=OpaqueStencil;
5640               break;
5641             }
5642             case DrawStippleCommand:
5643             {
5644               Image
5645                 *stipple_image;
5646
5647               ImageInfo
5648                 *image_info;
5649
5650               int
5651                 status;
5652
5653               static char
5654                 filename[MaxTextExtent] = "\0";
5655
5656               static const char
5657                 *StipplesMenu[] =
5658                 {
5659                   "Brick",
5660                   "Diagonal",
5661                   "Scales",
5662                   "Vertical",
5663                   "Wavy",
5664                   "Translucent",
5665                   "Opaque",
5666                   (char *) NULL,
5667                   (char *) NULL,
5668                 };
5669
5670               /*
5671                 Select a command from the pop-up menu.
5672               */
5673               StipplesMenu[7]="Open...";
5674               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5675                 command);
5676               if (entry < 0)
5677                 break;
5678               if (stipple != (Pixmap) NULL)
5679                 (void) XFreePixmap(display,stipple);
5680               stipple=(Pixmap) NULL;
5681               if (entry != 7)
5682                 {
5683                   switch (entry)
5684                   {
5685                     case 0:
5686                     {
5687                       stipple=XCreateBitmapFromData(display,root_window,
5688                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5689                       break;
5690                     }
5691                     case 1:
5692                     {
5693                       stipple=XCreateBitmapFromData(display,root_window,
5694                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5695                       break;
5696                     }
5697                     case 2:
5698                     {
5699                       stipple=XCreateBitmapFromData(display,root_window,
5700                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5701                       break;
5702                     }
5703                     case 3:
5704                     {
5705                       stipple=XCreateBitmapFromData(display,root_window,
5706                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5707                       break;
5708                     }
5709                     case 4:
5710                     {
5711                       stipple=XCreateBitmapFromData(display,root_window,
5712                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5713                       break;
5714                     }
5715                     case 5:
5716                     {
5717                       stipple=XCreateBitmapFromData(display,root_window,
5718                         (char *) HighlightBitmap,HighlightWidth,
5719                         HighlightHeight);
5720                       break;
5721                     }
5722                     case 6:
5723                     default:
5724                     {
5725                       stipple=XCreateBitmapFromData(display,root_window,
5726                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5727                       break;
5728                     }
5729                   }
5730                   break;
5731                 }
5732               XFileBrowserWidget(display,windows,"Stipple",filename);
5733               if (*filename == '\0')
5734                 break;
5735               /*
5736                 Read image.
5737               */
5738               XSetCursorState(display,windows,MagickTrue);
5739               XCheckRefreshWindows(display,windows);
5740               image_info=AcquireImageInfo();
5741               (void) CopyMagickString(image_info->filename,filename,
5742                 MaxTextExtent);
5743               stipple_image=ReadImage(image_info,exception);
5744               CatchException(exception);
5745               XSetCursorState(display,windows,MagickFalse);
5746               if (stipple_image == (Image *) NULL)
5747                 break;
5748               (void) AcquireUniqueFileResource(filename);
5749               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5750                 "xbm:%s",filename);
5751               (void) WriteImage(image_info,stipple_image,exception);
5752               stipple_image=DestroyImage(stipple_image);
5753               image_info=DestroyImageInfo(image_info);
5754               status=XReadBitmapFile(display,root_window,filename,&width,
5755                 &height,&stipple,&x,&y);
5756               (void) RelinquishUniqueFileResource(filename);
5757               if ((status != BitmapSuccess) != 0)
5758                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5759                   filename);
5760               break;
5761             }
5762             case DrawWidthCommand:
5763             {
5764               static char
5765                 width[MaxTextExtent] = "0";
5766
5767               static const char
5768                 *WidthsMenu[] =
5769                 {
5770                   "1",
5771                   "2",
5772                   "4",
5773                   "8",
5774                   "16",
5775                   "Dialog...",
5776                   (char *) NULL,
5777                 };
5778
5779               /*
5780                 Select a command from the pop-up menu.
5781               */
5782               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5783                 command);
5784               if (entry < 0)
5785                 break;
5786               if (entry != 5)
5787                 {
5788                   line_width=(unsigned int) StringToUnsignedLong(
5789                     WidthsMenu[entry]);
5790                   break;
5791                 }
5792               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5793                 width);
5794               if (*width == '\0')
5795                 break;
5796               line_width=(unsigned int) StringToUnsignedLong(width);
5797               break;
5798             }
5799             case DrawUndoCommand:
5800             {
5801               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5802                 image,exception);
5803               break;
5804             }
5805             case DrawHelpCommand:
5806             {
5807               XTextViewWidget(display,resource_info,windows,MagickFalse,
5808                 "Help Viewer - Image Rotation",ImageDrawHelp);
5809               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5810               break;
5811             }
5812             case DrawDismissCommand:
5813             {
5814               /*
5815                 Prematurely exit.
5816               */
5817               state|=EscapeState;
5818               state|=ExitState;
5819               break;
5820             }
5821             default:
5822               break;
5823           }
5824           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5825           continue;
5826         }
5827       switch (event.type)
5828       {
5829         case ButtonPress:
5830         {
5831           if (event.xbutton.button != Button1)
5832             break;
5833           if (event.xbutton.window != windows->image.id)
5834             break;
5835           /*
5836             exit loop.
5837           */
5838           x=event.xbutton.x;
5839           y=event.xbutton.y;
5840           state|=ExitState;
5841           break;
5842         }
5843         case ButtonRelease:
5844           break;
5845         case Expose:
5846           break;
5847         case KeyPress:
5848         {
5849           KeySym
5850             key_symbol;
5851
5852           if (event.xkey.window != windows->image.id)
5853             break;
5854           /*
5855             Respond to a user key press.
5856           */
5857           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5858             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5859           switch ((int) key_symbol)
5860           {
5861             case XK_Escape:
5862             case XK_F20:
5863             {
5864               /*
5865                 Prematurely exit.
5866               */
5867               state|=EscapeState;
5868               state|=ExitState;
5869               break;
5870             }
5871             case XK_F1:
5872             case XK_Help:
5873             {
5874               XTextViewWidget(display,resource_info,windows,MagickFalse,
5875                 "Help Viewer - Image Rotation",ImageDrawHelp);
5876               break;
5877             }
5878             default:
5879             {
5880               (void) XBell(display,0);
5881               break;
5882             }
5883           }
5884           break;
5885         }
5886         case MotionNotify:
5887         {
5888           /*
5889             Map and unmap Info widget as text cursor crosses its boundaries.
5890           */
5891           x=event.xmotion.x;
5892           y=event.xmotion.y;
5893           if (windows->info.mapped != MagickFalse)
5894             {
5895               if ((x < (int) (windows->info.x+windows->info.width)) &&
5896                   (y < (int) (windows->info.y+windows->info.height)))
5897                 (void) XWithdrawWindow(display,windows->info.id,
5898                   windows->info.screen);
5899             }
5900           else
5901             if ((x > (int) (windows->info.x+windows->info.width)) ||
5902                 (y > (int) (windows->info.y+windows->info.height)))
5903               (void) XMapWindow(display,windows->info.id);
5904           break;
5905         }
5906       }
5907     } while ((state & ExitState) == 0);
5908     (void) XSelectInput(display,windows->image.id,
5909       windows->image.attributes.event_mask);
5910     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5911     if ((state & EscapeState) != 0)
5912       break;
5913     /*
5914       Draw element as pointer moves until the button is released.
5915     */
5916     distance=0;
5917     degrees=0.0;
5918     line_info.x1=x;
5919     line_info.y1=y;
5920     line_info.x2=x;
5921     line_info.y2=y;
5922     rectangle_info.x=(ssize_t) x;
5923     rectangle_info.y=(ssize_t) y;
5924     rectangle_info.width=0;
5925     rectangle_info.height=0;
5926     number_coordinates=1;
5927     coordinate_info->x=x;
5928     coordinate_info->y=y;
5929     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5930     state=DefaultState;
5931     do
5932     {
5933       switch (element)
5934       {
5935         case PointElement:
5936         default:
5937         {
5938           if (number_coordinates > 1)
5939             {
5940               (void) XDrawLines(display,windows->image.id,
5941                 windows->image.highlight_context,coordinate_info,
5942                 number_coordinates,CoordModeOrigin);
5943               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5944                 coordinate_info[number_coordinates-1].x,
5945                 coordinate_info[number_coordinates-1].y);
5946               XInfoWidget(display,windows,text);
5947             }
5948           break;
5949         }
5950         case LineElement:
5951         {
5952           if (distance > 9)
5953             {
5954               /*
5955                 Display angle of the line.
5956               */
5957               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5958                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5959               (void) FormatLocaleString(text,MaxTextExtent," %g",
5960                 (double) degrees);
5961               XInfoWidget(display,windows,text);
5962               XHighlightLine(display,windows->image.id,
5963                 windows->image.highlight_context,&line_info);
5964             }
5965           else
5966             if (windows->info.mapped != MagickFalse)
5967               (void) XWithdrawWindow(display,windows->info.id,
5968                 windows->info.screen);
5969           break;
5970         }
5971         case RectangleElement:
5972         case FillRectangleElement:
5973         {
5974           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5975             {
5976               /*
5977                 Display info and draw drawing rectangle.
5978               */
5979               (void) FormatLocaleString(text,MaxTextExtent,
5980                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5981                 (double) rectangle_info.height,(double) rectangle_info.x,
5982                 (double) rectangle_info.y);
5983               XInfoWidget(display,windows,text);
5984               XHighlightRectangle(display,windows->image.id,
5985                 windows->image.highlight_context,&rectangle_info);
5986             }
5987           else
5988             if (windows->info.mapped != MagickFalse)
5989               (void) XWithdrawWindow(display,windows->info.id,
5990                 windows->info.screen);
5991           break;
5992         }
5993         case CircleElement:
5994         case FillCircleElement:
5995         case EllipseElement:
5996         case FillEllipseElement:
5997         {
5998           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5999             {
6000               /*
6001                 Display info and draw drawing rectangle.
6002               */
6003               (void) FormatLocaleString(text,MaxTextExtent,
6004                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6005                 (double) rectangle_info.height,(double) rectangle_info.x,
6006                 (double) rectangle_info.y);
6007               XInfoWidget(display,windows,text);
6008               XHighlightEllipse(display,windows->image.id,
6009                 windows->image.highlight_context,&rectangle_info);
6010             }
6011           else
6012             if (windows->info.mapped != MagickFalse)
6013               (void) XWithdrawWindow(display,windows->info.id,
6014                 windows->info.screen);
6015           break;
6016         }
6017         case PolygonElement:
6018         case FillPolygonElement:
6019         {
6020           if (number_coordinates > 1)
6021             (void) XDrawLines(display,windows->image.id,
6022               windows->image.highlight_context,coordinate_info,
6023               number_coordinates,CoordModeOrigin);
6024           if (distance > 9)
6025             {
6026               /*
6027                 Display angle of the line.
6028               */
6029               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6030                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6031               (void) FormatLocaleString(text,MaxTextExtent," %g",
6032                 (double) degrees);
6033               XInfoWidget(display,windows,text);
6034               XHighlightLine(display,windows->image.id,
6035                 windows->image.highlight_context,&line_info);
6036             }
6037           else
6038             if (windows->info.mapped != MagickFalse)
6039               (void) XWithdrawWindow(display,windows->info.id,
6040                 windows->info.screen);
6041           break;
6042         }
6043       }
6044       /*
6045         Wait for next event.
6046       */
6047       XScreenEvent(display,windows,&event);
6048       switch (element)
6049       {
6050         case PointElement:
6051         default:
6052         {
6053           if (number_coordinates > 1)
6054             (void) XDrawLines(display,windows->image.id,
6055               windows->image.highlight_context,coordinate_info,
6056               number_coordinates,CoordModeOrigin);
6057           break;
6058         }
6059         case LineElement:
6060         {
6061           if (distance > 9)
6062             XHighlightLine(display,windows->image.id,
6063               windows->image.highlight_context,&line_info);
6064           break;
6065         }
6066         case RectangleElement:
6067         case FillRectangleElement:
6068         {
6069           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6070             XHighlightRectangle(display,windows->image.id,
6071               windows->image.highlight_context,&rectangle_info);
6072           break;
6073         }
6074         case CircleElement:
6075         case FillCircleElement:
6076         case EllipseElement:
6077         case FillEllipseElement:
6078         {
6079           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6080             XHighlightEllipse(display,windows->image.id,
6081               windows->image.highlight_context,&rectangle_info);
6082           break;
6083         }
6084         case PolygonElement:
6085         case FillPolygonElement:
6086         {
6087           if (number_coordinates > 1)
6088             (void) XDrawLines(display,windows->image.id,
6089               windows->image.highlight_context,coordinate_info,
6090               number_coordinates,CoordModeOrigin);
6091           if (distance > 9)
6092             XHighlightLine(display,windows->image.id,
6093               windows->image.highlight_context,&line_info);
6094           break;
6095         }
6096       }
6097       switch (event.type)
6098       {
6099         case ButtonPress:
6100           break;
6101         case ButtonRelease:
6102         {
6103           /*
6104             User has committed to element.
6105           */
6106           line_info.x2=event.xbutton.x;
6107           line_info.y2=event.xbutton.y;
6108           rectangle_info.x=(ssize_t) event.xbutton.x;
6109           rectangle_info.y=(ssize_t) event.xbutton.y;
6110           coordinate_info[number_coordinates].x=event.xbutton.x;
6111           coordinate_info[number_coordinates].y=event.xbutton.y;
6112           if (((element != PolygonElement) &&
6113                (element != FillPolygonElement)) || (distance <= 9))
6114             {
6115               state|=ExitState;
6116               break;
6117             }
6118           number_coordinates++;
6119           if (number_coordinates < (int) max_coordinates)
6120             {
6121               line_info.x1=event.xbutton.x;
6122               line_info.y1=event.xbutton.y;
6123               break;
6124             }
6125           max_coordinates<<=1;
6126           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6127             max_coordinates,sizeof(*coordinate_info));
6128           if (coordinate_info == (XPoint *) NULL)
6129             (void) ThrowMagickException(exception,GetMagickModule(),
6130               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6131           break;
6132         }
6133         case Expose:
6134           break;
6135         case MotionNotify:
6136         {
6137           if (event.xmotion.window != windows->image.id)
6138             break;
6139           if (element != PointElement)
6140             {
6141               line_info.x2=event.xmotion.x;
6142               line_info.y2=event.xmotion.y;
6143               rectangle_info.x=(ssize_t) event.xmotion.x;
6144               rectangle_info.y=(ssize_t) event.xmotion.y;
6145               break;
6146             }
6147           coordinate_info[number_coordinates].x=event.xbutton.x;
6148           coordinate_info[number_coordinates].y=event.xbutton.y;
6149           number_coordinates++;
6150           if (number_coordinates < (int) max_coordinates)
6151             break;
6152           max_coordinates<<=1;
6153           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6154             max_coordinates,sizeof(*coordinate_info));
6155           if (coordinate_info == (XPoint *) NULL)
6156             (void) ThrowMagickException(exception,GetMagickModule(),
6157               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6158           break;
6159         }
6160         default:
6161           break;
6162       }
6163       /*
6164         Check boundary conditions.
6165       */
6166       if (line_info.x2 < 0)
6167         line_info.x2=0;
6168       else
6169         if (line_info.x2 > (int) windows->image.width)
6170           line_info.x2=(short) windows->image.width;
6171       if (line_info.y2 < 0)
6172         line_info.y2=0;
6173       else
6174         if (line_info.y2 > (int) windows->image.height)
6175           line_info.y2=(short) windows->image.height;
6176       distance=(unsigned int)
6177         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6178          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6179       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6180           ((state & ExitState) != 0))
6181         {
6182           if (rectangle_info.x < 0)
6183             rectangle_info.x=0;
6184           else
6185             if (rectangle_info.x > (ssize_t) windows->image.width)
6186               rectangle_info.x=(ssize_t) windows->image.width;
6187           if ((int) rectangle_info.x < x)
6188             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6189           else
6190             {
6191               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6192               rectangle_info.x=(ssize_t) x;
6193             }
6194           if (rectangle_info.y < 0)
6195             rectangle_info.y=0;
6196           else
6197             if (rectangle_info.y > (ssize_t) windows->image.height)
6198               rectangle_info.y=(ssize_t) windows->image.height;
6199           if ((int) rectangle_info.y < y)
6200             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6201           else
6202             {
6203               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6204               rectangle_info.y=(ssize_t) y;
6205             }
6206         }
6207     } while ((state & ExitState) == 0);
6208     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6209     if ((element == PointElement) || (element == PolygonElement) ||
6210         (element == FillPolygonElement))
6211       {
6212         /*
6213           Determine polygon bounding box.
6214         */
6215         rectangle_info.x=(ssize_t) coordinate_info->x;
6216         rectangle_info.y=(ssize_t) coordinate_info->y;
6217         x=coordinate_info->x;
6218         y=coordinate_info->y;
6219         for (i=1; i < number_coordinates; i++)
6220         {
6221           if (coordinate_info[i].x > x)
6222             x=coordinate_info[i].x;
6223           if (coordinate_info[i].y > y)
6224             y=coordinate_info[i].y;
6225           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6226             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6227           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6228             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6229         }
6230         rectangle_info.width=(size_t) (x-rectangle_info.x);
6231         rectangle_info.height=(size_t) (y-rectangle_info.y);
6232         for (i=0; i < number_coordinates; i++)
6233         {
6234           coordinate_info[i].x-=rectangle_info.x;
6235           coordinate_info[i].y-=rectangle_info.y;
6236         }
6237       }
6238     else
6239       if (distance <= 9)
6240         continue;
6241       else
6242         if ((element == RectangleElement) ||
6243             (element == CircleElement) || (element == EllipseElement))
6244           {
6245             rectangle_info.width--;
6246             rectangle_info.height--;
6247           }
6248     /*
6249       Drawing is relative to image configuration.
6250     */
6251     draw_info.x=(int) rectangle_info.x;
6252     draw_info.y=(int) rectangle_info.y;
6253     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6254       image,exception);
6255     width=(unsigned int) (*image)->columns;
6256     height=(unsigned int) (*image)->rows;
6257     x=0;
6258     y=0;
6259     if (windows->image.crop_geometry != (char *) NULL)
6260       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6261     draw_info.x+=windows->image.x-(line_width/2);
6262     if (draw_info.x < 0)
6263       draw_info.x=0;
6264     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6265     draw_info.y+=windows->image.y-(line_width/2);
6266     if (draw_info.y < 0)
6267       draw_info.y=0;
6268     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6269     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6270     if (draw_info.width > (unsigned int) (*image)->columns)
6271       draw_info.width=(unsigned int) (*image)->columns;
6272     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6273     if (draw_info.height > (unsigned int) (*image)->rows)
6274       draw_info.height=(unsigned int) (*image)->rows;
6275     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6276       width*draw_info.width/windows->image.ximage->width,
6277       height*draw_info.height/windows->image.ximage->height,
6278       draw_info.x+x,draw_info.y+y);
6279     /*
6280       Initialize drawing attributes.
6281     */
6282     draw_info.degrees=0.0;
6283     draw_info.element=element;
6284     draw_info.stipple=stipple;
6285     draw_info.line_width=line_width;
6286     draw_info.line_info=line_info;
6287     if (line_info.x1 > (int) (line_width/2))
6288       draw_info.line_info.x1=(short) line_width/2;
6289     if (line_info.y1 > (int) (line_width/2))
6290       draw_info.line_info.y1=(short) line_width/2;
6291     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6292     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6293     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6294       {
6295         draw_info.line_info.x2=(-draw_info.line_info.x2);
6296         draw_info.line_info.y2=(-draw_info.line_info.y2);
6297       }
6298     if (draw_info.line_info.x2 < 0)
6299       {
6300         draw_info.line_info.x2=(-draw_info.line_info.x2);
6301         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6302       }
6303     if (draw_info.line_info.y2 < 0)
6304       {
6305         draw_info.line_info.y2=(-draw_info.line_info.y2);
6306         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6307       }
6308     draw_info.rectangle_info=rectangle_info;
6309     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6310       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6311     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6312       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6313     draw_info.number_coordinates=(unsigned int) number_coordinates;
6314     draw_info.coordinate_info=coordinate_info;
6315     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6316     /*
6317       Draw element on image.
6318     */
6319     XSetCursorState(display,windows,MagickTrue);
6320     XCheckRefreshWindows(display,windows);
6321     status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6322     XSetCursorState(display,windows,MagickFalse);
6323     /*
6324       Update image colormap and return to image drawing.
6325     */
6326     XConfigureImageColormap(display,resource_info,windows,*image);
6327     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6328   }
6329   XSetCursorState(display,windows,MagickFalse);
6330   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6331   return(status != 0 ? MagickTrue : MagickFalse);
6332 }
6333 \f
6334 /*
6335 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6336 %                                                                             %
6337 %                                                                             %
6338 %                                                                             %
6339 +   X D r a w P a n R e c t a n g l e                                         %
6340 %                                                                             %
6341 %                                                                             %
6342 %                                                                             %
6343 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6344 %
6345 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6346 %  displays a zoom image and the rectangle shows which portion of the image is
6347 %  displayed in the Image window.
6348 %
6349 %  The format of the XDrawPanRectangle method is:
6350 %
6351 %      XDrawPanRectangle(Display *display,XWindows *windows)
6352 %
6353 %  A description of each parameter follows:
6354 %
6355 %    o display: Specifies a connection to an X server;  returned from
6356 %      XOpenDisplay.
6357 %
6358 %    o windows: Specifies a pointer to a XWindows structure.
6359 %
6360 */
6361 static void XDrawPanRectangle(Display *display,XWindows *windows)
6362 {
6363   MagickRealType
6364     scale_factor;
6365
6366   RectangleInfo
6367     highlight_info;
6368
6369   /*
6370     Determine dimensions of the panning rectangle.
6371   */
6372   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6373   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6374   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6375   scale_factor=(MagickRealType)
6376     windows->pan.height/windows->image.ximage->height;
6377   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6378   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6379   /*
6380     Display the panning rectangle.
6381   */
6382   (void) XClearWindow(display,windows->pan.id);
6383   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6384     &highlight_info);
6385 }
6386 \f
6387 /*
6388 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6389 %                                                                             %
6390 %                                                                             %
6391 %                                                                             %
6392 +   X I m a g e C a c h e                                                     %
6393 %                                                                             %
6394 %                                                                             %
6395 %                                                                             %
6396 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6397 %
6398 %  XImageCache() handles the creation, manipulation, and destruction of the
6399 %  image cache (undo and redo buffers).
6400 %
6401 %  The format of the XImageCache method is:
6402 %
6403 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6404 %        XWindows *windows,const CommandType command,Image **image,
6405 %        ExceptionInfo *exception)
6406 %
6407 %  A description of each parameter follows:
6408 %
6409 %    o display: Specifies a connection to an X server; returned from
6410 %      XOpenDisplay.
6411 %
6412 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6413 %
6414 %    o windows: Specifies a pointer to a XWindows structure.
6415 %
6416 %    o command: Specifies a command to perform.
6417 %
6418 %    o image: the image;  XImageCache may transform the image and return a new
6419 %      image pointer.
6420 %
6421 %    o exception: return any errors or warnings in this structure.
6422 %
6423 */
6424 static void XImageCache(Display *display,XResourceInfo *resource_info,
6425   XWindows *windows,const CommandType command,Image **image,
6426   ExceptionInfo *exception)
6427 {
6428   Image
6429     *cache_image;
6430
6431   static Image
6432     *redo_image = (Image *) NULL,
6433     *undo_image = (Image *) NULL;
6434
6435   switch (command)
6436   {
6437     case FreeBuffersCommand:
6438     {
6439       /*
6440         Free memory from the undo and redo cache.
6441       */
6442       while (undo_image != (Image *) NULL)
6443       {
6444         cache_image=undo_image;
6445         undo_image=GetPreviousImageInList(undo_image);
6446         cache_image->list=DestroyImage(cache_image->list);
6447         cache_image=DestroyImage(cache_image);
6448       }
6449       undo_image=NewImageList();
6450       if (redo_image != (Image *) NULL)
6451         redo_image=DestroyImage(redo_image);
6452       redo_image=NewImageList();
6453       return;
6454     }
6455     case UndoCommand:
6456     {
6457       char
6458         image_geometry[MaxTextExtent];
6459
6460       /*
6461         Undo the last image transformation.
6462       */
6463       if (undo_image == (Image *) NULL)
6464         {
6465           (void) XBell(display,0);
6466           return;
6467         }
6468       cache_image=undo_image;
6469       undo_image=GetPreviousImageInList(undo_image);
6470       windows->image.window_changes.width=(int) cache_image->columns;
6471       windows->image.window_changes.height=(int) cache_image->rows;
6472       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6473         windows->image.ximage->width,windows->image.ximage->height);
6474       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6475       if (windows->image.crop_geometry != (char *) NULL)
6476         windows->image.crop_geometry=(char *)
6477           RelinquishMagickMemory(windows->image.crop_geometry);
6478       windows->image.crop_geometry=cache_image->geometry;
6479       if (redo_image != (Image *) NULL)
6480         redo_image=DestroyImage(redo_image);
6481       redo_image=(*image);
6482       *image=cache_image->list;
6483       cache_image=DestroyImage(cache_image);
6484       if (windows->image.orphan != MagickFalse)
6485         return;
6486       XConfigureImageColormap(display,resource_info,windows,*image);
6487       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6488       return;
6489     }
6490     case CutCommand:
6491     case PasteCommand:
6492     case ApplyCommand:
6493     case HalfSizeCommand:
6494     case OriginalSizeCommand:
6495     case DoubleSizeCommand:
6496     case ResizeCommand:
6497     case TrimCommand:
6498     case CropCommand:
6499     case ChopCommand:
6500     case FlipCommand:
6501     case FlopCommand:
6502     case RotateRightCommand:
6503     case RotateLeftCommand:
6504     case RotateCommand:
6505     case ShearCommand:
6506     case RollCommand:
6507     case NegateCommand:
6508     case ContrastStretchCommand:
6509     case SigmoidalContrastCommand:
6510     case NormalizeCommand:
6511     case EqualizeCommand:
6512     case HueCommand:
6513     case SaturationCommand:
6514     case BrightnessCommand:
6515     case GammaCommand:
6516     case SpiffCommand:
6517     case DullCommand:
6518     case GrayscaleCommand:
6519     case MapCommand:
6520     case QuantizeCommand:
6521     case DespeckleCommand:
6522     case EmbossCommand:
6523     case ReduceNoiseCommand:
6524     case AddNoiseCommand:
6525     case SharpenCommand:
6526     case BlurCommand:
6527     case ThresholdCommand:
6528     case EdgeDetectCommand:
6529     case SpreadCommand:
6530     case ShadeCommand:
6531     case RaiseCommand:
6532     case SegmentCommand:
6533     case SolarizeCommand:
6534     case SepiaToneCommand:
6535     case SwirlCommand:
6536     case ImplodeCommand:
6537     case VignetteCommand:
6538     case WaveCommand:
6539     case OilPaintCommand:
6540     case CharcoalDrawCommand:
6541     case AnnotateCommand:
6542     case AddBorderCommand:
6543     case AddFrameCommand:
6544     case CompositeCommand:
6545     case CommentCommand:
6546     case LaunchCommand:
6547     case RegionofInterestCommand:
6548     case SaveToUndoBufferCommand:
6549     case RedoCommand:
6550     {
6551       Image
6552         *previous_image;
6553
6554       ssize_t
6555         bytes;
6556
6557       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6558       if (undo_image != (Image *) NULL)
6559         {
6560           /*
6561             Ensure the undo cache has enough memory available.
6562           */
6563           previous_image=undo_image;
6564           while (previous_image != (Image *) NULL)
6565           {
6566             bytes+=previous_image->list->columns*previous_image->list->rows*
6567               sizeof(PixelPacket);
6568             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6569               {
6570                 previous_image=GetPreviousImageInList(previous_image);
6571                 continue;
6572               }
6573             bytes-=previous_image->list->columns*previous_image->list->rows*
6574               sizeof(PixelPacket);
6575             if (previous_image == undo_image)
6576               undo_image=NewImageList();
6577             else
6578               previous_image->next->previous=NewImageList();
6579             break;
6580           }
6581           while (previous_image != (Image *) NULL)
6582           {
6583             /*
6584               Delete any excess memory from undo cache.
6585             */
6586             cache_image=previous_image;
6587             previous_image=GetPreviousImageInList(previous_image);
6588             cache_image->list=DestroyImage(cache_image->list);
6589             cache_image=DestroyImage(cache_image);
6590           }
6591         }
6592       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6593         break;
6594       /*
6595         Save image before transformations are applied.
6596       */
6597       cache_image=AcquireImage((ImageInfo *) NULL);
6598       if (cache_image == (Image *) NULL)
6599         break;
6600       XSetCursorState(display,windows,MagickTrue);
6601       XCheckRefreshWindows(display,windows);
6602       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6603       XSetCursorState(display,windows,MagickFalse);
6604       if (cache_image->list == (Image *) NULL)
6605         {
6606           cache_image=DestroyImage(cache_image);
6607           break;
6608         }
6609       cache_image->columns=(size_t) windows->image.ximage->width;
6610       cache_image->rows=(size_t) windows->image.ximage->height;
6611       cache_image->geometry=windows->image.crop_geometry;
6612       if (windows->image.crop_geometry != (char *) NULL)
6613         {
6614           cache_image->geometry=AcquireString((char *) NULL);
6615           (void) CopyMagickString(cache_image->geometry,
6616             windows->image.crop_geometry,MaxTextExtent);
6617         }
6618       if (undo_image == (Image *) NULL)
6619         {
6620           undo_image=cache_image;
6621           break;
6622         }
6623       undo_image->next=cache_image;
6624       undo_image->next->previous=undo_image;
6625       undo_image=undo_image->next;
6626       break;
6627     }
6628     default:
6629       break;
6630   }
6631   if (command == RedoCommand)
6632     {
6633       /*
6634         Redo the last image transformation.
6635       */
6636       if (redo_image == (Image *) NULL)
6637         {
6638           (void) XBell(display,0);
6639           return;
6640         }
6641       windows->image.window_changes.width=(int) redo_image->columns;
6642       windows->image.window_changes.height=(int) redo_image->rows;
6643       if (windows->image.crop_geometry != (char *) NULL)
6644         windows->image.crop_geometry=(char *)
6645           RelinquishMagickMemory(windows->image.crop_geometry);
6646       windows->image.crop_geometry=redo_image->geometry;
6647       *image=DestroyImage(*image);
6648       *image=redo_image;
6649       redo_image=NewImageList();
6650       if (windows->image.orphan != MagickFalse)
6651         return;
6652       XConfigureImageColormap(display,resource_info,windows,*image);
6653       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6654       return;
6655     }
6656   if (command != InfoCommand)
6657     return;
6658   /*
6659     Display image info.
6660   */
6661   XSetCursorState(display,windows,MagickTrue);
6662   XCheckRefreshWindows(display,windows);
6663   XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6664   XSetCursorState(display,windows,MagickFalse);
6665 }
6666 \f
6667 /*
6668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6669 %                                                                             %
6670 %                                                                             %
6671 %                                                                             %
6672 +   X I m a g e W i n d o w C o m m a n d                                     %
6673 %                                                                             %
6674 %                                                                             %
6675 %                                                                             %
6676 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6677 %
6678 %  XImageWindowCommand() makes a transform to the image or Image window as
6679 %  specified by a user menu button or keyboard command.
6680 %
6681 %  The format of the XImageWindowCommand method is:
6682 %
6683 %      CommandType XImageWindowCommand(Display *display,
6684 %        XResourceInfo *resource_info,XWindows *windows,
6685 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6686 %        ExceptionInfo *exception)
6687 %
6688 %  A description of each parameter follows:
6689 %
6690 %    o nexus:  Method XImageWindowCommand returns an image when the
6691 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6692 %      image is returned.
6693 %
6694 %    o display: Specifies a connection to an X server; returned from
6695 %      XOpenDisplay.
6696 %
6697 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6698 %
6699 %    o windows: Specifies a pointer to a XWindows structure.
6700 %
6701 %    o state: key mask.
6702 %
6703 %    o key_symbol: Specifies a command to perform.
6704 %
6705 %    o image: the image;  XImageWIndowCommand may transform the image and
6706 %      return a new image pointer.
6707 %
6708 %    o exception: return any errors or warnings in this structure.
6709 %
6710 */
6711 static CommandType XImageWindowCommand(Display *display,
6712   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6713   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6714 {
6715   static char
6716     delta[MaxTextExtent] = "";
6717
6718   static const char
6719     Digits[] = "01234567890";
6720
6721   static KeySym
6722     last_symbol = XK_0;
6723
6724   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6725     {
6726       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6727         {
6728           *delta='\0';
6729           resource_info->quantum=1;
6730         }
6731       last_symbol=key_symbol;
6732       delta[strlen(delta)+1]='\0';
6733       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6734       resource_info->quantum=StringToLong(delta);
6735       return(NullCommand);
6736     }
6737   last_symbol=key_symbol;
6738   if (resource_info->immutable)
6739     {
6740       /*
6741         Virtual image window has a restricted command set.
6742       */
6743       switch (key_symbol)
6744       {
6745         case XK_question:
6746           return(InfoCommand);
6747         case XK_p:
6748         case XK_Print:
6749           return(PrintCommand);
6750         case XK_space:
6751           return(NextCommand);
6752         case XK_q:
6753         case XK_Escape:
6754           return(QuitCommand);
6755         default:
6756           break;
6757       }
6758       return(NullCommand);
6759     }
6760   switch ((int) key_symbol)
6761   {
6762     case XK_o:
6763     {
6764       if ((state & ControlMask) == 0)
6765         break;
6766       return(OpenCommand);
6767     }
6768     case XK_space:
6769       return(NextCommand);
6770     case XK_BackSpace:
6771       return(FormerCommand);
6772     case XK_s:
6773     {
6774       if ((state & Mod1Mask) != 0)
6775         return(SwirlCommand);
6776       if ((state & ControlMask) == 0)
6777         return(ShearCommand);
6778       return(SaveCommand);
6779     }
6780     case XK_p:
6781     case XK_Print:
6782     {
6783       if ((state & Mod1Mask) != 0)
6784         return(OilPaintCommand);
6785       if ((state & Mod4Mask) != 0)
6786         return(ColorCommand);
6787       if ((state & ControlMask) == 0)
6788         return(NullCommand);
6789       return(PrintCommand);
6790     }
6791     case XK_d:
6792     {
6793       if ((state & Mod4Mask) != 0)
6794         return(DrawCommand);
6795       if ((state & ControlMask) == 0)
6796         return(NullCommand);
6797       return(DeleteCommand);
6798     }
6799     case XK_Select:
6800     {
6801       if ((state & ControlMask) == 0)
6802         return(NullCommand);
6803       return(SelectCommand);
6804     }
6805     case XK_n:
6806     {
6807       if ((state & ControlMask) == 0)
6808         return(NullCommand);
6809       return(NewCommand);
6810     }
6811     case XK_q:
6812     case XK_Escape:
6813       return(QuitCommand);
6814     case XK_z:
6815     case XK_Undo:
6816     {
6817       if ((state & ControlMask) == 0)
6818         return(NullCommand);
6819       return(UndoCommand);
6820     }
6821     case XK_r:
6822     case XK_Redo:
6823     {
6824       if ((state & ControlMask) == 0)
6825         return(RollCommand);
6826       return(RedoCommand);
6827     }
6828     case XK_x:
6829     {
6830       if ((state & ControlMask) == 0)
6831         return(NullCommand);
6832       return(CutCommand);
6833     }
6834     case XK_c:
6835     {
6836       if ((state & Mod1Mask) != 0)
6837         return(CharcoalDrawCommand);
6838       if ((state & ControlMask) == 0)
6839         return(CropCommand);
6840       return(CopyCommand);
6841     }
6842     case XK_v:
6843     case XK_Insert:
6844     {
6845       if ((state & Mod4Mask) != 0)
6846         return(CompositeCommand);
6847       if ((state & ControlMask) == 0)
6848         return(FlipCommand);
6849       return(PasteCommand);
6850     }
6851     case XK_less:
6852       return(HalfSizeCommand);
6853     case XK_minus:
6854       return(OriginalSizeCommand);
6855     case XK_greater:
6856       return(DoubleSizeCommand);
6857     case XK_percent:
6858       return(ResizeCommand);
6859     case XK_at:
6860       return(RefreshCommand);
6861     case XK_bracketleft:
6862       return(ChopCommand);
6863     case XK_h:
6864       return(FlopCommand);
6865     case XK_slash:
6866       return(RotateRightCommand);
6867     case XK_backslash:
6868       return(RotateLeftCommand);
6869     case XK_asterisk:
6870       return(RotateCommand);
6871     case XK_t:
6872       return(TrimCommand);
6873     case XK_H:
6874       return(HueCommand);
6875     case XK_S:
6876       return(SaturationCommand);
6877     case XK_L:
6878       return(BrightnessCommand);
6879     case XK_G:
6880       return(GammaCommand);
6881     case XK_C:
6882       return(SpiffCommand);
6883     case XK_Z:
6884       return(DullCommand);
6885     case XK_N:
6886       return(NormalizeCommand);
6887     case XK_equal:
6888       return(EqualizeCommand);
6889     case XK_asciitilde:
6890       return(NegateCommand);
6891     case XK_period:
6892       return(GrayscaleCommand);
6893     case XK_numbersign:
6894       return(QuantizeCommand);
6895     case XK_F2:
6896       return(DespeckleCommand);
6897     case XK_F3:
6898       return(EmbossCommand);
6899     case XK_F4:
6900       return(ReduceNoiseCommand);
6901     case XK_F5:
6902       return(AddNoiseCommand);
6903     case XK_F6:
6904       return(SharpenCommand);
6905     case XK_F7:
6906       return(BlurCommand);
6907     case XK_F8:
6908       return(ThresholdCommand);
6909     case XK_F9:
6910       return(EdgeDetectCommand);
6911     case XK_F10:
6912       return(SpreadCommand);
6913     case XK_F11:
6914       return(ShadeCommand);
6915     case XK_F12:
6916       return(RaiseCommand);
6917     case XK_F13:
6918       return(SegmentCommand);
6919     case XK_i:
6920     {
6921       if ((state & Mod1Mask) == 0)
6922         return(NullCommand);
6923       return(ImplodeCommand);
6924     }
6925     case XK_w:
6926     {
6927       if ((state & Mod1Mask) == 0)
6928         return(NullCommand);
6929       return(WaveCommand);
6930     }
6931     case XK_m:
6932     {
6933       if ((state & Mod4Mask) == 0)
6934         return(NullCommand);
6935       return(MatteCommand);
6936     }
6937     case XK_b:
6938     {
6939       if ((state & Mod4Mask) == 0)
6940         return(NullCommand);
6941       return(AddBorderCommand);
6942     }
6943     case XK_f:
6944     {
6945       if ((state & Mod4Mask) == 0)
6946         return(NullCommand);
6947       return(AddFrameCommand);
6948     }
6949     case XK_exclam:
6950     {
6951       if ((state & Mod4Mask) == 0)
6952         return(NullCommand);
6953       return(CommentCommand);
6954     }
6955     case XK_a:
6956     {
6957       if ((state & Mod1Mask) != 0)
6958         return(ApplyCommand);
6959       if ((state & Mod4Mask) != 0)
6960         return(AnnotateCommand);
6961       if ((state & ControlMask) == 0)
6962         return(NullCommand);
6963       return(RegionofInterestCommand);
6964     }
6965     case XK_question:
6966       return(InfoCommand);
6967     case XK_plus:
6968       return(ZoomCommand);
6969     case XK_P:
6970     {
6971       if ((state & ShiftMask) == 0)
6972         return(NullCommand);
6973       return(ShowPreviewCommand);
6974     }
6975     case XK_Execute:
6976       return(LaunchCommand);
6977     case XK_F1:
6978       return(HelpCommand);
6979     case XK_Find:
6980       return(BrowseDocumentationCommand);
6981     case XK_Menu:
6982     {
6983       (void) XMapRaised(display,windows->command.id);
6984       return(NullCommand);
6985     }
6986     case XK_Next:
6987     case XK_Prior:
6988     case XK_Home:
6989     case XK_KP_Home:
6990     {
6991       XTranslateImage(display,windows,*image,key_symbol);
6992       return(NullCommand);
6993     }
6994     case XK_Up:
6995     case XK_KP_Up:
6996     case XK_Down:
6997     case XK_KP_Down:
6998     case XK_Left:
6999     case XK_KP_Left:
7000     case XK_Right:
7001     case XK_KP_Right:
7002     {
7003       if ((state & Mod1Mask) != 0)
7004         {
7005           RectangleInfo
7006             crop_info;
7007
7008           /*
7009             Trim one pixel from edge of image.
7010           */
7011           crop_info.x=0;
7012           crop_info.y=0;
7013           crop_info.width=(size_t) windows->image.ximage->width;
7014           crop_info.height=(size_t) windows->image.ximage->height;
7015           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7016             {
7017               if (resource_info->quantum >= (int) crop_info.height)
7018                 resource_info->quantum=(int) crop_info.height-1;
7019               crop_info.height-=resource_info->quantum;
7020             }
7021           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7022             {
7023               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7024                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7025               crop_info.y+=resource_info->quantum;
7026               crop_info.height-=resource_info->quantum;
7027             }
7028           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7029             {
7030               if (resource_info->quantum >= (int) crop_info.width)
7031                 resource_info->quantum=(int) crop_info.width-1;
7032               crop_info.width-=resource_info->quantum;
7033             }
7034           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7035             {
7036               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7037                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7038               crop_info.x+=resource_info->quantum;
7039               crop_info.width-=resource_info->quantum;
7040             }
7041           if ((int) (windows->image.x+windows->image.width) >
7042               (int) crop_info.width)
7043             windows->image.x=(int) (crop_info.width-windows->image.width);
7044           if ((int) (windows->image.y+windows->image.height) >
7045               (int) crop_info.height)
7046             windows->image.y=(int) (crop_info.height-windows->image.height);
7047           XSetCropGeometry(display,windows,&crop_info,*image);
7048           windows->image.window_changes.width=(int) crop_info.width;
7049           windows->image.window_changes.height=(int) crop_info.height;
7050           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7051           (void) XConfigureImage(display,resource_info,windows,*image,
7052             exception);
7053           return(NullCommand);
7054         }
7055       XTranslateImage(display,windows,*image,key_symbol);
7056       return(NullCommand);
7057     }
7058     default:
7059       return(NullCommand);
7060   }
7061   return(NullCommand);
7062 }
7063 \f
7064 /*
7065 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7066 %                                                                             %
7067 %                                                                             %
7068 %                                                                             %
7069 +   X M a g i c k C o m m a n d                                               %
7070 %                                                                             %
7071 %                                                                             %
7072 %                                                                             %
7073 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7074 %
7075 %  XMagickCommand() makes a transform to the image or Image window as
7076 %  specified by a user menu button or keyboard command.
7077 %
7078 %  The format of the XMagickCommand method is:
7079 %
7080 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7081 %        XWindows *windows,const CommandType command,Image **image,
7082 %        ExceptionInfo *exception)
7083 %
7084 %  A description of each parameter follows:
7085 %
7086 %    o display: Specifies a connection to an X server; returned from
7087 %      XOpenDisplay.
7088 %
7089 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7090 %
7091 %    o windows: Specifies a pointer to a XWindows structure.
7092 %
7093 %    o command: Specifies a command to perform.
7094 %
7095 %    o image: the image;  XMagickCommand may transform the image and return a
7096 %      new image pointer.
7097 %
7098 %    o exception: return any errors or warnings in this structure.
7099 %
7100 */
7101 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7102   XWindows *windows,const CommandType command,Image **image,
7103   ExceptionInfo *exception)
7104 {
7105   char
7106     filename[MaxTextExtent],
7107     geometry[MaxTextExtent],
7108     modulate_factors[MaxTextExtent];
7109
7110   GeometryInfo
7111     geometry_info;
7112
7113   Image
7114     *nexus;
7115
7116   ImageInfo
7117     *image_info;
7118
7119   int
7120     x,
7121     y;
7122
7123   MagickStatusType
7124     flags,
7125     status;
7126
7127   QuantizeInfo
7128     quantize_info;
7129
7130   RectangleInfo
7131     page_geometry;
7132
7133   register int
7134     i;
7135
7136   static char
7137     color[MaxTextExtent] = "gray";
7138
7139   unsigned int
7140     height,
7141     width;
7142
7143   /*
7144     Process user command.
7145   */
7146   XCheckRefreshWindows(display,windows);
7147   XImageCache(display,resource_info,windows,command,image,exception);
7148   nexus=NewImageList();
7149   windows->image.window_changes.width=windows->image.ximage->width;
7150   windows->image.window_changes.height=windows->image.ximage->height;
7151   image_info=CloneImageInfo(resource_info->image_info);
7152   SetGeometryInfo(&geometry_info);
7153   GetQuantizeInfo(&quantize_info);
7154   switch (command)
7155   {
7156     case OpenCommand:
7157     {
7158       /*
7159         Load image.
7160       */
7161       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7162       break;
7163     }
7164     case NextCommand:
7165     {
7166       /*
7167         Display next image.
7168       */
7169       for (i=0; i < resource_info->quantum; i++)
7170         XClientMessage(display,windows->image.id,windows->im_protocols,
7171           windows->im_next_image,CurrentTime);
7172       break;
7173     }
7174     case FormerCommand:
7175     {
7176       /*
7177         Display former image.
7178       */
7179       for (i=0; i < resource_info->quantum; i++)
7180         XClientMessage(display,windows->image.id,windows->im_protocols,
7181           windows->im_former_image,CurrentTime);
7182       break;
7183     }
7184     case SelectCommand:
7185     {
7186       int
7187         status;
7188
7189       /*
7190         Select image.
7191       */
7192       status=chdir(resource_info->home_directory);
7193       if (status == -1)
7194         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7195           "UnableToOpenFile","%s",resource_info->home_directory);
7196       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7197       break;
7198     }
7199     case SaveCommand:
7200     {
7201       /*
7202         Save image.
7203       */
7204       status=XSaveImage(display,resource_info,windows,*image,exception);
7205       if (status == MagickFalse)
7206         {
7207           XNoticeWidget(display,windows,"Unable to write X image:",
7208             (*image)->filename);
7209           break;
7210         }
7211       break;
7212     }
7213     case PrintCommand:
7214     {
7215       /*
7216         Print image.
7217       */
7218       status=XPrintImage(display,resource_info,windows,*image,exception);
7219       if (status == MagickFalse)
7220         {
7221           XNoticeWidget(display,windows,"Unable to print X image:",
7222             (*image)->filename);
7223           break;
7224         }
7225       break;
7226     }
7227     case DeleteCommand:
7228     {
7229       static char
7230         filename[MaxTextExtent] = "\0";
7231
7232       /*
7233         Delete image file.
7234       */
7235       XFileBrowserWidget(display,windows,"Delete",filename);
7236       if (*filename == '\0')
7237         break;
7238       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
7239       if (status != MagickFalse)
7240         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7241       break;
7242     }
7243     case NewCommand:
7244     {
7245       int
7246         status;
7247
7248       static char
7249         color[MaxTextExtent] = "gray",
7250         geometry[MaxTextExtent] = "640x480";
7251
7252       static const char
7253         *format = "gradient";
7254
7255       /*
7256         Query user for canvas geometry.
7257       */
7258       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7259         geometry);
7260       if (*geometry == '\0')
7261         break;
7262       if (status == 0)
7263         format="xc";
7264       XColorBrowserWidget(display,windows,"Select",color);
7265       if (*color == '\0')
7266         break;
7267       /*
7268         Create canvas.
7269       */
7270       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7271         "%s:%s",format,color);
7272       (void) CloneString(&image_info->size,geometry);
7273       nexus=ReadImage(image_info,exception);
7274       CatchException(exception);
7275       XClientMessage(display,windows->image.id,windows->im_protocols,
7276         windows->im_next_image,CurrentTime);
7277       break;
7278     }
7279     case VisualDirectoryCommand:
7280     {
7281       /*
7282         Visual Image directory.
7283       */
7284       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7285       break;
7286     }
7287     case QuitCommand:
7288     {
7289       /*
7290         exit program.
7291       */
7292       if (resource_info->confirm_exit == MagickFalse)
7293         XClientMessage(display,windows->image.id,windows->im_protocols,
7294           windows->im_exit,CurrentTime);
7295       else
7296         {
7297           int
7298             status;
7299
7300           /*
7301             Confirm program exit.
7302           */
7303           status=XConfirmWidget(display,windows,"Do you really want to exit",
7304             resource_info->client_name);
7305           if (status > 0)
7306             XClientMessage(display,windows->image.id,windows->im_protocols,
7307               windows->im_exit,CurrentTime);
7308         }
7309       break;
7310     }
7311     case CutCommand:
7312     {
7313       /*
7314         Cut image.
7315       */
7316       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7317       break;
7318     }
7319     case CopyCommand:
7320     {
7321       /*
7322         Copy image.
7323       */
7324       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7325         exception);
7326       break;
7327     }
7328     case PasteCommand:
7329     {
7330       /*
7331         Paste image.
7332       */
7333       status=XPasteImage(display,resource_info,windows,*image,exception);
7334       if (status == MagickFalse)
7335         {
7336           XNoticeWidget(display,windows,"Unable to paste X image",
7337             (*image)->filename);
7338           break;
7339         }
7340       break;
7341     }
7342     case HalfSizeCommand:
7343     {
7344       /*
7345         Half image size.
7346       */
7347       windows->image.window_changes.width=windows->image.ximage->width/2;
7348       windows->image.window_changes.height=windows->image.ximage->height/2;
7349       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7350       break;
7351     }
7352     case OriginalSizeCommand:
7353     {
7354       /*
7355         Original image size.
7356       */
7357       windows->image.window_changes.width=(int) (*image)->columns;
7358       windows->image.window_changes.height=(int) (*image)->rows;
7359       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7360       break;
7361     }
7362     case DoubleSizeCommand:
7363     {
7364       /*
7365         Double the image size.
7366       */
7367       windows->image.window_changes.width=windows->image.ximage->width << 1;
7368       windows->image.window_changes.height=windows->image.ximage->height << 1;
7369       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7370       break;
7371     }
7372     case ResizeCommand:
7373     {
7374       int
7375         status;
7376
7377       size_t
7378         height,
7379         width;
7380
7381       ssize_t
7382         x,
7383         y;
7384
7385       /*
7386         Resize image.
7387       */
7388       width=(size_t) windows->image.ximage->width;
7389       height=(size_t) windows->image.ximage->height;
7390       x=0;
7391       y=0;
7392       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7393         (double) width,(double) height);
7394       status=XDialogWidget(display,windows,"Resize",
7395         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7396       if (*geometry == '\0')
7397         break;
7398       if (status == 0)
7399         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7400       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7401       windows->image.window_changes.width=(int) width;
7402       windows->image.window_changes.height=(int) height;
7403       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7404       break;
7405     }
7406     case ApplyCommand:
7407     {
7408       char
7409         image_geometry[MaxTextExtent];
7410
7411       if ((windows->image.crop_geometry == (char *) NULL) &&
7412           ((int) (*image)->columns == windows->image.ximage->width) &&
7413           ((int) (*image)->rows == windows->image.ximage->height))
7414         break;
7415       /*
7416         Apply size transforms to image.
7417       */
7418       XSetCursorState(display,windows,MagickTrue);
7419       XCheckRefreshWindows(display,windows);
7420       /*
7421         Crop and/or scale displayed image.
7422       */
7423       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7424         windows->image.ximage->width,windows->image.ximage->height);
7425       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7426       if (windows->image.crop_geometry != (char *) NULL)
7427         windows->image.crop_geometry=(char *)
7428           RelinquishMagickMemory(windows->image.crop_geometry);
7429       windows->image.x=0;
7430       windows->image.y=0;
7431       XConfigureImageColormap(display,resource_info,windows,*image);
7432       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7433       break;
7434     }
7435     case RefreshCommand:
7436     {
7437       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7438       break;
7439     }
7440     case RestoreCommand:
7441     {
7442       /*
7443         Restore Image window to its original size.
7444       */
7445       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7446           (windows->image.height == (unsigned int) (*image)->rows) &&
7447           (windows->image.crop_geometry == (char *) NULL))
7448         {
7449           (void) XBell(display,0);
7450           break;
7451         }
7452       windows->image.window_changes.width=(int) (*image)->columns;
7453       windows->image.window_changes.height=(int) (*image)->rows;
7454       if (windows->image.crop_geometry != (char *) NULL)
7455         {
7456           windows->image.crop_geometry=(char *)
7457             RelinquishMagickMemory(windows->image.crop_geometry);
7458           windows->image.crop_geometry=(char *) NULL;
7459           windows->image.x=0;
7460           windows->image.y=0;
7461         }
7462       XConfigureImageColormap(display,resource_info,windows,*image);
7463       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7464       break;
7465     }
7466     case CropCommand:
7467     {
7468       /*
7469         Crop image.
7470       */
7471       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7472         exception);
7473       break;
7474     }
7475     case ChopCommand:
7476     {
7477       /*
7478         Chop image.
7479       */
7480       status=XChopImage(display,resource_info,windows,image,exception);
7481       if (status == MagickFalse)
7482         {
7483           XNoticeWidget(display,windows,"Unable to cut X image",
7484             (*image)->filename);
7485           break;
7486         }
7487       break;
7488     }
7489     case FlopCommand:
7490     {
7491       Image
7492         *flop_image;
7493
7494       /*
7495         Flop image scanlines.
7496       */
7497       XSetCursorState(display,windows,MagickTrue);
7498       XCheckRefreshWindows(display,windows);
7499       flop_image=FlopImage(*image,exception);
7500       if (flop_image != (Image *) NULL)
7501         {
7502           *image=DestroyImage(*image);
7503           *image=flop_image;
7504         }
7505       CatchException(exception);
7506       XSetCursorState(display,windows,MagickFalse);
7507       if (windows->image.crop_geometry != (char *) NULL)
7508         {
7509           /*
7510             Flop crop geometry.
7511           */
7512           width=(unsigned int) (*image)->columns;
7513           height=(unsigned int) (*image)->rows;
7514           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7515             &width,&height);
7516           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7517             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7518         }
7519       if (windows->image.orphan != MagickFalse)
7520         break;
7521       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7522       break;
7523     }
7524     case FlipCommand:
7525     {
7526       Image
7527         *flip_image;
7528
7529       /*
7530         Flip image scanlines.
7531       */
7532       XSetCursorState(display,windows,MagickTrue);
7533       XCheckRefreshWindows(display,windows);
7534       flip_image=FlipImage(*image,exception);
7535       if (flip_image != (Image *) NULL)
7536         {
7537           *image=DestroyImage(*image);
7538           *image=flip_image;
7539         }
7540       CatchException(exception);
7541       XSetCursorState(display,windows,MagickFalse);
7542       if (windows->image.crop_geometry != (char *) NULL)
7543         {
7544           /*
7545             Flip crop geometry.
7546           */
7547           width=(unsigned int) (*image)->columns;
7548           height=(unsigned int) (*image)->rows;
7549           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7550             &width,&height);
7551           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7552             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7553         }
7554       if (windows->image.orphan != MagickFalse)
7555         break;
7556       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7557       break;
7558     }
7559     case RotateRightCommand:
7560     {
7561       /*
7562         Rotate image 90 degrees clockwise.
7563       */
7564       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7565       if (status == MagickFalse)
7566         {
7567           XNoticeWidget(display,windows,"Unable to rotate X image",
7568             (*image)->filename);
7569           break;
7570         }
7571       break;
7572     }
7573     case RotateLeftCommand:
7574     {
7575       /*
7576         Rotate image 90 degrees counter-clockwise.
7577       */
7578       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7579       if (status == MagickFalse)
7580         {
7581           XNoticeWidget(display,windows,"Unable to rotate X image",
7582             (*image)->filename);
7583           break;
7584         }
7585       break;
7586     }
7587     case RotateCommand:
7588     {
7589       /*
7590         Rotate image.
7591       */
7592       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7593       if (status == MagickFalse)
7594         {
7595           XNoticeWidget(display,windows,"Unable to rotate X image",
7596             (*image)->filename);
7597           break;
7598         }
7599       break;
7600     }
7601     case ShearCommand:
7602     {
7603       Image
7604         *shear_image;
7605
7606       static char
7607         geometry[MaxTextExtent] = "45.0x45.0";
7608
7609       /*
7610         Query user for shear color and geometry.
7611       */
7612       XColorBrowserWidget(display,windows,"Select",color);
7613       if (*color == '\0')
7614         break;
7615       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7616         geometry);
7617       if (*geometry == '\0')
7618         break;
7619       /*
7620         Shear image.
7621       */
7622       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7623         exception);
7624       XSetCursorState(display,windows,MagickTrue);
7625       XCheckRefreshWindows(display,windows);
7626       (void) QueryColorDatabase(color,&(*image)->background_color,
7627         exception);
7628       flags=ParseGeometry(geometry,&geometry_info);
7629       if ((flags & SigmaValue) == 0)
7630         geometry_info.sigma=geometry_info.rho;
7631       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7632         exception);
7633       if (shear_image != (Image *) NULL)
7634         {
7635           *image=DestroyImage(*image);
7636           *image=shear_image;
7637         }
7638       CatchException(exception);
7639       XSetCursorState(display,windows,MagickFalse);
7640       if (windows->image.orphan != MagickFalse)
7641         break;
7642       windows->image.window_changes.width=(int) (*image)->columns;
7643       windows->image.window_changes.height=(int) (*image)->rows;
7644       XConfigureImageColormap(display,resource_info,windows,*image);
7645       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7646       break;
7647     }
7648     case RollCommand:
7649     {
7650       Image
7651         *roll_image;
7652
7653       static char
7654         geometry[MaxTextExtent] = "+2+2";
7655
7656       /*
7657         Query user for the roll geometry.
7658       */
7659       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7660         geometry);
7661       if (*geometry == '\0')
7662         break;
7663       /*
7664         Roll image.
7665       */
7666       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7667         exception);
7668       XSetCursorState(display,windows,MagickTrue);
7669       XCheckRefreshWindows(display,windows);
7670       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7671         exception);
7672       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7673         exception);
7674       if (roll_image != (Image *) NULL)
7675         {
7676           *image=DestroyImage(*image);
7677           *image=roll_image;
7678         }
7679       CatchException(exception);
7680       XSetCursorState(display,windows,MagickFalse);
7681       if (windows->image.orphan != MagickFalse)
7682         break;
7683       windows->image.window_changes.width=(int) (*image)->columns;
7684       windows->image.window_changes.height=(int) (*image)->rows;
7685       XConfigureImageColormap(display,resource_info,windows,*image);
7686       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7687       break;
7688     }
7689     case TrimCommand:
7690     {
7691       static char
7692         fuzz[MaxTextExtent];
7693
7694       /*
7695         Query user for the fuzz factor.
7696       */
7697       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7698         (*image)->fuzz/(QuantumRange+1.0));
7699       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7700       if (*fuzz == '\0')
7701         break;
7702       (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7703       /*
7704         Trim image.
7705       */
7706       status=XTrimImage(display,resource_info,windows,*image,exception);
7707       if (status == MagickFalse)
7708         {
7709           XNoticeWidget(display,windows,"Unable to trim X image",
7710             (*image)->filename);
7711           break;
7712         }
7713       break;
7714     }
7715     case HueCommand:
7716     {
7717       static char
7718         hue_percent[MaxTextExtent] = "110";
7719
7720       /*
7721         Query user for percent hue change.
7722       */
7723       (void) XDialogWidget(display,windows,"Apply",
7724         "Enter percent change in image hue (0-200):",hue_percent);
7725       if (*hue_percent == '\0')
7726         break;
7727       /*
7728         Vary the image hue.
7729       */
7730       XSetCursorState(display,windows,MagickTrue);
7731       XCheckRefreshWindows(display,windows);
7732       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7733       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7734         MaxTextExtent);
7735       (void) ModulateImage(*image,modulate_factors,exception);
7736       XSetCursorState(display,windows,MagickFalse);
7737       if (windows->image.orphan != MagickFalse)
7738         break;
7739       XConfigureImageColormap(display,resource_info,windows,*image);
7740       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7741       break;
7742     }
7743     case SaturationCommand:
7744     {
7745       static char
7746         saturation_percent[MaxTextExtent] = "110";
7747
7748       /*
7749         Query user for percent saturation change.
7750       */
7751       (void) XDialogWidget(display,windows,"Apply",
7752         "Enter percent change in color saturation (0-200):",saturation_percent);
7753       if (*saturation_percent == '\0')
7754         break;
7755       /*
7756         Vary color saturation.
7757       */
7758       XSetCursorState(display,windows,MagickTrue);
7759       XCheckRefreshWindows(display,windows);
7760       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7761       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7762         MaxTextExtent);
7763       (void) ModulateImage(*image,modulate_factors,exception);
7764       XSetCursorState(display,windows,MagickFalse);
7765       if (windows->image.orphan != MagickFalse)
7766         break;
7767       XConfigureImageColormap(display,resource_info,windows,*image);
7768       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7769       break;
7770     }
7771     case BrightnessCommand:
7772     {
7773       static char
7774         brightness_percent[MaxTextExtent] = "110";
7775
7776       /*
7777         Query user for percent brightness change.
7778       */
7779       (void) XDialogWidget(display,windows,"Apply",
7780         "Enter percent change in color brightness (0-200):",brightness_percent);
7781       if (*brightness_percent == '\0')
7782         break;
7783       /*
7784         Vary the color brightness.
7785       */
7786       XSetCursorState(display,windows,MagickTrue);
7787       XCheckRefreshWindows(display,windows);
7788       (void) CopyMagickString(modulate_factors,brightness_percent,
7789         MaxTextExtent);
7790       (void) ModulateImage(*image,modulate_factors,exception);
7791       XSetCursorState(display,windows,MagickFalse);
7792       if (windows->image.orphan != MagickFalse)
7793         break;
7794       XConfigureImageColormap(display,resource_info,windows,*image);
7795       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7796       break;
7797     }
7798     case GammaCommand:
7799     {
7800       static char
7801         factor[MaxTextExtent] = "1.6";
7802
7803       /*
7804         Query user for gamma value.
7805       */
7806       (void) XDialogWidget(display,windows,"Gamma",
7807         "Enter gamma value (e.g. 1.2):",factor);
7808       if (*factor == '\0')
7809         break;
7810       /*
7811         Gamma correct image.
7812       */
7813       XSetCursorState(display,windows,MagickTrue);
7814       XCheckRefreshWindows(display,windows);
7815       (void) GammaImage(*image,atof(factor),exception);
7816       XSetCursorState(display,windows,MagickFalse);
7817       if (windows->image.orphan != MagickFalse)
7818         break;
7819       XConfigureImageColormap(display,resource_info,windows,*image);
7820       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7821       break;
7822     }
7823     case SpiffCommand:
7824     {
7825       /*
7826         Sharpen the image contrast.
7827       */
7828       XSetCursorState(display,windows,MagickTrue);
7829       XCheckRefreshWindows(display,windows);
7830       (void) ContrastImage(*image,MagickTrue,exception);
7831       XSetCursorState(display,windows,MagickFalse);
7832       if (windows->image.orphan != MagickFalse)
7833         break;
7834       XConfigureImageColormap(display,resource_info,windows,*image);
7835       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7836       break;
7837     }
7838     case DullCommand:
7839     {
7840       /*
7841         Dull the image contrast.
7842       */
7843       XSetCursorState(display,windows,MagickTrue);
7844       XCheckRefreshWindows(display,windows);
7845       (void) ContrastImage(*image,MagickFalse,exception);
7846       XSetCursorState(display,windows,MagickFalse);
7847       if (windows->image.orphan != MagickFalse)
7848         break;
7849       XConfigureImageColormap(display,resource_info,windows,*image);
7850       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7851       break;
7852     }
7853     case ContrastStretchCommand:
7854     {
7855       double
7856         black_point,
7857         white_point;
7858
7859       static char
7860         levels[MaxTextExtent] = "1%";
7861
7862       /*
7863         Query user for gamma value.
7864       */
7865       (void) XDialogWidget(display,windows,"Contrast Stretch",
7866         "Enter black and white points:",levels);
7867       if (*levels == '\0')
7868         break;
7869       /*
7870         Contrast stretch image.
7871       */
7872       XSetCursorState(display,windows,MagickTrue);
7873       XCheckRefreshWindows(display,windows);
7874       flags=ParseGeometry(levels,&geometry_info);
7875       black_point=geometry_info.rho;
7876       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7877       if ((flags & PercentValue) != 0)
7878         {
7879           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7880           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7881         }
7882       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7883       (void) ContrastStretchImage(*image,black_point,white_point,
7884         exception);
7885       XSetCursorState(display,windows,MagickFalse);
7886       if (windows->image.orphan != MagickFalse)
7887         break;
7888       XConfigureImageColormap(display,resource_info,windows,*image);
7889       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7890       break;
7891     }
7892     case SigmoidalContrastCommand:
7893     {
7894       GeometryInfo
7895         geometry_info;
7896
7897       MagickStatusType
7898         flags;
7899
7900       static char
7901         levels[MaxTextExtent] = "3x50%";
7902
7903       /*
7904         Query user for gamma value.
7905       */
7906       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7907         "Enter contrast and midpoint:",levels);
7908       if (*levels == '\0')
7909         break;
7910       /*
7911         Contrast stretch image.
7912       */
7913       XSetCursorState(display,windows,MagickTrue);
7914       XCheckRefreshWindows(display,windows);
7915       flags=ParseGeometry(levels,&geometry_info);
7916       if ((flags & SigmaValue) == 0)
7917         geometry_info.sigma=1.0*QuantumRange/2.0;
7918       if ((flags & PercentValue) != 0)
7919         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7920       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7921         geometry_info.sigma,exception);
7922       XSetCursorState(display,windows,MagickFalse);
7923       if (windows->image.orphan != MagickFalse)
7924         break;
7925       XConfigureImageColormap(display,resource_info,windows,*image);
7926       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7927       break;
7928     }
7929     case NormalizeCommand:
7930     {
7931       /*
7932         Perform histogram normalization on the image.
7933       */
7934       XSetCursorState(display,windows,MagickTrue);
7935       XCheckRefreshWindows(display,windows);
7936       (void) NormalizeImage(*image,exception);
7937       XSetCursorState(display,windows,MagickFalse);
7938       if (windows->image.orphan != MagickFalse)
7939         break;
7940       XConfigureImageColormap(display,resource_info,windows,*image);
7941       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7942       break;
7943     }
7944     case EqualizeCommand:
7945     {
7946       /*
7947         Perform histogram equalization on the image.
7948       */
7949       XSetCursorState(display,windows,MagickTrue);
7950       XCheckRefreshWindows(display,windows);
7951       (void) EqualizeImage(*image,exception);
7952       XSetCursorState(display,windows,MagickFalse);
7953       if (windows->image.orphan != MagickFalse)
7954         break;
7955       XConfigureImageColormap(display,resource_info,windows,*image);
7956       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7957       break;
7958     }
7959     case NegateCommand:
7960     {
7961       /*
7962         Negate colors in image.
7963       */
7964       XSetCursorState(display,windows,MagickTrue);
7965       XCheckRefreshWindows(display,windows);
7966       (void) NegateImage(*image,MagickFalse,exception);
7967       XSetCursorState(display,windows,MagickFalse);
7968       if (windows->image.orphan != MagickFalse)
7969         break;
7970       XConfigureImageColormap(display,resource_info,windows,*image);
7971       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7972       break;
7973     }
7974     case GrayscaleCommand:
7975     {
7976       /*
7977         Convert image to grayscale.
7978       */
7979       XSetCursorState(display,windows,MagickTrue);
7980       XCheckRefreshWindows(display,windows);
7981       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7982         GrayscaleType : GrayscaleMatteType);
7983       XSetCursorState(display,windows,MagickFalse);
7984       if (windows->image.orphan != MagickFalse)
7985         break;
7986       XConfigureImageColormap(display,resource_info,windows,*image);
7987       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7988       break;
7989     }
7990     case MapCommand:
7991     {
7992       Image
7993         *affinity_image;
7994
7995       static char
7996         filename[MaxTextExtent] = "\0";
7997
7998       /*
7999         Request image file name from user.
8000       */
8001       XFileBrowserWidget(display,windows,"Map",filename);
8002       if (*filename == '\0')
8003         break;
8004       /*
8005         Map image.
8006       */
8007       XSetCursorState(display,windows,MagickTrue);
8008       XCheckRefreshWindows(display,windows);
8009       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8010       affinity_image=ReadImage(image_info,exception);
8011       if (affinity_image != (Image *) NULL)
8012         {
8013           (void) RemapImage(&quantize_info,*image,affinity_image);
8014           affinity_image=DestroyImage(affinity_image);
8015         }
8016       CatchException(exception);
8017       XSetCursorState(display,windows,MagickFalse);
8018       if (windows->image.orphan != MagickFalse)
8019         break;
8020       XConfigureImageColormap(display,resource_info,windows,*image);
8021       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8022       break;
8023     }
8024     case QuantizeCommand:
8025     {
8026       int
8027         status;
8028
8029       static char
8030         colors[MaxTextExtent] = "256";
8031
8032       /*
8033         Query user for maximum number of colors.
8034       */
8035       status=XDialogWidget(display,windows,"Quantize",
8036         "Maximum number of colors:",colors);
8037       if (*colors == '\0')
8038         break;
8039       /*
8040         Color reduce the image.
8041       */
8042       XSetCursorState(display,windows,MagickTrue);
8043       XCheckRefreshWindows(display,windows);
8044       quantize_info.number_colors=StringToUnsignedLong(colors);
8045       quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8046       (void) QuantizeImage(&quantize_info,*image);
8047       XSetCursorState(display,windows,MagickFalse);
8048       if (windows->image.orphan != MagickFalse)
8049         break;
8050       XConfigureImageColormap(display,resource_info,windows,*image);
8051       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8052       break;
8053     }
8054     case DespeckleCommand:
8055     {
8056       Image
8057         *despeckle_image;
8058
8059       /*
8060         Despeckle image.
8061       */
8062       XSetCursorState(display,windows,MagickTrue);
8063       XCheckRefreshWindows(display,windows);
8064       despeckle_image=DespeckleImage(*image,exception);
8065       if (despeckle_image != (Image *) NULL)
8066         {
8067           *image=DestroyImage(*image);
8068           *image=despeckle_image;
8069         }
8070       CatchException(exception);
8071       XSetCursorState(display,windows,MagickFalse);
8072       if (windows->image.orphan != MagickFalse)
8073         break;
8074       XConfigureImageColormap(display,resource_info,windows,*image);
8075       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8076       break;
8077     }
8078     case EmbossCommand:
8079     {
8080       Image
8081         *emboss_image;
8082
8083       static char
8084         radius[MaxTextExtent] = "0.0x1.0";
8085
8086       /*
8087         Query user for emboss radius.
8088       */
8089       (void) XDialogWidget(display,windows,"Emboss",
8090         "Enter the emboss radius and standard deviation:",radius);
8091       if (*radius == '\0')
8092         break;
8093       /*
8094         Reduce noise in the image.
8095       */
8096       XSetCursorState(display,windows,MagickTrue);
8097       XCheckRefreshWindows(display,windows);
8098       flags=ParseGeometry(radius,&geometry_info);
8099       if ((flags & SigmaValue) == 0)
8100         geometry_info.sigma=1.0;
8101       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8102         exception);
8103       if (emboss_image != (Image *) NULL)
8104         {
8105           *image=DestroyImage(*image);
8106           *image=emboss_image;
8107         }
8108       CatchException(exception);
8109       XSetCursorState(display,windows,MagickFalse);
8110       if (windows->image.orphan != MagickFalse)
8111         break;
8112       XConfigureImageColormap(display,resource_info,windows,*image);
8113       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8114       break;
8115     }
8116     case ReduceNoiseCommand:
8117     {
8118       Image
8119         *noise_image;
8120
8121       static char
8122         radius[MaxTextExtent] = "0";
8123
8124       /*
8125         Query user for noise radius.
8126       */
8127       (void) XDialogWidget(display,windows,"Reduce Noise",
8128         "Enter the noise radius:",radius);
8129       if (*radius == '\0')
8130         break;
8131       /*
8132         Reduce noise in the image.
8133       */
8134       XSetCursorState(display,windows,MagickTrue);
8135       XCheckRefreshWindows(display,windows);
8136       flags=ParseGeometry(radius,&geometry_info);
8137       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8138         geometry_info.rho,(size_t) geometry_info.rho,exception);
8139       if (noise_image != (Image *) NULL)
8140         {
8141           *image=DestroyImage(*image);
8142           *image=noise_image;
8143         }
8144       CatchException(exception);
8145       XSetCursorState(display,windows,MagickFalse);
8146       if (windows->image.orphan != MagickFalse)
8147         break;
8148       XConfigureImageColormap(display,resource_info,windows,*image);
8149       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8150       break;
8151     }
8152     case AddNoiseCommand:
8153     {
8154       char
8155         **noises;
8156
8157       Image
8158         *noise_image;
8159
8160       static char
8161         noise_type[MaxTextExtent] = "Gaussian";
8162
8163       /*
8164         Add noise to the image.
8165       */
8166       noises=GetCommandOptions(MagickNoiseOptions);
8167       if (noises == (char **) NULL)
8168         break;
8169       XListBrowserWidget(display,windows,&windows->widget,
8170         (const char **) noises,"Add Noise",
8171         "Select a type of noise to add to your image:",noise_type);
8172       noises=DestroyStringList(noises);
8173       if (*noise_type == '\0')
8174         break;
8175       XSetCursorState(display,windows,MagickTrue);
8176       XCheckRefreshWindows(display,windows);
8177       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8178         MagickNoiseOptions,MagickFalse,noise_type),exception);
8179       if (noise_image != (Image *) NULL)
8180         {
8181           *image=DestroyImage(*image);
8182           *image=noise_image;
8183         }
8184       CatchException(exception);
8185       XSetCursorState(display,windows,MagickFalse);
8186       if (windows->image.orphan != MagickFalse)
8187         break;
8188       XConfigureImageColormap(display,resource_info,windows,*image);
8189       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8190       break;
8191     }
8192     case SharpenCommand:
8193     {
8194       Image
8195         *sharp_image;
8196
8197       static char
8198         radius[MaxTextExtent] = "0.0x1.0";
8199
8200       /*
8201         Query user for sharpen radius.
8202       */
8203       (void) XDialogWidget(display,windows,"Sharpen",
8204         "Enter the sharpen radius and standard deviation:",radius);
8205       if (*radius == '\0')
8206         break;
8207       /*
8208         Sharpen image scanlines.
8209       */
8210       XSetCursorState(display,windows,MagickTrue);
8211       XCheckRefreshWindows(display,windows);
8212       flags=ParseGeometry(radius,&geometry_info);
8213       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8214         exception);
8215       if (sharp_image != (Image *) NULL)
8216         {
8217           *image=DestroyImage(*image);
8218           *image=sharp_image;
8219         }
8220       CatchException(exception);
8221       XSetCursorState(display,windows,MagickFalse);
8222       if (windows->image.orphan != MagickFalse)
8223         break;
8224       XConfigureImageColormap(display,resource_info,windows,*image);
8225       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8226       break;
8227     }
8228     case BlurCommand:
8229     {
8230       Image
8231         *blur_image;
8232
8233       static char
8234         radius[MaxTextExtent] = "0.0x1.0";
8235
8236       /*
8237         Query user for blur radius.
8238       */
8239       (void) XDialogWidget(display,windows,"Blur",
8240         "Enter the blur radius and standard deviation:",radius);
8241       if (*radius == '\0')
8242         break;
8243       /*
8244         Blur an image.
8245       */
8246       XSetCursorState(display,windows,MagickTrue);
8247       XCheckRefreshWindows(display,windows);
8248       flags=ParseGeometry(radius,&geometry_info);
8249       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8250         exception);
8251       if (blur_image != (Image *) NULL)
8252         {
8253           *image=DestroyImage(*image);
8254           *image=blur_image;
8255         }
8256       CatchException(exception);
8257       XSetCursorState(display,windows,MagickFalse);
8258       if (windows->image.orphan != MagickFalse)
8259         break;
8260       XConfigureImageColormap(display,resource_info,windows,*image);
8261       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8262       break;
8263     }
8264     case ThresholdCommand:
8265     {
8266       double
8267         threshold;
8268
8269       static char
8270         factor[MaxTextExtent] = "128";
8271
8272       /*
8273         Query user for threshold value.
8274       */
8275       (void) XDialogWidget(display,windows,"Threshold",
8276         "Enter threshold value:",factor);
8277       if (*factor == '\0')
8278         break;
8279       /*
8280         Gamma correct image.
8281       */
8282       XSetCursorState(display,windows,MagickTrue);
8283       XCheckRefreshWindows(display,windows);
8284       threshold=SiPrefixToDouble(factor,QuantumRange);
8285       (void) BilevelImage(*image,threshold);
8286       XSetCursorState(display,windows,MagickFalse);
8287       if (windows->image.orphan != MagickFalse)
8288         break;
8289       XConfigureImageColormap(display,resource_info,windows,*image);
8290       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8291       break;
8292     }
8293     case EdgeDetectCommand:
8294     {
8295       Image
8296         *edge_image;
8297
8298       static char
8299         radius[MaxTextExtent] = "0";
8300
8301       /*
8302         Query user for edge factor.
8303       */
8304       (void) XDialogWidget(display,windows,"Detect Edges",
8305         "Enter the edge detect radius:",radius);
8306       if (*radius == '\0')
8307         break;
8308       /*
8309         Detect edge in image.
8310       */
8311       XSetCursorState(display,windows,MagickTrue);
8312       XCheckRefreshWindows(display,windows);
8313       flags=ParseGeometry(radius,&geometry_info);
8314       edge_image=EdgeImage(*image,geometry_info.rho,exception);
8315       if (edge_image != (Image *) NULL)
8316         {
8317           *image=DestroyImage(*image);
8318           *image=edge_image;
8319         }
8320       CatchException(exception);
8321       XSetCursorState(display,windows,MagickFalse);
8322       if (windows->image.orphan != MagickFalse)
8323         break;
8324       XConfigureImageColormap(display,resource_info,windows,*image);
8325       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8326       break;
8327     }
8328     case SpreadCommand:
8329     {
8330       Image
8331         *spread_image;
8332
8333       static char
8334         amount[MaxTextExtent] = "2";
8335
8336       /*
8337         Query user for spread amount.
8338       */
8339       (void) XDialogWidget(display,windows,"Spread",
8340         "Enter the displacement amount:",amount);
8341       if (*amount == '\0')
8342         break;
8343       /*
8344         Displace image pixels by a random amount.
8345       */
8346       XSetCursorState(display,windows,MagickTrue);
8347       XCheckRefreshWindows(display,windows);
8348       flags=ParseGeometry(amount,&geometry_info);
8349       spread_image=EdgeImage(*image,geometry_info.rho,exception);
8350       if (spread_image != (Image *) NULL)
8351         {
8352           *image=DestroyImage(*image);
8353           *image=spread_image;
8354         }
8355       CatchException(exception);
8356       XSetCursorState(display,windows,MagickFalse);
8357       if (windows->image.orphan != MagickFalse)
8358         break;
8359       XConfigureImageColormap(display,resource_info,windows,*image);
8360       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8361       break;
8362     }
8363     case ShadeCommand:
8364     {
8365       Image
8366         *shade_image;
8367
8368       int
8369         status;
8370
8371       static char
8372         geometry[MaxTextExtent] = "30x30";
8373
8374       /*
8375         Query user for the shade geometry.
8376       */
8377       status=XDialogWidget(display,windows,"Shade",
8378         "Enter the azimuth and elevation of the light source:",geometry);
8379       if (*geometry == '\0')
8380         break;
8381       /*
8382         Shade image pixels.
8383       */
8384       XSetCursorState(display,windows,MagickTrue);
8385       XCheckRefreshWindows(display,windows);
8386       flags=ParseGeometry(geometry,&geometry_info);
8387       if ((flags & SigmaValue) == 0)
8388         geometry_info.sigma=1.0;
8389       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8390         geometry_info.rho,geometry_info.sigma,exception);
8391       if (shade_image != (Image *) NULL)
8392         {
8393           *image=DestroyImage(*image);
8394           *image=shade_image;
8395         }
8396       CatchException(exception);
8397       XSetCursorState(display,windows,MagickFalse);
8398       if (windows->image.orphan != MagickFalse)
8399         break;
8400       XConfigureImageColormap(display,resource_info,windows,*image);
8401       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8402       break;
8403     }
8404     case RaiseCommand:
8405     {
8406       static char
8407         bevel_width[MaxTextExtent] = "10";
8408
8409       /*
8410         Query user for bevel width.
8411       */
8412       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8413       if (*bevel_width == '\0')
8414         break;
8415       /*
8416         Raise an image.
8417       */
8418       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8419         exception);
8420       XSetCursorState(display,windows,MagickTrue);
8421       XCheckRefreshWindows(display,windows);
8422       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8423         exception);
8424       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8425       XSetCursorState(display,windows,MagickFalse);
8426       if (windows->image.orphan != MagickFalse)
8427         break;
8428       XConfigureImageColormap(display,resource_info,windows,*image);
8429       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8430       break;
8431     }
8432     case SegmentCommand:
8433     {
8434       static char
8435         threshold[MaxTextExtent] = "1.0x1.5";
8436
8437       /*
8438         Query user for smoothing threshold.
8439       */
8440       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8441         threshold);
8442       if (*threshold == '\0')
8443         break;
8444       /*
8445         Segment an image.
8446       */
8447       XSetCursorState(display,windows,MagickTrue);
8448       XCheckRefreshWindows(display,windows);
8449       flags=ParseGeometry(threshold,&geometry_info);
8450       if ((flags & SigmaValue) == 0)
8451         geometry_info.sigma=1.0;
8452       (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8453         geometry_info.sigma);
8454       XSetCursorState(display,windows,MagickFalse);
8455       if (windows->image.orphan != MagickFalse)
8456         break;
8457       XConfigureImageColormap(display,resource_info,windows,*image);
8458       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8459       break;
8460     }
8461     case SepiaToneCommand:
8462     {
8463       double
8464         threshold;
8465
8466       Image
8467         *sepia_image;
8468
8469       static char
8470         factor[MaxTextExtent] = "80%";
8471
8472       /*
8473         Query user for sepia-tone factor.
8474       */
8475       (void) XDialogWidget(display,windows,"Sepia Tone",
8476         "Enter the sepia tone factor (0 - 99.9%):",factor);
8477       if (*factor == '\0')
8478         break;
8479       /*
8480         Sepia tone image pixels.
8481       */
8482       XSetCursorState(display,windows,MagickTrue);
8483       XCheckRefreshWindows(display,windows);
8484       threshold=SiPrefixToDouble(factor,QuantumRange);
8485       sepia_image=SepiaToneImage(*image,threshold,exception);
8486       if (sepia_image != (Image *) NULL)
8487         {
8488           *image=DestroyImage(*image);
8489           *image=sepia_image;
8490         }
8491       CatchException(exception);
8492       XSetCursorState(display,windows,MagickFalse);
8493       if (windows->image.orphan != MagickFalse)
8494         break;
8495       XConfigureImageColormap(display,resource_info,windows,*image);
8496       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8497       break;
8498     }
8499     case SolarizeCommand:
8500     {
8501       double
8502         threshold;
8503
8504       static char
8505         factor[MaxTextExtent] = "60%";
8506
8507       /*
8508         Query user for solarize factor.
8509       */
8510       (void) XDialogWidget(display,windows,"Solarize",
8511         "Enter the solarize factor (0 - 99.9%):",factor);
8512       if (*factor == '\0')
8513         break;
8514       /*
8515         Solarize image pixels.
8516       */
8517       XSetCursorState(display,windows,MagickTrue);
8518       XCheckRefreshWindows(display,windows);
8519       threshold=SiPrefixToDouble(factor,QuantumRange);
8520       (void) SolarizeImage(*image,threshold,exception);
8521       XSetCursorState(display,windows,MagickFalse);
8522       if (windows->image.orphan != MagickFalse)
8523         break;
8524       XConfigureImageColormap(display,resource_info,windows,*image);
8525       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8526       break;
8527     }
8528     case SwirlCommand:
8529     {
8530       Image
8531         *swirl_image;
8532
8533       static char
8534         degrees[MaxTextExtent] = "60";
8535
8536       /*
8537         Query user for swirl angle.
8538       */
8539       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8540         degrees);
8541       if (*degrees == '\0')
8542         break;
8543       /*
8544         Swirl image pixels about the center.
8545       */
8546       XSetCursorState(display,windows,MagickTrue);
8547       XCheckRefreshWindows(display,windows);
8548       flags=ParseGeometry(degrees,&geometry_info);
8549       swirl_image=SwirlImage(*image,geometry_info.rho,exception);
8550       if (swirl_image != (Image *) NULL)
8551         {
8552           *image=DestroyImage(*image);
8553           *image=swirl_image;
8554         }
8555       CatchException(exception);
8556       XSetCursorState(display,windows,MagickFalse);
8557       if (windows->image.orphan != MagickFalse)
8558         break;
8559       XConfigureImageColormap(display,resource_info,windows,*image);
8560       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8561       break;
8562     }
8563     case ImplodeCommand:
8564     {
8565       Image
8566         *implode_image;
8567
8568       static char
8569         factor[MaxTextExtent] = "0.3";
8570
8571       /*
8572         Query user for implode factor.
8573       */
8574       (void) XDialogWidget(display,windows,"Implode",
8575         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8576       if (*factor == '\0')
8577         break;
8578       /*
8579         Implode image pixels about the center.
8580       */
8581       XSetCursorState(display,windows,MagickTrue);
8582       XCheckRefreshWindows(display,windows);
8583       flags=ParseGeometry(factor,&geometry_info);
8584       implode_image=ImplodeImage(*image,geometry_info.rho,exception);
8585       if (implode_image != (Image *) NULL)
8586         {
8587           *image=DestroyImage(*image);
8588           *image=implode_image;
8589         }
8590       CatchException(exception);
8591       XSetCursorState(display,windows,MagickFalse);
8592       if (windows->image.orphan != MagickFalse)
8593         break;
8594       XConfigureImageColormap(display,resource_info,windows,*image);
8595       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8596       break;
8597     }
8598     case VignetteCommand:
8599     {
8600       Image
8601         *vignette_image;
8602
8603       static char
8604         geometry[MaxTextExtent] = "0x20";
8605
8606       /*
8607         Query user for the vignette geometry.
8608       */
8609       (void) XDialogWidget(display,windows,"Vignette",
8610         "Enter the radius, sigma, and x and y offsets:",geometry);
8611       if (*geometry == '\0')
8612         break;
8613       /*
8614         Soften the edges of the image in vignette style
8615       */
8616       XSetCursorState(display,windows,MagickTrue);
8617       XCheckRefreshWindows(display,windows);
8618       flags=ParseGeometry(geometry,&geometry_info);
8619       if ((flags & SigmaValue) == 0)
8620         geometry_info.sigma=1.0;
8621       if ((flags & XiValue) == 0)
8622         geometry_info.xi=0.1*(*image)->columns;
8623       if ((flags & PsiValue) == 0)
8624         geometry_info.psi=0.1*(*image)->rows;
8625       vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8626         (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8627         0.5),exception);
8628       if (vignette_image != (Image *) NULL)
8629         {
8630           *image=DestroyImage(*image);
8631           *image=vignette_image;
8632         }
8633       CatchException(exception);
8634       XSetCursorState(display,windows,MagickFalse);
8635       if (windows->image.orphan != MagickFalse)
8636         break;
8637       XConfigureImageColormap(display,resource_info,windows,*image);
8638       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8639       break;
8640     }
8641     case WaveCommand:
8642     {
8643       Image
8644         *wave_image;
8645
8646       static char
8647         geometry[MaxTextExtent] = "25x150";
8648
8649       /*
8650         Query user for the wave geometry.
8651       */
8652       (void) XDialogWidget(display,windows,"Wave",
8653         "Enter the amplitude and length of the wave:",geometry);
8654       if (*geometry == '\0')
8655         break;
8656       /*
8657         Alter an image along a sine wave.
8658       */
8659       XSetCursorState(display,windows,MagickTrue);
8660       XCheckRefreshWindows(display,windows);
8661       flags=ParseGeometry(geometry,&geometry_info);
8662       if ((flags & SigmaValue) == 0)
8663         geometry_info.sigma=1.0;
8664       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8665         exception);
8666       if (wave_image != (Image *) NULL)
8667         {
8668           *image=DestroyImage(*image);
8669           *image=wave_image;
8670         }
8671       CatchException(exception);
8672       XSetCursorState(display,windows,MagickFalse);
8673       if (windows->image.orphan != MagickFalse)
8674         break;
8675       XConfigureImageColormap(display,resource_info,windows,*image);
8676       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8677       break;
8678     }
8679     case OilPaintCommand:
8680     {
8681       Image
8682         *paint_image;
8683
8684       static char
8685         radius[MaxTextExtent] = "0";
8686
8687       /*
8688         Query user for circular neighborhood radius.
8689       */
8690       (void) XDialogWidget(display,windows,"Oil Paint",
8691         "Enter the mask radius:",radius);
8692       if (*radius == '\0')
8693         break;
8694       /*
8695         OilPaint image scanlines.
8696       */
8697       XSetCursorState(display,windows,MagickTrue);
8698       XCheckRefreshWindows(display,windows);
8699       flags=ParseGeometry(radius,&geometry_info);
8700       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8701         exception);
8702       if (paint_image != (Image *) NULL)
8703         {
8704           *image=DestroyImage(*image);
8705           *image=paint_image;
8706         }
8707       CatchException(exception);
8708       XSetCursorState(display,windows,MagickFalse);
8709       if (windows->image.orphan != MagickFalse)
8710         break;
8711       XConfigureImageColormap(display,resource_info,windows,*image);
8712       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8713       break;
8714     }
8715     case CharcoalDrawCommand:
8716     {
8717       Image
8718         *charcoal_image;
8719
8720       static char
8721         radius[MaxTextExtent] = "0x1";
8722
8723       /*
8724         Query user for charcoal radius.
8725       */
8726       (void) XDialogWidget(display,windows,"Charcoal Draw",
8727         "Enter the charcoal radius and sigma:",radius);
8728       if (*radius == '\0')
8729         break;
8730       /*
8731         Charcoal the image.
8732       */
8733       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8734         exception);
8735       XSetCursorState(display,windows,MagickTrue);
8736       XCheckRefreshWindows(display,windows);
8737       flags=ParseGeometry(radius,&geometry_info);
8738       if ((flags & SigmaValue) == 0)
8739         geometry_info.sigma=geometry_info.rho;
8740       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8741         exception);
8742       if (charcoal_image != (Image *) NULL)
8743         {
8744           *image=DestroyImage(*image);
8745           *image=charcoal_image;
8746         }
8747       CatchException(exception);
8748       XSetCursorState(display,windows,MagickFalse);
8749       if (windows->image.orphan != MagickFalse)
8750         break;
8751       XConfigureImageColormap(display,resource_info,windows,*image);
8752       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8753       break;
8754     }
8755     case AnnotateCommand:
8756     {
8757       /*
8758         Annotate the image with text.
8759       */
8760       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8761       if (status == MagickFalse)
8762         {
8763           XNoticeWidget(display,windows,"Unable to annotate X image",
8764             (*image)->filename);
8765           break;
8766         }
8767       break;
8768     }
8769     case DrawCommand:
8770     {
8771       /*
8772         Draw image.
8773       */
8774       status=XDrawEditImage(display,resource_info,windows,image,exception);
8775       if (status == MagickFalse)
8776         {
8777           XNoticeWidget(display,windows,"Unable to draw on the X image",
8778             (*image)->filename);
8779           break;
8780         }
8781       break;
8782     }
8783     case ColorCommand:
8784     {
8785       /*
8786         Color edit.
8787       */
8788       status=XColorEditImage(display,resource_info,windows,image,exception);
8789       if (status == MagickFalse)
8790         {
8791           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8792             (*image)->filename);
8793           break;
8794         }
8795       break;
8796     }
8797     case MatteCommand:
8798     {
8799       /*
8800         Matte edit.
8801       */
8802       status=XMatteEditImage(display,resource_info,windows,image,exception);
8803       if (status == MagickFalse)
8804         {
8805           XNoticeWidget(display,windows,"Unable to matte edit X image",
8806             (*image)->filename);
8807           break;
8808         }
8809       break;
8810     }
8811     case CompositeCommand:
8812     {
8813       /*
8814         Composite image.
8815       */
8816       status=XCompositeImage(display,resource_info,windows,*image,
8817         exception);
8818       if (status == MagickFalse)
8819         {
8820           XNoticeWidget(display,windows,"Unable to composite X image",
8821             (*image)->filename);
8822           break;
8823         }
8824       break;
8825     }
8826     case AddBorderCommand:
8827     {
8828       Image
8829         *border_image;
8830
8831       static char
8832         geometry[MaxTextExtent] = "6x6";
8833
8834       /*
8835         Query user for border color and geometry.
8836       */
8837       XColorBrowserWidget(display,windows,"Select",color);
8838       if (*color == '\0')
8839         break;
8840       (void) XDialogWidget(display,windows,"Add Border",
8841         "Enter border geometry:",geometry);
8842       if (*geometry == '\0')
8843         break;
8844       /*
8845         Add a border to the image.
8846       */
8847       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8848         exception);
8849       XSetCursorState(display,windows,MagickTrue);
8850       XCheckRefreshWindows(display,windows);
8851       (void) QueryColorDatabase(color,&(*image)->border_color,
8852         exception);
8853       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8854         exception);
8855       border_image=BorderImage(*image,&page_geometry,exception);
8856       if (border_image != (Image *) NULL)
8857         {
8858           *image=DestroyImage(*image);
8859           *image=border_image;
8860         }
8861       CatchException(exception);
8862       XSetCursorState(display,windows,MagickFalse);
8863       if (windows->image.orphan != MagickFalse)
8864         break;
8865       windows->image.window_changes.width=(int) (*image)->columns;
8866       windows->image.window_changes.height=(int) (*image)->rows;
8867       XConfigureImageColormap(display,resource_info,windows,*image);
8868       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8869       break;
8870     }
8871     case AddFrameCommand:
8872     {
8873       FrameInfo
8874         frame_info;
8875
8876       Image
8877         *frame_image;
8878
8879       static char
8880         geometry[MaxTextExtent] = "6x6";
8881
8882       /*
8883         Query user for frame color and geometry.
8884       */
8885       XColorBrowserWidget(display,windows,"Select",color);
8886       if (*color == '\0')
8887         break;
8888       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8889         geometry);
8890       if (*geometry == '\0')
8891         break;
8892       /*
8893         Surround image with an ornamental border.
8894       */
8895       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8896         exception);
8897       XSetCursorState(display,windows,MagickTrue);
8898       XCheckRefreshWindows(display,windows);
8899       (void) QueryColorDatabase(color,&(*image)->matte_color,
8900         exception);
8901       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8902         exception);
8903       frame_info.width=page_geometry.width;
8904       frame_info.height=page_geometry.height;
8905       frame_info.outer_bevel=page_geometry.x;
8906       frame_info.inner_bevel=page_geometry.y;
8907       frame_info.x=(ssize_t) frame_info.width;
8908       frame_info.y=(ssize_t) frame_info.height;
8909       frame_info.width=(*image)->columns+2*frame_info.width;
8910       frame_info.height=(*image)->rows+2*frame_info.height;
8911       frame_image=FrameImage(*image,&frame_info,exception);
8912       if (frame_image != (Image *) NULL)
8913         {
8914           *image=DestroyImage(*image);
8915           *image=frame_image;
8916         }
8917       CatchException(exception);
8918       XSetCursorState(display,windows,MagickFalse);
8919       if (windows->image.orphan != MagickFalse)
8920         break;
8921       windows->image.window_changes.width=(int) (*image)->columns;
8922       windows->image.window_changes.height=(int) (*image)->rows;
8923       XConfigureImageColormap(display,resource_info,windows,*image);
8924       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8925       break;
8926     }
8927     case CommentCommand:
8928     {
8929       const char
8930         *value;
8931
8932       FILE
8933         *file;
8934
8935       int
8936         unique_file;
8937
8938       /*
8939         Edit image comment.
8940       */
8941       unique_file=AcquireUniqueFileResource(image_info->filename);
8942       if (unique_file == -1)
8943         XNoticeWidget(display,windows,"Unable to edit image comment",
8944           image_info->filename);
8945       value=GetImageProperty(*image,"comment");
8946       if (value == (char *) NULL)
8947         unique_file=close(unique_file)-1;
8948       else
8949         {
8950           register const char
8951             *p;
8952
8953           file=fdopen(unique_file,"w");
8954           if (file == (FILE *) NULL)
8955             {
8956               XNoticeWidget(display,windows,"Unable to edit image comment",
8957                 image_info->filename);
8958               break;
8959             }
8960           for (p=value; *p != '\0'; p++)
8961             (void) fputc((int) *p,file);
8962           (void) fputc('\n',file);
8963           (void) fclose(file);
8964         }
8965       XSetCursorState(display,windows,MagickTrue);
8966       XCheckRefreshWindows(display,windows);
8967       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8968         exception);
8969       if (status == MagickFalse)
8970         XNoticeWidget(display,windows,"Unable to edit image comment",
8971           (char *) NULL);
8972       else
8973         {
8974           char
8975             *comment;
8976
8977           comment=FileToString(image_info->filename,~0UL,exception);
8978           if (comment != (char *) NULL)
8979             {
8980               (void) SetImageProperty(*image,"comment",comment);
8981               (*image)->taint=MagickTrue;
8982             }
8983         }
8984       (void) RelinquishUniqueFileResource(image_info->filename);
8985       XSetCursorState(display,windows,MagickFalse);
8986       break;
8987     }
8988     case LaunchCommand:
8989     {
8990       /*
8991         Launch program.
8992       */
8993       XSetCursorState(display,windows,MagickTrue);
8994       XCheckRefreshWindows(display,windows);
8995       (void) AcquireUniqueFilename(filename);
8996       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
8997         filename);
8998       status=WriteImage(image_info,*image,exception);
8999       if (status == MagickFalse)
9000         XNoticeWidget(display,windows,"Unable to launch image editor",
9001           (char *) NULL);
9002       else
9003         {
9004           nexus=ReadImage(resource_info->image_info,exception);
9005           CatchException(exception);
9006           XClientMessage(display,windows->image.id,windows->im_protocols,
9007             windows->im_next_image,CurrentTime);
9008         }
9009       (void) RelinquishUniqueFileResource(filename);
9010       XSetCursorState(display,windows,MagickFalse);
9011       break;
9012     }
9013     case RegionofInterestCommand:
9014     {
9015       /*
9016         Apply an image processing technique to a region of interest.
9017       */
9018       (void) XROIImage(display,resource_info,windows,image,exception);
9019       break;
9020     }
9021     case InfoCommand:
9022       break;
9023     case ZoomCommand:
9024     {
9025       /*
9026         Zoom image.
9027       */
9028       if (windows->magnify.mapped != MagickFalse)
9029         (void) XRaiseWindow(display,windows->magnify.id);
9030       else
9031         {
9032           /*
9033             Make magnify image.
9034           */
9035           XSetCursorState(display,windows,MagickTrue);
9036           (void) XMapRaised(display,windows->magnify.id);
9037           XSetCursorState(display,windows,MagickFalse);
9038         }
9039       break;
9040     }
9041     case ShowPreviewCommand:
9042     {
9043       char
9044         **previews;
9045
9046       Image
9047         *preview_image;
9048
9049       static char
9050         preview_type[MaxTextExtent] = "Gamma";
9051
9052       /*
9053         Select preview type from menu.
9054       */
9055       previews=GetCommandOptions(MagickPreviewOptions);
9056       if (previews == (char **) NULL)
9057         break;
9058       XListBrowserWidget(display,windows,&windows->widget,
9059         (const char **) previews,"Preview",
9060         "Select an enhancement, effect, or F/X:",preview_type);
9061       previews=DestroyStringList(previews);
9062       if (*preview_type == '\0')
9063         break;
9064       /*
9065         Show image preview.
9066       */
9067       XSetCursorState(display,windows,MagickTrue);
9068       XCheckRefreshWindows(display,windows);
9069       image_info->preview_type=(PreviewType)
9070         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9071       image_info->group=(ssize_t) windows->image.id;
9072       (void) DeleteImageProperty(*image,"label");
9073       (void) SetImageProperty(*image,"label","Preview");
9074       (void) AcquireUniqueFilename(filename);
9075       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9076         filename);
9077       status=WriteImage(image_info,*image,exception);
9078       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9079       preview_image=ReadImage(image_info,exception);
9080       (void) RelinquishUniqueFileResource(filename);
9081       if (preview_image == (Image *) NULL)
9082         break;
9083       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9084         filename);
9085       status=WriteImage(image_info,preview_image,exception);
9086       preview_image=DestroyImage(preview_image);
9087       if (status == MagickFalse)
9088         XNoticeWidget(display,windows,"Unable to show image preview",
9089           (*image)->filename);
9090       XDelay(display,1500);
9091       XSetCursorState(display,windows,MagickFalse);
9092       break;
9093     }
9094     case ShowHistogramCommand:
9095     {
9096       Image
9097         *histogram_image;
9098
9099       /*
9100         Show image histogram.
9101       */
9102       XSetCursorState(display,windows,MagickTrue);
9103       XCheckRefreshWindows(display,windows);
9104       image_info->group=(ssize_t) windows->image.id;
9105       (void) DeleteImageProperty(*image,"label");
9106       (void) SetImageProperty(*image,"label","Histogram");
9107       (void) AcquireUniqueFilename(filename);
9108       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9109         filename);
9110       status=WriteImage(image_info,*image,exception);
9111       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9112       histogram_image=ReadImage(image_info,exception);
9113       (void) RelinquishUniqueFileResource(filename);
9114       if (histogram_image == (Image *) NULL)
9115         break;
9116       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9117         "show:%s",filename);
9118       status=WriteImage(image_info,histogram_image,exception);
9119       histogram_image=DestroyImage(histogram_image);
9120       if (status == MagickFalse)
9121         XNoticeWidget(display,windows,"Unable to show histogram",
9122           (*image)->filename);
9123       XDelay(display,1500);
9124       XSetCursorState(display,windows,MagickFalse);
9125       break;
9126     }
9127     case ShowMatteCommand:
9128     {
9129       Image
9130         *matte_image;
9131
9132       if ((*image)->matte == MagickFalse)
9133         {
9134           XNoticeWidget(display,windows,
9135             "Image does not have any matte information",(*image)->filename);
9136           break;
9137         }
9138       /*
9139         Show image matte.
9140       */
9141       XSetCursorState(display,windows,MagickTrue);
9142       XCheckRefreshWindows(display,windows);
9143       image_info->group=(ssize_t) windows->image.id;
9144       (void) DeleteImageProperty(*image,"label");
9145       (void) SetImageProperty(*image,"label","Matte");
9146       (void) AcquireUniqueFilename(filename);
9147       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9148         filename);
9149       status=WriteImage(image_info,*image,exception);
9150       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9151       matte_image=ReadImage(image_info,exception);
9152       (void) RelinquishUniqueFileResource(filename);
9153       if (matte_image == (Image *) NULL)
9154         break;
9155       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9156         filename);
9157       status=WriteImage(image_info,matte_image,exception);
9158       matte_image=DestroyImage(matte_image);
9159       if (status == MagickFalse)
9160         XNoticeWidget(display,windows,"Unable to show matte",
9161           (*image)->filename);
9162       XDelay(display,1500);
9163       XSetCursorState(display,windows,MagickFalse);
9164       break;
9165     }
9166     case BackgroundCommand:
9167     {
9168       /*
9169         Background image.
9170       */
9171       status=XBackgroundImage(display,resource_info,windows,image,exception);
9172       if (status == MagickFalse)
9173         break;
9174       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9175       if (nexus != (Image *) NULL)
9176         XClientMessage(display,windows->image.id,windows->im_protocols,
9177           windows->im_next_image,CurrentTime);
9178       break;
9179     }
9180     case SlideShowCommand:
9181     {
9182       static char
9183         delay[MaxTextExtent] = "5";
9184
9185       /*
9186         Display next image after pausing.
9187       */
9188       (void) XDialogWidget(display,windows,"Slide Show",
9189         "Pause how many 1/100ths of a second between images:",delay);
9190       if (*delay == '\0')
9191         break;
9192       resource_info->delay=StringToUnsignedLong(delay);
9193       XClientMessage(display,windows->image.id,windows->im_protocols,
9194         windows->im_next_image,CurrentTime);
9195       break;
9196     }
9197     case PreferencesCommand:
9198     {
9199       /*
9200         Set user preferences.
9201       */
9202       status=XPreferencesWidget(display,resource_info,windows);
9203       if (status == MagickFalse)
9204         break;
9205       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9206       if (nexus != (Image *) NULL)
9207         XClientMessage(display,windows->image.id,windows->im_protocols,
9208           windows->im_next_image,CurrentTime);
9209       break;
9210     }
9211     case HelpCommand:
9212     {
9213       /*
9214         User requested help.
9215       */
9216       XTextViewWidget(display,resource_info,windows,MagickFalse,
9217         "Help Viewer - Display",DisplayHelp);
9218       break;
9219     }
9220     case BrowseDocumentationCommand:
9221     {
9222       Atom
9223         mozilla_atom;
9224
9225       Window
9226         mozilla_window,
9227         root_window;
9228
9229       /*
9230         Browse the ImageMagick documentation.
9231       */
9232       root_window=XRootWindow(display,XDefaultScreen(display));
9233       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9234       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9235       if (mozilla_window != (Window) NULL)
9236         {
9237           char
9238             command[MaxTextExtent],
9239             *url;
9240
9241           /*
9242             Display documentation using Netscape remote control.
9243           */
9244           url=GetMagickHomeURL();
9245           (void) FormatLocaleString(command,MaxTextExtent,
9246             "openurl(%s,new-tab)",url);
9247           url=DestroyString(url);
9248           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9249           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9250             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9251           XSetCursorState(display,windows,MagickFalse);
9252           break;
9253         }
9254       XSetCursorState(display,windows,MagickTrue);
9255       XCheckRefreshWindows(display,windows);
9256       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9257         exception);
9258       if (status == MagickFalse)
9259         XNoticeWidget(display,windows,"Unable to browse documentation",
9260           (char *) NULL);
9261       XDelay(display,1500);
9262       XSetCursorState(display,windows,MagickFalse);
9263       break;
9264     }
9265     case VersionCommand:
9266     {
9267       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9268         GetMagickCopyright());
9269       break;
9270     }
9271     case SaveToUndoBufferCommand:
9272       break;
9273     default:
9274     {
9275       (void) XBell(display,0);
9276       break;
9277     }
9278   }
9279   image_info=DestroyImageInfo(image_info);
9280   return(nexus);
9281 }
9282 \f
9283 /*
9284 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9285 %                                                                             %
9286 %                                                                             %
9287 %                                                                             %
9288 +   X M a g n i f y I m a g e                                                 %
9289 %                                                                             %
9290 %                                                                             %
9291 %                                                                             %
9292 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9293 %
9294 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9295 %  The magnified portion is displayed in a separate window.
9296 %
9297 %  The format of the XMagnifyImage method is:
9298 %
9299 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9300 %
9301 %  A description of each parameter follows:
9302 %
9303 %    o display: Specifies a connection to an X server;  returned from
9304 %      XOpenDisplay.
9305 %
9306 %    o windows: Specifies a pointer to a XWindows structure.
9307 %
9308 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9309 %      the entire image is refreshed.
9310 %
9311 */
9312 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9313 {
9314   char
9315     text[MaxTextExtent];
9316
9317   register int
9318     x,
9319     y;
9320
9321   size_t
9322     state;
9323
9324   /*
9325     Update magnified image until the mouse button is released.
9326   */
9327   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9328   state=DefaultState;
9329   x=event->xbutton.x;
9330   y=event->xbutton.y;
9331   windows->magnify.x=(int) windows->image.x+x;
9332   windows->magnify.y=(int) windows->image.y+y;
9333   do
9334   {
9335     /*
9336       Map and unmap Info widget as text cursor crosses its boundaries.
9337     */
9338     if (windows->info.mapped != MagickFalse)
9339       {
9340         if ((x < (int) (windows->info.x+windows->info.width)) &&
9341             (y < (int) (windows->info.y+windows->info.height)))
9342           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9343       }
9344     else
9345       if ((x > (int) (windows->info.x+windows->info.width)) ||
9346           (y > (int) (windows->info.y+windows->info.height)))
9347         (void) XMapWindow(display,windows->info.id);
9348     if (windows->info.mapped != MagickFalse)
9349       {
9350         /*
9351           Display pointer position.
9352         */
9353         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9354           windows->magnify.x,windows->magnify.y);
9355         XInfoWidget(display,windows,text);
9356       }
9357     /*
9358       Wait for next event.
9359     */
9360     XScreenEvent(display,windows,event);
9361     switch (event->type)
9362     {
9363       case ButtonPress:
9364         break;
9365       case ButtonRelease:
9366       {
9367         /*
9368           User has finished magnifying image.
9369         */
9370         x=event->xbutton.x;
9371         y=event->xbutton.y;
9372         state|=ExitState;
9373         break;
9374       }
9375       case Expose:
9376         break;
9377       case MotionNotify:
9378       {
9379         x=event->xmotion.x;
9380         y=event->xmotion.y;
9381         break;
9382       }
9383       default:
9384         break;
9385     }
9386     /*
9387       Check boundary conditions.
9388     */
9389     if (x < 0)
9390       x=0;
9391     else
9392       if (x >= (int) windows->image.width)
9393         x=(int) windows->image.width-1;
9394     if (y < 0)
9395       y=0;
9396     else
9397      if (y >= (int) windows->image.height)
9398        y=(int) windows->image.height-1;
9399   } while ((state & ExitState) == 0);
9400   /*
9401     Display magnified image.
9402   */
9403   XSetCursorState(display,windows,MagickFalse);
9404 }
9405 \f
9406 /*
9407 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9408 %                                                                             %
9409 %                                                                             %
9410 %                                                                             %
9411 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9412 %                                                                             %
9413 %                                                                             %
9414 %                                                                             %
9415 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9416 %
9417 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9418 %  pixel as specified by the key symbol.
9419 %
9420 %  The format of the XMagnifyWindowCommand method is:
9421 %
9422 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9423 %        const MagickStatusType state,const KeySym key_symbol)
9424 %
9425 %  A description of each parameter follows:
9426 %
9427 %    o display: Specifies a connection to an X server; returned from
9428 %      XOpenDisplay.
9429 %
9430 %    o windows: Specifies a pointer to a XWindows structure.
9431 %
9432 %    o state: key mask.
9433 %
9434 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9435 %      to trim.
9436 %
9437 */
9438 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9439   const MagickStatusType state,const KeySym key_symbol)
9440 {
9441   unsigned int
9442     quantum;
9443
9444   /*
9445     User specified a magnify factor or position.
9446   */
9447   quantum=1;
9448   if ((state & Mod1Mask) != 0)
9449     quantum=10;
9450   switch ((int) key_symbol)
9451   {
9452     case QuitCommand:
9453     {
9454       (void) XWithdrawWindow(display,windows->magnify.id,
9455         windows->magnify.screen);
9456       break;
9457     }
9458     case XK_Home:
9459     case XK_KP_Home:
9460     {
9461       windows->magnify.x=(int) windows->image.width/2;
9462       windows->magnify.y=(int) windows->image.height/2;
9463       break;
9464     }
9465     case XK_Left:
9466     case XK_KP_Left:
9467     {
9468       if (windows->magnify.x > 0)
9469         windows->magnify.x-=quantum;
9470       break;
9471     }
9472     case XK_Up:
9473     case XK_KP_Up:
9474     {
9475       if (windows->magnify.y > 0)
9476         windows->magnify.y-=quantum;
9477       break;
9478     }
9479     case XK_Right:
9480     case XK_KP_Right:
9481     {
9482       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9483         windows->magnify.x+=quantum;
9484       break;
9485     }
9486     case XK_Down:
9487     case XK_KP_Down:
9488     {
9489       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9490         windows->magnify.y+=quantum;
9491       break;
9492     }
9493     case XK_0:
9494     case XK_1:
9495     case XK_2:
9496     case XK_3:
9497     case XK_4:
9498     case XK_5:
9499     case XK_6:
9500     case XK_7:
9501     case XK_8:
9502     case XK_9:
9503     {
9504       windows->magnify.data=(key_symbol-XK_0);
9505       break;
9506     }
9507     case XK_KP_0:
9508     case XK_KP_1:
9509     case XK_KP_2:
9510     case XK_KP_3:
9511     case XK_KP_4:
9512     case XK_KP_5:
9513     case XK_KP_6:
9514     case XK_KP_7:
9515     case XK_KP_8:
9516     case XK_KP_9:
9517     {
9518       windows->magnify.data=(key_symbol-XK_KP_0);
9519       break;
9520     }
9521     default:
9522       break;
9523   }
9524   XMakeMagnifyImage(display,windows);
9525 }
9526 \f
9527 /*
9528 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9529 %                                                                             %
9530 %                                                                             %
9531 %                                                                             %
9532 +   X M a k e P a n I m a g e                                                 %
9533 %                                                                             %
9534 %                                                                             %
9535 %                                                                             %
9536 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9537 %
9538 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9539 %  icon window.
9540 %
9541 %  The format of the XMakePanImage method is:
9542 %
9543 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9544 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9545 %
9546 %  A description of each parameter follows:
9547 %
9548 %    o display: Specifies a connection to an X server;  returned from
9549 %      XOpenDisplay.
9550 %
9551 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9552 %
9553 %    o windows: Specifies a pointer to a XWindows structure.
9554 %
9555 %    o image: the image.
9556 %
9557 %    o exception: return any errors or warnings in this structure.
9558 %
9559 */
9560 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9561   XWindows *windows,Image *image,ExceptionInfo *exception)
9562 {
9563   MagickStatusType
9564     status;
9565
9566   /*
9567     Create and display image for panning icon.
9568   */
9569   XSetCursorState(display,windows,MagickTrue);
9570   XCheckRefreshWindows(display,windows);
9571   windows->pan.x=(int) windows->image.x;
9572   windows->pan.y=(int) windows->image.y;
9573   status=XMakeImage(display,resource_info,&windows->pan,image,
9574     windows->pan.width,windows->pan.height,exception);
9575   if (status == MagickFalse)
9576     ThrowXWindowFatalException(ResourceLimitError,
9577      "MemoryAllocationFailed",image->filename);
9578   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9579     windows->pan.pixmap);
9580   (void) XClearWindow(display,windows->pan.id);
9581   XDrawPanRectangle(display,windows);
9582   XSetCursorState(display,windows,MagickFalse);
9583 }
9584 \f
9585 /*
9586 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9587 %                                                                             %
9588 %                                                                             %
9589 %                                                                             %
9590 +   X M a t t a E d i t I m a g e                                             %
9591 %                                                                             %
9592 %                                                                             %
9593 %                                                                             %
9594 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9595 %
9596 %  XMatteEditImage() allows the user to interactively change the Matte channel
9597 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9598 %  before the matte information is stored.
9599 %
9600 %  The format of the XMatteEditImage method is:
9601 %
9602 %      MagickBooleanType XMatteEditImage(Display *display,
9603 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9604 %        ExceptionInfo *exception)
9605 %
9606 %  A description of each parameter follows:
9607 %
9608 %    o display: Specifies a connection to an X server;  returned from
9609 %      XOpenDisplay.
9610 %
9611 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9612 %
9613 %    o windows: Specifies a pointer to a XWindows structure.
9614 %
9615 %    o image: the image; returned from ReadImage.
9616 %
9617 %    o exception: return any errors or warnings in this structure.
9618 %
9619 */
9620 static MagickBooleanType XMatteEditImage(Display *display,
9621   XResourceInfo *resource_info,XWindows *windows,Image **image,
9622   ExceptionInfo *exception)
9623 {
9624   static char
9625     matte[MaxTextExtent] = "0";
9626
9627   static const char
9628     *MatteEditMenu[] =
9629     {
9630       "Method",
9631       "Border Color",
9632       "Fuzz",
9633       "Matte Value",
9634       "Undo",
9635       "Help",
9636       "Dismiss",
9637       (char *) NULL
9638     };
9639
9640   static const ModeType
9641     MatteEditCommands[] =
9642     {
9643       MatteEditMethod,
9644       MatteEditBorderCommand,
9645       MatteEditFuzzCommand,
9646       MatteEditValueCommand,
9647       MatteEditUndoCommand,
9648       MatteEditHelpCommand,
9649       MatteEditDismissCommand
9650     };
9651
9652   static PaintMethod
9653     method = PointMethod;
9654
9655   static XColor
9656     border_color = { 0, 0, 0, 0, 0, 0 };
9657
9658   char
9659     command[MaxTextExtent],
9660     text[MaxTextExtent];
9661
9662   Cursor
9663     cursor;
9664
9665   int
9666     entry,
9667     id,
9668     x,
9669     x_offset,
9670     y,
9671     y_offset;
9672
9673   register int
9674     i;
9675
9676   register Quantum
9677     *q;
9678
9679   unsigned int
9680     height,
9681     width;
9682
9683   size_t
9684     state;
9685
9686   XEvent
9687     event;
9688
9689   /*
9690     Map Command widget.
9691   */
9692   (void) CloneString(&windows->command.name,"Matte Edit");
9693   windows->command.data=4;
9694   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9695   (void) XMapRaised(display,windows->command.id);
9696   XClientMessage(display,windows->image.id,windows->im_protocols,
9697     windows->im_update_widget,CurrentTime);
9698   /*
9699     Make cursor.
9700   */
9701   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9702     resource_info->background_color,resource_info->foreground_color);
9703   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9704   /*
9705     Track pointer until button 1 is pressed.
9706   */
9707   XQueryPosition(display,windows->image.id,&x,&y);
9708   (void) XSelectInput(display,windows->image.id,
9709     windows->image.attributes.event_mask | PointerMotionMask);
9710   state=DefaultState;
9711   do
9712   {
9713     if (windows->info.mapped != MagickFalse)
9714       {
9715         /*
9716           Display pointer position.
9717         */
9718         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9719           x+windows->image.x,y+windows->image.y);
9720         XInfoWidget(display,windows,text);
9721       }
9722     /*
9723       Wait for next event.
9724     */
9725     XScreenEvent(display,windows,&event);
9726     if (event.xany.window == windows->command.id)
9727       {
9728         /*
9729           Select a command from the Command widget.
9730         */
9731         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9732         if (id < 0)
9733           {
9734             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9735             continue;
9736           }
9737         switch (MatteEditCommands[id])
9738         {
9739           case MatteEditMethod:
9740           {
9741             char
9742               **methods;
9743
9744             /*
9745               Select a method from the pop-up menu.
9746             */
9747             methods=GetCommandOptions(MagickMethodOptions);
9748             if (methods == (char **) NULL)
9749               break;
9750             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9751               (const char **) methods,command);
9752             if (entry >= 0)
9753               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9754                 MagickFalse,methods[entry]);
9755             methods=DestroyStringList(methods);
9756             break;
9757           }
9758           case MatteEditBorderCommand:
9759           {
9760             const char
9761               *ColorMenu[MaxNumberPens];
9762
9763             int
9764               pen_number;
9765
9766             /*
9767               Initialize menu selections.
9768             */
9769             for (i=0; i < (int) (MaxNumberPens-2); i++)
9770               ColorMenu[i]=resource_info->pen_colors[i];
9771             ColorMenu[MaxNumberPens-2]="Browser...";
9772             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9773             /*
9774               Select a pen color from the pop-up menu.
9775             */
9776             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9777               (const char **) ColorMenu,command);
9778             if (pen_number < 0)
9779               break;
9780             if (pen_number == (MaxNumberPens-2))
9781               {
9782                 static char
9783                   color_name[MaxTextExtent] = "gray";
9784
9785                 /*
9786                   Select a pen color from a dialog.
9787                 */
9788                 resource_info->pen_colors[pen_number]=color_name;
9789                 XColorBrowserWidget(display,windows,"Select",color_name);
9790                 if (*color_name == '\0')
9791                   break;
9792               }
9793             /*
9794               Set border color.
9795             */
9796             (void) XParseColor(display,windows->map_info->colormap,
9797               resource_info->pen_colors[pen_number],&border_color);
9798             break;
9799           }
9800           case MatteEditFuzzCommand:
9801           {
9802             static char
9803               fuzz[MaxTextExtent];
9804
9805             static const char
9806               *FuzzMenu[] =
9807               {
9808                 "0%",
9809                 "2%",
9810                 "5%",
9811                 "10%",
9812                 "15%",
9813                 "Dialog...",
9814                 (char *) NULL,
9815               };
9816
9817             /*
9818               Select a command from the pop-up menu.
9819             */
9820             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9821               command);
9822             if (entry < 0)
9823               break;
9824             if (entry != 5)
9825               {
9826                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
9827                   QuantumRange+1.0);
9828                 break;
9829               }
9830             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9831             (void) XDialogWidget(display,windows,"Ok",
9832               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9833             if (*fuzz == '\0')
9834               break;
9835             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9836             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9837             break;
9838           }
9839           case MatteEditValueCommand:
9840           {
9841             static char
9842               message[MaxTextExtent];
9843
9844             static const char
9845               *MatteMenu[] =
9846               {
9847                 "Opaque",
9848                 "Transparent",
9849                 "Dialog...",
9850                 (char *) NULL,
9851               };
9852
9853             /*
9854               Select a command from the pop-up menu.
9855             */
9856             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9857               command);
9858             if (entry < 0)
9859               break;
9860             if (entry != 2)
9861               {
9862                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9863                   OpaqueAlpha);
9864                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9865                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9866                     (Quantum) TransparentAlpha);
9867                 break;
9868               }
9869             (void) FormatLocaleString(message,MaxTextExtent,
9870               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9871               QuantumRange);
9872             (void) XDialogWidget(display,windows,"Matte",message,matte);
9873             if (*matte == '\0')
9874               break;
9875             break;
9876           }
9877           case MatteEditUndoCommand:
9878           {
9879             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9880               image,exception);
9881             break;
9882           }
9883           case MatteEditHelpCommand:
9884           {
9885             XTextViewWidget(display,resource_info,windows,MagickFalse,
9886               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9887             break;
9888           }
9889           case MatteEditDismissCommand:
9890           {
9891             /*
9892               Prematurely exit.
9893             */
9894             state|=EscapeState;
9895             state|=ExitState;
9896             break;
9897           }
9898           default:
9899             break;
9900         }
9901         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9902         continue;
9903       }
9904     switch (event.type)
9905     {
9906       case ButtonPress:
9907       {
9908         if (event.xbutton.button != Button1)
9909           break;
9910         if ((event.xbutton.window != windows->image.id) &&
9911             (event.xbutton.window != windows->magnify.id))
9912           break;
9913         /*
9914           Update matte data.
9915         */
9916         x=event.xbutton.x;
9917         y=event.xbutton.y;
9918         (void) XMagickCommand(display,resource_info,windows,
9919           SaveToUndoBufferCommand,image,exception);
9920         state|=UpdateConfigurationState;
9921         break;
9922       }
9923       case ButtonRelease:
9924       {
9925         if (event.xbutton.button != Button1)
9926           break;
9927         if ((event.xbutton.window != windows->image.id) &&
9928             (event.xbutton.window != windows->magnify.id))
9929           break;
9930         /*
9931           Update colormap information.
9932         */
9933         x=event.xbutton.x;
9934         y=event.xbutton.y;
9935         XConfigureImageColormap(display,resource_info,windows,*image);
9936         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9937         XInfoWidget(display,windows,text);
9938         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9939         state&=(~UpdateConfigurationState);
9940         break;
9941       }
9942       case Expose:
9943         break;
9944       case KeyPress:
9945       {
9946         char
9947           command[MaxTextExtent];
9948
9949         KeySym
9950           key_symbol;
9951
9952         if (event.xkey.window == windows->magnify.id)
9953           {
9954             Window
9955               window;
9956
9957             window=windows->magnify.id;
9958             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9959           }
9960         if (event.xkey.window != windows->image.id)
9961           break;
9962         /*
9963           Respond to a user key press.
9964         */
9965         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9966           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9967         switch ((int) key_symbol)
9968         {
9969           case XK_Escape:
9970           case XK_F20:
9971           {
9972             /*
9973               Prematurely exit.
9974             */
9975             state|=ExitState;
9976             break;
9977           }
9978           case XK_F1:
9979           case XK_Help:
9980           {
9981             XTextViewWidget(display,resource_info,windows,MagickFalse,
9982               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9983             break;
9984           }
9985           default:
9986           {
9987             (void) XBell(display,0);
9988             break;
9989           }
9990         }
9991         break;
9992       }
9993       case MotionNotify:
9994       {
9995         /*
9996           Map and unmap Info widget as cursor crosses its boundaries.
9997         */
9998         x=event.xmotion.x;
9999         y=event.xmotion.y;
10000         if (windows->info.mapped != MagickFalse)
10001           {
10002             if ((x < (int) (windows->info.x+windows->info.width)) &&
10003                 (y < (int) (windows->info.y+windows->info.height)))
10004               (void) XWithdrawWindow(display,windows->info.id,
10005                 windows->info.screen);
10006           }
10007         else
10008           if ((x > (int) (windows->info.x+windows->info.width)) ||
10009               (y > (int) (windows->info.y+windows->info.height)))
10010             (void) XMapWindow(display,windows->info.id);
10011         break;
10012       }
10013       default:
10014         break;
10015     }
10016     if (event.xany.window == windows->magnify.id)
10017       {
10018         x=windows->magnify.x-windows->image.x;
10019         y=windows->magnify.y-windows->image.y;
10020       }
10021     x_offset=x;
10022     y_offset=y;
10023     if ((state & UpdateConfigurationState) != 0)
10024       {
10025         CacheView
10026           *image_view;
10027
10028         int
10029           x,
10030           y;
10031
10032         /*
10033           Matte edit is relative to image configuration.
10034         */
10035         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10036           MagickTrue);
10037         XPutPixel(windows->image.ximage,x_offset,y_offset,
10038           windows->pixel_info->background_color.pixel);
10039         width=(unsigned int) (*image)->columns;
10040         height=(unsigned int) (*image)->rows;
10041         x=0;
10042         y=0;
10043         if (windows->image.crop_geometry != (char *) NULL)
10044           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10045             &height);
10046         x_offset=(int) (width*(windows->image.x+x_offset)/
10047           windows->image.ximage->width+x);
10048         y_offset=(int) (height*(windows->image.y+y_offset)/
10049           windows->image.ximage->height+y);
10050         if ((x_offset < 0) || (y_offset < 0))
10051           continue;
10052         if ((x_offset >= (int) (*image)->columns) ||
10053             (y_offset >= (int) (*image)->rows))
10054           continue;
10055         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10056           return(MagickFalse);
10057         (*image)->matte=MagickTrue;
10058         image_view=AcquireCacheView(*image);
10059         switch (method)
10060         {
10061           case PointMethod:
10062           default:
10063           {
10064             /*
10065               Update matte information using point algorithm.
10066             */
10067             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10068               (ssize_t) y_offset,1,1,exception);
10069             if (q == (Quantum *) NULL)
10070               break;
10071             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10072             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10073             break;
10074           }
10075           case ReplaceMethod:
10076           {
10077             PixelPacket
10078               pixel,
10079               target;
10080
10081             /*
10082               Update matte information using replace algorithm.
10083             */
10084             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10085               (ssize_t) y_offset,&target,exception);
10086             for (y=0; y < (int) (*image)->rows; y++)
10087             {
10088               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10089                 (*image)->columns,1,exception);
10090               if (q == (Quantum *) NULL)
10091                 break;
10092               for (x=0; x < (int) (*image)->columns; x++)
10093               {
10094                 GetPixelPacket(*image,q,&pixel);
10095                 if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
10096                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10097                 q+=GetPixelChannels(*image);
10098               }
10099               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10100                 break;
10101             }
10102             break;
10103           }
10104           case FloodfillMethod:
10105           case FillToBorderMethod:
10106           {
10107             ChannelType
10108               channel_mask;
10109
10110             DrawInfo
10111               *draw_info;
10112
10113             PixelInfo
10114               target;
10115
10116             /*
10117               Update matte information using floodfill algorithm.
10118             */
10119             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10120               (ssize_t) y_offset,&target,exception);
10121             if (method == FillToBorderMethod)
10122               {
10123                 target.red=(MagickRealType) ScaleShortToQuantum(
10124                   border_color.red);
10125                 target.green=(MagickRealType) ScaleShortToQuantum(
10126                   border_color.green);
10127                 target.blue=(MagickRealType) ScaleShortToQuantum(
10128                   border_color.blue);
10129               }
10130             draw_info=CloneDrawInfo(resource_info->image_info,
10131               (DrawInfo *) NULL);
10132             draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10133               (char **) NULL));
10134             channel_mask=SetPixelChannelMask(*image,AlphaChannel); 
10135             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10136               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10137               MagickFalse : MagickTrue,exception);
10138             (void) SetPixelChannelMap(*image,channel_mask);
10139             draw_info=DestroyDrawInfo(draw_info);
10140             break;
10141           }
10142           case ResetMethod:
10143           {
10144             /*
10145               Update matte information using reset algorithm.
10146             */
10147             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10148               return(MagickFalse);
10149             for (y=0; y < (int) (*image)->rows; y++)
10150             {
10151               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10152                 (*image)->columns,1,exception);
10153               if (q == (Quantum *) NULL)
10154                 break;
10155               for (x=0; x < (int) (*image)->columns; x++)
10156               {
10157                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10158                 q+=GetPixelChannels(*image);
10159               }
10160               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10161                 break;
10162             }
10163             if (StringToLong(matte) == (long) OpaqueAlpha)
10164               (*image)->matte=MagickFalse;
10165             break;
10166           }
10167         }
10168         image_view=DestroyCacheView(image_view);
10169         state&=(~UpdateConfigurationState);
10170       }
10171   } while ((state & ExitState) == 0);
10172   (void) XSelectInput(display,windows->image.id,
10173     windows->image.attributes.event_mask);
10174   XSetCursorState(display,windows,MagickFalse);
10175   (void) XFreeCursor(display,cursor);
10176   return(MagickTrue);
10177 }
10178 \f
10179 /*
10180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10181 %                                                                             %
10182 %                                                                             %
10183 %                                                                             %
10184 +   X O p e n I m a g e                                                       %
10185 %                                                                             %
10186 %                                                                             %
10187 %                                                                             %
10188 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10189 %
10190 %  XOpenImage() loads an image from a file.
10191 %
10192 %  The format of the XOpenImage method is:
10193 %
10194 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10195 %       XWindows *windows,const unsigned int command)
10196 %
10197 %  A description of each parameter follows:
10198 %
10199 %    o display: Specifies a connection to an X server; returned from
10200 %      XOpenDisplay.
10201 %
10202 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10203 %
10204 %    o windows: Specifies a pointer to a XWindows structure.
10205 %
10206 %    o command: A value other than zero indicates that the file is selected
10207 %      from the command line argument list.
10208 %
10209 */
10210 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10211   XWindows *windows,const MagickBooleanType command)
10212 {
10213   const MagickInfo
10214     *magick_info;
10215
10216   ExceptionInfo
10217     *exception;
10218
10219   Image
10220     *nexus;
10221
10222   ImageInfo
10223     *image_info;
10224
10225   static char
10226     filename[MaxTextExtent] = "\0";
10227
10228   /*
10229     Request file name from user.
10230   */
10231   if (command == MagickFalse)
10232     XFileBrowserWidget(display,windows,"Open",filename);
10233   else
10234     {
10235       char
10236         **filelist,
10237         **files;
10238
10239       int
10240         count,
10241         status;
10242
10243       register int
10244         i,
10245         j;
10246
10247       /*
10248         Select next image from the command line.
10249       */
10250       status=XGetCommand(display,windows->image.id,&files,&count);
10251       if (status == 0)
10252         {
10253           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10254           return((Image *) NULL);
10255         }
10256       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10257       if (filelist == (char **) NULL)
10258         {
10259           ThrowXWindowFatalException(ResourceLimitError,
10260             "MemoryAllocationFailed","...");
10261           (void) XFreeStringList(files);
10262           return((Image *) NULL);
10263         }
10264       j=0;
10265       for (i=1; i < count; i++)
10266         if (*files[i] != '-')
10267           filelist[j++]=files[i];
10268       filelist[j]=(char *) NULL;
10269       XListBrowserWidget(display,windows,&windows->widget,
10270         (const char **) filelist,"Load","Select Image to Load:",filename);
10271       filelist=(char **) RelinquishMagickMemory(filelist);
10272       (void) XFreeStringList(files);
10273     }
10274   if (*filename == '\0')
10275     return((Image *) NULL);
10276   image_info=CloneImageInfo(resource_info->image_info);
10277   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10278     (void *) NULL);
10279   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10280   exception=AcquireExceptionInfo();
10281   (void) SetImageInfo(image_info,0,exception);
10282   if (LocaleCompare(image_info->magick,"X") == 0)
10283     {
10284       char
10285         seconds[MaxTextExtent];
10286
10287       /*
10288         User may want to delay the X server screen grab.
10289       */
10290       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10291       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10292         seconds);
10293       if (*seconds == '\0')
10294         return((Image *) NULL);
10295       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10296     }
10297   magick_info=GetMagickInfo(image_info->magick,exception);
10298   if ((magick_info != (const MagickInfo *) NULL) &&
10299       (magick_info->raw != MagickFalse))
10300     {
10301       char
10302         geometry[MaxTextExtent];
10303
10304       /*
10305         Request image size from the user.
10306       */
10307       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10308       if (image_info->size != (char *) NULL)
10309         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10310       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10311         geometry);
10312       (void) CloneString(&image_info->size,geometry);
10313     }
10314   /*
10315     Load the image.
10316   */
10317   XSetCursorState(display,windows,MagickTrue);
10318   XCheckRefreshWindows(display,windows);
10319   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10320   nexus=ReadImage(image_info,exception);
10321   CatchException(exception);
10322   XSetCursorState(display,windows,MagickFalse);
10323   if (nexus != (Image *) NULL)
10324     XClientMessage(display,windows->image.id,windows->im_protocols,
10325       windows->im_next_image,CurrentTime);
10326   else
10327     {
10328       char
10329         *text,
10330         **textlist;
10331
10332       /*
10333         Unknown image format.
10334       */
10335       text=FileToString(filename,~0,exception);
10336       if (text == (char *) NULL)
10337         return((Image *) NULL);
10338       textlist=StringToList(text);
10339       if (textlist != (char **) NULL)
10340         {
10341           char
10342             title[MaxTextExtent];
10343
10344           register int
10345             i;
10346
10347           (void) FormatLocaleString(title,MaxTextExtent,
10348             "Unknown format: %s",filename);
10349           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10350             (const char **) textlist);
10351           for (i=0; textlist[i] != (char *) NULL; i++)
10352             textlist[i]=DestroyString(textlist[i]);
10353           textlist=(char **) RelinquishMagickMemory(textlist);
10354         }
10355       text=DestroyString(text);
10356     }
10357   exception=DestroyExceptionInfo(exception);
10358   image_info=DestroyImageInfo(image_info);
10359   return(nexus);
10360 }
10361 \f
10362 /*
10363 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10364 %                                                                             %
10365 %                                                                             %
10366 %                                                                             %
10367 +   X P a n I m a g e                                                         %
10368 %                                                                             %
10369 %                                                                             %
10370 %                                                                             %
10371 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10372 %
10373 %  XPanImage() pans the image until the mouse button is released.
10374 %
10375 %  The format of the XPanImage method is:
10376 %
10377 %      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10378 %
10379 %  A description of each parameter follows:
10380 %
10381 %    o display: Specifies a connection to an X server;  returned from
10382 %      XOpenDisplay.
10383 %
10384 %    o windows: Specifies a pointer to a XWindows structure.
10385 %
10386 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10387 %      the entire image is refreshed.
10388 %
10389 */
10390 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10391 {
10392   char
10393     text[MaxTextExtent];
10394
10395   Cursor
10396     cursor;
10397
10398   MagickRealType
10399     x_factor,
10400     y_factor;
10401
10402   RectangleInfo
10403     pan_info;
10404
10405   size_t
10406     state;
10407
10408   /*
10409     Define cursor.
10410   */
10411   if ((windows->image.ximage->width > (int) windows->image.width) &&
10412       (windows->image.ximage->height > (int) windows->image.height))
10413     cursor=XCreateFontCursor(display,XC_fleur);
10414   else
10415     if (windows->image.ximage->width > (int) windows->image.width)
10416       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10417     else
10418       if (windows->image.ximage->height > (int) windows->image.height)
10419         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10420       else
10421         cursor=XCreateFontCursor(display,XC_arrow);
10422   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10423   /*
10424     Pan image as pointer moves until the mouse button is released.
10425   */
10426   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10427   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10428   pan_info.width=windows->pan.width*windows->image.width/
10429     windows->image.ximage->width;
10430   pan_info.height=windows->pan.height*windows->image.height/
10431     windows->image.ximage->height;
10432   pan_info.x=0;
10433   pan_info.y=0;
10434   state=UpdateConfigurationState;
10435   do
10436   {
10437     switch (event->type)
10438     {
10439       case ButtonPress:
10440       {
10441         /*
10442           User choose an initial pan location.
10443         */
10444         pan_info.x=(ssize_t) event->xbutton.x;
10445         pan_info.y=(ssize_t) event->xbutton.y;
10446         state|=UpdateConfigurationState;
10447         break;
10448       }
10449       case ButtonRelease:
10450       {
10451         /*
10452           User has finished panning the image.
10453         */
10454         pan_info.x=(ssize_t) event->xbutton.x;
10455         pan_info.y=(ssize_t) event->xbutton.y;
10456         state|=UpdateConfigurationState | ExitState;
10457         break;
10458       }
10459       case MotionNotify:
10460       {
10461         pan_info.x=(ssize_t) event->xmotion.x;
10462         pan_info.y=(ssize_t) event->xmotion.y;
10463         state|=UpdateConfigurationState;
10464       }
10465       default:
10466         break;
10467     }
10468     if ((state & UpdateConfigurationState) != 0)
10469       {
10470         /*
10471           Check boundary conditions.
10472         */
10473         if (pan_info.x < (ssize_t) (pan_info.width/2))
10474           pan_info.x=0;
10475         else
10476           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10477         if (pan_info.x < 0)
10478           pan_info.x=0;
10479         else
10480           if ((int) (pan_info.x+windows->image.width) >
10481               windows->image.ximage->width)
10482             pan_info.x=(ssize_t)
10483               (windows->image.ximage->width-windows->image.width);
10484         if (pan_info.y < (ssize_t) (pan_info.height/2))
10485           pan_info.y=0;
10486         else
10487           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10488         if (pan_info.y < 0)
10489           pan_info.y=0;
10490         else
10491           if ((int) (pan_info.y+windows->image.height) >
10492               windows->image.ximage->height)
10493             pan_info.y=(ssize_t)
10494               (windows->image.ximage->height-windows->image.height);
10495         if ((windows->image.x != (int) pan_info.x) ||
10496             (windows->image.y != (int) pan_info.y))
10497           {
10498             /*
10499               Display image pan offset.
10500             */
10501             windows->image.x=(int) pan_info.x;
10502             windows->image.y=(int) pan_info.y;
10503             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10504               windows->image.width,windows->image.height,windows->image.x,
10505               windows->image.y);
10506             XInfoWidget(display,windows,text);
10507             /*
10508               Refresh Image window.
10509             */
10510             XDrawPanRectangle(display,windows);
10511             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10512           }
10513         state&=(~UpdateConfigurationState);
10514       }
10515     /*
10516       Wait for next event.
10517     */
10518     if ((state & ExitState) == 0)
10519       XScreenEvent(display,windows,event);
10520   } while ((state & ExitState) == 0);
10521   /*
10522     Restore cursor.
10523   */
10524   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10525   (void) XFreeCursor(display,cursor);
10526   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10527 }
10528 \f
10529 /*
10530 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10531 %                                                                             %
10532 %                                                                             %
10533 %                                                                             %
10534 +   X P a s t e I m a g e                                                     %
10535 %                                                                             %
10536 %                                                                             %
10537 %                                                                             %
10538 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10539 %
10540 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10541 %  window image at a location the user chooses with the pointer.
10542 %
10543 %  The format of the XPasteImage method is:
10544 %
10545 %      MagickBooleanType XPasteImage(Display *display,
10546 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10547 %        ExceptionInfo *exception)
10548 %
10549 %  A description of each parameter follows:
10550 %
10551 %    o display: Specifies a connection to an X server;  returned from
10552 %      XOpenDisplay.
10553 %
10554 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10555 %
10556 %    o windows: Specifies a pointer to a XWindows structure.
10557 %
10558 %    o image: the image; returned from ReadImage.
10559 %
10560 %    o exception: return any errors or warnings in this structure.
10561 %
10562 */
10563 static MagickBooleanType XPasteImage(Display *display,
10564   XResourceInfo *resource_info,XWindows *windows,Image *image,
10565   ExceptionInfo *exception)
10566 {
10567   static const char
10568     *PasteMenu[] =
10569     {
10570       "Operator",
10571       "Help",
10572       "Dismiss",
10573       (char *) NULL
10574     };
10575
10576   static const ModeType
10577     PasteCommands[] =
10578     {
10579       PasteOperatorsCommand,
10580       PasteHelpCommand,
10581       PasteDismissCommand
10582     };
10583
10584   static CompositeOperator
10585     compose = CopyCompositeOp;
10586
10587   char
10588     text[MaxTextExtent];
10589
10590   Cursor
10591     cursor;
10592
10593   Image
10594     *paste_image;
10595
10596   int
10597     entry,
10598     id,
10599     x,
10600     y;
10601
10602   MagickRealType
10603     scale_factor;
10604
10605   RectangleInfo
10606     highlight_info,
10607     paste_info;
10608
10609   unsigned int
10610     height,
10611     width;
10612
10613   size_t
10614     state;
10615
10616   XEvent
10617     event;
10618
10619   /*
10620     Copy image.
10621   */
10622   if (resource_info->copy_image == (Image *) NULL)
10623     return(MagickFalse);
10624   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10625   /*
10626     Map Command widget.
10627   */
10628   (void) CloneString(&windows->command.name,"Paste");
10629   windows->command.data=1;
10630   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10631   (void) XMapRaised(display,windows->command.id);
10632   XClientMessage(display,windows->image.id,windows->im_protocols,
10633     windows->im_update_widget,CurrentTime);
10634   /*
10635     Track pointer until button 1 is pressed.
10636   */
10637   XSetCursorState(display,windows,MagickFalse);
10638   XQueryPosition(display,windows->image.id,&x,&y);
10639   (void) XSelectInput(display,windows->image.id,
10640     windows->image.attributes.event_mask | PointerMotionMask);
10641   paste_info.x=(ssize_t) windows->image.x+x;
10642   paste_info.y=(ssize_t) windows->image.y+y;
10643   paste_info.width=0;
10644   paste_info.height=0;
10645   cursor=XCreateFontCursor(display,XC_ul_angle);
10646   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10647   state=DefaultState;
10648   do
10649   {
10650     if (windows->info.mapped != MagickFalse)
10651       {
10652         /*
10653           Display pointer position.
10654         */
10655         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10656           (long) paste_info.x,(long) paste_info.y);
10657         XInfoWidget(display,windows,text);
10658       }
10659     highlight_info=paste_info;
10660     highlight_info.x=paste_info.x-windows->image.x;
10661     highlight_info.y=paste_info.y-windows->image.y;
10662     XHighlightRectangle(display,windows->image.id,
10663       windows->image.highlight_context,&highlight_info);
10664     /*
10665       Wait for next event.
10666     */
10667     XScreenEvent(display,windows,&event);
10668     XHighlightRectangle(display,windows->image.id,
10669       windows->image.highlight_context,&highlight_info);
10670     if (event.xany.window == windows->command.id)
10671       {
10672         /*
10673           Select a command from the Command widget.
10674         */
10675         id=XCommandWidget(display,windows,PasteMenu,&event);
10676         if (id < 0)
10677           continue;
10678         switch (PasteCommands[id])
10679         {
10680           case PasteOperatorsCommand:
10681           {
10682             char
10683               command[MaxTextExtent],
10684               **operators;
10685
10686             /*
10687               Select a command from the pop-up menu.
10688             */
10689             operators=GetCommandOptions(MagickComposeOptions);
10690             if (operators == (char **) NULL)
10691               break;
10692             entry=XMenuWidget(display,windows,PasteMenu[id],
10693               (const char **) operators,command);
10694             if (entry >= 0)
10695               compose=(CompositeOperator) ParseCommandOption(
10696                 MagickComposeOptions,MagickFalse,operators[entry]);
10697             operators=DestroyStringList(operators);
10698             break;
10699           }
10700           case PasteHelpCommand:
10701           {
10702             XTextViewWidget(display,resource_info,windows,MagickFalse,
10703               "Help Viewer - Image Composite",ImagePasteHelp);
10704             break;
10705           }
10706           case PasteDismissCommand:
10707           {
10708             /*
10709               Prematurely exit.
10710             */
10711             state|=EscapeState;
10712             state|=ExitState;
10713             break;
10714           }
10715           default:
10716             break;
10717         }
10718         continue;
10719       }
10720     switch (event.type)
10721     {
10722       case ButtonPress:
10723       {
10724         if (image->debug != MagickFalse)
10725           (void) LogMagickEvent(X11Event,GetMagickModule(),
10726             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10727             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10728         if (event.xbutton.button != Button1)
10729           break;
10730         if (event.xbutton.window != windows->image.id)
10731           break;
10732         /*
10733           Paste rectangle is relative to image configuration.
10734         */
10735         width=(unsigned int) image->columns;
10736         height=(unsigned int) image->rows;
10737         x=0;
10738         y=0;
10739         if (windows->image.crop_geometry != (char *) NULL)
10740           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10741             &width,&height);
10742         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10743         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10744         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10745         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10746         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10747         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10748         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10749         break;
10750       }
10751       case ButtonRelease:
10752       {
10753         if (image->debug != MagickFalse)
10754           (void) LogMagickEvent(X11Event,GetMagickModule(),
10755             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10756             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10757         if (event.xbutton.button != Button1)
10758           break;
10759         if (event.xbutton.window != windows->image.id)
10760           break;
10761         if ((paste_info.width != 0) && (paste_info.height != 0))
10762           {
10763             /*
10764               User has selected the location of the paste image.
10765             */
10766             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10767             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10768             state|=ExitState;
10769           }
10770         break;
10771       }
10772       case Expose:
10773         break;
10774       case KeyPress:
10775       {
10776         char
10777           command[MaxTextExtent];
10778
10779         KeySym
10780           key_symbol;
10781
10782         int
10783           length;
10784
10785         if (event.xkey.window != windows->image.id)
10786           break;
10787         /*
10788           Respond to a user key press.
10789         */
10790         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10791           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10792         *(command+length)='\0';
10793         if (image->debug != MagickFalse)
10794           (void) LogMagickEvent(X11Event,GetMagickModule(),
10795             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10796         switch ((int) key_symbol)
10797         {
10798           case XK_Escape:
10799           case XK_F20:
10800           {
10801             /*
10802               Prematurely exit.
10803             */
10804             paste_image=DestroyImage(paste_image);
10805             state|=EscapeState;
10806             state|=ExitState;
10807             break;
10808           }
10809           case XK_F1:
10810           case XK_Help:
10811           {
10812             (void) XSetFunction(display,windows->image.highlight_context,
10813               GXcopy);
10814             XTextViewWidget(display,resource_info,windows,MagickFalse,
10815               "Help Viewer - Image Composite",ImagePasteHelp);
10816             (void) XSetFunction(display,windows->image.highlight_context,
10817               GXinvert);
10818             break;
10819           }
10820           default:
10821           {
10822             (void) XBell(display,0);
10823             break;
10824           }
10825         }
10826         break;
10827       }
10828       case MotionNotify:
10829       {
10830         /*
10831           Map and unmap Info widget as text cursor crosses its boundaries.
10832         */
10833         x=event.xmotion.x;
10834         y=event.xmotion.y;
10835         if (windows->info.mapped != MagickFalse)
10836           {
10837             if ((x < (int) (windows->info.x+windows->info.width)) &&
10838                 (y < (int) (windows->info.y+windows->info.height)))
10839               (void) XWithdrawWindow(display,windows->info.id,
10840                 windows->info.screen);
10841           }
10842         else
10843           if ((x > (int) (windows->info.x+windows->info.width)) ||
10844               (y > (int) (windows->info.y+windows->info.height)))
10845             (void) XMapWindow(display,windows->info.id);
10846         paste_info.x=(ssize_t) windows->image.x+x;
10847         paste_info.y=(ssize_t) windows->image.y+y;
10848         break;
10849       }
10850       default:
10851       {
10852         if (image->debug != MagickFalse)
10853           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10854             event.type);
10855         break;
10856       }
10857     }
10858   } while ((state & ExitState) == 0);
10859   (void) XSelectInput(display,windows->image.id,
10860     windows->image.attributes.event_mask);
10861   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10862   XSetCursorState(display,windows,MagickFalse);
10863   (void) XFreeCursor(display,cursor);
10864   if ((state & EscapeState) != 0)
10865     return(MagickTrue);
10866   /*
10867     Image pasting is relative to image configuration.
10868   */
10869   XSetCursorState(display,windows,MagickTrue);
10870   XCheckRefreshWindows(display,windows);
10871   width=(unsigned int) image->columns;
10872   height=(unsigned int) image->rows;
10873   x=0;
10874   y=0;
10875   if (windows->image.crop_geometry != (char *) NULL)
10876     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10877   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10878   paste_info.x+=x;
10879   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10880   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10881   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10882   paste_info.y+=y;
10883   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10884   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10885   /*
10886     Paste image with X Image window.
10887   */
10888   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10889   paste_image=DestroyImage(paste_image);
10890   XSetCursorState(display,windows,MagickFalse);
10891   /*
10892     Update image colormap.
10893   */
10894   XConfigureImageColormap(display,resource_info,windows,image);
10895   (void) XConfigureImage(display,resource_info,windows,image,exception);
10896   return(MagickTrue);
10897 }
10898 \f
10899 /*
10900 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10901 %                                                                             %
10902 %                                                                             %
10903 %                                                                             %
10904 +   X P r i n t I m a g e                                                     %
10905 %                                                                             %
10906 %                                                                             %
10907 %                                                                             %
10908 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10909 %
10910 %  XPrintImage() prints an image to a Postscript printer.
10911 %
10912 %  The format of the XPrintImage method is:
10913 %
10914 %      MagickBooleanType XPrintImage(Display *display,
10915 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10916 %        ExceptionInfo *exception)
10917 %
10918 %  A description of each parameter follows:
10919 %
10920 %    o display: Specifies a connection to an X server; returned from
10921 %      XOpenDisplay.
10922 %
10923 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10924 %
10925 %    o windows: Specifies a pointer to a XWindows structure.
10926 %
10927 %    o image: the image.
10928 %
10929 %    o exception: return any errors or warnings in this structure.
10930 %
10931 */
10932 static MagickBooleanType XPrintImage(Display *display,
10933   XResourceInfo *resource_info,XWindows *windows,Image *image,
10934   ExceptionInfo *exception)
10935 {
10936   char
10937     filename[MaxTextExtent],
10938     geometry[MaxTextExtent];
10939
10940   Image
10941     *print_image;
10942
10943   ImageInfo
10944     *image_info;
10945
10946   MagickStatusType
10947     status;
10948
10949   /*
10950     Request Postscript page geometry from user.
10951   */
10952   image_info=CloneImageInfo(resource_info->image_info);
10953   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10954   if (image_info->page != (char *) NULL)
10955     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10956   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10957     "Select Postscript Page Geometry:",geometry);
10958   if (*geometry == '\0')
10959     return(MagickTrue);
10960   image_info->page=GetPageGeometry(geometry);
10961   /*
10962     Apply image transforms.
10963   */
10964   XSetCursorState(display,windows,MagickTrue);
10965   XCheckRefreshWindows(display,windows);
10966   print_image=CloneImage(image,0,0,MagickTrue,exception);
10967   if (print_image == (Image *) NULL)
10968     return(MagickFalse);
10969   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10970     windows->image.ximage->width,windows->image.ximage->height);
10971   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10972   /*
10973     Print image.
10974   */
10975   (void) AcquireUniqueFilename(filename);
10976   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10977     filename);
10978   status=WriteImage(image_info,print_image,exception);
10979   (void) RelinquishUniqueFileResource(filename);
10980   print_image=DestroyImage(print_image);
10981   image_info=DestroyImageInfo(image_info);
10982   XSetCursorState(display,windows,MagickFalse);
10983   return(status != 0 ? MagickTrue : MagickFalse);
10984 }
10985 \f
10986 /*
10987 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10988 %                                                                             %
10989 %                                                                             %
10990 %                                                                             %
10991 +   X R O I I m a g e                                                         %
10992 %                                                                             %
10993 %                                                                             %
10994 %                                                                             %
10995 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10996 %
10997 %  XROIImage() applies an image processing technique to a region of interest.
10998 %
10999 %  The format of the XROIImage method is:
11000 %
11001 %      MagickBooleanType XROIImage(Display *display,
11002 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11003 %        ExceptionInfo *exception)
11004 %
11005 %  A description of each parameter follows:
11006 %
11007 %    o display: Specifies a connection to an X server; returned from
11008 %      XOpenDisplay.
11009 %
11010 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11011 %
11012 %    o windows: Specifies a pointer to a XWindows structure.
11013 %
11014 %    o image: the image; returned from ReadImage.
11015 %
11016 %    o exception: return any errors or warnings in this structure.
11017 %
11018 */
11019 static MagickBooleanType XROIImage(Display *display,
11020   XResourceInfo *resource_info,XWindows *windows,Image **image,
11021   ExceptionInfo *exception)
11022 {
11023 #define ApplyMenus  7
11024
11025   static const char
11026     *ROIMenu[] =
11027     {
11028       "Help",
11029       "Dismiss",
11030       (char *) NULL
11031     },
11032     *ApplyMenu[] =
11033     {
11034       "File",
11035       "Edit",
11036       "Transform",
11037       "Enhance",
11038       "Effects",
11039       "F/X",
11040       "Miscellany",
11041       "Help",
11042       "Dismiss",
11043       (char *) NULL
11044     },
11045     *FileMenu[] =
11046     {
11047       "Save...",
11048       "Print...",
11049       (char *) NULL
11050     },
11051     *EditMenu[] =
11052     {
11053       "Undo",
11054       "Redo",
11055       (char *) NULL
11056     },
11057     *TransformMenu[] =
11058     {
11059       "Flop",
11060       "Flip",
11061       "Rotate Right",
11062       "Rotate Left",
11063       (char *) NULL
11064     },
11065     *EnhanceMenu[] =
11066     {
11067       "Hue...",
11068       "Saturation...",
11069       "Brightness...",
11070       "Gamma...",
11071       "Spiff",
11072       "Dull",
11073       "Contrast Stretch...",
11074       "Sigmoidal Contrast...",
11075       "Normalize",
11076       "Equalize",
11077       "Negate",
11078       "Grayscale",
11079       "Map...",
11080       "Quantize...",
11081       (char *) NULL
11082     },
11083     *EffectsMenu[] =
11084     {
11085       "Despeckle",
11086       "Emboss",
11087       "Reduce Noise",
11088       "Add Noise",
11089       "Sharpen...",
11090       "Blur...",
11091       "Threshold...",
11092       "Edge Detect...",
11093       "Spread...",
11094       "Shade...",
11095       "Raise...",
11096       "Segment...",
11097       (char *) NULL
11098     },
11099     *FXMenu[] =
11100     {
11101       "Solarize...",
11102       "Sepia Tone...",
11103       "Swirl...",
11104       "Implode...",
11105       "Vignette...",
11106       "Wave...",
11107       "Oil Paint...",
11108       "Charcoal Draw...",
11109       (char *) NULL
11110     },
11111     *MiscellanyMenu[] =
11112     {
11113       "Image Info",
11114       "Zoom Image",
11115       "Show Preview...",
11116       "Show Histogram",
11117       "Show Matte",
11118       (char *) NULL
11119     };
11120
11121   static const char
11122     **Menus[ApplyMenus] =
11123     {
11124       FileMenu,
11125       EditMenu,
11126       TransformMenu,
11127       EnhanceMenu,
11128       EffectsMenu,
11129       FXMenu,
11130       MiscellanyMenu
11131     };
11132
11133   static const CommandType
11134     ApplyCommands[] =
11135     {
11136       NullCommand,
11137       NullCommand,
11138       NullCommand,
11139       NullCommand,
11140       NullCommand,
11141       NullCommand,
11142       NullCommand,
11143       HelpCommand,
11144       QuitCommand
11145     },
11146     FileCommands[] =
11147     {
11148       SaveCommand,
11149       PrintCommand
11150     },
11151     EditCommands[] =
11152     {
11153       UndoCommand,
11154       RedoCommand
11155     },
11156     TransformCommands[] =
11157     {
11158       FlopCommand,
11159       FlipCommand,
11160       RotateRightCommand,
11161       RotateLeftCommand
11162     },
11163     EnhanceCommands[] =
11164     {
11165       HueCommand,
11166       SaturationCommand,
11167       BrightnessCommand,
11168       GammaCommand,
11169       SpiffCommand,
11170       DullCommand,
11171       ContrastStretchCommand,
11172       SigmoidalContrastCommand,
11173       NormalizeCommand,
11174       EqualizeCommand,
11175       NegateCommand,
11176       GrayscaleCommand,
11177       MapCommand,
11178       QuantizeCommand
11179     },
11180     EffectsCommands[] =
11181     {
11182       DespeckleCommand,
11183       EmbossCommand,
11184       ReduceNoiseCommand,
11185       AddNoiseCommand,
11186       SharpenCommand,
11187       BlurCommand,
11188       EdgeDetectCommand,
11189       SpreadCommand,
11190       ShadeCommand,
11191       RaiseCommand,
11192       SegmentCommand
11193     },
11194     FXCommands[] =
11195     {
11196       SolarizeCommand,
11197       SepiaToneCommand,
11198       SwirlCommand,
11199       ImplodeCommand,
11200       VignetteCommand,
11201       WaveCommand,
11202       OilPaintCommand,
11203       CharcoalDrawCommand
11204     },
11205     MiscellanyCommands[] =
11206     {
11207       InfoCommand,
11208       ZoomCommand,
11209       ShowPreviewCommand,
11210       ShowHistogramCommand,
11211       ShowMatteCommand
11212     },
11213     ROICommands[] =
11214     {
11215       ROIHelpCommand,
11216       ROIDismissCommand
11217     };
11218
11219   static const CommandType
11220     *Commands[ApplyMenus] =
11221     {
11222       FileCommands,
11223       EditCommands,
11224       TransformCommands,
11225       EnhanceCommands,
11226       EffectsCommands,
11227       FXCommands,
11228       MiscellanyCommands
11229     };
11230
11231   char
11232     command[MaxTextExtent],
11233     text[MaxTextExtent];
11234
11235   CommandType
11236     command_type;
11237
11238   Cursor
11239     cursor;
11240
11241   Image
11242     *roi_image;
11243
11244   int
11245     entry,
11246     id,
11247     x,
11248     y;
11249
11250   MagickRealType
11251     scale_factor;
11252
11253   MagickProgressMonitor
11254     progress_monitor;
11255
11256   RectangleInfo
11257     crop_info,
11258     highlight_info,
11259     roi_info;
11260
11261   unsigned int
11262     height,
11263     width;
11264
11265   size_t
11266     state;
11267
11268   XEvent
11269     event;
11270
11271   /*
11272     Map Command widget.
11273   */
11274   (void) CloneString(&windows->command.name,"ROI");
11275   windows->command.data=0;
11276   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11277   (void) XMapRaised(display,windows->command.id);
11278   XClientMessage(display,windows->image.id,windows->im_protocols,
11279     windows->im_update_widget,CurrentTime);
11280   /*
11281     Track pointer until button 1 is pressed.
11282   */
11283   XQueryPosition(display,windows->image.id,&x,&y);
11284   (void) XSelectInput(display,windows->image.id,
11285     windows->image.attributes.event_mask | PointerMotionMask);
11286   roi_info.x=(ssize_t) windows->image.x+x;
11287   roi_info.y=(ssize_t) windows->image.y+y;
11288   roi_info.width=0;
11289   roi_info.height=0;
11290   cursor=XCreateFontCursor(display,XC_fleur);
11291   state=DefaultState;
11292   do
11293   {
11294     if (windows->info.mapped != MagickFalse)
11295       {
11296         /*
11297           Display pointer position.
11298         */
11299         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11300           (long) roi_info.x,(long) roi_info.y);
11301         XInfoWidget(display,windows,text);
11302       }
11303     /*
11304       Wait for next event.
11305     */
11306     XScreenEvent(display,windows,&event);
11307     if (event.xany.window == windows->command.id)
11308       {
11309         /*
11310           Select a command from the Command widget.
11311         */
11312         id=XCommandWidget(display,windows,ROIMenu,&event);
11313         if (id < 0)
11314           continue;
11315         switch (ROICommands[id])
11316         {
11317           case ROIHelpCommand:
11318           {
11319             XTextViewWidget(display,resource_info,windows,MagickFalse,
11320               "Help Viewer - Region of Interest",ImageROIHelp);
11321             break;
11322           }
11323           case ROIDismissCommand:
11324           {
11325             /*
11326               Prematurely exit.
11327             */
11328             state|=EscapeState;
11329             state|=ExitState;
11330             break;
11331           }
11332           default:
11333             break;
11334         }
11335         continue;
11336       }
11337     switch (event.type)
11338     {
11339       case ButtonPress:
11340       {
11341         if (event.xbutton.button != Button1)
11342           break;
11343         if (event.xbutton.window != windows->image.id)
11344           break;
11345         /*
11346           Note first corner of region of interest rectangle-- exit loop.
11347         */
11348         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11349         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11350         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11351         state|=ExitState;
11352         break;
11353       }
11354       case ButtonRelease:
11355         break;
11356       case Expose:
11357         break;
11358       case KeyPress:
11359       {
11360         KeySym
11361           key_symbol;
11362
11363         if (event.xkey.window != windows->image.id)
11364           break;
11365         /*
11366           Respond to a user key press.
11367         */
11368         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11369           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11370         switch ((int) key_symbol)
11371         {
11372           case XK_Escape:
11373           case XK_F20:
11374           {
11375             /*
11376               Prematurely exit.
11377             */
11378             state|=EscapeState;
11379             state|=ExitState;
11380             break;
11381           }
11382           case XK_F1:
11383           case XK_Help:
11384           {
11385             XTextViewWidget(display,resource_info,windows,MagickFalse,
11386               "Help Viewer - Region of Interest",ImageROIHelp);
11387             break;
11388           }
11389           default:
11390           {
11391             (void) XBell(display,0);
11392             break;
11393           }
11394         }
11395         break;
11396       }
11397       case MotionNotify:
11398       {
11399         /*
11400           Map and unmap Info widget as text cursor crosses its boundaries.
11401         */
11402         x=event.xmotion.x;
11403         y=event.xmotion.y;
11404         if (windows->info.mapped != MagickFalse)
11405           {
11406             if ((x < (int) (windows->info.x+windows->info.width)) &&
11407                 (y < (int) (windows->info.y+windows->info.height)))
11408               (void) XWithdrawWindow(display,windows->info.id,
11409                 windows->info.screen);
11410           }
11411         else
11412           if ((x > (int) (windows->info.x+windows->info.width)) ||
11413               (y > (int) (windows->info.y+windows->info.height)))
11414             (void) XMapWindow(display,windows->info.id);
11415         roi_info.x=(ssize_t) windows->image.x+x;
11416         roi_info.y=(ssize_t) windows->image.y+y;
11417         break;
11418       }
11419       default:
11420         break;
11421     }
11422   } while ((state & ExitState) == 0);
11423   (void) XSelectInput(display,windows->image.id,
11424     windows->image.attributes.event_mask);
11425   if ((state & EscapeState) != 0)
11426     {
11427       /*
11428         User want to exit without region of interest.
11429       */
11430       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11431       (void) XFreeCursor(display,cursor);
11432       return(MagickTrue);
11433     }
11434   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11435   do
11436   {
11437     /*
11438       Size rectangle as pointer moves until the mouse button is released.
11439     */
11440     x=(int) roi_info.x;
11441     y=(int) roi_info.y;
11442     roi_info.width=0;
11443     roi_info.height=0;
11444     state=DefaultState;
11445     do
11446     {
11447       highlight_info=roi_info;
11448       highlight_info.x=roi_info.x-windows->image.x;
11449       highlight_info.y=roi_info.y-windows->image.y;
11450       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11451         {
11452           /*
11453             Display info and draw region of interest rectangle.
11454           */
11455           if (windows->info.mapped == MagickFalse)
11456             (void) XMapWindow(display,windows->info.id);
11457           (void) FormatLocaleString(text,MaxTextExtent,
11458             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11459             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11460           XInfoWidget(display,windows,text);
11461           XHighlightRectangle(display,windows->image.id,
11462             windows->image.highlight_context,&highlight_info);
11463         }
11464       else
11465         if (windows->info.mapped != MagickFalse)
11466           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11467       /*
11468         Wait for next event.
11469       */
11470       XScreenEvent(display,windows,&event);
11471       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11472         XHighlightRectangle(display,windows->image.id,
11473           windows->image.highlight_context,&highlight_info);
11474       switch (event.type)
11475       {
11476         case ButtonPress:
11477         {
11478           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11479           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11480           break;
11481         }
11482         case ButtonRelease:
11483         {
11484           /*
11485             User has committed to region of interest rectangle.
11486           */
11487           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11488           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11489           XSetCursorState(display,windows,MagickFalse);
11490           state|=ExitState;
11491           if (LocaleCompare(windows->command.name,"Apply") == 0)
11492             break;
11493           (void) CloneString(&windows->command.name,"Apply");
11494           windows->command.data=ApplyMenus;
11495           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11496           break;
11497         }
11498         case Expose:
11499           break;
11500         case MotionNotify:
11501         {
11502           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11503           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11504         }
11505         default:
11506           break;
11507       }
11508       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11509           ((state & ExitState) != 0))
11510         {
11511           /*
11512             Check boundary conditions.
11513           */
11514           if (roi_info.x < 0)
11515             roi_info.x=0;
11516           else
11517             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11518               roi_info.x=(ssize_t) windows->image.ximage->width;
11519           if ((int) roi_info.x < x)
11520             roi_info.width=(unsigned int) (x-roi_info.x);
11521           else
11522             {
11523               roi_info.width=(unsigned int) (roi_info.x-x);
11524               roi_info.x=(ssize_t) x;
11525             }
11526           if (roi_info.y < 0)
11527             roi_info.y=0;
11528           else
11529             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11530               roi_info.y=(ssize_t) windows->image.ximage->height;
11531           if ((int) roi_info.y < y)
11532             roi_info.height=(unsigned int) (y-roi_info.y);
11533           else
11534             {
11535               roi_info.height=(unsigned int) (roi_info.y-y);
11536               roi_info.y=(ssize_t) y;
11537             }
11538         }
11539     } while ((state & ExitState) == 0);
11540     /*
11541       Wait for user to grab a corner of the rectangle or press return.
11542     */
11543     state=DefaultState;
11544     command_type=NullCommand;
11545     (void) XMapWindow(display,windows->info.id);
11546     do
11547     {
11548       if (windows->info.mapped != MagickFalse)
11549         {
11550           /*
11551             Display pointer position.
11552           */
11553           (void) FormatLocaleString(text,MaxTextExtent,
11554             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11555             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11556           XInfoWidget(display,windows,text);
11557         }
11558       highlight_info=roi_info;
11559       highlight_info.x=roi_info.x-windows->image.x;
11560       highlight_info.y=roi_info.y-windows->image.y;
11561       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11562         {
11563           state|=EscapeState;
11564           state|=ExitState;
11565           break;
11566         }
11567       if ((state & UpdateRegionState) != 0)
11568         {
11569           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11570           switch (command_type)
11571           {
11572             case UndoCommand:
11573             case RedoCommand:
11574             {
11575               (void) XMagickCommand(display,resource_info,windows,command_type,
11576                 image,exception);
11577               break;
11578             }
11579             default:
11580             {
11581               /*
11582                 Region of interest is relative to image configuration.
11583               */
11584               progress_monitor=SetImageProgressMonitor(*image,
11585                 (MagickProgressMonitor) NULL,(*image)->client_data);
11586               crop_info=roi_info;
11587               width=(unsigned int) (*image)->columns;
11588               height=(unsigned int) (*image)->rows;
11589               x=0;
11590               y=0;
11591               if (windows->image.crop_geometry != (char *) NULL)
11592                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11593                   &width,&height);
11594               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11595               crop_info.x+=x;
11596               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11597               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11598               scale_factor=(MagickRealType)
11599                 height/windows->image.ximage->height;
11600               crop_info.y+=y;
11601               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11602               crop_info.height=(unsigned int)
11603                 (scale_factor*crop_info.height+0.5);
11604               roi_image=CropImage(*image,&crop_info,exception);
11605               (void) SetImageProgressMonitor(*image,progress_monitor,
11606                 (*image)->client_data);
11607               if (roi_image == (Image *) NULL)
11608                 continue;
11609               /*
11610                 Apply image processing technique to the region of interest.
11611               */
11612               windows->image.orphan=MagickTrue;
11613               (void) XMagickCommand(display,resource_info,windows,command_type,
11614                 &roi_image,exception);
11615               progress_monitor=SetImageProgressMonitor(*image,
11616                 (MagickProgressMonitor) NULL,(*image)->client_data);
11617               (void) XMagickCommand(display,resource_info,windows,
11618                 SaveToUndoBufferCommand,image,exception);
11619               windows->image.orphan=MagickFalse;
11620               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11621                 crop_info.x,crop_info.y);
11622               roi_image=DestroyImage(roi_image);
11623               (void) SetImageProgressMonitor(*image,progress_monitor,
11624                 (*image)->client_data);
11625               break;
11626             }
11627           }
11628           if (command_type != InfoCommand)
11629             {
11630               XConfigureImageColormap(display,resource_info,windows,*image);
11631               (void) XConfigureImage(display,resource_info,windows,*image,exception);
11632             }
11633           XCheckRefreshWindows(display,windows);
11634           XInfoWidget(display,windows,text);
11635           (void) XSetFunction(display,windows->image.highlight_context,
11636             GXinvert);
11637           state&=(~UpdateRegionState);
11638         }
11639       XHighlightRectangle(display,windows->image.id,
11640         windows->image.highlight_context,&highlight_info);
11641       XScreenEvent(display,windows,&event);
11642       if (event.xany.window == windows->command.id)
11643         {
11644           /*
11645             Select a command from the Command widget.
11646           */
11647           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11648           command_type=NullCommand;
11649           id=XCommandWidget(display,windows,ApplyMenu,&event);
11650           if (id >= 0)
11651             {
11652               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11653               command_type=ApplyCommands[id];
11654               if (id < ApplyMenus)
11655                 {
11656                   /*
11657                     Select a command from a pop-up menu.
11658                   */
11659                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11660                     (const char **) Menus[id],command);
11661                   if (entry >= 0)
11662                     {
11663                       (void) CopyMagickString(command,Menus[id][entry],
11664                         MaxTextExtent);
11665                       command_type=Commands[id][entry];
11666                     }
11667                 }
11668             }
11669           (void) XSetFunction(display,windows->image.highlight_context,
11670             GXinvert);
11671           XHighlightRectangle(display,windows->image.id,
11672             windows->image.highlight_context,&highlight_info);
11673           if (command_type == HelpCommand)
11674             {
11675               (void) XSetFunction(display,windows->image.highlight_context,
11676                 GXcopy);
11677               XTextViewWidget(display,resource_info,windows,MagickFalse,
11678                 "Help Viewer - Region of Interest",ImageROIHelp);
11679               (void) XSetFunction(display,windows->image.highlight_context,
11680                 GXinvert);
11681               continue;
11682             }
11683           if (command_type == QuitCommand)
11684             {
11685               /*
11686                 exit.
11687               */
11688               state|=EscapeState;
11689               state|=ExitState;
11690               continue;
11691             }
11692           if (command_type != NullCommand)
11693             state|=UpdateRegionState;
11694           continue;
11695         }
11696       XHighlightRectangle(display,windows->image.id,
11697         windows->image.highlight_context,&highlight_info);
11698       switch (event.type)
11699       {
11700         case ButtonPress:
11701         {
11702           x=windows->image.x;
11703           y=windows->image.y;
11704           if (event.xbutton.button != Button1)
11705             break;
11706           if (event.xbutton.window != windows->image.id)
11707             break;
11708           x=windows->image.x+event.xbutton.x;
11709           y=windows->image.y+event.xbutton.y;
11710           if ((x < (int) (roi_info.x+RoiDelta)) &&
11711               (x > (int) (roi_info.x-RoiDelta)) &&
11712               (y < (int) (roi_info.y+RoiDelta)) &&
11713               (y > (int) (roi_info.y-RoiDelta)))
11714             {
11715               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11716               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11717               state|=UpdateConfigurationState;
11718               break;
11719             }
11720           if ((x < (int) (roi_info.x+RoiDelta)) &&
11721               (x > (int) (roi_info.x-RoiDelta)) &&
11722               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11723               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11724             {
11725               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11726               state|=UpdateConfigurationState;
11727               break;
11728             }
11729           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11730               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11731               (y < (int) (roi_info.y+RoiDelta)) &&
11732               (y > (int) (roi_info.y-RoiDelta)))
11733             {
11734               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11735               state|=UpdateConfigurationState;
11736               break;
11737             }
11738           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11739               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11740               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11741               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11742             {
11743               state|=UpdateConfigurationState;
11744               break;
11745             }
11746         }
11747         case ButtonRelease:
11748         {
11749           if (event.xbutton.window == windows->pan.id)
11750             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11751                 (highlight_info.y != crop_info.y-windows->image.y))
11752               XHighlightRectangle(display,windows->image.id,
11753                 windows->image.highlight_context,&highlight_info);
11754           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11755             event.xbutton.time);
11756           break;
11757         }
11758         case Expose:
11759         {
11760           if (event.xexpose.window == windows->image.id)
11761             if (event.xexpose.count == 0)
11762               {
11763                 event.xexpose.x=(int) highlight_info.x;
11764                 event.xexpose.y=(int) highlight_info.y;
11765                 event.xexpose.width=(int) highlight_info.width;
11766                 event.xexpose.height=(int) highlight_info.height;
11767                 XRefreshWindow(display,&windows->image,&event);
11768               }
11769           if (event.xexpose.window == windows->info.id)
11770             if (event.xexpose.count == 0)
11771               XInfoWidget(display,windows,text);
11772           break;
11773         }
11774         case KeyPress:
11775         {
11776           KeySym
11777             key_symbol;
11778
11779           if (event.xkey.window != windows->image.id)
11780             break;
11781           /*
11782             Respond to a user key press.
11783           */
11784           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11785             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11786           switch ((int) key_symbol)
11787           {
11788             case XK_Shift_L:
11789             case XK_Shift_R:
11790               break;
11791             case XK_Escape:
11792             case XK_F20:
11793               state|=EscapeState;
11794             case XK_Return:
11795             {
11796               state|=ExitState;
11797               break;
11798             }
11799             case XK_Home:
11800             case XK_KP_Home:
11801             {
11802               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11803               roi_info.y=(ssize_t) (windows->image.height/2L-
11804                 roi_info.height/2L);
11805               break;
11806             }
11807             case XK_Left:
11808             case XK_KP_Left:
11809             {
11810               roi_info.x--;
11811               break;
11812             }
11813             case XK_Up:
11814             case XK_KP_Up:
11815             case XK_Next:
11816             {
11817               roi_info.y--;
11818               break;
11819             }
11820             case XK_Right:
11821             case XK_KP_Right:
11822             {
11823               roi_info.x++;
11824               break;
11825             }
11826             case XK_Prior:
11827             case XK_Down:
11828             case XK_KP_Down:
11829             {
11830               roi_info.y++;
11831               break;
11832             }
11833             case XK_F1:
11834             case XK_Help:
11835             {
11836               (void) XSetFunction(display,windows->image.highlight_context,
11837                 GXcopy);
11838               XTextViewWidget(display,resource_info,windows,MagickFalse,
11839                 "Help Viewer - Region of Interest",ImageROIHelp);
11840               (void) XSetFunction(display,windows->image.highlight_context,
11841                 GXinvert);
11842               break;
11843             }
11844             default:
11845             {
11846               command_type=XImageWindowCommand(display,resource_info,windows,
11847                 event.xkey.state,key_symbol,image,exception);
11848               if (command_type != NullCommand)
11849                 state|=UpdateRegionState;
11850               break;
11851             }
11852           }
11853           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11854             event.xkey.time);
11855           break;
11856         }
11857         case KeyRelease:
11858           break;
11859         case MotionNotify:
11860         {
11861           if (event.xbutton.window != windows->image.id)
11862             break;
11863           /*
11864             Map and unmap Info widget as text cursor crosses its boundaries.
11865           */
11866           x=event.xmotion.x;
11867           y=event.xmotion.y;
11868           if (windows->info.mapped != MagickFalse)
11869             {
11870               if ((x < (int) (windows->info.x+windows->info.width)) &&
11871                   (y < (int) (windows->info.y+windows->info.height)))
11872                 (void) XWithdrawWindow(display,windows->info.id,
11873                   windows->info.screen);
11874             }
11875           else
11876             if ((x > (int) (windows->info.x+windows->info.width)) ||
11877                 (y > (int) (windows->info.y+windows->info.height)))
11878               (void) XMapWindow(display,windows->info.id);
11879           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11880           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11881           break;
11882         }
11883         case SelectionRequest:
11884         {
11885           XSelectionEvent
11886             notify;
11887
11888           XSelectionRequestEvent
11889             *request;
11890
11891           /*
11892             Set primary selection.
11893           */
11894           (void) FormatLocaleString(text,MaxTextExtent,
11895             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11896             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11897           request=(&(event.xselectionrequest));
11898           (void) XChangeProperty(request->display,request->requestor,
11899             request->property,request->target,8,PropModeReplace,
11900             (unsigned char *) text,(int) strlen(text));
11901           notify.type=SelectionNotify;
11902           notify.display=request->display;
11903           notify.requestor=request->requestor;
11904           notify.selection=request->selection;
11905           notify.target=request->target;
11906           notify.time=request->time;
11907           if (request->property == None)
11908             notify.property=request->target;
11909           else
11910             notify.property=request->property;
11911           (void) XSendEvent(request->display,request->requestor,False,0,
11912             (XEvent *) &notify);
11913         }
11914         default:
11915           break;
11916       }
11917       if ((state & UpdateConfigurationState) != 0)
11918         {
11919           (void) XPutBackEvent(display,&event);
11920           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11921           break;
11922         }
11923     } while ((state & ExitState) == 0);
11924   } while ((state & ExitState) == 0);
11925   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11926   XSetCursorState(display,windows,MagickFalse);
11927   if ((state & EscapeState) != 0)
11928     return(MagickTrue);
11929   return(MagickTrue);
11930 }
11931 \f
11932 /*
11933 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11934 %                                                                             %
11935 %                                                                             %
11936 %                                                                             %
11937 +   X R o t a t e I m a g e                                                   %
11938 %                                                                             %
11939 %                                                                             %
11940 %                                                                             %
11941 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11942 %
11943 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11944 %  rotation angle is computed from the slope of a line drawn by the user.
11945 %
11946 %  The format of the XRotateImage method is:
11947 %
11948 %      MagickBooleanType XRotateImage(Display *display,
11949 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11950 %        Image **image,ExceptionInfo *exception)
11951 %
11952 %  A description of each parameter follows:
11953 %
11954 %    o display: Specifies a connection to an X server; returned from
11955 %      XOpenDisplay.
11956 %
11957 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11958 %
11959 %    o windows: Specifies a pointer to a XWindows structure.
11960 %
11961 %    o degrees: Specifies the number of degrees to rotate the image.
11962 %
11963 %    o image: the image.
11964 %
11965 %    o exception: return any errors or warnings in this structure.
11966 %
11967 */
11968 static MagickBooleanType XRotateImage(Display *display,
11969   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
11970   ExceptionInfo *exception)
11971 {
11972   static const char
11973     *RotateMenu[] =
11974     {
11975       "Pixel Color",
11976       "Direction",
11977       "Help",
11978       "Dismiss",
11979       (char *) NULL
11980     };
11981
11982   static ModeType
11983     direction = HorizontalRotateCommand;
11984
11985   static const ModeType
11986     DirectionCommands[] =
11987     {
11988       HorizontalRotateCommand,
11989       VerticalRotateCommand
11990     },
11991     RotateCommands[] =
11992     {
11993       RotateColorCommand,
11994       RotateDirectionCommand,
11995       RotateHelpCommand,
11996       RotateDismissCommand
11997     };
11998
11999   static unsigned int
12000     pen_id = 0;
12001
12002   char
12003     command[MaxTextExtent],
12004     text[MaxTextExtent];
12005
12006   Image
12007     *rotate_image;
12008
12009   int
12010     id,
12011     x,
12012     y;
12013
12014   MagickRealType
12015     normalized_degrees;
12016
12017   register int
12018     i;
12019
12020   unsigned int
12021     height,
12022     rotations,
12023     width;
12024
12025   if (degrees == 0.0)
12026     {
12027       unsigned int
12028         distance;
12029
12030       size_t
12031         state;
12032
12033       XEvent
12034         event;
12035
12036       XSegment
12037         rotate_info;
12038
12039       /*
12040         Map Command widget.
12041       */
12042       (void) CloneString(&windows->command.name,"Rotate");
12043       windows->command.data=2;
12044       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12045       (void) XMapRaised(display,windows->command.id);
12046       XClientMessage(display,windows->image.id,windows->im_protocols,
12047         windows->im_update_widget,CurrentTime);
12048       /*
12049         Wait for first button press.
12050       */
12051       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12052       XQueryPosition(display,windows->image.id,&x,&y);
12053       rotate_info.x1=x;
12054       rotate_info.y1=y;
12055       rotate_info.x2=x;
12056       rotate_info.y2=y;
12057       state=DefaultState;
12058       do
12059       {
12060         XHighlightLine(display,windows->image.id,
12061           windows->image.highlight_context,&rotate_info);
12062         /*
12063           Wait for next event.
12064         */
12065         XScreenEvent(display,windows,&event);
12066         XHighlightLine(display,windows->image.id,
12067           windows->image.highlight_context,&rotate_info);
12068         if (event.xany.window == windows->command.id)
12069           {
12070             /*
12071               Select a command from the Command widget.
12072             */
12073             id=XCommandWidget(display,windows,RotateMenu,&event);
12074             if (id < 0)
12075               continue;
12076             (void) XSetFunction(display,windows->image.highlight_context,
12077               GXcopy);
12078             switch (RotateCommands[id])
12079             {
12080               case RotateColorCommand:
12081               {
12082                 const char
12083                   *ColorMenu[MaxNumberPens];
12084
12085                 int
12086                   pen_number;
12087
12088                 XColor
12089                   color;
12090
12091                 /*
12092                   Initialize menu selections.
12093                 */
12094                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12095                   ColorMenu[i]=resource_info->pen_colors[i];
12096                 ColorMenu[MaxNumberPens-2]="Browser...";
12097                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12098                 /*
12099                   Select a pen color from the pop-up menu.
12100                 */
12101                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12102                   (const char **) ColorMenu,command);
12103                 if (pen_number < 0)
12104                   break;
12105                 if (pen_number == (MaxNumberPens-2))
12106                   {
12107                     static char
12108                       color_name[MaxTextExtent] = "gray";
12109
12110                     /*
12111                       Select a pen color from a dialog.
12112                     */
12113                     resource_info->pen_colors[pen_number]=color_name;
12114                     XColorBrowserWidget(display,windows,"Select",color_name);
12115                     if (*color_name == '\0')
12116                       break;
12117                   }
12118                 /*
12119                   Set pen color.
12120                 */
12121                 (void) XParseColor(display,windows->map_info->colormap,
12122                   resource_info->pen_colors[pen_number],&color);
12123                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12124                   (unsigned int) MaxColors,&color);
12125                 windows->pixel_info->pen_colors[pen_number]=color;
12126                 pen_id=(unsigned int) pen_number;
12127                 break;
12128               }
12129               case RotateDirectionCommand:
12130               {
12131                 static const char
12132                   *Directions[] =
12133                   {
12134                     "horizontal",
12135                     "vertical",
12136                     (char *) NULL,
12137                   };
12138
12139                 /*
12140                   Select a command from the pop-up menu.
12141                 */
12142                 id=XMenuWidget(display,windows,RotateMenu[id],
12143                   Directions,command);
12144                 if (id >= 0)
12145                   direction=DirectionCommands[id];
12146                 break;
12147               }
12148               case RotateHelpCommand:
12149               {
12150                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12151                   "Help Viewer - Image Rotation",ImageRotateHelp);
12152                 break;
12153               }
12154               case RotateDismissCommand:
12155               {
12156                 /*
12157                   Prematurely exit.
12158                 */
12159                 state|=EscapeState;
12160                 state|=ExitState;
12161                 break;
12162               }
12163               default:
12164                 break;
12165             }
12166             (void) XSetFunction(display,windows->image.highlight_context,
12167               GXinvert);
12168             continue;
12169           }
12170         switch (event.type)
12171         {
12172           case ButtonPress:
12173           {
12174             if (event.xbutton.button != Button1)
12175               break;
12176             if (event.xbutton.window != windows->image.id)
12177               break;
12178             /*
12179               exit loop.
12180             */
12181             (void) XSetFunction(display,windows->image.highlight_context,
12182               GXcopy);
12183             rotate_info.x1=event.xbutton.x;
12184             rotate_info.y1=event.xbutton.y;
12185             state|=ExitState;
12186             break;
12187           }
12188           case ButtonRelease:
12189             break;
12190           case Expose:
12191             break;
12192           case KeyPress:
12193           {
12194             char
12195               command[MaxTextExtent];
12196
12197             KeySym
12198               key_symbol;
12199
12200             if (event.xkey.window != windows->image.id)
12201               break;
12202             /*
12203               Respond to a user key press.
12204             */
12205             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12206               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12207             switch ((int) key_symbol)
12208             {
12209               case XK_Escape:
12210               case XK_F20:
12211               {
12212                 /*
12213                   Prematurely exit.
12214                 */
12215                 state|=EscapeState;
12216                 state|=ExitState;
12217                 break;
12218               }
12219               case XK_F1:
12220               case XK_Help:
12221               {
12222                 (void) XSetFunction(display,windows->image.highlight_context,
12223                   GXcopy);
12224                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12225                   "Help Viewer - Image Rotation",ImageRotateHelp);
12226                 (void) XSetFunction(display,windows->image.highlight_context,
12227                   GXinvert);
12228                 break;
12229               }
12230               default:
12231               {
12232                 (void) XBell(display,0);
12233                 break;
12234               }
12235             }
12236             break;
12237           }
12238           case MotionNotify:
12239           {
12240             rotate_info.x1=event.xmotion.x;
12241             rotate_info.y1=event.xmotion.y;
12242           }
12243         }
12244         rotate_info.x2=rotate_info.x1;
12245         rotate_info.y2=rotate_info.y1;
12246         if (direction == HorizontalRotateCommand)
12247           rotate_info.x2+=32;
12248         else
12249           rotate_info.y2-=32;
12250       } while ((state & ExitState) == 0);
12251       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12252       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12253       if ((state & EscapeState) != 0)
12254         return(MagickTrue);
12255       /*
12256         Draw line as pointer moves until the mouse button is released.
12257       */
12258       distance=0;
12259       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12260       state=DefaultState;
12261       do
12262       {
12263         if (distance > 9)
12264           {
12265             /*
12266               Display info and draw rotation line.
12267             */
12268             if (windows->info.mapped == MagickFalse)
12269               (void) XMapWindow(display,windows->info.id);
12270             (void) FormatLocaleString(text,MaxTextExtent," %g",
12271               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12272             XInfoWidget(display,windows,text);
12273             XHighlightLine(display,windows->image.id,
12274               windows->image.highlight_context,&rotate_info);
12275           }
12276         else
12277           if (windows->info.mapped != MagickFalse)
12278             (void) XWithdrawWindow(display,windows->info.id,
12279               windows->info.screen);
12280         /*
12281           Wait for next event.
12282         */
12283         XScreenEvent(display,windows,&event);
12284         if (distance > 9)
12285           XHighlightLine(display,windows->image.id,
12286             windows->image.highlight_context,&rotate_info);
12287         switch (event.type)
12288         {
12289           case ButtonPress:
12290             break;
12291           case ButtonRelease:
12292           {
12293             /*
12294               User has committed to rotation line.
12295             */
12296             rotate_info.x2=event.xbutton.x;
12297             rotate_info.y2=event.xbutton.y;
12298             state|=ExitState;
12299             break;
12300           }
12301           case Expose:
12302             break;
12303           case MotionNotify:
12304           {
12305             rotate_info.x2=event.xmotion.x;
12306             rotate_info.y2=event.xmotion.y;
12307           }
12308           default:
12309             break;
12310         }
12311         /*
12312           Check boundary conditions.
12313         */
12314         if (rotate_info.x2 < 0)
12315           rotate_info.x2=0;
12316         else
12317           if (rotate_info.x2 > (int) windows->image.width)
12318             rotate_info.x2=(short) windows->image.width;
12319         if (rotate_info.y2 < 0)
12320           rotate_info.y2=0;
12321         else
12322           if (rotate_info.y2 > (int) windows->image.height)
12323             rotate_info.y2=(short) windows->image.height;
12324         /*
12325           Compute rotation angle from the slope of the line.
12326         */
12327         degrees=0.0;
12328         distance=(unsigned int)
12329           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12330           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12331         if (distance > 9)
12332           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12333             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12334       } while ((state & ExitState) == 0);
12335       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12336       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12337       if (distance <= 9)
12338         return(MagickTrue);
12339     }
12340   if (direction == VerticalRotateCommand)
12341     degrees-=90.0;
12342   if (degrees == 0.0)
12343     return(MagickTrue);
12344   /*
12345     Rotate image.
12346   */
12347   normalized_degrees=degrees;
12348   while (normalized_degrees < -45.0)
12349     normalized_degrees+=360.0;
12350   for (rotations=0; normalized_degrees > 45.0; rotations++)
12351     normalized_degrees-=90.0;
12352   if (normalized_degrees != 0.0)
12353     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12354       exception);
12355   XSetCursorState(display,windows,MagickTrue);
12356   XCheckRefreshWindows(display,windows);
12357   (*image)->background_color.red=ScaleShortToQuantum(
12358     windows->pixel_info->pen_colors[pen_id].red);
12359   (*image)->background_color.green=ScaleShortToQuantum(
12360     windows->pixel_info->pen_colors[pen_id].green);
12361   (*image)->background_color.blue=ScaleShortToQuantum(
12362     windows->pixel_info->pen_colors[pen_id].blue);
12363   rotate_image=RotateImage(*image,degrees,exception);
12364   XSetCursorState(display,windows,MagickFalse);
12365   if (rotate_image == (Image *) NULL)
12366     return(MagickFalse);
12367   *image=DestroyImage(*image);
12368   *image=rotate_image;
12369   if (windows->image.crop_geometry != (char *) NULL)
12370     {
12371       /*
12372         Rotate crop geometry.
12373       */
12374       width=(unsigned int) (*image)->columns;
12375       height=(unsigned int) (*image)->rows;
12376       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12377       switch (rotations % 4)
12378       {
12379         default:
12380         case 0:
12381           break;
12382         case 1:
12383         {
12384           /*
12385             Rotate 90 degrees.
12386           */
12387           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12388             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12389             (int) height-y,x);
12390           break;
12391         }
12392         case 2:
12393         {
12394           /*
12395             Rotate 180 degrees.
12396           */
12397           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12398             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12399           break;
12400         }
12401         case 3:
12402         {
12403           /*
12404             Rotate 270 degrees.
12405           */
12406           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12407             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12408           break;
12409         }
12410       }
12411     }
12412   if (windows->image.orphan != MagickFalse)
12413     return(MagickTrue);
12414   if (normalized_degrees != 0.0)
12415     {
12416       /*
12417         Update image colormap.
12418       */
12419       windows->image.window_changes.width=(int) (*image)->columns;
12420       windows->image.window_changes.height=(int) (*image)->rows;
12421       if (windows->image.crop_geometry != (char *) NULL)
12422         {
12423           /*
12424             Obtain dimensions of image from crop geometry.
12425           */
12426           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12427             &width,&height);
12428           windows->image.window_changes.width=(int) width;
12429           windows->image.window_changes.height=(int) height;
12430         }
12431       XConfigureImageColormap(display,resource_info,windows,*image);
12432     }
12433   else
12434     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12435       {
12436         windows->image.window_changes.width=windows->image.ximage->height;
12437         windows->image.window_changes.height=windows->image.ximage->width;
12438       }
12439   /*
12440     Update image configuration.
12441   */
12442   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12443   return(MagickTrue);
12444 }
12445 \f
12446 /*
12447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12448 %                                                                             %
12449 %                                                                             %
12450 %                                                                             %
12451 +   X S a v e I m a g e                                                       %
12452 %                                                                             %
12453 %                                                                             %
12454 %                                                                             %
12455 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12456 %
12457 %  XSaveImage() saves an image to a file.
12458 %
12459 %  The format of the XSaveImage method is:
12460 %
12461 %      MagickBooleanType XSaveImage(Display *display,
12462 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12463 %        ExceptionInfo *exception)
12464 %
12465 %  A description of each parameter follows:
12466 %
12467 %    o display: Specifies a connection to an X server; returned from
12468 %      XOpenDisplay.
12469 %
12470 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12471 %
12472 %    o windows: Specifies a pointer to a XWindows structure.
12473 %
12474 %    o image: the image.
12475 %
12476 %    o exception: return any errors or warnings in this structure.
12477 %
12478 */
12479 static MagickBooleanType XSaveImage(Display *display,
12480   XResourceInfo *resource_info,XWindows *windows,Image *image,
12481   ExceptionInfo *exception)
12482 {
12483   char
12484     filename[MaxTextExtent],
12485     geometry[MaxTextExtent];
12486
12487   Image
12488     *save_image;
12489
12490   ImageInfo
12491     *image_info;
12492
12493   MagickStatusType
12494     status;
12495
12496   /*
12497     Request file name from user.
12498   */
12499   if (resource_info->write_filename != (char *) NULL)
12500     (void) CopyMagickString(filename,resource_info->write_filename,
12501       MaxTextExtent);
12502   else
12503     {
12504       char
12505         path[MaxTextExtent];
12506
12507       int
12508         status;
12509
12510       GetPathComponent(image->filename,HeadPath,path);
12511       GetPathComponent(image->filename,TailPath,filename);
12512       if (*path != '\0')
12513         {
12514           status=chdir(path);
12515           if (status == -1)
12516             (void) ThrowMagickException(exception,GetMagickModule(),
12517               FileOpenError,"UnableToOpenFile","%s",path);
12518         }
12519     }
12520   XFileBrowserWidget(display,windows,"Save",filename);
12521   if (*filename == '\0')
12522     return(MagickTrue);
12523   if (IsPathAccessible(filename) != MagickFalse)
12524     {
12525       int
12526         status;
12527
12528       /*
12529         File exists-- seek user's permission before overwriting.
12530       */
12531       status=XConfirmWidget(display,windows,"Overwrite",filename);
12532       if (status <= 0)
12533         return(MagickTrue);
12534     }
12535   image_info=CloneImageInfo(resource_info->image_info);
12536   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12537   (void) SetImageInfo(image_info,1,exception);
12538   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12539       (LocaleCompare(image_info->magick,"JPG") == 0))
12540     {
12541       char
12542         quality[MaxTextExtent];
12543
12544       int
12545         status;
12546
12547       /*
12548         Request JPEG quality from user.
12549       */
12550       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12551         image->quality);
12552       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12553         quality);
12554       if (*quality == '\0')
12555         return(MagickTrue);
12556       image->quality=StringToUnsignedLong(quality);
12557       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12558     }
12559   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12560       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12561       (LocaleCompare(image_info->magick,"PS") == 0) ||
12562       (LocaleCompare(image_info->magick,"PS2") == 0))
12563     {
12564       char
12565         geometry[MaxTextExtent];
12566
12567       /*
12568         Request page geometry from user.
12569       */
12570       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12571       if (LocaleCompare(image_info->magick,"PDF") == 0)
12572         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12573       if (image_info->page != (char *) NULL)
12574         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12575       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12576         "Select page geometry:",geometry);
12577       if (*geometry != '\0')
12578         image_info->page=GetPageGeometry(geometry);
12579     }
12580   /*
12581     Apply image transforms.
12582   */
12583   XSetCursorState(display,windows,MagickTrue);
12584   XCheckRefreshWindows(display,windows);
12585   save_image=CloneImage(image,0,0,MagickTrue,exception);
12586   if (save_image == (Image *) NULL)
12587     return(MagickFalse);
12588   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12589     windows->image.ximage->width,windows->image.ximage->height);
12590   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12591   /*
12592     Write image.
12593   */
12594   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12595   status=WriteImage(image_info,save_image,exception);
12596   if (status != MagickFalse)
12597     image->taint=MagickFalse;
12598   save_image=DestroyImage(save_image);
12599   image_info=DestroyImageInfo(image_info);
12600   XSetCursorState(display,windows,MagickFalse);
12601   return(status != 0 ? MagickTrue : MagickFalse);
12602 }
12603 \f
12604 /*
12605 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12606 %                                                                             %
12607 %                                                                             %
12608 %                                                                             %
12609 +   X S c r e e n E v e n t                                                   %
12610 %                                                                             %
12611 %                                                                             %
12612 %                                                                             %
12613 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12614 %
12615 %  XScreenEvent() handles global events associated with the Pan and Magnify
12616 %  windows.
12617 %
12618 %  The format of the XScreenEvent function is:
12619 %
12620 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12621 %
12622 %  A description of each parameter follows:
12623 %
12624 %    o display: Specifies a pointer to the Display structure;  returned from
12625 %      XOpenDisplay.
12626 %
12627 %    o windows: Specifies a pointer to a XWindows structure.
12628 %
12629 %    o event: Specifies a pointer to a X11 XEvent structure.
12630 %
12631 %
12632 */
12633
12634 #if defined(__cplusplus) || defined(c_plusplus)
12635 extern "C" {
12636 #endif
12637
12638 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12639 {
12640   register XWindows
12641     *windows;
12642
12643   windows=(XWindows *) data;
12644   if ((event->type == ClientMessage) &&
12645       (event->xclient.window == windows->image.id))
12646     return(MagickFalse);
12647   return(MagickTrue);
12648 }
12649
12650 #if defined(__cplusplus) || defined(c_plusplus)
12651 }
12652 #endif
12653
12654 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12655 {
12656   register int
12657     x,
12658     y;
12659
12660   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12661   if (event->xany.window == windows->command.id)
12662     return;
12663   switch (event->type)
12664   {
12665     case ButtonPress:
12666     case ButtonRelease:
12667     {
12668       if ((event->xbutton.button == Button3) &&
12669           (event->xbutton.state & Mod1Mask))
12670         {
12671           /*
12672             Convert Alt-Button3 to Button2.
12673           */
12674           event->xbutton.button=Button2;
12675           event->xbutton.state&=(~Mod1Mask);
12676         }
12677       if (event->xbutton.window == windows->backdrop.id)
12678         {
12679           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12680             event->xbutton.time);
12681           break;
12682         }
12683       if (event->xbutton.window == windows->pan.id)
12684         {
12685           XPanImage(display,windows,event);
12686           break;
12687         }
12688       if (event->xbutton.window == windows->image.id)
12689         if (event->xbutton.button == Button2)
12690           {
12691             /*
12692               Update magnified image.
12693             */
12694             x=event->xbutton.x;
12695             y=event->xbutton.y;
12696             if (x < 0)
12697               x=0;
12698             else
12699               if (x >= (int) windows->image.width)
12700                 x=(int) (windows->image.width-1);
12701             windows->magnify.x=(int) windows->image.x+x;
12702             if (y < 0)
12703               y=0;
12704             else
12705              if (y >= (int) windows->image.height)
12706                y=(int) (windows->image.height-1);
12707             windows->magnify.y=windows->image.y+y;
12708             if (windows->magnify.mapped == MagickFalse)
12709               (void) XMapRaised(display,windows->magnify.id);
12710             XMakeMagnifyImage(display,windows);
12711             if (event->type == ButtonRelease)
12712               (void) XWithdrawWindow(display,windows->info.id,
12713                 windows->info.screen);
12714             break;
12715           }
12716       break;
12717     }
12718     case ClientMessage:
12719     {
12720       /*
12721         If client window delete message, exit.
12722       */
12723       if (event->xclient.message_type != windows->wm_protocols)
12724         break;
12725       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12726         break;
12727       if (event->xclient.window == windows->magnify.id)
12728         {
12729           (void) XWithdrawWindow(display,windows->magnify.id,
12730             windows->magnify.screen);
12731           break;
12732         }
12733       break;
12734     }
12735     case ConfigureNotify:
12736     {
12737       if (event->xconfigure.window == windows->magnify.id)
12738         {
12739           unsigned int
12740             magnify;
12741
12742           /*
12743             Magnify window has a new configuration.
12744           */
12745           windows->magnify.width=(unsigned int) event->xconfigure.width;
12746           windows->magnify.height=(unsigned int) event->xconfigure.height;
12747           if (windows->magnify.mapped == MagickFalse)
12748             break;
12749           magnify=1;
12750           while ((int) magnify <= event->xconfigure.width)
12751             magnify<<=1;
12752           while ((int) magnify <= event->xconfigure.height)
12753             magnify<<=1;
12754           magnify>>=1;
12755           if (((int) magnify != event->xconfigure.width) ||
12756               ((int) magnify != event->xconfigure.height))
12757             {
12758               XWindowChanges
12759                 window_changes;
12760
12761               window_changes.width=(int) magnify;
12762               window_changes.height=(int) magnify;
12763               (void) XReconfigureWMWindow(display,windows->magnify.id,
12764                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12765                 &window_changes);
12766               break;
12767             }
12768           XMakeMagnifyImage(display,windows);
12769           break;
12770         }
12771       break;
12772     }
12773     case Expose:
12774     {
12775       if (event->xexpose.window == windows->image.id)
12776         {
12777           XRefreshWindow(display,&windows->image,event);
12778           break;
12779         }
12780       if (event->xexpose.window == windows->pan.id)
12781         if (event->xexpose.count == 0)
12782           {
12783             XDrawPanRectangle(display,windows);
12784             break;
12785           }
12786       if (event->xexpose.window == windows->magnify.id)
12787         if (event->xexpose.count == 0)
12788           {
12789             XMakeMagnifyImage(display,windows);
12790             break;
12791           }
12792       break;
12793     }
12794     case KeyPress:
12795     {
12796       char
12797         command[MaxTextExtent];
12798
12799       KeySym
12800         key_symbol;
12801
12802       if (event->xkey.window != windows->magnify.id)
12803         break;
12804       /*
12805         Respond to a user key press.
12806       */
12807       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12808         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12809       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12810       break;
12811     }
12812     case MapNotify:
12813     {
12814       if (event->xmap.window == windows->magnify.id)
12815         {
12816           windows->magnify.mapped=MagickTrue;
12817           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12818           break;
12819         }
12820       if (event->xmap.window == windows->info.id)
12821         {
12822           windows->info.mapped=MagickTrue;
12823           break;
12824         }
12825       break;
12826     }
12827     case MotionNotify:
12828     {
12829       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12830       if (event->xmotion.window == windows->image.id)
12831         if (windows->magnify.mapped != MagickFalse)
12832           {
12833             /*
12834               Update magnified image.
12835             */
12836             x=event->xmotion.x;
12837             y=event->xmotion.y;
12838             if (x < 0)
12839               x=0;
12840             else
12841               if (x >= (int) windows->image.width)
12842                 x=(int) (windows->image.width-1);
12843             windows->magnify.x=(int) windows->image.x+x;
12844             if (y < 0)
12845               y=0;
12846             else
12847              if (y >= (int) windows->image.height)
12848                y=(int) (windows->image.height-1);
12849             windows->magnify.y=windows->image.y+y;
12850             XMakeMagnifyImage(display,windows);
12851           }
12852       break;
12853     }
12854     case UnmapNotify:
12855     {
12856       if (event->xunmap.window == windows->magnify.id)
12857         {
12858           windows->magnify.mapped=MagickFalse;
12859           break;
12860         }
12861       if (event->xunmap.window == windows->info.id)
12862         {
12863           windows->info.mapped=MagickFalse;
12864           break;
12865         }
12866       break;
12867     }
12868     default:
12869       break;
12870   }
12871 }
12872 \f
12873 /*
12874 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12875 %                                                                             %
12876 %                                                                             %
12877 %                                                                             %
12878 +   X S e t C r o p G e o m e t r y                                           %
12879 %                                                                             %
12880 %                                                                             %
12881 %                                                                             %
12882 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12883 %
12884 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12885 %  and translates it to a cropping geometry relative to the image.
12886 %
12887 %  The format of the XSetCropGeometry method is:
12888 %
12889 %      void XSetCropGeometry(Display *display,XWindows *windows,
12890 %        RectangleInfo *crop_info,Image *image)
12891 %
12892 %  A description of each parameter follows:
12893 %
12894 %    o display: Specifies a connection to an X server; returned from
12895 %      XOpenDisplay.
12896 %
12897 %    o windows: Specifies a pointer to a XWindows structure.
12898 %
12899 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12900 %      Image window to crop.
12901 %
12902 %    o image: the image.
12903 %
12904 */
12905 static void XSetCropGeometry(Display *display,XWindows *windows,
12906   RectangleInfo *crop_info,Image *image)
12907 {
12908   char
12909     text[MaxTextExtent];
12910
12911   int
12912     x,
12913     y;
12914
12915   MagickRealType
12916     scale_factor;
12917
12918   unsigned int
12919     height,
12920     width;
12921
12922   if (windows->info.mapped != MagickFalse)
12923     {
12924       /*
12925         Display info on cropping rectangle.
12926       */
12927       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12928         (double) crop_info->width,(double) crop_info->height,(double)
12929         crop_info->x,(double) crop_info->y);
12930       XInfoWidget(display,windows,text);
12931     }
12932   /*
12933     Cropping geometry is relative to any previous crop geometry.
12934   */
12935   x=0;
12936   y=0;
12937   width=(unsigned int) image->columns;
12938   height=(unsigned int) image->rows;
12939   if (windows->image.crop_geometry != (char *) NULL)
12940     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12941   else
12942     windows->image.crop_geometry=AcquireString((char *) NULL);
12943   /*
12944     Define the crop geometry string from the cropping rectangle.
12945   */
12946   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12947   if (crop_info->x > 0)
12948     x+=(int) (scale_factor*crop_info->x+0.5);
12949   width=(unsigned int) (scale_factor*crop_info->width+0.5);
12950   if (width == 0)
12951     width=1;
12952   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12953   if (crop_info->y > 0)
12954     y+=(int) (scale_factor*crop_info->y+0.5);
12955   height=(unsigned int) (scale_factor*crop_info->height+0.5);
12956   if (height == 0)
12957     height=1;
12958   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12959     "%ux%u%+d%+d",width,height,x,y);
12960 }
12961 \f
12962 /*
12963 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12964 %                                                                             %
12965 %                                                                             %
12966 %                                                                             %
12967 +   X T i l e I m a g e                                                       %
12968 %                                                                             %
12969 %                                                                             %
12970 %                                                                             %
12971 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12972 %
12973 %  XTileImage() loads or deletes a selected tile from a visual image directory.
12974 %  The load or delete command is chosen from a menu.
12975 %
12976 %  The format of the XTileImage method is:
12977 %
12978 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
12979 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
12980 %
12981 %  A description of each parameter follows:
12982 %
12983 %    o tile_image:  XTileImage reads or deletes the tile image
12984 %      and returns it.  A null image is returned if an error occurs.
12985 %
12986 %    o display: Specifies a connection to an X server;  returned from
12987 %      XOpenDisplay.
12988 %
12989 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12990 %
12991 %    o windows: Specifies a pointer to a XWindows structure.
12992 %
12993 %    o image: the image; returned from ReadImage.
12994 %
12995 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
12996 %      the entire image is refreshed.
12997 %
12998 %    o exception: return any errors or warnings in this structure.
12999 %
13000 */
13001 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13002   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13003 {
13004   static const char
13005     *VerbMenu[] =
13006     {
13007       "Load",
13008       "Next",
13009       "Former",
13010       "Delete",
13011       "Update",
13012       (char *) NULL,
13013     };
13014
13015   static const ModeType
13016     TileCommands[] =
13017     {
13018       TileLoadCommand,
13019       TileNextCommand,
13020       TileFormerCommand,
13021       TileDeleteCommand,
13022       TileUpdateCommand
13023     };
13024
13025   char
13026     command[MaxTextExtent],
13027     filename[MaxTextExtent];
13028
13029   Image
13030     *tile_image;
13031
13032   int
13033     id,
13034     status,
13035     tile,
13036     x,
13037     y;
13038
13039   MagickRealType
13040     scale_factor;
13041
13042   register char
13043     *p,
13044     *q;
13045
13046   register int
13047     i;
13048
13049   unsigned int
13050     height,
13051     width;
13052
13053   /*
13054     Tile image is relative to montage image configuration.
13055   */
13056   x=0;
13057   y=0;
13058   width=(unsigned int) image->columns;
13059   height=(unsigned int) image->rows;
13060   if (windows->image.crop_geometry != (char *) NULL)
13061     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13062   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13063   event->xbutton.x+=windows->image.x;
13064   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13065   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13066   event->xbutton.y+=windows->image.y;
13067   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13068   /*
13069     Determine size and location of each tile in the visual image directory.
13070   */
13071   width=(unsigned int) image->columns;
13072   height=(unsigned int) image->rows;
13073   x=0;
13074   y=0;
13075   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13076   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13077     (event->xbutton.x-x)/width;
13078   if (tile < 0)
13079     {
13080       /*
13081         Button press is outside any tile.
13082       */
13083       (void) XBell(display,0);
13084       return((Image *) NULL);
13085     }
13086   /*
13087     Determine file name from the tile directory.
13088   */
13089   p=image->directory;
13090   for (i=tile; (i != 0) && (*p != '\0'); )
13091   {
13092     if (*p == '\n')
13093       i--;
13094     p++;
13095   }
13096   if (*p == '\0')
13097     {
13098       /*
13099         Button press is outside any tile.
13100       */
13101       (void) XBell(display,0);
13102       return((Image *) NULL);
13103     }
13104   /*
13105     Select a command from the pop-up menu.
13106   */
13107   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13108   if (id < 0)
13109     return((Image *) NULL);
13110   q=p;
13111   while ((*q != '\n') && (*q != '\0'))
13112     q++;
13113   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13114   /*
13115     Perform command for the selected tile.
13116   */
13117   XSetCursorState(display,windows,MagickTrue);
13118   XCheckRefreshWindows(display,windows);
13119   tile_image=NewImageList();
13120   switch (TileCommands[id])
13121   {
13122     case TileLoadCommand:
13123     {
13124       /*
13125         Load tile image.
13126       */
13127       XCheckRefreshWindows(display,windows);
13128       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13129         MaxTextExtent);
13130       (void) CopyMagickString(resource_info->image_info->filename,filename,
13131         MaxTextExtent);
13132       tile_image=ReadImage(resource_info->image_info,exception);
13133       CatchException(exception);
13134       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13135       break;
13136     }
13137     case TileNextCommand:
13138     {
13139       /*
13140         Display next image.
13141       */
13142       XClientMessage(display,windows->image.id,windows->im_protocols,
13143         windows->im_next_image,CurrentTime);
13144       break;
13145     }
13146     case TileFormerCommand:
13147     {
13148       /*
13149         Display former image.
13150       */
13151       XClientMessage(display,windows->image.id,windows->im_protocols,
13152         windows->im_former_image,CurrentTime);
13153       break;
13154     }
13155     case TileDeleteCommand:
13156     {
13157       /*
13158         Delete tile image.
13159       */
13160       if (IsPathAccessible(filename) == MagickFalse)
13161         {
13162           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13163           break;
13164         }
13165       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13166       if (status <= 0)
13167         break;
13168       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13169       if (status != MagickFalse)
13170         {
13171           XNoticeWidget(display,windows,"Unable to delete image file:",
13172             filename);
13173           break;
13174         }
13175     }
13176     case TileUpdateCommand:
13177     {
13178       int
13179         x_offset,
13180         y_offset;
13181
13182       PixelPacket
13183         pixel;
13184
13185       register int
13186         j;
13187
13188       register Quantum
13189         *s;
13190
13191       /*
13192         Ensure all the images exist.
13193       */
13194       tile=0;
13195       for (p=image->directory; *p != '\0'; p++)
13196       {
13197         CacheView
13198           *image_view;
13199
13200         q=p;
13201         while ((*q != '\n') && (*q != '\0'))
13202           q++;
13203         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13204         p=q;
13205         if (IsPathAccessible(filename) != MagickFalse)
13206           {
13207             tile++;
13208             continue;
13209           }
13210         /*
13211           Overwrite tile with background color.
13212         */
13213         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13214         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13215         image_view=AcquireCacheView(image);
13216         (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13217         for (i=0; i < (int) height; i++)
13218         {
13219           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13220             y_offset+i,width,1,exception);
13221           if (s == (Quantum *) NULL)
13222             break;
13223           for (j=0; j < (int) width; j++)
13224           {
13225             SetPixelPacket(image,&pixel,s);
13226             s+=GetPixelChannels(image);
13227           }
13228           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13229             break;
13230         }
13231         image_view=DestroyCacheView(image_view);
13232         tile++;
13233       }
13234       windows->image.window_changes.width=(int) image->columns;
13235       windows->image.window_changes.height=(int) image->rows;
13236       XConfigureImageColormap(display,resource_info,windows,image);
13237       (void) XConfigureImage(display,resource_info,windows,image,exception);
13238       break;
13239     }
13240     default:
13241       break;
13242   }
13243   XSetCursorState(display,windows,MagickFalse);
13244   return(tile_image);
13245 }
13246 \f
13247 /*
13248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13249 %                                                                             %
13250 %                                                                             %
13251 %                                                                             %
13252 +   X T r a n s l a t e I m a g e                                             %
13253 %                                                                             %
13254 %                                                                             %
13255 %                                                                             %
13256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13257 %
13258 %  XTranslateImage() translates the image within an Image window by one pixel
13259 %  as specified by the key symbol.  If the image has a `montage string the
13260 %  translation is respect to the width and height contained within the string.
13261 %
13262 %  The format of the XTranslateImage method is:
13263 %
13264 %      void XTranslateImage(Display *display,XWindows *windows,
13265 %        Image *image,const KeySym key_symbol)
13266 %
13267 %  A description of each parameter follows:
13268 %
13269 %    o display: Specifies a connection to an X server; returned from
13270 %      XOpenDisplay.
13271 %
13272 %    o windows: Specifies a pointer to a XWindows structure.
13273 %
13274 %    o image: the image.
13275 %
13276 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13277 %      to trim.
13278 %
13279 */
13280 static void XTranslateImage(Display *display,XWindows *windows,
13281   Image *image,const KeySym key_symbol)
13282 {
13283   char
13284     text[MaxTextExtent];
13285
13286   int
13287     x,
13288     y;
13289
13290   unsigned int
13291     x_offset,
13292     y_offset;
13293
13294   /*
13295     User specified a pan position offset.
13296   */
13297   x_offset=windows->image.width;
13298   y_offset=windows->image.height;
13299   if (image->montage != (char *) NULL)
13300     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13301   switch ((int) key_symbol)
13302   {
13303     case XK_Home:
13304     case XK_KP_Home:
13305     {
13306       windows->image.x=(int) windows->image.width/2;
13307       windows->image.y=(int) windows->image.height/2;
13308       break;
13309     }
13310     case XK_Left:
13311     case XK_KP_Left:
13312     {
13313       windows->image.x-=x_offset;
13314       break;
13315     }
13316     case XK_Next:
13317     case XK_Up:
13318     case XK_KP_Up:
13319     {
13320       windows->image.y-=y_offset;
13321       break;
13322     }
13323     case XK_Right:
13324     case XK_KP_Right:
13325     {
13326       windows->image.x+=x_offset;
13327       break;
13328     }
13329     case XK_Prior:
13330     case XK_Down:
13331     case XK_KP_Down:
13332     {
13333       windows->image.y+=y_offset;
13334       break;
13335     }
13336     default:
13337       return;
13338   }
13339   /*
13340     Check boundary conditions.
13341   */
13342   if (windows->image.x < 0)
13343     windows->image.x=0;
13344   else
13345     if ((int) (windows->image.x+windows->image.width) >
13346         windows->image.ximage->width)
13347       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13348   if (windows->image.y < 0)
13349     windows->image.y=0;
13350   else
13351     if ((int) (windows->image.y+windows->image.height) >
13352         windows->image.ximage->height)
13353       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13354   /*
13355     Refresh Image window.
13356   */
13357   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13358     windows->image.width,windows->image.height,windows->image.x,
13359     windows->image.y);
13360   XInfoWidget(display,windows,text);
13361   XCheckRefreshWindows(display,windows);
13362   XDrawPanRectangle(display,windows);
13363   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13364   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13365 }
13366 \f
13367 /*
13368 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13369 %                                                                             %
13370 %                                                                             %
13371 %                                                                             %
13372 +   X T r i m I m a g e                                                       %
13373 %                                                                             %
13374 %                                                                             %
13375 %                                                                             %
13376 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13377 %
13378 %  XTrimImage() trims the edges from the Image window.
13379 %
13380 %  The format of the XTrimImage method is:
13381 %
13382 %      MagickBooleanType XTrimImage(Display *display,
13383 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13384 %        ExceptionInfo *exception)
13385 %
13386 %  A description of each parameter follows:
13387 %
13388 %    o display: Specifies a connection to an X server; returned from
13389 %      XOpenDisplay.
13390 %
13391 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13392 %
13393 %    o windows: Specifies a pointer to a XWindows structure.
13394 %
13395 %    o image: the image.
13396 %
13397 %    o exception: return any errors or warnings in this structure.
13398 %
13399 */
13400 static MagickBooleanType XTrimImage(Display *display,
13401   XResourceInfo *resource_info,XWindows *windows,Image *image,
13402   ExceptionInfo *exception)
13403 {
13404   RectangleInfo
13405     trim_info;
13406
13407   register int
13408     x,
13409     y;
13410
13411   size_t
13412     background,
13413     pixel;
13414
13415   /*
13416     Trim edges from image.
13417   */
13418   XSetCursorState(display,windows,MagickTrue);
13419   XCheckRefreshWindows(display,windows);
13420   /*
13421     Crop the left edge.
13422   */
13423   background=XGetPixel(windows->image.ximage,0,0);
13424   trim_info.width=(size_t) windows->image.ximage->width;
13425   for (x=0; x < windows->image.ximage->width; x++)
13426   {
13427     for (y=0; y < windows->image.ximage->height; y++)
13428     {
13429       pixel=XGetPixel(windows->image.ximage,x,y);
13430       if (pixel != background)
13431         break;
13432     }
13433     if (y < windows->image.ximage->height)
13434       break;
13435   }
13436   trim_info.x=(ssize_t) x;
13437   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13438     {
13439       XSetCursorState(display,windows,MagickFalse);
13440       return(MagickFalse);
13441     }
13442   /*
13443     Crop the right edge.
13444   */
13445   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13446   for (x=windows->image.ximage->width-1; x != 0; x--)
13447   {
13448     for (y=0; y < windows->image.ximage->height; y++)
13449     {
13450       pixel=XGetPixel(windows->image.ximage,x,y);
13451       if (pixel != background)
13452         break;
13453     }
13454     if (y < windows->image.ximage->height)
13455       break;
13456   }
13457   trim_info.width=(size_t) (x-trim_info.x+1);
13458   /*
13459     Crop the top edge.
13460   */
13461   background=XGetPixel(windows->image.ximage,0,0);
13462   trim_info.height=(size_t) windows->image.ximage->height;
13463   for (y=0; y < windows->image.ximage->height; y++)
13464   {
13465     for (x=0; x < windows->image.ximage->width; x++)
13466     {
13467       pixel=XGetPixel(windows->image.ximage,x,y);
13468       if (pixel != background)
13469         break;
13470     }
13471     if (x < windows->image.ximage->width)
13472       break;
13473   }
13474   trim_info.y=(ssize_t) y;
13475   /*
13476     Crop the bottom edge.
13477   */
13478   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13479   for (y=windows->image.ximage->height-1; y != 0; y--)
13480   {
13481     for (x=0; x < windows->image.ximage->width; x++)
13482     {
13483       pixel=XGetPixel(windows->image.ximage,x,y);
13484       if (pixel != background)
13485         break;
13486     }
13487     if (x < windows->image.ximage->width)
13488       break;
13489   }
13490   trim_info.height=(size_t) y-trim_info.y+1;
13491   if (((unsigned int) trim_info.width != windows->image.width) ||
13492       ((unsigned int) trim_info.height != windows->image.height))
13493     {
13494       /*
13495         Reconfigure Image window as defined by the trimming rectangle.
13496       */
13497       XSetCropGeometry(display,windows,&trim_info,image);
13498       windows->image.window_changes.width=(int) trim_info.width;
13499       windows->image.window_changes.height=(int) trim_info.height;
13500       (void) XConfigureImage(display,resource_info,windows,image,exception);
13501     }
13502   XSetCursorState(display,windows,MagickFalse);
13503   return(MagickTrue);
13504 }
13505 \f
13506 /*
13507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13508 %                                                                             %
13509 %                                                                             %
13510 %                                                                             %
13511 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13512 %                                                                             %
13513 %                                                                             %
13514 %                                                                             %
13515 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13516 %
13517 %  XVisualDirectoryImage() creates a Visual Image Directory.
13518 %
13519 %  The format of the XVisualDirectoryImage method is:
13520 %
13521 %      Image *XVisualDirectoryImage(Display *display,
13522 %        XResourceInfo *resource_info,XWindows *windows,
13523 %        ExceptionInfo *exception)
13524 %
13525 %  A description of each parameter follows:
13526 %
13527 %    o display: Specifies a connection to an X server; returned from
13528 %      XOpenDisplay.
13529 %
13530 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13531 %
13532 %    o windows: Specifies a pointer to a XWindows structure.
13533 %
13534 %    o exception: return any errors or warnings in this structure.
13535 %
13536 */
13537 static Image *XVisualDirectoryImage(Display *display,
13538   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13539 {
13540 #define TileImageTag  "Scale/Image"
13541 #define XClientName  "montage"
13542
13543   char
13544     **filelist;
13545
13546   Image
13547     *images,
13548     *montage_image,
13549     *next_image,
13550     *thumbnail_image;
13551
13552   ImageInfo
13553     *read_info;
13554
13555   int
13556     number_files;
13557
13558   MagickBooleanType
13559     backdrop;
13560
13561   MagickStatusType
13562     status;
13563
13564   MontageInfo
13565     *montage_info;
13566
13567   RectangleInfo
13568     geometry;
13569
13570   register int
13571     i;
13572
13573   static char
13574     filename[MaxTextExtent] = "\0",
13575     filenames[MaxTextExtent] = "*";
13576
13577   XResourceInfo
13578     background_resources;
13579
13580   /*
13581     Request file name from user.
13582   */
13583   XFileBrowserWidget(display,windows,"Directory",filenames);
13584   if (*filenames == '\0')
13585     return((Image *) NULL);
13586   /*
13587     Expand the filenames.
13588   */
13589   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13590   if (filelist == (char **) NULL)
13591     {
13592       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13593         filenames);
13594       return((Image *) NULL);
13595     }
13596   number_files=1;
13597   filelist[0]=filenames;
13598   status=ExpandFilenames(&number_files,&filelist);
13599   if ((status == MagickFalse) || (number_files == 0))
13600     {
13601       if (number_files == 0)
13602         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13603       else
13604         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13605           filenames);
13606       return((Image *) NULL);
13607     }
13608   /*
13609     Set image background resources.
13610   */
13611   background_resources=(*resource_info);
13612   background_resources.window_id=AcquireString("");
13613   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13614     "0x%lx",windows->image.id);
13615   background_resources.backdrop=MagickTrue;
13616   /*
13617     Read each image and convert them to a tile.
13618   */
13619   backdrop=(windows->visual_info->klass == TrueColor) ||
13620     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13621   read_info=CloneImageInfo(resource_info->image_info);
13622   (void) SetImageOption(read_info,"jpeg:size","120x120");
13623   (void) CloneString(&read_info->size,DefaultTileGeometry);
13624   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13625     (void *) NULL);
13626   images=NewImageList();
13627   XSetCursorState(display,windows,MagickTrue);
13628   XCheckRefreshWindows(display,windows);
13629   for (i=0; i < (int) number_files; i++)
13630   {
13631     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13632     filelist[i]=DestroyString(filelist[i]);
13633     *read_info->magick='\0';
13634     next_image=ReadImage(read_info,exception);
13635     CatchException(exception);
13636     if (next_image != (Image *) NULL)
13637       {
13638         (void) DeleteImageProperty(next_image,"label");
13639         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13640           read_info,next_image,DefaultTileLabel));
13641         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13642           exception);
13643         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13644           geometry.height,exception);
13645         if (thumbnail_image != (Image *) NULL)
13646           {
13647             next_image=DestroyImage(next_image);
13648             next_image=thumbnail_image;
13649           }
13650         if (backdrop)
13651           {
13652             (void) XDisplayBackgroundImage(display,&background_resources,
13653               next_image,exception);
13654             XSetCursorState(display,windows,MagickTrue);
13655           }
13656         AppendImageToList(&images,next_image);
13657         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13658           {
13659             MagickBooleanType
13660               proceed;
13661
13662             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13663               (MagickSizeType) number_files);
13664             if (proceed == MagickFalse)
13665               break;
13666           }
13667       }
13668   }
13669   filelist=(char **) RelinquishMagickMemory(filelist);
13670   if (images == (Image *) NULL)
13671     {
13672       read_info=DestroyImageInfo(read_info);
13673       XSetCursorState(display,windows,MagickFalse);
13674       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13675       return((Image *) NULL);
13676     }
13677   /*
13678     Create the Visual Image Directory.
13679   */
13680   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13681   montage_info->pointsize=10;
13682   if (resource_info->font != (char *) NULL)
13683     (void) CloneString(&montage_info->font,resource_info->font);
13684   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13685   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13686     images),exception);
13687   images=DestroyImageList(images);
13688   montage_info=DestroyMontageInfo(montage_info);
13689   read_info=DestroyImageInfo(read_info);
13690   XSetCursorState(display,windows,MagickFalse);
13691   if (montage_image == (Image *) NULL)
13692     return(montage_image);
13693   XClientMessage(display,windows->image.id,windows->im_protocols,
13694     windows->im_next_image,CurrentTime);
13695   return(montage_image);
13696 }
13697 \f
13698 /*
13699 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13700 %                                                                             %
13701 %                                                                             %
13702 %                                                                             %
13703 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13704 %                                                                             %
13705 %                                                                             %
13706 %                                                                             %
13707 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13708 %
13709 %  XDisplayBackgroundImage() displays an image in the background of a window.
13710 %
13711 %  The format of the XDisplayBackgroundImage method is:
13712 %
13713 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13714 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13715 %
13716 %  A description of each parameter follows:
13717 %
13718 %    o display: Specifies a connection to an X server;  returned from
13719 %      XOpenDisplay.
13720 %
13721 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13722 %
13723 %    o image: the image.
13724 %
13725 %    o exception: return any errors or warnings in this structure.
13726 %
13727 */
13728 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13729   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13730 {
13731   char
13732     geometry[MaxTextExtent],
13733     visual_type[MaxTextExtent];
13734
13735   int
13736     height,
13737     status,
13738     width;
13739
13740   RectangleInfo
13741     geometry_info;
13742
13743   static XPixelInfo
13744     pixel;
13745
13746   static XStandardColormap
13747     *map_info;
13748
13749   static XVisualInfo
13750     *visual_info = (XVisualInfo *) NULL;
13751
13752   static XWindowInfo
13753     window_info;
13754
13755   size_t
13756     delay;
13757
13758   Window
13759     root_window;
13760
13761   XGCValues
13762     context_values;
13763
13764   XResourceInfo
13765     resources;
13766
13767   XWindowAttributes
13768     window_attributes;
13769
13770   /*
13771     Determine target window.
13772   */
13773   assert(image != (Image *) NULL);
13774   assert(image->signature == MagickSignature);
13775   if (image->debug != MagickFalse)
13776     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13777   resources=(*resource_info);
13778   window_info.id=(Window) NULL;
13779   root_window=XRootWindow(display,XDefaultScreen(display));
13780   if (LocaleCompare(resources.window_id,"root") == 0)
13781     window_info.id=root_window;
13782   else
13783     {
13784       if (isdigit((unsigned char) *resources.window_id) != 0)
13785         window_info.id=XWindowByID(display,root_window,
13786           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13787       if (window_info.id == (Window) NULL)
13788         window_info.id=XWindowByName(display,root_window,resources.window_id);
13789     }
13790   if (window_info.id == (Window) NULL)
13791     {
13792       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13793         resources.window_id);
13794       return(MagickFalse);
13795     }
13796   /*
13797     Determine window visual id.
13798   */
13799   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13800   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13801   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13802   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13803   if (status != 0)
13804     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13805       XVisualIDFromVisual(window_attributes.visual));
13806   if (visual_info == (XVisualInfo *) NULL)
13807     {
13808       /*
13809         Allocate standard colormap.
13810       */
13811       map_info=XAllocStandardColormap();
13812       if (map_info == (XStandardColormap *) NULL)
13813         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13814           image->filename);
13815       map_info->colormap=(Colormap) NULL;
13816       pixel.pixels=(unsigned long *) NULL;
13817       /*
13818         Initialize visual info.
13819       */
13820       resources.map_type=(char *) NULL;
13821       resources.visual_type=visual_type;
13822       visual_info=XBestVisualInfo(display,map_info,&resources);
13823       if (visual_info == (XVisualInfo *) NULL)
13824         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13825           resources.visual_type);
13826       /*
13827         Initialize window info.
13828       */
13829       window_info.ximage=(XImage *) NULL;
13830       window_info.matte_image=(XImage *) NULL;
13831       window_info.pixmap=(Pixmap) NULL;
13832       window_info.matte_pixmap=(Pixmap) NULL;
13833     }
13834   /*
13835     Free previous root colors.
13836   */
13837   if (window_info.id == root_window)
13838     (void) XDestroyWindowColors(display,root_window);
13839   /*
13840     Initialize Standard Colormap.
13841   */
13842   resources.colormap=SharedColormap;
13843   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13844   /*
13845     Graphic context superclass.
13846   */
13847   context_values.background=pixel.background_color.pixel;
13848   context_values.foreground=pixel.foreground_color.pixel;
13849   pixel.annotate_context=XCreateGC(display,window_info.id,
13850     (size_t) (GCBackground | GCForeground),&context_values);
13851   if (pixel.annotate_context == (GC) NULL)
13852     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13853       image->filename);
13854   /*
13855     Initialize Image window attributes.
13856   */
13857   window_info.name=AcquireString("\0");
13858   window_info.icon_name=AcquireString("\0");
13859   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13860     &resources,&window_info);
13861   /*
13862     Create the X image.
13863   */
13864   window_info.width=(unsigned int) image->columns;
13865   window_info.height=(unsigned int) image->rows;
13866   if ((image->columns != window_info.width) ||
13867       (image->rows != window_info.height))
13868     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13869       image->filename);
13870   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13871     window_attributes.width,window_attributes.height);
13872   geometry_info.width=window_info.width;
13873   geometry_info.height=window_info.height;
13874   geometry_info.x=(ssize_t) window_info.x;
13875   geometry_info.y=(ssize_t) window_info.y;
13876   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13877     &geometry_info.width,&geometry_info.height);
13878   window_info.width=(unsigned int) geometry_info.width;
13879   window_info.height=(unsigned int) geometry_info.height;
13880   window_info.x=(int) geometry_info.x;
13881   window_info.y=(int) geometry_info.y;
13882   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13883     window_info.height,exception);
13884   if (status == MagickFalse)
13885     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13886       image->filename);
13887   window_info.x=0;
13888   window_info.y=0;
13889   if (image->debug != MagickFalse)
13890     {
13891       (void) LogMagickEvent(X11Event,GetMagickModule(),
13892         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13893         (double) image->columns,(double) image->rows);
13894       if (image->colors != 0)
13895         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13896           image->colors);
13897       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13898     }
13899   /*
13900     Adjust image dimensions as specified by backdrop or geometry options.
13901   */
13902   width=(int) window_info.width;
13903   height=(int) window_info.height;
13904   if (resources.backdrop != MagickFalse)
13905     {
13906       /*
13907         Center image on window.
13908       */
13909       window_info.x=(window_attributes.width/2)-
13910         (window_info.ximage->width/2);
13911       window_info.y=(window_attributes.height/2)-
13912         (window_info.ximage->height/2);
13913       width=window_attributes.width;
13914       height=window_attributes.height;
13915     }
13916   if ((resources.image_geometry != (char *) NULL) &&
13917       (*resources.image_geometry != '\0'))
13918     {
13919       char
13920         default_geometry[MaxTextExtent];
13921
13922       int
13923         flags,
13924         gravity;
13925
13926       XSizeHints
13927         *size_hints;
13928
13929       /*
13930         User specified geometry.
13931       */
13932       size_hints=XAllocSizeHints();
13933       if (size_hints == (XSizeHints *) NULL)
13934         ThrowXWindowFatalException(ResourceLimitFatalError,
13935           "MemoryAllocationFailed",image->filename);
13936       size_hints->flags=0L;
13937       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13938         width,height);
13939       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13940         default_geometry,window_info.border_width,size_hints,&window_info.x,
13941         &window_info.y,&width,&height,&gravity);
13942       if (flags & (XValue | YValue))
13943         {
13944           width=window_attributes.width;
13945           height=window_attributes.height;
13946         }
13947       (void) XFree((void *) size_hints);
13948     }
13949   /*
13950     Create the X pixmap.
13951   */
13952   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13953     (unsigned int) height,window_info.depth);
13954   if (window_info.pixmap == (Pixmap) NULL)
13955     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13956       image->filename);
13957   /*
13958     Display pixmap on the window.
13959   */
13960   if (((unsigned int) width > window_info.width) ||
13961       ((unsigned int) height > window_info.height))
13962     (void) XFillRectangle(display,window_info.pixmap,
13963       window_info.annotate_context,0,0,(unsigned int) width,
13964       (unsigned int) height);
13965   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13966     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13967     window_info.width,(unsigned int) window_info.height);
13968   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13969   (void) XClearWindow(display,window_info.id);
13970   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13971   XDelay(display,delay == 0UL ? 10UL : delay);
13972   (void) XSync(display,MagickFalse);
13973   return(window_info.id == root_window ? MagickTrue : MagickFalse);
13974 }
13975 \f
13976 /*
13977 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13978 %                                                                             %
13979 %                                                                             %
13980 %                                                                             %
13981 +   X D i s p l a y I m a g e                                                 %
13982 %                                                                             %
13983 %                                                                             %
13984 %                                                                             %
13985 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13986 %
13987 %  XDisplayImage() displays an image via X11.  A new image is created and
13988 %  returned if the user interactively transforms the displayed image.
13989 %
13990 %  The format of the XDisplayImage method is:
13991 %
13992 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13993 %        char **argv,int argc,Image **image,size_t *state,
13994 %        ExceptionInfo *exception)
13995 %
13996 %  A description of each parameter follows:
13997 %
13998 %    o nexus:  Method XDisplayImage returns an image when the
13999 %      user chooses 'Open Image' from the command menu or picks a tile
14000 %      from the image directory.  Otherwise a null image is returned.
14001 %
14002 %    o display: Specifies a connection to an X server;  returned from
14003 %      XOpenDisplay.
14004 %
14005 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14006 %
14007 %    o argv: Specifies the application's argument list.
14008 %
14009 %    o argc: Specifies the number of arguments.
14010 %
14011 %    o image: Specifies an address to an address of an Image structure;
14012 %
14013 %    o exception: return any errors or warnings in this structure.
14014 %
14015 */
14016 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14017   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14018 {
14019 #define MagnifySize  256  /* must be a power of 2 */
14020 #define MagickMenus  10
14021 #define MagickTitle  "Commands"
14022
14023   static const char
14024     *CommandMenu[] =
14025     {
14026       "File",
14027       "Edit",
14028       "View",
14029       "Transform",
14030       "Enhance",
14031       "Effects",
14032       "F/X",
14033       "Image Edit",
14034       "Miscellany",
14035       "Help",
14036       (char *) NULL
14037     },
14038     *FileMenu[] =
14039     {
14040       "Open...",
14041       "Next",
14042       "Former",
14043       "Select...",
14044       "Save...",
14045       "Print...",
14046       "Delete...",
14047       "New...",
14048       "Visual Directory...",
14049       "Quit",
14050       (char *) NULL
14051     },
14052     *EditMenu[] =
14053     {
14054       "Undo",
14055       "Redo",
14056       "Cut",
14057       "Copy",
14058       "Paste",
14059       (char *) NULL
14060     },
14061     *ViewMenu[] =
14062     {
14063       "Half Size",
14064       "Original Size",
14065       "Double Size",
14066       "Resize...",
14067       "Apply",
14068       "Refresh",
14069       "Restore",
14070       (char *) NULL
14071     },
14072     *TransformMenu[] =
14073     {
14074       "Crop",
14075       "Chop",
14076       "Flop",
14077       "Flip",
14078       "Rotate Right",
14079       "Rotate Left",
14080       "Rotate...",
14081       "Shear...",
14082       "Roll...",
14083       "Trim Edges",
14084       (char *) NULL
14085     },
14086     *EnhanceMenu[] =
14087     {
14088       "Hue...",
14089       "Saturation...",
14090       "Brightness...",
14091       "Gamma...",
14092       "Spiff",
14093       "Dull",
14094       "Contrast Stretch...",
14095       "Sigmoidal Contrast...",
14096       "Normalize",
14097       "Equalize",
14098       "Negate",
14099       "Grayscale",
14100       "Map...",
14101       "Quantize...",
14102       (char *) NULL
14103     },
14104     *EffectsMenu[] =
14105     {
14106       "Despeckle",
14107       "Emboss",
14108       "Reduce Noise",
14109       "Add Noise...",
14110       "Sharpen...",
14111       "Blur...",
14112       "Threshold...",
14113       "Edge Detect...",
14114       "Spread...",
14115       "Shade...",
14116       "Raise...",
14117       "Segment...",
14118       (char *) NULL
14119     },
14120     *FXMenu[] =
14121     {
14122       "Solarize...",
14123       "Sepia Tone...",
14124       "Swirl...",
14125       "Implode...",
14126       "Vignette...",
14127       "Wave...",
14128       "Oil Paint...",
14129       "Charcoal Draw...",
14130       (char *) NULL
14131     },
14132     *ImageEditMenu[] =
14133     {
14134       "Annotate...",
14135       "Draw...",
14136       "Color...",
14137       "Matte...",
14138       "Composite...",
14139       "Add Border...",
14140       "Add Frame...",
14141       "Comment...",
14142       "Launch...",
14143       "Region of Interest...",
14144       (char *) NULL
14145     },
14146     *MiscellanyMenu[] =
14147     {
14148       "Image Info",
14149       "Zoom Image",
14150       "Show Preview...",
14151       "Show Histogram",
14152       "Show Matte",
14153       "Background...",
14154       "Slide Show...",
14155       "Preferences...",
14156       (char *) NULL
14157     },
14158     *HelpMenu[] =
14159     {
14160       "Overview",
14161       "Browse Documentation",
14162       "About Display",
14163       (char *) NULL
14164     },
14165     *ShortCutsMenu[] =
14166     {
14167       "Next",
14168       "Former",
14169       "Open...",
14170       "Save...",
14171       "Print...",
14172       "Undo",
14173       "Restore",
14174       "Image Info",
14175       "Quit",
14176       (char *) NULL
14177     },
14178     *VirtualMenu[] =
14179     {
14180       "Image Info",
14181       "Print",
14182       "Next",
14183       "Quit",
14184       (char *) NULL
14185     };
14186
14187   static const char
14188     **Menus[MagickMenus] =
14189     {
14190       FileMenu,
14191       EditMenu,
14192       ViewMenu,
14193       TransformMenu,
14194       EnhanceMenu,
14195       EffectsMenu,
14196       FXMenu,
14197       ImageEditMenu,
14198       MiscellanyMenu,
14199       HelpMenu
14200     };
14201
14202   static CommandType
14203     CommandMenus[] =
14204     {
14205       NullCommand,
14206       NullCommand,
14207       NullCommand,
14208       NullCommand,
14209       NullCommand,
14210       NullCommand,
14211       NullCommand,
14212       NullCommand,
14213       NullCommand,
14214       NullCommand,
14215     },
14216     FileCommands[] =
14217     {
14218       OpenCommand,
14219       NextCommand,
14220       FormerCommand,
14221       SelectCommand,
14222       SaveCommand,
14223       PrintCommand,
14224       DeleteCommand,
14225       NewCommand,
14226       VisualDirectoryCommand,
14227       QuitCommand
14228     },
14229     EditCommands[] =
14230     {
14231       UndoCommand,
14232       RedoCommand,
14233       CutCommand,
14234       CopyCommand,
14235       PasteCommand
14236     },
14237     ViewCommands[] =
14238     {
14239       HalfSizeCommand,
14240       OriginalSizeCommand,
14241       DoubleSizeCommand,
14242       ResizeCommand,
14243       ApplyCommand,
14244       RefreshCommand,
14245       RestoreCommand
14246     },
14247     TransformCommands[] =
14248     {
14249       CropCommand,
14250       ChopCommand,
14251       FlopCommand,
14252       FlipCommand,
14253       RotateRightCommand,
14254       RotateLeftCommand,
14255       RotateCommand,
14256       ShearCommand,
14257       RollCommand,
14258       TrimCommand
14259     },
14260     EnhanceCommands[] =
14261     {
14262       HueCommand,
14263       SaturationCommand,
14264       BrightnessCommand,
14265       GammaCommand,
14266       SpiffCommand,
14267       DullCommand,
14268       ContrastStretchCommand,
14269       SigmoidalContrastCommand,
14270       NormalizeCommand,
14271       EqualizeCommand,
14272       NegateCommand,
14273       GrayscaleCommand,
14274       MapCommand,
14275       QuantizeCommand
14276     },
14277     EffectsCommands[] =
14278     {
14279       DespeckleCommand,
14280       EmbossCommand,
14281       ReduceNoiseCommand,
14282       AddNoiseCommand,
14283       SharpenCommand,
14284       BlurCommand,
14285       ThresholdCommand,
14286       EdgeDetectCommand,
14287       SpreadCommand,
14288       ShadeCommand,
14289       RaiseCommand,
14290       SegmentCommand
14291     },
14292     FXCommands[] =
14293     {
14294       SolarizeCommand,
14295       SepiaToneCommand,
14296       SwirlCommand,
14297       ImplodeCommand,
14298       VignetteCommand,
14299       WaveCommand,
14300       OilPaintCommand,
14301       CharcoalDrawCommand
14302     },
14303     ImageEditCommands[] =
14304     {
14305       AnnotateCommand,
14306       DrawCommand,
14307       ColorCommand,
14308       MatteCommand,
14309       CompositeCommand,
14310       AddBorderCommand,
14311       AddFrameCommand,
14312       CommentCommand,
14313       LaunchCommand,
14314       RegionofInterestCommand
14315     },
14316     MiscellanyCommands[] =
14317     {
14318       InfoCommand,
14319       ZoomCommand,
14320       ShowPreviewCommand,
14321       ShowHistogramCommand,
14322       ShowMatteCommand,
14323       BackgroundCommand,
14324       SlideShowCommand,
14325       PreferencesCommand
14326     },
14327     HelpCommands[] =
14328     {
14329       HelpCommand,
14330       BrowseDocumentationCommand,
14331       VersionCommand
14332     },
14333     ShortCutsCommands[] =
14334     {
14335       NextCommand,
14336       FormerCommand,
14337       OpenCommand,
14338       SaveCommand,
14339       PrintCommand,
14340       UndoCommand,
14341       RestoreCommand,
14342       InfoCommand,
14343       QuitCommand
14344     },
14345     VirtualCommands[] =
14346     {
14347       InfoCommand,
14348       PrintCommand,
14349       NextCommand,
14350       QuitCommand
14351     };
14352
14353   static CommandType
14354     *Commands[MagickMenus] =
14355     {
14356       FileCommands,
14357       EditCommands,
14358       ViewCommands,
14359       TransformCommands,
14360       EnhanceCommands,
14361       EffectsCommands,
14362       FXCommands,
14363       ImageEditCommands,
14364       MiscellanyCommands,
14365       HelpCommands
14366     };
14367
14368   char
14369     command[MaxTextExtent],
14370     *directory,
14371     geometry[MaxTextExtent],
14372     resource_name[MaxTextExtent];
14373
14374   CommandType
14375     command_type;
14376
14377   Image
14378     *display_image,
14379     *nexus;
14380
14381   int
14382     entry,
14383     id;
14384
14385   KeySym
14386     key_symbol;
14387
14388   MagickStatusType
14389     context_mask,
14390     status;
14391
14392   RectangleInfo
14393     geometry_info;
14394
14395   register int
14396     i;
14397
14398   static char
14399     working_directory[MaxTextExtent];
14400
14401   static XPoint
14402     vid_info;
14403
14404   static XWindowInfo
14405     *magick_windows[MaxXWindows];
14406
14407   static unsigned int
14408     number_windows;
14409
14410   struct stat
14411     attributes;
14412
14413   time_t
14414     timer,
14415     timestamp,
14416     update_time;
14417
14418   unsigned int
14419     height,
14420     width;
14421
14422   size_t
14423     delay;
14424
14425   WarningHandler
14426     warning_handler;
14427
14428   Window
14429     root_window;
14430
14431   XClassHint
14432     *class_hints;
14433
14434   XEvent
14435     event;
14436
14437   XFontStruct
14438     *font_info;
14439
14440   XGCValues
14441     context_values;
14442
14443   XPixelInfo
14444     *icon_pixel,
14445     *pixel;
14446
14447   XResourceInfo
14448     *icon_resources;
14449
14450   XStandardColormap
14451     *icon_map,
14452     *map_info;
14453
14454   XVisualInfo
14455     *icon_visual,
14456     *visual_info;
14457
14458   XWindowChanges
14459     window_changes;
14460
14461   XWindows
14462     *windows;
14463
14464   XWMHints
14465     *manager_hints;
14466
14467   assert(image != (Image **) NULL);
14468   assert((*image)->signature == MagickSignature);
14469   if ((*image)->debug != MagickFalse)
14470     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14471   display_image=(*image);
14472   warning_handler=(WarningHandler) NULL;
14473   windows=XSetWindows((XWindows *) ~0);
14474   if (windows != (XWindows *) NULL)
14475     {
14476       int
14477         status;
14478
14479       status=chdir(working_directory);
14480       if (status == -1)
14481         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14482           "UnableToOpenFile","%s",working_directory);
14483       warning_handler=resource_info->display_warnings ?
14484         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14485       warning_handler=resource_info->display_warnings ?
14486         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14487     }
14488   else
14489     {
14490       /*
14491         Allocate windows structure.
14492       */
14493       resource_info->colors=display_image->colors;
14494       windows=XSetWindows(XInitializeWindows(display,resource_info));
14495       if (windows == (XWindows *) NULL)
14496         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14497           (*image)->filename);
14498       /*
14499         Initialize window id's.
14500       */
14501       number_windows=0;
14502       magick_windows[number_windows++]=(&windows->icon);
14503       magick_windows[number_windows++]=(&windows->backdrop);
14504       magick_windows[number_windows++]=(&windows->image);
14505       magick_windows[number_windows++]=(&windows->info);
14506       magick_windows[number_windows++]=(&windows->command);
14507       magick_windows[number_windows++]=(&windows->widget);
14508       magick_windows[number_windows++]=(&windows->popup);
14509       magick_windows[number_windows++]=(&windows->magnify);
14510       magick_windows[number_windows++]=(&windows->pan);
14511       for (i=0; i < (int) number_windows; i++)
14512         magick_windows[i]->id=(Window) NULL;
14513       vid_info.x=0;
14514       vid_info.y=0;
14515     }
14516   /*
14517     Initialize font info.
14518   */
14519   if (windows->font_info != (XFontStruct *) NULL)
14520     (void) XFreeFont(display,windows->font_info);
14521   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14522   if (windows->font_info == (XFontStruct *) NULL)
14523     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14524       resource_info->font);
14525   /*
14526     Initialize Standard Colormap.
14527   */
14528   map_info=windows->map_info;
14529   icon_map=windows->icon_map;
14530   visual_info=windows->visual_info;
14531   icon_visual=windows->icon_visual;
14532   pixel=windows->pixel_info;
14533   icon_pixel=windows->icon_pixel;
14534   font_info=windows->font_info;
14535   icon_resources=windows->icon_resources;
14536   class_hints=windows->class_hints;
14537   manager_hints=windows->manager_hints;
14538   root_window=XRootWindow(display,visual_info->screen);
14539   nexus=NewImageList();
14540   if (display_image->debug != MagickFalse)
14541     {
14542       (void) LogMagickEvent(X11Event,GetMagickModule(),
14543         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14544         (double) display_image->scene,(double) display_image->columns,
14545         (double) display_image->rows);
14546       if (display_image->colors != 0)
14547         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14548           display_image->colors);
14549       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14550         display_image->magick);
14551     }
14552   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14553     map_info,pixel);
14554   display_image->taint=MagickFalse;
14555   /*
14556     Initialize graphic context.
14557   */
14558   windows->context.id=(Window) NULL;
14559   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14560     resource_info,&windows->context);
14561   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14562   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14563   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14564   manager_hints->flags=InputHint | StateHint;
14565   manager_hints->input=MagickFalse;
14566   manager_hints->initial_state=WithdrawnState;
14567   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14568     &windows->context);
14569   if (display_image->debug != MagickFalse)
14570     (void) LogMagickEvent(X11Event,GetMagickModule(),
14571       "Window id: 0x%lx (context)",windows->context.id);
14572   context_values.background=pixel->background_color.pixel;
14573   context_values.font=font_info->fid;
14574   context_values.foreground=pixel->foreground_color.pixel;
14575   context_values.graphics_exposures=MagickFalse;
14576   context_mask=(MagickStatusType)
14577     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14578   if (pixel->annotate_context != (GC) NULL)
14579     (void) XFreeGC(display,pixel->annotate_context);
14580   pixel->annotate_context=XCreateGC(display,windows->context.id,
14581     context_mask,&context_values);
14582   if (pixel->annotate_context == (GC) NULL)
14583     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14584       display_image->filename);
14585   context_values.background=pixel->depth_color.pixel;
14586   if (pixel->widget_context != (GC) NULL)
14587     (void) XFreeGC(display,pixel->widget_context);
14588   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14589     &context_values);
14590   if (pixel->widget_context == (GC) NULL)
14591     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14592       display_image->filename);
14593   context_values.background=pixel->foreground_color.pixel;
14594   context_values.foreground=pixel->background_color.pixel;
14595   context_values.plane_mask=context_values.background ^
14596     context_values.foreground;
14597   if (pixel->highlight_context != (GC) NULL)
14598     (void) XFreeGC(display,pixel->highlight_context);
14599   pixel->highlight_context=XCreateGC(display,windows->context.id,
14600     (size_t) (context_mask | GCPlaneMask),&context_values);
14601   if (pixel->highlight_context == (GC) NULL)
14602     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14603       display_image->filename);
14604   (void) XDestroyWindow(display,windows->context.id);
14605   /*
14606     Initialize icon window.
14607   */
14608   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14609     icon_resources,&windows->icon);
14610   windows->icon.geometry=resource_info->icon_geometry;
14611   XBestIconSize(display,&windows->icon,display_image);
14612   windows->icon.attributes.colormap=XDefaultColormap(display,
14613     icon_visual->screen);
14614   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14615   manager_hints->flags=InputHint | StateHint;
14616   manager_hints->input=MagickFalse;
14617   manager_hints->initial_state=IconicState;
14618   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14619     &windows->icon);
14620   if (display_image->debug != MagickFalse)
14621     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14622       windows->icon.id);
14623   /*
14624     Initialize graphic context for icon window.
14625   */
14626   if (icon_pixel->annotate_context != (GC) NULL)
14627     (void) XFreeGC(display,icon_pixel->annotate_context);
14628   context_values.background=icon_pixel->background_color.pixel;
14629   context_values.foreground=icon_pixel->foreground_color.pixel;
14630   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14631     (size_t) (GCBackground | GCForeground),&context_values);
14632   if (icon_pixel->annotate_context == (GC) NULL)
14633     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14634       display_image->filename);
14635   windows->icon.annotate_context=icon_pixel->annotate_context;
14636   /*
14637     Initialize Image window.
14638   */
14639   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14640     &windows->image);
14641   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14642   if (resource_info->use_shared_memory == MagickFalse)
14643     windows->image.shared_memory=MagickFalse;
14644   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14645     {
14646       char
14647         *title;
14648
14649       title=InterpretImageProperties(resource_info->image_info,display_image,
14650         resource_info->title);
14651       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14652       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14653       title=DestroyString(title);
14654     }
14655   else
14656     {
14657       char
14658         filename[MaxTextExtent];
14659
14660       /*
14661         Window name is the base of the filename.
14662       */
14663       GetPathComponent(display_image->magick_filename,TailPath,filename);
14664       if (GetImageListLength(display_image) == 1)
14665         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14666           "%s: %s",MagickPackageName,filename);
14667       else
14668         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14669           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14670           (double) display_image->scene,(double) GetImageListLength(
14671           display_image));
14672       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14673     }
14674   if (resource_info->immutable)
14675     windows->image.immutable=MagickTrue;
14676   windows->image.use_pixmap=resource_info->use_pixmap;
14677   windows->image.geometry=resource_info->image_geometry;
14678   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14679     XDisplayWidth(display,visual_info->screen),
14680     XDisplayHeight(display,visual_info->screen));
14681   geometry_info.width=display_image->columns;
14682   geometry_info.height=display_image->rows;
14683   geometry_info.x=0;
14684   geometry_info.y=0;
14685   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14686     &geometry_info.width,&geometry_info.height);
14687   windows->image.width=(unsigned int) geometry_info.width;
14688   windows->image.height=(unsigned int) geometry_info.height;
14689   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14690     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14691     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14692     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14693   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14694     resource_info,&windows->backdrop);
14695   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14696     {
14697       /*
14698         Initialize backdrop window.
14699       */
14700       windows->backdrop.x=0;
14701       windows->backdrop.y=0;
14702       (void) CloneString(&windows->backdrop.name,"Backdrop");
14703       windows->backdrop.flags=(size_t) (USSize | USPosition);
14704       windows->backdrop.width=(unsigned int)
14705         XDisplayWidth(display,visual_info->screen);
14706       windows->backdrop.height=(unsigned int)
14707         XDisplayHeight(display,visual_info->screen);
14708       windows->backdrop.border_width=0;
14709       windows->backdrop.immutable=MagickTrue;
14710       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14711         ButtonReleaseMask;
14712       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14713         StructureNotifyMask;
14714       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14715       manager_hints->icon_window=windows->icon.id;
14716       manager_hints->input=MagickTrue;
14717       manager_hints->initial_state=resource_info->iconic ? IconicState :
14718         NormalState;
14719       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14720         &windows->backdrop);
14721       if (display_image->debug != MagickFalse)
14722         (void) LogMagickEvent(X11Event,GetMagickModule(),
14723           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14724       (void) XMapWindow(display,windows->backdrop.id);
14725       (void) XClearWindow(display,windows->backdrop.id);
14726       if (windows->image.id != (Window) NULL)
14727         {
14728           (void) XDestroyWindow(display,windows->image.id);
14729           windows->image.id=(Window) NULL;
14730         }
14731       /*
14732         Position image in the center the backdrop.
14733       */
14734       windows->image.flags|=USPosition;
14735       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14736         (windows->image.width/2);
14737       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14738         (windows->image.height/2);
14739     }
14740   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14741   manager_hints->icon_window=windows->icon.id;
14742   manager_hints->input=MagickTrue;
14743   manager_hints->initial_state=resource_info->iconic ? IconicState :
14744     NormalState;
14745   if (windows->group_leader.id != (Window) NULL)
14746     {
14747       /*
14748         Follow the leader.
14749       */
14750       manager_hints->flags|=WindowGroupHint;
14751       manager_hints->window_group=windows->group_leader.id;
14752       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14753       if (display_image->debug != MagickFalse)
14754         (void) LogMagickEvent(X11Event,GetMagickModule(),
14755           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14756     }
14757   XMakeWindow(display,
14758     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14759     argv,argc,class_hints,manager_hints,&windows->image);
14760   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14761     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14762   if (windows->group_leader.id != (Window) NULL)
14763     (void) XSetTransientForHint(display,windows->image.id,
14764       windows->group_leader.id);
14765   if (display_image->debug != MagickFalse)
14766     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14767       windows->image.id);
14768   /*
14769     Initialize Info widget.
14770   */
14771   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14772     &windows->info);
14773   (void) CloneString(&windows->info.name,"Info");
14774   (void) CloneString(&windows->info.icon_name,"Info");
14775   windows->info.border_width=1;
14776   windows->info.x=2;
14777   windows->info.y=2;
14778   windows->info.flags|=PPosition;
14779   windows->info.attributes.win_gravity=UnmapGravity;
14780   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14781     StructureNotifyMask;
14782   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14783   manager_hints->input=MagickFalse;
14784   manager_hints->initial_state=NormalState;
14785   manager_hints->window_group=windows->image.id;
14786   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14787     &windows->info);
14788   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14789     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14790   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14791     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14792   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14793   if (windows->image.mapped != MagickFalse)
14794     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14795   if (display_image->debug != MagickFalse)
14796     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14797       windows->info.id);
14798   /*
14799     Initialize Command widget.
14800   */
14801   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14802     resource_info,&windows->command);
14803   windows->command.data=MagickMenus;
14804   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14805   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14806     resource_info->client_name);
14807   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14808     resource_name,"geometry",(char *) NULL);
14809   (void) CloneString(&windows->command.name,MagickTitle);
14810   windows->command.border_width=0;
14811   windows->command.flags|=PPosition;
14812   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14813     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14814     OwnerGrabButtonMask | StructureNotifyMask;
14815   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14816   manager_hints->input=MagickTrue;
14817   manager_hints->initial_state=NormalState;
14818   manager_hints->window_group=windows->image.id;
14819   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14820     &windows->command);
14821   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14822     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14823     HighlightHeight);
14824   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14825     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14826   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14827   if (windows->command.mapped != MagickFalse)
14828     (void) XMapRaised(display,windows->command.id);
14829   if (display_image->debug != MagickFalse)
14830     (void) LogMagickEvent(X11Event,GetMagickModule(),
14831       "Window id: 0x%lx (command)",windows->command.id);
14832   /*
14833     Initialize Widget window.
14834   */
14835   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14836     resource_info,&windows->widget);
14837   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14838     resource_info->client_name);
14839   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14840     resource_name,"geometry",(char *) NULL);
14841   windows->widget.border_width=0;
14842   windows->widget.flags|=PPosition;
14843   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14844     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14845     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14846     StructureNotifyMask;
14847   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14848   manager_hints->input=MagickTrue;
14849   manager_hints->initial_state=NormalState;
14850   manager_hints->window_group=windows->image.id;
14851   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14852     &windows->widget);
14853   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14854     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14855   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14856     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14857   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14858   if (display_image->debug != MagickFalse)
14859     (void) LogMagickEvent(X11Event,GetMagickModule(),
14860       "Window id: 0x%lx (widget)",windows->widget.id);
14861   /*
14862     Initialize popup window.
14863   */
14864   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14865     resource_info,&windows->popup);
14866   windows->popup.border_width=0;
14867   windows->popup.flags|=PPosition;
14868   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14869     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14870     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14871   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14872   manager_hints->input=MagickTrue;
14873   manager_hints->initial_state=NormalState;
14874   manager_hints->window_group=windows->image.id;
14875   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14876     &windows->popup);
14877   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14878     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14879   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14880     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14881   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14882   if (display_image->debug != MagickFalse)
14883     (void) LogMagickEvent(X11Event,GetMagickModule(),
14884       "Window id: 0x%lx (pop up)",windows->popup.id);
14885   /*
14886     Initialize Magnify window and cursor.
14887   */
14888   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14889     resource_info,&windows->magnify);
14890   if (resource_info->use_shared_memory == MagickFalse)
14891     windows->magnify.shared_memory=MagickFalse;
14892   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14893     resource_info->client_name);
14894   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14895     resource_name,"geometry",(char *) NULL);
14896   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14897     resource_info->magnify);
14898   if (windows->magnify.cursor != (Cursor) NULL)
14899     (void) XFreeCursor(display,windows->magnify.cursor);
14900   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14901     map_info->colormap,resource_info->background_color,
14902     resource_info->foreground_color);
14903   if (windows->magnify.cursor == (Cursor) NULL)
14904     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14905       display_image->filename);
14906   windows->magnify.width=MagnifySize;
14907   windows->magnify.height=MagnifySize;
14908   windows->magnify.flags|=PPosition;
14909   windows->magnify.min_width=MagnifySize;
14910   windows->magnify.min_height=MagnifySize;
14911   windows->magnify.width_inc=MagnifySize;
14912   windows->magnify.height_inc=MagnifySize;
14913   windows->magnify.data=resource_info->magnify;
14914   windows->magnify.attributes.cursor=windows->magnify.cursor;
14915   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14916     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14917     StructureNotifyMask;
14918   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14919   manager_hints->input=MagickTrue;
14920   manager_hints->initial_state=NormalState;
14921   manager_hints->window_group=windows->image.id;
14922   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14923     &windows->magnify);
14924   if (display_image->debug != MagickFalse)
14925     (void) LogMagickEvent(X11Event,GetMagickModule(),
14926       "Window id: 0x%lx (magnify)",windows->magnify.id);
14927   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14928   /*
14929     Initialize panning window.
14930   */
14931   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14932     resource_info,&windows->pan);
14933   (void) CloneString(&windows->pan.name,"Pan Icon");
14934   windows->pan.width=windows->icon.width;
14935   windows->pan.height=windows->icon.height;
14936   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14937     resource_info->client_name);
14938   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14939     resource_name,"geometry",(char *) NULL);
14940   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14941     &windows->pan.width,&windows->pan.height);
14942   windows->pan.flags|=PPosition;
14943   windows->pan.immutable=MagickTrue;
14944   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14945     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14946     StructureNotifyMask;
14947   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14948   manager_hints->input=MagickFalse;
14949   manager_hints->initial_state=NormalState;
14950   manager_hints->window_group=windows->image.id;
14951   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14952     &windows->pan);
14953   if (display_image->debug != MagickFalse)
14954     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14955       windows->pan.id);
14956   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14957   if (windows->info.mapped != MagickFalse)
14958     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14959   if ((windows->image.mapped == MagickFalse) ||
14960       (windows->backdrop.id != (Window) NULL))
14961     (void) XMapWindow(display,windows->image.id);
14962   /*
14963     Set our progress monitor and warning handlers.
14964   */
14965   if (warning_handler == (WarningHandler) NULL)
14966     {
14967       warning_handler=resource_info->display_warnings ?
14968         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14969       warning_handler=resource_info->display_warnings ?
14970         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14971     }
14972   /*
14973     Initialize Image and Magnify X images.
14974   */
14975   windows->image.x=0;
14976   windows->image.y=0;
14977   windows->magnify.shape=MagickFalse;
14978   width=(unsigned int) display_image->columns;
14979   height=(unsigned int) display_image->rows;
14980   if ((display_image->columns != width) || (display_image->rows != height))
14981     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14982       display_image->filename);
14983   status=XMakeImage(display,resource_info,&windows->image,display_image,
14984     width,height,exception);
14985   if (status == MagickFalse)
14986     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14987       display_image->filename);
14988   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14989     windows->magnify.width,windows->magnify.height,exception);
14990   if (status == MagickFalse)
14991     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14992       display_image->filename);
14993   if (windows->magnify.mapped != MagickFalse)
14994     (void) XMapRaised(display,windows->magnify.id);
14995   if (windows->pan.mapped != MagickFalse)
14996     (void) XMapRaised(display,windows->pan.id);
14997   windows->image.window_changes.width=(int) display_image->columns;
14998   windows->image.window_changes.height=(int) display_image->rows;
14999   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15000   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15001   (void) XSync(display,MagickFalse);
15002   /*
15003     Respond to events.
15004   */
15005   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15006   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15007   update_time=0;
15008   if (resource_info->update != MagickFalse)
15009     {
15010       MagickBooleanType
15011         status;
15012
15013       /*
15014         Determine when file data was last modified.
15015       */
15016       status=GetPathAttributes(display_image->filename,&attributes);
15017       if (status != MagickFalse)
15018         update_time=attributes.st_mtime;
15019     }
15020   *state&=(~FormerImageState);
15021   *state&=(~MontageImageState);
15022   *state&=(~NextImageState);
15023   do
15024   {
15025     /*
15026       Handle a window event.
15027     */
15028     if (windows->image.mapped != MagickFalse)
15029       if ((display_image->delay != 0) || (resource_info->update != 0))
15030         {
15031           if (timer < time((time_t *) NULL))
15032             {
15033               if (resource_info->update == MagickFalse)
15034                 *state|=NextImageState | ExitState;
15035               else
15036                 {
15037                   MagickBooleanType
15038                     status;
15039
15040                   /*
15041                     Determine if image file was modified.
15042                   */
15043                   status=GetPathAttributes(display_image->filename,&attributes);
15044                   if (status != MagickFalse)
15045                     if (update_time != attributes.st_mtime)
15046                       {
15047                         /*
15048                           Redisplay image.
15049                         */
15050                         (void) FormatLocaleString(
15051                           resource_info->image_info->filename,MaxTextExtent,
15052                           "%s:%s",display_image->magick,
15053                           display_image->filename);
15054                         nexus=ReadImage(resource_info->image_info,
15055                           &display_image->exception);
15056                         if (nexus != (Image *) NULL)
15057                           {
15058                             nexus=DestroyImage(nexus);
15059                             *state|=NextImageState | ExitState;
15060                           }
15061                       }
15062                   delay=display_image->delay/MagickMax(
15063                     display_image->ticks_per_second,1L);
15064                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15065                 }
15066             }
15067           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15068             {
15069               /*
15070                 Do not block if delay > 0.
15071               */
15072               XDelay(display,SuspendTime << 2);
15073               continue;
15074             }
15075         }
15076     timestamp=time((time_t *) NULL);
15077     (void) XNextEvent(display,&event);
15078     if (windows->image.stasis == MagickFalse)
15079       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15080         MagickTrue : MagickFalse;
15081     if (windows->magnify.stasis == MagickFalse)
15082       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15083         MagickTrue : MagickFalse;
15084     if (event.xany.window == windows->command.id)
15085       {
15086         /*
15087           Select a command from the Command widget.
15088         */
15089         id=XCommandWidget(display,windows,CommandMenu,&event);
15090         if (id < 0)
15091           continue;
15092         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15093         command_type=CommandMenus[id];
15094         if (id < MagickMenus)
15095           {
15096             /*
15097               Select a command from a pop-up menu.
15098             */
15099             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15100               command);
15101             if (entry < 0)
15102               continue;
15103             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15104             command_type=Commands[id][entry];
15105           }
15106         if (command_type != NullCommand)
15107           nexus=XMagickCommand(display,resource_info,windows,command_type,
15108             &display_image,exception);
15109         continue;
15110       }
15111     switch (event.type)
15112     {
15113       case ButtonPress:
15114       {
15115         if (display_image->debug != MagickFalse)
15116           (void) LogMagickEvent(X11Event,GetMagickModule(),
15117             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15118             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15119         if ((event.xbutton.button == Button3) &&
15120             (event.xbutton.state & Mod1Mask))
15121           {
15122             /*
15123               Convert Alt-Button3 to Button2.
15124             */
15125             event.xbutton.button=Button2;
15126             event.xbutton.state&=(~Mod1Mask);
15127           }
15128         if (event.xbutton.window == windows->backdrop.id)
15129           {
15130             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15131               event.xbutton.time);
15132             break;
15133           }
15134         if (event.xbutton.window == windows->image.id)
15135           {
15136             switch (event.xbutton.button)
15137             {
15138               case Button1:
15139               {
15140                 if (resource_info->immutable)
15141                   {
15142                     /*
15143                       Select a command from the Virtual menu.
15144                     */
15145                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15146                       command);
15147                     if (entry >= 0)
15148                       nexus=XMagickCommand(display,resource_info,windows,
15149                         VirtualCommands[entry],&display_image,exception);
15150                     break;
15151                   }
15152                 /*
15153                   Map/unmap Command widget.
15154                 */
15155                 if (windows->command.mapped != MagickFalse)
15156                   (void) XWithdrawWindow(display,windows->command.id,
15157                     windows->command.screen);
15158                 else
15159                   {
15160                     (void) XCommandWidget(display,windows,CommandMenu,
15161                       (XEvent *) NULL);
15162                     (void) XMapRaised(display,windows->command.id);
15163                   }
15164                 break;
15165               }
15166               case Button2:
15167               {
15168                 /*
15169                   User pressed the image magnify button.
15170                 */
15171                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15172                   &display_image,exception);
15173                 XMagnifyImage(display,windows,&event);
15174                 break;
15175               }
15176               case Button3:
15177               {
15178                 if (resource_info->immutable)
15179                   {
15180                     /*
15181                       Select a command from the Virtual menu.
15182                     */
15183                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15184                       command);
15185                     if (entry >= 0)
15186                       nexus=XMagickCommand(display,resource_info,windows,
15187                         VirtualCommands[entry],&display_image,exception);
15188                     break;
15189                   }
15190                 if (display_image->montage != (char *) NULL)
15191                   {
15192                     /*
15193                       Open or delete a tile from a visual image directory.
15194                     */
15195                     nexus=XTileImage(display,resource_info,windows,
15196                       display_image,&event,exception);
15197                     if (nexus != (Image *) NULL)
15198                       *state|=MontageImageState | NextImageState | ExitState;
15199                     vid_info.x=(short int) windows->image.x;
15200                     vid_info.y=(short int) windows->image.y;
15201                     break;
15202                   }
15203                 /*
15204                   Select a command from the Short Cuts menu.
15205                 */
15206                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15207                   command);
15208                 if (entry >= 0)
15209                   nexus=XMagickCommand(display,resource_info,windows,
15210                     ShortCutsCommands[entry],&display_image,exception);
15211                 break;
15212               }
15213               case Button4:
15214               {
15215                 /*
15216                   Wheel up.
15217                 */
15218                 XTranslateImage(display,windows,*image,XK_Up);
15219                 break;
15220               }
15221               case Button5:
15222               {
15223                 /*
15224                   Wheel down.
15225                 */
15226                 XTranslateImage(display,windows,*image,XK_Down);
15227                 break;
15228               }
15229               default:
15230                 break;
15231             }
15232             break;
15233           }
15234         if (event.xbutton.window == windows->magnify.id)
15235           {
15236             int
15237               factor;
15238
15239             static const char
15240               *MagnifyMenu[] =
15241               {
15242                 "2",
15243                 "4",
15244                 "5",
15245                 "6",
15246                 "7",
15247                 "8",
15248                 "9",
15249                 "3",
15250                 (char *) NULL,
15251               };
15252
15253             static KeySym
15254               MagnifyCommands[] =
15255               {
15256                 XK_2,
15257                 XK_4,
15258                 XK_5,
15259                 XK_6,
15260                 XK_7,
15261                 XK_8,
15262                 XK_9,
15263                 XK_3
15264               };
15265
15266             /*
15267               Select a magnify factor from the pop-up menu.
15268             */
15269             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15270             if (factor >= 0)
15271               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15272             break;
15273           }
15274         if (event.xbutton.window == windows->pan.id)
15275           {
15276             switch (event.xbutton.button)
15277             {
15278               case Button4:
15279               {
15280                 /*
15281                   Wheel up.
15282                 */
15283                 XTranslateImage(display,windows,*image,XK_Up);
15284                 break;
15285               }
15286               case Button5:
15287               {
15288                 /*
15289                   Wheel down.
15290                 */
15291                 XTranslateImage(display,windows,*image,XK_Down);
15292                 break;
15293               }
15294               default:
15295               {
15296                 XPanImage(display,windows,&event);
15297                 break;
15298               }
15299             }
15300             break;
15301           }
15302         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15303           1L);
15304         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15305         break;
15306       }
15307       case ButtonRelease:
15308       {
15309         if (display_image->debug != MagickFalse)
15310           (void) LogMagickEvent(X11Event,GetMagickModule(),
15311             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15312             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15313         break;
15314       }
15315       case ClientMessage:
15316       {
15317         if (display_image->debug != MagickFalse)
15318           (void) LogMagickEvent(X11Event,GetMagickModule(),
15319             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15320             event.xclient.message_type,event.xclient.format,(unsigned long)
15321             event.xclient.data.l[0]);
15322         if (event.xclient.message_type == windows->im_protocols)
15323           {
15324             if (*event.xclient.data.l == (long) windows->im_update_widget)
15325               {
15326                 (void) CloneString(&windows->command.name,MagickTitle);
15327                 windows->command.data=MagickMenus;
15328                 (void) XCommandWidget(display,windows,CommandMenu,
15329                   (XEvent *) NULL);
15330                 break;
15331               }
15332             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15333               {
15334                 /*
15335                   Update graphic context and window colormap.
15336                 */
15337                 for (i=0; i < (int) number_windows; i++)
15338                 {
15339                   if (magick_windows[i]->id == windows->icon.id)
15340                     continue;
15341                   context_values.background=pixel->background_color.pixel;
15342                   context_values.foreground=pixel->foreground_color.pixel;
15343                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15344                     context_mask,&context_values);
15345                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15346                     context_mask,&context_values);
15347                   context_values.background=pixel->foreground_color.pixel;
15348                   context_values.foreground=pixel->background_color.pixel;
15349                   context_values.plane_mask=context_values.background ^
15350                     context_values.foreground;
15351                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15352                     (size_t) (context_mask | GCPlaneMask),
15353                     &context_values);
15354                   magick_windows[i]->attributes.background_pixel=
15355                     pixel->background_color.pixel;
15356                   magick_windows[i]->attributes.border_pixel=
15357                     pixel->border_color.pixel;
15358                   magick_windows[i]->attributes.colormap=map_info->colormap;
15359                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15360                     (unsigned long) magick_windows[i]->mask,
15361                     &magick_windows[i]->attributes);
15362                 }
15363                 if (windows->pan.mapped != MagickFalse)
15364                   {
15365                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15366                       windows->pan.pixmap);
15367                     (void) XClearWindow(display,windows->pan.id);
15368                     XDrawPanRectangle(display,windows);
15369                   }
15370                 if (windows->backdrop.id != (Window) NULL)
15371                   (void) XInstallColormap(display,map_info->colormap);
15372                 break;
15373               }
15374             if (*event.xclient.data.l == (long) windows->im_former_image)
15375               {
15376                 *state|=FormerImageState | ExitState;
15377                 break;
15378               }
15379             if (*event.xclient.data.l == (long) windows->im_next_image)
15380               {
15381                 *state|=NextImageState | ExitState;
15382                 break;
15383               }
15384             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15385               {
15386                 *state|=RetainColorsState;
15387                 break;
15388               }
15389             if (*event.xclient.data.l == (long) windows->im_exit)
15390               {
15391                 *state|=ExitState;
15392                 break;
15393               }
15394             break;
15395           }
15396         if (event.xclient.message_type == windows->dnd_protocols)
15397           {
15398             Atom
15399               selection,
15400               type;
15401
15402             int
15403               format,
15404               status;
15405
15406             unsigned char
15407               *data;
15408
15409             unsigned long
15410               after,
15411               length;
15412
15413             /*
15414               Display image named by the Drag-and-Drop selection.
15415             */
15416             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15417               break;
15418             selection=XInternAtom(display,"DndSelection",MagickFalse);
15419             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15420               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15421               &length,&after,&data);
15422             if ((status != Success) || (length == 0))
15423               break;
15424             if (*event.xclient.data.l == 2)
15425               {
15426                 /*
15427                   Offix DND.
15428                 */
15429                 (void) CopyMagickString(resource_info->image_info->filename,
15430                   (char *) data,MaxTextExtent);
15431               }
15432             else
15433               {
15434                 /*
15435                   XDND.
15436                 */
15437                 if (strncmp((char *) data, "file:", 5) != 0)
15438                   {
15439                     (void) XFree((void *) data);
15440                     break;
15441                   }
15442                 (void) CopyMagickString(resource_info->image_info->filename,
15443                   ((char *) data)+5,MaxTextExtent);
15444               }
15445             nexus=ReadImage(resource_info->image_info,
15446               &display_image->exception);
15447             CatchException(&display_image->exception);
15448             if (nexus != (Image *) NULL)
15449               *state|=NextImageState | ExitState;
15450             (void) XFree((void *) data);
15451             break;
15452           }
15453         /*
15454           If client window delete message, exit.
15455         */
15456         if (event.xclient.message_type != windows->wm_protocols)
15457           break;
15458         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15459           break;
15460         (void) XWithdrawWindow(display,event.xclient.window,
15461           visual_info->screen);
15462         if (event.xclient.window == windows->image.id)
15463           {
15464             *state|=ExitState;
15465             break;
15466           }
15467         if (event.xclient.window == windows->pan.id)
15468           {
15469             /*
15470               Restore original image size when pan window is deleted.
15471             */
15472             windows->image.window_changes.width=windows->image.ximage->width;
15473             windows->image.window_changes.height=windows->image.ximage->height;
15474             (void) XConfigureImage(display,resource_info,windows,
15475               display_image,exception);
15476           }
15477         break;
15478       }
15479       case ConfigureNotify:
15480       {
15481         if (display_image->debug != MagickFalse)
15482           (void) LogMagickEvent(X11Event,GetMagickModule(),
15483             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15484             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15485             event.xconfigure.y,event.xconfigure.send_event);
15486         if (event.xconfigure.window == windows->image.id)
15487           {
15488             /*
15489               Image window has a new configuration.
15490             */
15491             if (event.xconfigure.send_event != 0)
15492               {
15493                 XWindowChanges
15494                   window_changes;
15495
15496                 /*
15497                   Position the transient windows relative of the Image window.
15498                 */
15499                 if (windows->command.geometry == (char *) NULL)
15500                   if (windows->command.mapped == MagickFalse)
15501                     {
15502                       windows->command.x=event.xconfigure.x-
15503                         windows->command.width-25;
15504                       windows->command.y=event.xconfigure.y;
15505                       XConstrainWindowPosition(display,&windows->command);
15506                       window_changes.x=windows->command.x;
15507                       window_changes.y=windows->command.y;
15508                       (void) XReconfigureWMWindow(display,windows->command.id,
15509                         windows->command.screen,(unsigned int) (CWX | CWY),
15510                         &window_changes);
15511                     }
15512                 if (windows->widget.geometry == (char *) NULL)
15513                   if (windows->widget.mapped == MagickFalse)
15514                     {
15515                       windows->widget.x=event.xconfigure.x+
15516                         event.xconfigure.width/10;
15517                       windows->widget.y=event.xconfigure.y+
15518                         event.xconfigure.height/10;
15519                       XConstrainWindowPosition(display,&windows->widget);
15520                       window_changes.x=windows->widget.x;
15521                       window_changes.y=windows->widget.y;
15522                       (void) XReconfigureWMWindow(display,windows->widget.id,
15523                         windows->widget.screen,(unsigned int) (CWX | CWY),
15524                         &window_changes);
15525                     }
15526                 if (windows->magnify.geometry == (char *) NULL)
15527                   if (windows->magnify.mapped == MagickFalse)
15528                     {
15529                       windows->magnify.x=event.xconfigure.x+
15530                         event.xconfigure.width+25;
15531                       windows->magnify.y=event.xconfigure.y;
15532                       XConstrainWindowPosition(display,&windows->magnify);
15533                       window_changes.x=windows->magnify.x;
15534                       window_changes.y=windows->magnify.y;
15535                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15536                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15537                         &window_changes);
15538                     }
15539                 if (windows->pan.geometry == (char *) NULL)
15540                   if (windows->pan.mapped == MagickFalse)
15541                     {
15542                       windows->pan.x=event.xconfigure.x+
15543                         event.xconfigure.width+25;
15544                       windows->pan.y=event.xconfigure.y+
15545                         windows->magnify.height+50;
15546                       XConstrainWindowPosition(display,&windows->pan);
15547                       window_changes.x=windows->pan.x;
15548                       window_changes.y=windows->pan.y;
15549                       (void) XReconfigureWMWindow(display,windows->pan.id,
15550                         windows->pan.screen,(unsigned int) (CWX | CWY),
15551                         &window_changes);
15552                     }
15553               }
15554             if ((event.xconfigure.width == (int) windows->image.width) &&
15555                 (event.xconfigure.height == (int) windows->image.height))
15556               break;
15557             windows->image.width=(unsigned int) event.xconfigure.width;
15558             windows->image.height=(unsigned int) event.xconfigure.height;
15559             windows->image.x=0;
15560             windows->image.y=0;
15561             if (display_image->montage != (char *) NULL)
15562               {
15563                 windows->image.x=vid_info.x;
15564                 windows->image.y=vid_info.y;
15565               }
15566             if ((windows->image.mapped != MagickFalse) &&
15567                 (windows->image.stasis != MagickFalse))
15568               {
15569                 /*
15570                   Update image window configuration.
15571                 */
15572                 windows->image.window_changes.width=event.xconfigure.width;
15573                 windows->image.window_changes.height=event.xconfigure.height;
15574                 (void) XConfigureImage(display,resource_info,windows,
15575                   display_image,exception);
15576               }
15577             /*
15578               Update pan window configuration.
15579             */
15580             if ((event.xconfigure.width < windows->image.ximage->width) ||
15581                 (event.xconfigure.height < windows->image.ximage->height))
15582               {
15583                 (void) XMapRaised(display,windows->pan.id);
15584                 XDrawPanRectangle(display,windows);
15585               }
15586             else
15587               if (windows->pan.mapped != MagickFalse)
15588                 (void) XWithdrawWindow(display,windows->pan.id,
15589                   windows->pan.screen);
15590             break;
15591           }
15592         if (event.xconfigure.window == windows->magnify.id)
15593           {
15594             unsigned int
15595               magnify;
15596
15597             /*
15598               Magnify window has a new configuration.
15599             */
15600             windows->magnify.width=(unsigned int) event.xconfigure.width;
15601             windows->magnify.height=(unsigned int) event.xconfigure.height;
15602             if (windows->magnify.mapped == MagickFalse)
15603               break;
15604             magnify=1;
15605             while ((int) magnify <= event.xconfigure.width)
15606               magnify<<=1;
15607             while ((int) magnify <= event.xconfigure.height)
15608               magnify<<=1;
15609             magnify>>=1;
15610             if (((int) magnify != event.xconfigure.width) ||
15611                 ((int) magnify != event.xconfigure.height))
15612               {
15613                 window_changes.width=(int) magnify;
15614                 window_changes.height=(int) magnify;
15615                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15616                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15617                   &window_changes);
15618                 break;
15619               }
15620             if ((windows->magnify.mapped != MagickFalse) &&
15621                 (windows->magnify.stasis != MagickFalse))
15622               {
15623                 status=XMakeImage(display,resource_info,&windows->magnify,
15624                   display_image,windows->magnify.width,windows->magnify.height,
15625                   exception);
15626                 XMakeMagnifyImage(display,windows);
15627               }
15628             break;
15629           }
15630         if ((windows->magnify.mapped != MagickFalse) &&
15631             (event.xconfigure.window == windows->pan.id))
15632           {
15633             /*
15634               Pan icon window has a new configuration.
15635             */
15636             if (event.xconfigure.send_event != 0)
15637               {
15638                 windows->pan.x=event.xconfigure.x;
15639                 windows->pan.y=event.xconfigure.y;
15640               }
15641             windows->pan.width=(unsigned int) event.xconfigure.width;
15642             windows->pan.height=(unsigned int) event.xconfigure.height;
15643             break;
15644           }
15645         if (event.xconfigure.window == windows->icon.id)
15646           {
15647             /*
15648               Icon window has a new configuration.
15649             */
15650             windows->icon.width=(unsigned int) event.xconfigure.width;
15651             windows->icon.height=(unsigned int) event.xconfigure.height;
15652             break;
15653           }
15654         break;
15655       }
15656       case DestroyNotify:
15657       {
15658         /*
15659           Group leader has exited.
15660         */
15661         if (display_image->debug != MagickFalse)
15662           (void) LogMagickEvent(X11Event,GetMagickModule(),
15663             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15664         if (event.xdestroywindow.window == windows->group_leader.id)
15665           {
15666             *state|=ExitState;
15667             break;
15668           }
15669         break;
15670       }
15671       case EnterNotify:
15672       {
15673         /*
15674           Selectively install colormap.
15675         */
15676         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15677           if (event.xcrossing.mode != NotifyUngrab)
15678             XInstallColormap(display,map_info->colormap);
15679         break;
15680       }
15681       case Expose:
15682       {
15683         if (display_image->debug != MagickFalse)
15684           (void) LogMagickEvent(X11Event,GetMagickModule(),
15685             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15686             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15687             event.xexpose.y);
15688         /*
15689           Refresh windows that are now exposed.
15690         */
15691         if ((event.xexpose.window == windows->image.id) &&
15692             (windows->image.mapped != MagickFalse))
15693           {
15694             XRefreshWindow(display,&windows->image,&event);
15695             delay=display_image->delay/MagickMax(
15696               display_image->ticks_per_second,1L);
15697             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15698             break;
15699           }
15700         if ((event.xexpose.window == windows->magnify.id) &&
15701             (windows->magnify.mapped != MagickFalse))
15702           {
15703             XMakeMagnifyImage(display,windows);
15704             break;
15705           }
15706         if (event.xexpose.window == windows->pan.id)
15707           {
15708             XDrawPanRectangle(display,windows);
15709             break;
15710           }
15711         if (event.xexpose.window == windows->icon.id)
15712           {
15713             XRefreshWindow(display,&windows->icon,&event);
15714             break;
15715           }
15716         break;
15717       }
15718       case KeyPress:
15719       {
15720         int
15721           length;
15722
15723         /*
15724           Respond to a user key press.
15725         */
15726         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15727           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15728         *(command+length)='\0';
15729         if (display_image->debug != MagickFalse)
15730           (void) LogMagickEvent(X11Event,GetMagickModule(),
15731             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15732             key_symbol,command);
15733         if (event.xkey.window == windows->image.id)
15734           {
15735             command_type=XImageWindowCommand(display,resource_info,windows,
15736               event.xkey.state,key_symbol,&display_image,exception);
15737             if (command_type != NullCommand)
15738               nexus=XMagickCommand(display,resource_info,windows,command_type,
15739                 &display_image,exception);
15740           }
15741         if (event.xkey.window == windows->magnify.id)
15742           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15743         if (event.xkey.window == windows->pan.id)
15744           {
15745             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15746               (void) XWithdrawWindow(display,windows->pan.id,
15747                 windows->pan.screen);
15748             else
15749               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15750                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15751                   "Help Viewer - Image Pan",ImagePanHelp);
15752               else
15753                 XTranslateImage(display,windows,*image,key_symbol);
15754           }
15755         delay=display_image->delay/MagickMax(
15756           display_image->ticks_per_second,1L);
15757         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15758         break;
15759       }
15760       case KeyRelease:
15761       {
15762         /*
15763           Respond to a user key release.
15764         */
15765         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15766           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15767         if (display_image->debug != MagickFalse)
15768           (void) LogMagickEvent(X11Event,GetMagickModule(),
15769             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15770         break;
15771       }
15772       case LeaveNotify:
15773       {
15774         /*
15775           Selectively uninstall colormap.
15776         */
15777         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15778           if (event.xcrossing.mode != NotifyUngrab)
15779             XUninstallColormap(display,map_info->colormap);
15780         break;
15781       }
15782       case MapNotify:
15783       {
15784         if (display_image->debug != MagickFalse)
15785           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15786             event.xmap.window);
15787         if (event.xmap.window == windows->backdrop.id)
15788           {
15789             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15790               CurrentTime);
15791             windows->backdrop.mapped=MagickTrue;
15792             break;
15793           }
15794         if (event.xmap.window == windows->image.id)
15795           {
15796             if (windows->backdrop.id != (Window) NULL)
15797               (void) XInstallColormap(display,map_info->colormap);
15798             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15799               {
15800                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15801                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15802               }
15803             if (((int) windows->image.width < windows->image.ximage->width) ||
15804                 ((int) windows->image.height < windows->image.ximage->height))
15805               (void) XMapRaised(display,windows->pan.id);
15806             windows->image.mapped=MagickTrue;
15807             break;
15808           }
15809         if (event.xmap.window == windows->magnify.id)
15810           {
15811             XMakeMagnifyImage(display,windows);
15812             windows->magnify.mapped=MagickTrue;
15813             (void) XWithdrawWindow(display,windows->info.id,
15814               windows->info.screen);
15815             break;
15816           }
15817         if (event.xmap.window == windows->pan.id)
15818           {
15819             XMakePanImage(display,resource_info,windows,display_image,
15820               exception);
15821             windows->pan.mapped=MagickTrue;
15822             break;
15823           }
15824         if (event.xmap.window == windows->info.id)
15825           {
15826             windows->info.mapped=MagickTrue;
15827             break;
15828           }
15829         if (event.xmap.window == windows->icon.id)
15830           {
15831             MagickBooleanType
15832               taint;
15833
15834             /*
15835               Create an icon image.
15836             */
15837             taint=display_image->taint;
15838             XMakeStandardColormap(display,icon_visual,icon_resources,
15839               display_image,icon_map,icon_pixel);
15840             (void) XMakeImage(display,icon_resources,&windows->icon,
15841               display_image,windows->icon.width,windows->icon.height,
15842               exception);
15843             display_image->taint=taint;
15844             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15845               windows->icon.pixmap);
15846             (void) XClearWindow(display,windows->icon.id);
15847             (void) XWithdrawWindow(display,windows->info.id,
15848               windows->info.screen);
15849             windows->icon.mapped=MagickTrue;
15850             break;
15851           }
15852         if (event.xmap.window == windows->command.id)
15853           {
15854             windows->command.mapped=MagickTrue;
15855             break;
15856           }
15857         if (event.xmap.window == windows->popup.id)
15858           {
15859             windows->popup.mapped=MagickTrue;
15860             break;
15861           }
15862         if (event.xmap.window == windows->widget.id)
15863           {
15864             windows->widget.mapped=MagickTrue;
15865             break;
15866           }
15867         break;
15868       }
15869       case MappingNotify:
15870       {
15871         (void) XRefreshKeyboardMapping(&event.xmapping);
15872         break;
15873       }
15874       case NoExpose:
15875         break;
15876       case PropertyNotify:
15877       {
15878         Atom
15879           type;
15880
15881         int
15882           format,
15883           status;
15884
15885         unsigned char
15886           *data;
15887
15888         unsigned long
15889           after,
15890           length;
15891
15892         if (display_image->debug != MagickFalse)
15893           (void) LogMagickEvent(X11Event,GetMagickModule(),
15894             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15895             event.xproperty.atom,event.xproperty.state);
15896         if (event.xproperty.atom != windows->im_remote_command)
15897           break;
15898         /*
15899           Display image named by the remote command protocol.
15900         */
15901         status=XGetWindowProperty(display,event.xproperty.window,
15902           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15903           AnyPropertyType,&type,&format,&length,&after,&data);
15904         if ((status != Success) || (length == 0))
15905           break;
15906         if (LocaleCompare((char *) data,"-quit") == 0)
15907           {
15908             XClientMessage(display,windows->image.id,windows->im_protocols,
15909               windows->im_exit,CurrentTime);
15910             (void) XFree((void *) data);
15911             break;
15912           }
15913         (void) CopyMagickString(resource_info->image_info->filename,
15914           (char *) data,MaxTextExtent);
15915         (void) XFree((void *) data);
15916         nexus=ReadImage(resource_info->image_info,&display_image->exception);
15917         CatchException(&display_image->exception);
15918         if (nexus != (Image *) NULL)
15919           *state|=NextImageState | ExitState;
15920         break;
15921       }
15922       case ReparentNotify:
15923       {
15924         if (display_image->debug != MagickFalse)
15925           (void) LogMagickEvent(X11Event,GetMagickModule(),
15926             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15927             event.xreparent.window);
15928         break;
15929       }
15930       case UnmapNotify:
15931       {
15932         if (display_image->debug != MagickFalse)
15933           (void) LogMagickEvent(X11Event,GetMagickModule(),
15934             "Unmap Notify: 0x%lx",event.xunmap.window);
15935         if (event.xunmap.window == windows->backdrop.id)
15936           {
15937             windows->backdrop.mapped=MagickFalse;
15938             break;
15939           }
15940         if (event.xunmap.window == windows->image.id)
15941           {
15942             windows->image.mapped=MagickFalse;
15943             break;
15944           }
15945         if (event.xunmap.window == windows->magnify.id)
15946           {
15947             windows->magnify.mapped=MagickFalse;
15948             break;
15949           }
15950         if (event.xunmap.window == windows->pan.id)
15951           {
15952             windows->pan.mapped=MagickFalse;
15953             break;
15954           }
15955         if (event.xunmap.window == windows->info.id)
15956           {
15957             windows->info.mapped=MagickFalse;
15958             break;
15959           }
15960         if (event.xunmap.window == windows->icon.id)
15961           {
15962             if (map_info->colormap == icon_map->colormap)
15963               XConfigureImageColormap(display,resource_info,windows,
15964                 display_image);
15965             (void) XFreeStandardColormap(display,icon_visual,icon_map,
15966               icon_pixel);
15967             windows->icon.mapped=MagickFalse;
15968             break;
15969           }
15970         if (event.xunmap.window == windows->command.id)
15971           {
15972             windows->command.mapped=MagickFalse;
15973             break;
15974           }
15975         if (event.xunmap.window == windows->popup.id)
15976           {
15977             if (windows->backdrop.id != (Window) NULL)
15978               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15979                 CurrentTime);
15980             windows->popup.mapped=MagickFalse;
15981             break;
15982           }
15983         if (event.xunmap.window == windows->widget.id)
15984           {
15985             if (windows->backdrop.id != (Window) NULL)
15986               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15987                 CurrentTime);
15988             windows->widget.mapped=MagickFalse;
15989             break;
15990           }
15991         break;
15992       }
15993       default:
15994       {
15995         if (display_image->debug != MagickFalse)
15996           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15997             event.type);
15998         break;
15999       }
16000     }
16001   } while (!(*state & ExitState));
16002   if ((*state & ExitState) == 0)
16003     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16004       &display_image,exception);
16005   else
16006     if (resource_info->confirm_edit != MagickFalse)
16007       {
16008         /*
16009           Query user if image has changed.
16010         */
16011         if ((resource_info->immutable == MagickFalse) &&
16012             (display_image->taint != MagickFalse))
16013           {
16014             int
16015               status;
16016
16017             status=XConfirmWidget(display,windows,"Your image changed.",
16018               "Do you want to save it");
16019             if (status == 0)
16020               *state&=(~ExitState);
16021             else
16022               if (status > 0)
16023                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16024                   &display_image,exception);
16025           }
16026       }
16027   if ((windows->visual_info->klass == GrayScale) ||
16028       (windows->visual_info->klass == PseudoColor) ||
16029       (windows->visual_info->klass == DirectColor))
16030     {
16031       /*
16032         Withdraw pan and Magnify window.
16033       */
16034       if (windows->info.mapped != MagickFalse)
16035         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16036       if (windows->magnify.mapped != MagickFalse)
16037         (void) XWithdrawWindow(display,windows->magnify.id,
16038           windows->magnify.screen);
16039       if (windows->command.mapped != MagickFalse)
16040         (void) XWithdrawWindow(display,windows->command.id,
16041           windows->command.screen);
16042     }
16043   if (windows->pan.mapped != MagickFalse)
16044     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16045   if (resource_info->backdrop == MagickFalse)
16046     if (windows->backdrop.mapped)
16047       {
16048         (void) XWithdrawWindow(display,windows->backdrop.id,
16049           windows->backdrop.screen);
16050         (void) XDestroyWindow(display,windows->backdrop.id);
16051         windows->backdrop.id=(Window) NULL;
16052         (void) XWithdrawWindow(display,windows->image.id,
16053           windows->image.screen);
16054         (void) XDestroyWindow(display,windows->image.id);
16055         windows->image.id=(Window) NULL;
16056       }
16057   XSetCursorState(display,windows,MagickTrue);
16058   XCheckRefreshWindows(display,windows);
16059   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16060     *state&=(~ExitState);
16061   if (*state & ExitState)
16062     {
16063       /*
16064         Free Standard Colormap.
16065       */
16066       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16067       if (resource_info->map_type == (char *) NULL)
16068         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16069       /*
16070         Free X resources.
16071       */
16072       if (resource_info->copy_image != (Image *) NULL)
16073         {
16074           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16075           resource_info->copy_image=NewImageList();
16076         }
16077       DestroyXResources();
16078     }
16079   (void) XSync(display,MagickFalse);
16080   /*
16081     Restore our progress monitor and warning handlers.
16082   */
16083   (void) SetErrorHandler(warning_handler);
16084   (void) SetWarningHandler(warning_handler);
16085   /*
16086     Change to home directory.
16087   */
16088   directory=getcwd(working_directory,MaxTextExtent);
16089   (void) directory;
16090   {
16091     int
16092       status;
16093
16094     status=chdir(resource_info->home_directory);
16095     if (status == -1)
16096       (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16097         FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16098   }
16099   *image=display_image;
16100   return(nexus);
16101 }
16102 #else
16103 \f
16104 /*
16105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16106 %                                                                             %
16107 %                                                                             %
16108 %                                                                             %
16109 +   D i s p l a y I m a g e s                                                 %
16110 %                                                                             %
16111 %                                                                             %
16112 %                                                                             %
16113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16114 %
16115 %  DisplayImages() displays an image sequence to any X window screen.  It
16116 %  returns a value other than 0 if successful.  Check the exception member
16117 %  of image to determine the reason for any failure.
16118 %
16119 %  The format of the DisplayImages method is:
16120 %
16121 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16122 %        Image *images,ExceptionInfo *exception)
16123 %
16124 %  A description of each parameter follows:
16125 %
16126 %    o image_info: the image info.
16127 %
16128 %    o image: the image.
16129 %
16130 %    o exception: return any errors or warnings in this structure.
16131 %
16132 */
16133 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16134   Image *image,ExceptionInfo *exception)
16135 {
16136   assert(image_info != (const ImageInfo *) NULL);
16137   assert(image_info->signature == MagickSignature);
16138   assert(image != (Image *) NULL);
16139   assert(image->signature == MagickSignature);
16140   if (image->debug != MagickFalse)
16141     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16142   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16143     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16144   return(MagickFalse);
16145 }
16146 \f
16147 /*
16148 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16149 %                                                                             %
16150 %                                                                             %
16151 %                                                                             %
16152 +   R e m o t e D i s p l a y C o m m a n d                                   %
16153 %                                                                             %
16154 %                                                                             %
16155 %                                                                             %
16156 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16157 %
16158 %  RemoteDisplayCommand() encourages a remote display program to display the
16159 %  specified image filename.
16160 %
16161 %  The format of the RemoteDisplayCommand method is:
16162 %
16163 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16164 %        const char *window,const char *filename,ExceptionInfo *exception)
16165 %
16166 %  A description of each parameter follows:
16167 %
16168 %    o image_info: the image info.
16169 %
16170 %    o window: Specifies the name or id of an X window.
16171 %
16172 %    o filename: the name of the image filename to display.
16173 %
16174 %    o exception: return any errors or warnings in this structure.
16175 %
16176 */
16177 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16178   const char *window,const char *filename,ExceptionInfo *exception)
16179 {
16180   assert(image_info != (const ImageInfo *) NULL);
16181   assert(image_info->signature == MagickSignature);
16182   assert(filename != (char *) NULL);
16183   (void) window;
16184   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16185   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16186     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16187   return(MagickFalse);
16188 }
16189 #endif