]> granicus.if.org Git - imagemagick/blob - MagickCore/display.c
ac6a95313d2ab187f55cb6431cec8ee3a6f1218f
[imagemagick] / MagickCore / display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7 %               D   D    I    SS     P   P  L      A   A   Y Y                %
8 %               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9 %               D   D    I       SS  P      L      A   A    Y                 %
10 %               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11 %                                                                             %
12 %                                                                             %
13 %        MagickCore Methods to Interactively Display and Edit an Image        %
14 %                                                                             %
15 %                             Software Design                                 %
16 %                               John Cristy                                   %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/client.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/colorspace.h"
49 #include "MagickCore/composite.h"
50 #include "MagickCore/constitute.h"
51 #include "MagickCore/decorate.h"
52 #include "MagickCore/delegate.h"
53 #include "MagickCore/display.h"
54 #include "MagickCore/display-private.h"
55 #include "MagickCore/draw.h"
56 #include "MagickCore/effect.h"
57 #include "MagickCore/enhance.h"
58 #include "MagickCore/exception.h"
59 #include "MagickCore/exception-private.h"
60 #include "MagickCore/fx.h"
61 #include "MagickCore/geometry.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/magick.h"
67 #include "MagickCore/memory_.h"
68 #include "MagickCore/monitor.h"
69 #include "MagickCore/monitor-private.h"
70 #include "MagickCore/montage.h"
71 #include "MagickCore/option.h"
72 #include "MagickCore/paint.h"
73 #include "MagickCore/pixel.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/PreRvIcccm.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum.h"
78 #include "MagickCore/quantum-private.h"
79 #include "MagickCore/resize.h"
80 #include "MagickCore/resource_.h"
81 #include "MagickCore/shear.h"
82 #include "MagickCore/segment.h"
83 #include "MagickCore/string_.h"
84 #include "MagickCore/string-private.h"
85 #include "MagickCore/transform.h"
86 #include "MagickCore/threshold.h"
87 #include "MagickCore/utility.h"
88 #include "MagickCore/utility-private.h"
89 #include "MagickCore/version.h"
90 #include "MagickCore/widget.h"
91 #include "MagickCore/widget-private.h"
92 #include "MagickCore/xwindow.h"
93 #include "MagickCore/xwindow-private.h"
94 \f
95 #if defined(MAGICKCORE_X11_DELEGATE)
96 /*
97   Define declarations.
98 */
99 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
100 \f
101 /*
102   Constant declarations.
103 */
104 static const unsigned char
105   HighlightBitmap[8] =
106   {
107     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
108   },
109   OpaqueBitmap[8] =
110   {
111     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
112   },
113   ShadowBitmap[8] =
114   {
115     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
116   };
117
118 static const char
119   *PageSizes[] =
120   {
121     "Letter",
122     "Tabloid",
123     "Ledger",
124     "Legal",
125     "Statement",
126     "Executive",
127     "A3",
128     "A4",
129     "A5",
130     "B4",
131     "B5",
132     "Folio",
133     "Quarto",
134     "10x14",
135     (char *) NULL
136   };
137 \f
138 /*
139   Help widget declarations.
140 */
141 static const char
142   *ImageAnnotateHelp[] =
143   {
144     "In annotate mode, the Command widget has these options:",
145     "",
146     "    Font Name",
147     "      fixed",
148     "      variable",
149     "      5x8",
150     "      6x10",
151     "      7x13bold",
152     "      8x13bold",
153     "      9x15bold",
154     "      10x20",
155     "      12x24",
156     "      Browser...",
157     "    Font Color",
158     "      black",
159     "      blue",
160     "      cyan",
161     "      green",
162     "      gray",
163     "      red",
164     "      magenta",
165     "      yellow",
166     "      white",
167     "      transparent",
168     "      Browser...",
169     "    Font Color",
170     "      black",
171     "      blue",
172     "      cyan",
173     "      green",
174     "      gray",
175     "      red",
176     "      magenta",
177     "      yellow",
178     "      white",
179     "      transparent",
180     "      Browser...",
181     "    Rotate Text",
182     "      -90",
183     "      -45",
184     "      -30",
185     "      0",
186     "      30",
187     "      45",
188     "      90",
189     "      180",
190     "      Dialog...",
191     "    Help",
192     "    Dismiss",
193     "",
194     "Choose a font name from the Font Name sub-menu.  Additional",
195     "font names can be specified with the font browser.  You can",
196     "change the menu names by setting the X resources font1",
197     "through font9.",
198     "",
199     "Choose a font color from the Font Color sub-menu.",
200     "Additional font colors can be specified with the color",
201     "browser.  You can change the menu colors by setting the X",
202     "resources pen1 through pen9.",
203     "",
204     "If you select the color browser and press Grab, you can",
205     "choose the font color by moving the pointer to the desired",
206     "color on the screen and press any button.",
207     "",
208     "If you choose to rotate the text, choose Rotate Text from the",
209     "menu and select an angle.  Typically you will only want to",
210     "rotate one line of text at a time.  Depending on the angle you",
211     "choose, subsequent lines may end up overwriting each other.",
212     "",
213     "Choosing a font and its color is optional.  The default font",
214     "is fixed and the default color is black.  However, you must",
215     "choose a location to begin entering text and press button 1.",
216     "An underscore character will appear at the location of the",
217     "pointer.  The cursor changes to a pencil to indicate you are",
218     "in text mode.  To exit immediately, press Dismiss.",
219     "",
220     "In text mode, any key presses will display the character at",
221     "the location of the underscore and advance the underscore",
222     "cursor.  Enter your text and once completed press Apply to",
223     "finish your image annotation.  To correct errors press BACK",
224     "SPACE.  To delete an entire line of text, press DELETE.  Any",
225     "text that exceeds the boundaries of the image window is",
226     "automagically continued onto the next line.",
227     "",
228     "The actual color you request for the font is saved in the",
229     "image.  However, the color that appears in your image window",
230     "may be different.  For example, on a monochrome screen the",
231     "text will appear black or white even if you choose the color",
232     "red as the font color.  However, the image saved to a file",
233     "with -write is written with red lettering.  To assure the",
234     "correct color text in the final image, any PseudoClass image",
235     "is promoted to DirectClass (see miff(5)).  To force a",
236     "PseudoClass image to remain PseudoClass, use -colors.",
237     (char *) NULL,
238   },
239   *ImageChopHelp[] =
240   {
241     "In chop mode, the Command widget has these options:",
242     "",
243     "    Direction",
244     "      horizontal",
245     "      vertical",
246     "    Help",
247     "    Dismiss",
248     "",
249     "If the you choose the horizontal direction (this the",
250     "default), the area of the image between the two horizontal",
251     "endpoints of the chop line is removed.  Otherwise, the area",
252     "of the image between the two vertical endpoints of the chop",
253     "line is removed.",
254     "",
255     "Select a location within the image window to begin your chop,",
256     "press and hold any button.  Next, move the pointer to",
257     "another location in the image.  As you move a line will",
258     "connect the initial location and the pointer.  When you",
259     "release the button, the area within the image to chop is",
260     "determined by which direction you choose from the Command",
261     "widget.",
262     "",
263     "To cancel the image chopping, move the pointer back to the",
264     "starting point of the line and release the button.",
265     (char *) NULL,
266   },
267   *ImageColorEditHelp[] =
268   {
269     "In color edit mode, the Command widget has these options:",
270     "",
271     "    Method",
272     "      point",
273     "      replace",
274     "      floodfill",
275     "      filltoborder",
276     "      reset",
277     "    Pixel Color",
278     "      black",
279     "      blue",
280     "      cyan",
281     "      green",
282     "      gray",
283     "      red",
284     "      magenta",
285     "      yellow",
286     "      white",
287     "      Browser...",
288     "    Border Color",
289     "      black",
290     "      blue",
291     "      cyan",
292     "      green",
293     "      gray",
294     "      red",
295     "      magenta",
296     "      yellow",
297     "      white",
298     "      Browser...",
299     "    Fuzz",
300     "      0%",
301     "      2%",
302     "      5%",
303     "      10%",
304     "      15%",
305     "      Dialog...",
306     "    Undo",
307     "    Help",
308     "    Dismiss",
309     "",
310     "Choose a color editing method from the Method sub-menu",
311     "of the Command widget.  The point method recolors any pixel",
312     "selected with the pointer until the button is released.  The",
313     "replace method recolors any pixel that matches the color of",
314     "the pixel you select with a button press.  Floodfill recolors",
315     "any pixel that matches the color of the pixel you select with",
316     "a button press and is a neighbor.  Whereas filltoborder recolors",
317     "any neighbor pixel that is not the border color.  Finally reset",
318     "changes the entire image to the designated color.",
319     "",
320     "Next, choose a pixel color from the Pixel Color sub-menu.",
321     "Additional pixel colors can be specified with the color",
322     "browser.  You can change the menu colors by setting the X",
323     "resources pen1 through pen9.",
324     "",
325     "Now press button 1 to select a pixel within the image window",
326     "to change its color.  Additional pixels may be recolored as",
327     "prescribed by the method you choose.",
328     "",
329     "If the Magnify widget is mapped, it can be helpful in positioning",
330     "your pointer within the image (refer to button 2).",
331     "",
332     "The actual color you request for the pixels is saved in the",
333     "image.  However, the color that appears in your image window",
334     "may be different.  For example, on a monochrome screen the",
335     "pixel will appear black or white even if you choose the",
336     "color red as the pixel color.  However, the image saved to a",
337     "file with -write is written with red pixels.  To assure the",
338     "correct color text in the final image, any PseudoClass image",
339     "is promoted to DirectClass (see miff(5)).  To force a",
340     "PseudoClass image to remain PseudoClass, use -colors.",
341     (char *) NULL,
342   },
343   *ImageCompositeHelp[] =
344   {
345     "First a widget window is displayed requesting you to enter an",
346     "image name. Press Composite, Grab or type a file name.",
347     "Press Cancel if you choose not to create a composite image.",
348     "When you choose Grab, move the pointer to the desired window",
349     "and press any button.",
350     "",
351     "If the Composite image does not have any matte information,",
352     "you are informed and the file browser is displayed again.",
353     "Enter the name of a mask image.  The image is typically",
354     "grayscale and the same size as the composite image.  If the",
355     "image is not grayscale, it is converted to grayscale and the",
356     "resulting intensities are used as matte information.",
357     "",
358     "A small window appears showing the location of the cursor in",
359     "the image window. You are now in composite mode.  To exit",
360     "immediately, press Dismiss.  In composite mode, the Command",
361     "widget has these options:",
362     "",
363     "    Operators",
364     "      Over",
365     "      In",
366     "      Out",
367     "      Atop",
368     "      Xor",
369     "      Plus",
370     "      Minus",
371     "      Add",
372     "      Subtract",
373     "      Difference",
374     "      Multiply",
375     "      Bumpmap",
376     "      Copy",
377     "      CopyRed",
378     "      CopyGreen",
379     "      CopyBlue",
380     "      CopyOpacity",
381     "      Clear",
382     "    Dissolve",
383     "    Displace",
384     "    Help",
385     "    Dismiss",
386     "",
387     "Choose a composite operation from the Operators sub-menu of",
388     "the Command widget.  How each operator behaves is described",
389     "below.  Image window is the image currently displayed on",
390     "your X server and image is the image obtained with the File",
391     "Browser widget.",
392     "",
393     "Over     The result is the union of the two image shapes,",
394     "         with image obscuring image window in the region of",
395     "         overlap.",
396     "",
397     "In       The result is simply image cut by the shape of",
398     "         image window.  None of the image data of image",
399     "         window is in the result.",
400     "",
401     "Out      The resulting image is image with the shape of",
402     "         image window cut out.",
403     "",
404     "Atop     The result is the same shape as image image window,",
405     "         with image obscuring image window where the image",
406     "         shapes overlap.  Note this differs from over",
407     "         because the portion of image outside image window's",
408     "         shape does not appear in the result.",
409     "",
410     "Xor      The result is the image data from both image and",
411     "         image window that is outside the overlap region.",
412     "         The overlap region is blank.",
413     "",
414     "Plus     The result is just the sum of the image data.",
415     "         Output values are cropped to QuantumRange (no overflow).",
416     "",
417     "Minus    The result of image - image window, with underflow",
418     "         cropped to zero.",
419     "",
420     "Add      The result of image + image window, with overflow",
421     "         wrapping around (mod 256).",
422     "",
423     "Subtract The result of image - image window, with underflow",
424     "         wrapping around (mod 256).  The add and subtract",
425     "         operators can be used to perform reversible",
426     "         transformations.",
427     "",
428     "Difference",
429     "         The result of abs(image - image window).  This",
430     "         useful for comparing two very similar images.",
431     "",
432     "Multiply",
433     "         The result of image * image window.  This",
434     "         useful for the creation of drop-shadows.",
435     "",
436     "Bumpmap  The result of surface normals from image * image",
437     "         window.",
438     "",
439     "Copy     The resulting image is image window replaced with",
440     "         image.  Here the matte information is ignored.",
441     "",
442     "CopyRed  The red layer of the image window is replace with",
443     "         the red layer of the image.  The other layers are",
444     "         untouched.",
445     "",
446     "CopyGreen",
447     "         The green layer of the image window is replace with",
448     "         the green layer of the image.  The other layers are",
449     "         untouched.",
450     "",
451     "CopyBlue The blue layer of the image window is replace with",
452     "         the blue layer of the image.  The other layers are",
453     "         untouched.",
454     "",
455     "CopyOpacity",
456     "         The matte layer of the image window is replace with",
457     "         the matte layer of the image.  The other layers are",
458     "         untouched.",
459     "",
460     "The image compositor requires a matte, or alpha channel in",
461     "the image for some operations.  This extra channel usually",
462     "defines a mask which represents a sort of a cookie-cutter",
463     "for the image.  This the case when matte is opaque (full",
464     "coverage) for pixels inside the shape, zero outside, and",
465     "between 0 and QuantumRange on the boundary.  If image does not",
466     "have a matte channel, it is initialized with 0 for any pixel",
467     "matching in color to pixel location (0,0), otherwise QuantumRange.",
468     "",
469     "If you choose Dissolve, the composite operator becomes Over.  The",
470     "image matte channel percent transparency is initialized to factor.",
471     "The image window is initialized to (100-factor). Where factor is the",
472     "value you specify in the Dialog widget.",
473     "",
474     "Displace shifts the image pixels as defined by a displacement",
475     "map.  With this option, image is used as a displacement map.",
476     "Black, within the displacement map, is a maximum positive",
477     "displacement.  White is a maximum negative displacement and",
478     "middle gray is neutral.  The displacement is scaled to determine",
479     "the pixel shift.  By default, the displacement applies in both the",
480     "horizontal and vertical directions.  However, if you specify a mask,",
481     "image is the horizontal X displacement and mask the vertical Y",
482     "displacement.",
483     "",
484     "Note that matte information for image window is not retained",
485     "for colormapped X server visuals (e.g. StaticColor,",
486     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
487     "behavior may require a TrueColor or DirectColor visual or a",
488     "Standard Colormap.",
489     "",
490     "Choosing a composite operator is optional.  The default",
491     "operator is replace.  However, you must choose a location to",
492     "composite your image and press button 1.  Press and hold the",
493     "button before releasing and an outline of the image will",
494     "appear to help you identify your location.",
495     "",
496     "The actual colors of the composite image is saved.  However,",
497     "the color that appears in image window may be different.",
498     "For example, on a monochrome screen image window will appear",
499     "black or white even though your composited image may have",
500     "many colors.  If the image is saved to a file it is written",
501     "with the correct colors.  To assure the correct colors are",
502     "saved in the final image, any PseudoClass image is promoted",
503     "to DirectClass (see miff(5)).  To force a PseudoClass image",
504     "to remain PseudoClass, use -colors.",
505     (char *) NULL,
506   },
507   *ImageCutHelp[] =
508   {
509     "In cut mode, the Command widget has these options:",
510     "",
511     "    Help",
512     "    Dismiss",
513     "",
514     "To define a cut region, press button 1 and drag.  The",
515     "cut region is defined by a highlighted rectangle that",
516     "expands or contracts as it follows the pointer.  Once you",
517     "are satisfied with the cut region, release the button.",
518     "You are now in rectify mode.  In rectify mode, the Command",
519     "widget has these options:",
520     "",
521     "    Cut",
522     "    Help",
523     "    Dismiss",
524     "",
525     "You can make adjustments by moving the pointer to one of the",
526     "cut rectangle corners, pressing a button, and dragging.",
527     "Finally, press Cut to commit your copy region.  To",
528     "exit without cutting the image, press Dismiss.",
529     (char *) NULL,
530   },
531   *ImageCopyHelp[] =
532   {
533     "In copy mode, the Command widget has these options:",
534     "",
535     "    Help",
536     "    Dismiss",
537     "",
538     "To define a copy region, press button 1 and drag.  The",
539     "copy region is defined by a highlighted rectangle that",
540     "expands or contracts as it follows the pointer.  Once you",
541     "are satisfied with the copy region, release the button.",
542     "You are now in rectify mode.  In rectify mode, the Command",
543     "widget has these options:",
544     "",
545     "    Copy",
546     "    Help",
547     "    Dismiss",
548     "",
549     "You can make adjustments by moving the pointer to one of the",
550     "copy rectangle corners, pressing a button, and dragging.",
551     "Finally, press Copy to commit your copy region.  To",
552     "exit without copying the image, press Dismiss.",
553     (char *) NULL,
554   },
555   *ImageCropHelp[] =
556   {
557     "In crop mode, the Command widget has these options:",
558     "",
559     "    Help",
560     "    Dismiss",
561     "",
562     "To define a cropping region, press button 1 and drag.  The",
563     "cropping region is defined by a highlighted rectangle that",
564     "expands or contracts as it follows the pointer.  Once you",
565     "are satisfied with the cropping region, release the button.",
566     "You are now in rectify mode.  In rectify mode, the Command",
567     "widget has these options:",
568     "",
569     "    Crop",
570     "    Help",
571     "    Dismiss",
572     "",
573     "You can make adjustments by moving the pointer to one of the",
574     "cropping rectangle corners, pressing a button, and dragging.",
575     "Finally, press Crop to commit your cropping region.  To",
576     "exit without cropping the image, press Dismiss.",
577     (char *) NULL,
578   },
579   *ImageDrawHelp[] =
580   {
581     "The cursor changes to a crosshair to indicate you are in",
582     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
583     "the Command widget has these options:",
584     "",
585     "    Element",
586     "      point",
587     "      line",
588     "      rectangle",
589     "      fill rectangle",
590     "      circle",
591     "      fill circle",
592     "      ellipse",
593     "      fill ellipse",
594     "      polygon",
595     "      fill polygon",
596     "    Color",
597     "      black",
598     "      blue",
599     "      cyan",
600     "      green",
601     "      gray",
602     "      red",
603     "      magenta",
604     "      yellow",
605     "      white",
606     "      transparent",
607     "      Browser...",
608     "    Stipple",
609     "      Brick",
610     "      Diagonal",
611     "      Scales",
612     "      Vertical",
613     "      Wavy",
614     "      Translucent",
615     "      Opaque",
616     "      Open...",
617     "    Width",
618     "      1",
619     "      2",
620     "      4",
621     "      8",
622     "      16",
623     "      Dialog...",
624     "    Undo",
625     "    Help",
626     "    Dismiss",
627     "",
628     "Choose a drawing primitive from the Element sub-menu.",
629     "",
630     "Choose a color from the Color sub-menu.  Additional",
631     "colors can be specified with the color browser.",
632     "",
633     "If you choose the color browser and press Grab, you can",
634     "select the color by moving the pointer to the desired",
635     "color on the screen and press any button.  The transparent",
636     "color updates the image matte channel and is useful for",
637     "image compositing.",
638     "",
639     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
640     "Additional stipples can be specified with the file browser.",
641     "Stipples obtained from the file browser must be on disk in the",
642     "X11 bitmap format.",
643     "",
644     "Choose a width, if appropriate, from the Width sub-menu.  To",
645     "choose a specific width select the Dialog widget.",
646     "",
647     "Choose a point in the Image window and press button 1 and",
648     "hold.  Next, move the pointer to another location in the",
649     "image.  As you move, a line connects the initial location and",
650     "the pointer.  When you release the button, the image is",
651     "updated with the primitive you just drew.  For polygons, the",
652     "image is updated when you press and release the button without",
653     "moving the pointer.",
654     "",
655     "To cancel image drawing, move the pointer back to the",
656     "starting point of the line and release the button.",
657     (char *) NULL,
658   },
659   *DisplayHelp[] =
660   {
661     "BUTTONS",
662     "  The effects of each button press is described below.  Three",
663     "  buttons are required.  If you have a two button mouse,",
664     "  button 1 and 3 are returned.  Press ALT and button 3 to",
665     "  simulate button 2.",
666     "",
667     "  1    Press this button to map or unmap the Command widget.",
668     "",
669     "  2    Press and drag to define a region of the image to",
670     "       magnify.",
671     "",
672     "  3    Press and drag to choose from a select set of commands.",
673     "       This button behaves differently if the image being",
674     "       displayed is a visual image directory.  Here, choose a",
675     "       particular tile of the directory and press this button and",
676     "       drag to select a command from a pop-up menu.  Choose from",
677     "       these menu items:",
678     "",
679     "           Open",
680     "           Next",
681     "           Former",
682     "           Delete",
683     "           Update",
684     "",
685     "       If you choose Open, the image represented by the tile is",
686     "       displayed.  To return to the visual image directory, choose",
687     "       Next from the Command widget.  Next and Former moves to the",
688     "       next or former image respectively.  Choose Delete to delete",
689     "       a particular image tile.  Finally, choose Update to",
690     "       synchronize all the image tiles with their respective",
691     "       images.",
692     "",
693     "COMMAND WIDGET",
694     "  The Command widget lists a number of sub-menus and commands.",
695     "  They are",
696     "",
697     "      File",
698     "        Open...",
699     "        Next",
700     "        Former",
701     "        Select...",
702     "        Save...",
703     "        Print...",
704     "        Delete...",
705     "        New...",
706     "        Visual Directory...",
707     "        Quit",
708     "      Edit",
709     "        Undo",
710     "        Redo",
711     "        Cut",
712     "        Copy",
713     "        Paste",
714     "      View",
715     "        Half Size",
716     "        Original Size",
717     "        Double Size",
718     "        Resize...",
719     "        Apply",
720     "        Refresh",
721     "        Restore",
722     "      Transform",
723     "        Crop",
724     "        Chop",
725     "        Flop",
726     "        Flip",
727     "        Rotate Right",
728     "        Rotate Left",
729     "        Rotate...",
730     "        Shear...",
731     "        Roll...",
732     "        Trim Edges",
733     "      Enhance",
734     "        Brightness...",
735     "        Saturation...",
736     "        Hue...",
737     "        Gamma...",
738     "        Sharpen...",
739     "        Dull",
740     "        Contrast Stretch...",
741     "        Sigmoidal Contrast...",
742     "        Normalize",
743     "        Equalize",
744     "        Negate",
745     "        Grayscale",
746     "        Map...",
747     "        Quantize...",
748     "      Effects",
749     "        Despeckle",
750     "        Emboss",
751     "        Reduce Noise",
752     "        Add Noise",
753     "        Sharpen...",
754     "        Blur...",
755     "        Threshold...",
756     "        Edge Detect...",
757     "        Spread...",
758     "        Shade...",
759     "        Painting...",
760     "        Segment...",
761     "      F/X",
762     "        Solarize...",
763     "        Sepia Tone...",
764     "        Swirl...",
765     "        Implode...",
766     "        Vignette...",
767     "        Wave...",
768     "        Oil Painting...",
769     "        Charcoal Drawing...",
770     "      Image Edit",
771     "        Annotate...",
772     "        Draw...",
773     "        Color...",
774     "        Matte...",
775     "        Composite...",
776     "        Add Border...",
777     "        Add Frame...",
778     "        Comment...",
779     "        Launch...",
780     "        Region of Interest...",
781     "      Miscellany",
782     "        Image Info",
783     "        Zoom Image",
784     "        Show Preview...",
785     "        Show Histogram",
786     "        Show Matte",
787     "        Background...",
788     "        Slide Show",
789     "        Preferences...",
790     "      Help",
791     "        Overview",
792     "        Browse Documentation",
793     "        About Display",
794     "",
795     "  Menu items with a indented triangle have a sub-menu.  They",
796     "  are represented above as the indented items.  To access a",
797     "  sub-menu item, move the pointer to the appropriate menu and",
798     "  press a button and drag.  When you find the desired sub-menu",
799     "  item, release the button and the command is executed.  Move",
800     "  the pointer away from the sub-menu if you decide not to",
801     "  execute a particular command.",
802     "",
803     "KEYBOARD ACCELERATORS",
804     "  Accelerators are one or two key presses that effect a",
805     "  particular command.  The keyboard accelerators that",
806     "  display(1) understands is:",
807     "",
808     "  Ctl+O     Press to open an image from a file.",
809     "",
810     "  space     Press to display the next image.",
811     "",
812     "            If the image is a multi-paged document such as a Postscript",
813     "            document, you can skip ahead several pages by preceding",
814     "            this command with a number.  For example to display the",
815     "            third page beyond the current page, press 3<space>.",
816     "",
817     "  backspace Press to display the former image.",
818     "",
819     "            If the image is a multi-paged document such as a Postscript",
820     "            document, you can skip behind several pages by preceding",
821     "            this command with a number.  For example to display the",
822     "            third page preceding the current page, press 3<backspace>.",
823     "",
824     "  Ctl+S     Press to write the image to a file.",
825     "",
826     "  Ctl+P     Press to print the image to a Postscript printer.",
827     "",
828     "  Ctl+D     Press to delete an image file.",
829     "",
830     "  Ctl+N     Press to create a blank canvas.",
831     "",
832     "  Ctl+Q     Press to discard all images and exit program.",
833     "",
834     "  Ctl+Z     Press to undo last image transformation.",
835     "",
836     "  Ctl+R     Press to redo last image transformation.",
837     "",
838     "  Ctl+X     Press to cut a region of the image.",
839     "",
840     "  Ctl+C     Press to copy a region of the image.",
841     "",
842     "  Ctl+V     Press to paste a region to the image.",
843     "",
844     "  <         Press to half the image size.",
845     "",
846     "  -         Press to return to the original image size.",
847     "",
848     "  >         Press to double the image size.",
849     "",
850     "  %         Press to resize the image to a width and height you",
851     "            specify.",
852     "",
853     "Cmd-A       Press to make any image transformations permanent."
854     "",
855     "            By default, any image size transformations are applied",
856     "            to the original image to create the image displayed on",
857     "            the X server.  However, the transformations are not",
858     "            permanent (i.e. the original image does not change",
859     "            size only the X image does).  For example, if you",
860     "            press > the X image will appear to double in size,",
861     "            but the original image will in fact remain the same size.",
862     "            To force the original image to double in size, press >",
863     "            followed by Cmd-A.",
864     "",
865     "  @         Press to refresh the image window.",
866     "",
867     "  C         Press to cut out a rectangular region of the image.",
868     "",
869     "  [         Press to chop the image.",
870     "",
871     "  H         Press to flop image in the horizontal direction.",
872     "",
873     "  V         Press to flip image in the vertical direction.",
874     "",
875     "  /         Press to rotate the image 90 degrees clockwise.",
876     "",
877     " \\         Press to rotate the image 90 degrees counter-clockwise.",
878     "",
879     "  *         Press to rotate the image the number of degrees you",
880     "            specify.",
881     "",
882     "  S         Press to shear the image the number of degrees you",
883     "            specify.",
884     "",
885     "  R         Press to roll the image.",
886     "",
887     "  T         Press to trim the image edges.",
888     "",
889     "  Shft-H    Press to vary the image hue.",
890     "",
891     "  Shft-S    Press to vary the color saturation.",
892     "",
893     "  Shft-L    Press to vary the color brightness.",
894     "",
895     "  Shft-G    Press to gamma correct the image.",
896     "",
897     "  Shft-C    Press to sharpen the image contrast.",
898     "",
899     "  Shft-Z    Press to dull the image contrast.",
900     "",
901     "  =         Press to perform histogram equalization on the image.",
902     "",
903     "  Shft-N    Press to perform histogram normalization on the image.",
904     "",
905     "  Shft-~    Press to negate the colors of the image.",
906     "",
907     "  .         Press to convert the image colors to gray.",
908     "",
909     "  Shft-#    Press to set the maximum number of unique colors in the",
910     "            image.",
911     "",
912     "  F2        Press to reduce the speckles in an image.",
913     "",
914     "  F3        Press to eliminate peak noise from an image.",
915     "",
916     "  F4        Press to add noise to an image.",
917     "",
918     "  F5        Press to sharpen an image.",
919     "",
920     "  F6        Press to delete an image file.",
921     "",
922     "  F7        Press to threshold the image.",
923     "",
924     "  F8        Press to detect edges within an image.",
925     "",
926     "  F9        Press to emboss an image.",
927     "",
928     "  F10       Press to displace pixels by a random amount.",
929     "",
930     "  F11       Press to negate all pixels above the threshold level.",
931     "",
932     "  F12       Press to shade the image using a distant light source.",
933     "",
934     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
935     "",
936     "  F14       Press to segment the image by color.",
937     "",
938     "  Meta-S    Press to swirl image pixels about the center.",
939     "",
940     "  Meta-I    Press to implode image pixels about the center.",
941     "",
942     "  Meta-W    Press to alter an image along a sine wave.",
943     "",
944     "  Meta-P    Press to simulate an oil painting.",
945     "",
946     "  Meta-C    Press to simulate a charcoal drawing.",
947     "",
948     "  Alt-A     Press to annotate the image with text.",
949     "",
950     "  Alt-D     Press to draw on an image.",
951     "",
952     "  Alt-P     Press to edit an image pixel color.",
953     "",
954     "  Alt-M     Press to edit the image matte information.",
955     "",
956     "  Alt-V     Press to composite the image with another.",
957     "",
958     "  Alt-B     Press to add a border to the image.",
959     "",
960     "  Alt-F     Press to add an ornamental border to the image.",
961     "",
962     "  Alt-Shft-!",
963     "            Press to add an image comment.",
964     "",
965     "  Ctl-A     Press to apply image processing techniques to a region",
966     "            of interest.",
967     "",
968     "  Shft-?    Press to display information about the image.",
969     "",
970     "  Shft-+    Press to map the zoom image window.",
971     "",
972     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
973     "",
974     "  F1        Press to display helpful information about display(1).",
975     "",
976     "  Find      Press to browse documentation about ImageMagick.",
977     "",
978     "  1-9       Press to change the level of magnification.",
979     "",
980     "  Use the arrow keys to move the image one pixel up, down,",
981     "  left, or right within the magnify window.  Be sure to first",
982     "  map the magnify window by pressing button 2.",
983     "",
984     "  Press ALT and one of the arrow keys to trim off one pixel",
985     "  from any side of the image.",
986     (char *) NULL,
987   },
988   *ImageMatteEditHelp[] =
989   {
990     "Matte information within an image is useful for some",
991     "operations such as image compositing (See IMAGE",
992     "COMPOSITING).  This extra channel usually defines a mask",
993     "which represents a sort of a cookie-cutter for the image.",
994     "This the case when matte is opaque (full coverage) for",
995     "pixels inside the shape, zero outside, and between 0 and",
996     "QuantumRange on the boundary.",
997     "",
998     "A small window appears showing the location of the cursor in",
999     "the image window. You are now in matte edit mode.  To exit",
1000     "immediately, press Dismiss.  In matte edit mode, the Command",
1001     "widget has these options:",
1002     "",
1003     "    Method",
1004     "      point",
1005     "      replace",
1006     "      floodfill",
1007     "      filltoborder",
1008     "      reset",
1009     "    Border Color",
1010     "      black",
1011     "      blue",
1012     "      cyan",
1013     "      green",
1014     "      gray",
1015     "      red",
1016     "      magenta",
1017     "      yellow",
1018     "      white",
1019     "      Browser...",
1020     "    Fuzz",
1021     "      0%",
1022     "      2%",
1023     "      5%",
1024     "      10%",
1025     "      15%",
1026     "      Dialog...",
1027     "    Matte",
1028     "      Opaque",
1029     "      Transparent",
1030     "      Dialog...",
1031     "    Undo",
1032     "    Help",
1033     "    Dismiss",
1034     "",
1035     "Choose a matte editing method from the Method sub-menu of",
1036     "the Command widget.  The point method changes the matte value",
1037     "of any pixel selected with the pointer until the button is",
1038     "is released.  The replace method changes the matte value of",
1039     "any pixel that matches the color of the pixel you select with",
1040     "a button press.  Floodfill changes the matte value of any pixel",
1041     "that matches the color of the pixel you select with a button",
1042     "press and is a neighbor.  Whereas filltoborder changes the matte",
1043     "value any neighbor pixel that is not the border color.  Finally",
1044     "reset changes the entire image to the designated matte value.",
1045     "",
1046     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1047     "select the Dialog entry.  Here a dialog appears requesting a matte",
1048     "value.  The value you select is assigned as the opacity value of the",
1049     "selected pixel or pixels.",
1050     "",
1051     "Now, press any button to select a pixel within the image",
1052     "window to change its matte value.",
1053     "",
1054     "If the Magnify widget is mapped, it can be helpful in positioning",
1055     "your pointer within the image (refer to button 2).",
1056     "",
1057     "Matte information is only valid in a DirectClass image.",
1058     "Therefore, any PseudoClass image is promoted to DirectClass",
1059     "(see miff(5)).  Note that matte information for PseudoClass",
1060     "is not retained for colormapped X server visuals (e.g.",
1061     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1062     "immediately save your image to a file (refer to Write).",
1063     "Correct matte editing behavior may require a TrueColor or",
1064     "DirectColor visual or a Standard Colormap.",
1065     (char *) NULL,
1066   },
1067   *ImagePanHelp[] =
1068   {
1069     "When an image exceeds the width or height of the X server",
1070     "screen, display maps a small panning icon.  The rectangle",
1071     "within the panning icon shows the area that is currently",
1072     "displayed in the image window.  To pan about the image,",
1073     "press any button and drag the pointer within the panning",
1074     "icon.  The pan rectangle moves with the pointer and the",
1075     "image window is updated to reflect the location of the",
1076     "rectangle within the panning icon.  When you have selected",
1077     "the area of the image you wish to view, release the button.",
1078     "",
1079     "Use the arrow keys to pan the image one pixel up, down,",
1080     "left, or right within the image window.",
1081     "",
1082     "The panning icon is withdrawn if the image becomes smaller",
1083     "than the dimensions of the X server screen.",
1084     (char *) NULL,
1085   },
1086   *ImagePasteHelp[] =
1087   {
1088     "A small window appears showing the location of the cursor in",
1089     "the image window. You are now in paste mode.  To exit",
1090     "immediately, press Dismiss.  In paste mode, the Command",
1091     "widget has these options:",
1092     "",
1093     "    Operators",
1094     "      over",
1095     "      in",
1096     "      out",
1097     "      atop",
1098     "      xor",
1099     "      plus",
1100     "      minus",
1101     "      add",
1102     "      subtract",
1103     "      difference",
1104     "      replace",
1105     "    Help",
1106     "    Dismiss",
1107     "",
1108     "Choose a composite operation from the Operators sub-menu of",
1109     "the Command widget.  How each operator behaves is described",
1110     "below.  Image window is the image currently displayed on",
1111     "your X server and image is the image obtained with the File",
1112     "Browser widget.",
1113     "",
1114     "Over     The result is the union of the two image shapes,",
1115     "         with image obscuring image window in the region of",
1116     "         overlap.",
1117     "",
1118     "In       The result is simply image cut by the shape of",
1119     "         image window.  None of the image data of image",
1120     "         window is in the result.",
1121     "",
1122     "Out      The resulting image is image with the shape of",
1123     "         image window cut out.",
1124     "",
1125     "Atop     The result is the same shape as image image window,",
1126     "         with image obscuring image window where the image",
1127     "         shapes overlap.  Note this differs from over",
1128     "         because the portion of image outside image window's",
1129     "         shape does not appear in the result.",
1130     "",
1131     "Xor      The result is the image data from both image and",
1132     "         image window that is outside the overlap region.",
1133     "         The overlap region is blank.",
1134     "",
1135     "Plus     The result is just the sum of the image data.",
1136     "         Output values are cropped to QuantumRange (no overflow).",
1137     "         This operation is independent of the matte",
1138     "         channels.",
1139     "",
1140     "Minus    The result of image - image window, with underflow",
1141     "         cropped to zero.",
1142     "",
1143     "Add      The result of image + image window, with overflow",
1144     "         wrapping around (mod 256).",
1145     "",
1146     "Subtract The result of image - image window, with underflow",
1147     "         wrapping around (mod 256).  The add and subtract",
1148     "         operators can be used to perform reversible",
1149     "         transformations.",
1150     "",
1151     "Difference",
1152     "         The result of abs(image - image window).  This",
1153     "         useful for comparing two very similar images.",
1154     "",
1155     "Copy     The resulting image is image window replaced with",
1156     "         image.  Here the matte information is ignored.",
1157     "",
1158     "CopyRed  The red layer of the image window is replace with",
1159     "         the red layer of the image.  The other layers are",
1160     "         untouched.",
1161     "",
1162     "CopyGreen",
1163     "         The green layer of the image window is replace with",
1164     "         the green layer of the image.  The other layers are",
1165     "         untouched.",
1166     "",
1167     "CopyBlue The blue layer of the image window is replace with",
1168     "         the blue layer of the image.  The other layers are",
1169     "         untouched.",
1170     "",
1171     "CopyOpacity",
1172     "         The matte layer of the image window is replace with",
1173     "         the matte layer of the image.  The other layers are",
1174     "         untouched.",
1175     "",
1176     "The image compositor requires a matte, or alpha channel in",
1177     "the image for some operations.  This extra channel usually",
1178     "defines a mask which represents a sort of a cookie-cutter",
1179     "for the image.  This the case when matte is opaque (full",
1180     "coverage) for pixels inside the shape, zero outside, and",
1181     "between 0 and QuantumRange on the boundary.  If image does not",
1182     "have a matte channel, it is initialized with 0 for any pixel",
1183     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1184     "",
1185     "Note that matte information for image window is not retained",
1186     "for colormapped X server visuals (e.g. StaticColor,",
1187     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1188     "behavior may require a TrueColor or DirectColor visual or a",
1189     "Standard Colormap.",
1190     "",
1191     "Choosing a composite operator is optional.  The default",
1192     "operator is replace.  However, you must choose a location to",
1193     "paste your image and press button 1.  Press and hold the",
1194     "button before releasing and an outline of the image will",
1195     "appear to help you identify your location.",
1196     "",
1197     "The actual colors of the pasted image is saved.  However,",
1198     "the color that appears in image window may be different.",
1199     "For example, on a monochrome screen image window will appear",
1200     "black or white even though your pasted image may have",
1201     "many colors.  If the image is saved to a file it is written",
1202     "with the correct colors.  To assure the correct colors are",
1203     "saved in the final image, any PseudoClass image is promoted",
1204     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1205     "to remain PseudoClass, use -colors.",
1206     (char *) NULL,
1207   },
1208   *ImageROIHelp[] =
1209   {
1210     "In region of interest mode, the Command widget has these",
1211     "options:",
1212     "",
1213     "    Help",
1214     "    Dismiss",
1215     "",
1216     "To define a region of interest, press button 1 and drag.",
1217     "The region of interest is defined by a highlighted rectangle",
1218     "that expands or contracts as it follows the pointer.  Once",
1219     "you are satisfied with the region of interest, release the",
1220     "button.  You are now in apply mode.  In apply mode the",
1221     "Command widget has these options:",
1222     "",
1223     "      File",
1224     "        Save...",
1225     "        Print...",
1226     "      Edit",
1227     "        Undo",
1228     "        Redo",
1229     "      Transform",
1230     "        Flop",
1231     "        Flip",
1232     "        Rotate Right",
1233     "        Rotate Left",
1234     "      Enhance",
1235     "        Hue...",
1236     "        Saturation...",
1237     "        Brightness...",
1238     "        Gamma...",
1239     "        Spiff",
1240     "        Dull",
1241     "        Contrast Stretch",
1242     "        Sigmoidal Contrast...",
1243     "        Normalize",
1244     "        Equalize",
1245     "        Negate",
1246     "        Grayscale",
1247     "        Map...",
1248     "        Quantize...",
1249     "      Effects",
1250     "        Despeckle",
1251     "        Emboss",
1252     "        Reduce Noise",
1253     "        Sharpen...",
1254     "        Blur...",
1255     "        Threshold...",
1256     "        Edge Detect...",
1257     "        Spread...",
1258     "        Shade...",
1259     "        Raise...",
1260     "        Segment...",
1261     "      F/X",
1262     "        Solarize...",
1263     "        Sepia Tone...",
1264     "        Swirl...",
1265     "        Implode...",
1266     "        Vignette...",
1267     "        Wave...",
1268     "        Oil Painting...",
1269     "        Charcoal Drawing...",
1270     "      Miscellany",
1271     "        Image Info",
1272     "        Zoom Image",
1273     "        Show Preview...",
1274     "        Show Histogram",
1275     "        Show Matte",
1276     "      Help",
1277     "      Dismiss",
1278     "",
1279     "You can make adjustments to the region of interest by moving",
1280     "the pointer to one of the rectangle corners, pressing a",
1281     "button, and dragging.  Finally, choose an image processing",
1282     "technique from the Command widget.  You can choose more than",
1283     "one image processing technique to apply to an area.",
1284     "Alternatively, you can move the region of interest before",
1285     "applying another image processing technique.  To exit, press",
1286     "Dismiss.",
1287     (char *) NULL,
1288   },
1289   *ImageRotateHelp[] =
1290   {
1291     "In rotate mode, the Command widget has these options:",
1292     "",
1293     "    Pixel Color",
1294     "      black",
1295     "      blue",
1296     "      cyan",
1297     "      green",
1298     "      gray",
1299     "      red",
1300     "      magenta",
1301     "      yellow",
1302     "      white",
1303     "      Browser...",
1304     "    Direction",
1305     "      horizontal",
1306     "      vertical",
1307     "    Help",
1308     "    Dismiss",
1309     "",
1310     "Choose a background color from the Pixel Color sub-menu.",
1311     "Additional background colors can be specified with the color",
1312     "browser.  You can change the menu colors by setting the X",
1313     "resources pen1 through pen9.",
1314     "",
1315     "If you choose the color browser and press Grab, you can",
1316     "select the background color by moving the pointer to the",
1317     "desired color on the screen and press any button.",
1318     "",
1319     "Choose a point in the image window and press this button and",
1320     "hold.  Next, move the pointer to another location in the",
1321     "image.  As you move a line connects the initial location and",
1322     "the pointer.  When you release the button, the degree of",
1323     "image rotation is determined by the slope of the line you",
1324     "just drew.  The slope is relative to the direction you",
1325     "choose from the Direction sub-menu of the Command widget.",
1326     "",
1327     "To cancel the image rotation, move the pointer back to the",
1328     "starting point of the line and release the button.",
1329     (char *) NULL,
1330   };
1331 \f
1332 /*
1333   Enumeration declarations.
1334 */
1335 typedef enum
1336 {
1337   CopyMode,
1338   CropMode,
1339   CutMode
1340 } ClipboardMode;
1341
1342 typedef enum
1343 {
1344   OpenCommand,
1345   NextCommand,
1346   FormerCommand,
1347   SelectCommand,
1348   SaveCommand,
1349   PrintCommand,
1350   DeleteCommand,
1351   NewCommand,
1352   VisualDirectoryCommand,
1353   QuitCommand,
1354   UndoCommand,
1355   RedoCommand,
1356   CutCommand,
1357   CopyCommand,
1358   PasteCommand,
1359   HalfSizeCommand,
1360   OriginalSizeCommand,
1361   DoubleSizeCommand,
1362   ResizeCommand,
1363   ApplyCommand,
1364   RefreshCommand,
1365   RestoreCommand,
1366   CropCommand,
1367   ChopCommand,
1368   FlopCommand,
1369   FlipCommand,
1370   RotateRightCommand,
1371   RotateLeftCommand,
1372   RotateCommand,
1373   ShearCommand,
1374   RollCommand,
1375   TrimCommand,
1376   HueCommand,
1377   SaturationCommand,
1378   BrightnessCommand,
1379   GammaCommand,
1380   SpiffCommand,
1381   DullCommand,
1382   ContrastStretchCommand,
1383   SigmoidalContrastCommand,
1384   NormalizeCommand,
1385   EqualizeCommand,
1386   NegateCommand,
1387   GrayscaleCommand,
1388   MapCommand,
1389   QuantizeCommand,
1390   DespeckleCommand,
1391   EmbossCommand,
1392   ReduceNoiseCommand,
1393   AddNoiseCommand,
1394   SharpenCommand,
1395   BlurCommand,
1396   ThresholdCommand,
1397   EdgeDetectCommand,
1398   SpreadCommand,
1399   ShadeCommand,
1400   RaiseCommand,
1401   SegmentCommand,
1402   SolarizeCommand,
1403   SepiaToneCommand,
1404   SwirlCommand,
1405   ImplodeCommand,
1406   VignetteCommand,
1407   WaveCommand,
1408   OilPaintCommand,
1409   CharcoalDrawCommand,
1410   AnnotateCommand,
1411   DrawCommand,
1412   ColorCommand,
1413   MatteCommand,
1414   CompositeCommand,
1415   AddBorderCommand,
1416   AddFrameCommand,
1417   CommentCommand,
1418   LaunchCommand,
1419   RegionofInterestCommand,
1420   ROIHelpCommand,
1421   ROIDismissCommand,
1422   InfoCommand,
1423   ZoomCommand,
1424   ShowPreviewCommand,
1425   ShowHistogramCommand,
1426   ShowMatteCommand,
1427   BackgroundCommand,
1428   SlideShowCommand,
1429   PreferencesCommand,
1430   HelpCommand,
1431   BrowseDocumentationCommand,
1432   VersionCommand,
1433   SaveToUndoBufferCommand,
1434   FreeBuffersCommand,
1435   NullCommand
1436 } CommandType;
1437
1438 typedef enum
1439 {
1440   AnnotateNameCommand,
1441   AnnotateFontColorCommand,
1442   AnnotateBackgroundColorCommand,
1443   AnnotateRotateCommand,
1444   AnnotateHelpCommand,
1445   AnnotateDismissCommand,
1446   TextHelpCommand,
1447   TextApplyCommand,
1448   ChopDirectionCommand,
1449   ChopHelpCommand,
1450   ChopDismissCommand,
1451   HorizontalChopCommand,
1452   VerticalChopCommand,
1453   ColorEditMethodCommand,
1454   ColorEditColorCommand,
1455   ColorEditBorderCommand,
1456   ColorEditFuzzCommand,
1457   ColorEditUndoCommand,
1458   ColorEditHelpCommand,
1459   ColorEditDismissCommand,
1460   CompositeOperatorsCommand,
1461   CompositeDissolveCommand,
1462   CompositeDisplaceCommand,
1463   CompositeHelpCommand,
1464   CompositeDismissCommand,
1465   CropHelpCommand,
1466   CropDismissCommand,
1467   RectifyCopyCommand,
1468   RectifyHelpCommand,
1469   RectifyDismissCommand,
1470   DrawElementCommand,
1471   DrawColorCommand,
1472   DrawStippleCommand,
1473   DrawWidthCommand,
1474   DrawUndoCommand,
1475   DrawHelpCommand,
1476   DrawDismissCommand,
1477   MatteEditMethod,
1478   MatteEditBorderCommand,
1479   MatteEditFuzzCommand,
1480   MatteEditValueCommand,
1481   MatteEditUndoCommand,
1482   MatteEditHelpCommand,
1483   MatteEditDismissCommand,
1484   PasteOperatorsCommand,
1485   PasteHelpCommand,
1486   PasteDismissCommand,
1487   RotateColorCommand,
1488   RotateDirectionCommand,
1489   RotateCropCommand,
1490   RotateSharpenCommand,
1491   RotateHelpCommand,
1492   RotateDismissCommand,
1493   HorizontalRotateCommand,
1494   VerticalRotateCommand,
1495   TileLoadCommand,
1496   TileNextCommand,
1497   TileFormerCommand,
1498   TileDeleteCommand,
1499   TileUpdateCommand
1500 } ModeType;
1501 \f
1502 /*
1503   Stipples.
1504 */
1505 #define BricksWidth  20
1506 #define BricksHeight  20
1507 #define DiagonalWidth  16
1508 #define DiagonalHeight  16
1509 #define HighlightWidth  8
1510 #define HighlightHeight  8
1511 #define OpaqueWidth  8
1512 #define OpaqueHeight  8
1513 #define ScalesWidth  16
1514 #define ScalesHeight  16
1515 #define ShadowWidth  8
1516 #define ShadowHeight  8
1517 #define VerticalWidth  16
1518 #define VerticalHeight  16
1519 #define WavyWidth  16
1520 #define WavyHeight  16
1521 \f
1522 /*
1523   Constant declaration.
1524 */
1525 static const int
1526   RoiDelta = 8;
1527
1528 static const unsigned char
1529   BricksBitmap[] =
1530   {
1531     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1532     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1533     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1534     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1535     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1536   },
1537   DiagonalBitmap[] =
1538   {
1539     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1540     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1541     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1542   },
1543   ScalesBitmap[] =
1544   {
1545     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1546     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1547     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1548   },
1549   VerticalBitmap[] =
1550   {
1551     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1552     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1553     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1554   },
1555   WavyBitmap[] =
1556   {
1557     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1558     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1559     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1560   };
1561 \f
1562 /*
1563   Function prototypes.
1564 */
1565 static CommandType
1566   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1567     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1568
1569 static Image
1570   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1571     Image **,ExceptionInfo *),
1572   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1573   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1574     ExceptionInfo *),
1575   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1576     ExceptionInfo *);
1577
1578 static MagickBooleanType
1579   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1580     ExceptionInfo *),
1581   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1582     ExceptionInfo *),
1583   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1584     ExceptionInfo *),
1585   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1586     ExceptionInfo *),
1587   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1588     ExceptionInfo *),
1589   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1590     ExceptionInfo *),
1591   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1592   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1593     ExceptionInfo *),
1594   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1595     ExceptionInfo *),
1596   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1597   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1598   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1599     ExceptionInfo *),
1600   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1601   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1602   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1603
1604 static void
1605   XDrawPanRectangle(Display *,XWindows *),
1606   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1607     ExceptionInfo *),
1608   XMagnifyImage(Display *,XWindows *,XEvent *),
1609   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1610   XPanImage(Display *,XWindows *,XEvent *),
1611   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1612     const KeySym),
1613   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1614   XScreenEvent(Display *,XWindows *,XEvent *),
1615   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1616 \f
1617 /*
1618 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1619 %                                                                             %
1620 %                                                                             %
1621 %                                                                             %
1622 %   D i s p l a y I m a g e s                                                 %
1623 %                                                                             %
1624 %                                                                             %
1625 %                                                                             %
1626 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1627 %
1628 %  DisplayImages() displays an image sequence to any X window screen.  It
1629 %  returns a value other than 0 if successful.  Check the exception member
1630 %  of image to determine the reason for any failure.
1631 %
1632 %  The format of the DisplayImages method is:
1633 %
1634 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1635 %        Image *images,ExceptionInfo *exception)
1636 %
1637 %  A description of each parameter follows:
1638 %
1639 %    o image_info: the image info.
1640 %
1641 %    o image: the image.
1642 %
1643 %    o exception: return any errors or warnings in this structure.
1644 %
1645 */
1646 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1647   Image *images,ExceptionInfo *exception)
1648 {
1649   char
1650     *argv[1];
1651
1652   Display
1653     *display;
1654
1655   Image
1656     *image;
1657
1658   register ssize_t
1659     i;
1660
1661   size_t
1662     state;
1663
1664   XrmDatabase
1665     resource_database;
1666
1667   XResourceInfo
1668     resource_info;
1669
1670   assert(image_info != (const ImageInfo *) NULL);
1671   assert(image_info->signature == MagickSignature);
1672   assert(images != (Image *) NULL);
1673   assert(images->signature == MagickSignature);
1674   if (images->debug != MagickFalse)
1675     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1676   display=XOpenDisplay(image_info->server_name);
1677   if (display == (Display *) NULL)
1678     {
1679       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1680         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1681       return(MagickFalse);
1682     }
1683   if (exception->severity != UndefinedException)
1684     CatchException(exception);
1685   (void) XSetErrorHandler(XError);
1686   resource_database=XGetResourceDatabase(display,GetClientName());
1687   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1688   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1689   if (image_info->page != (char *) NULL)
1690     resource_info.image_geometry=AcquireString(image_info->page);
1691   resource_info.immutable=MagickTrue;
1692   argv[0]=AcquireString(GetClientName());
1693   state=DefaultState;
1694   for (i=0; (state & ExitState) == 0; i++)
1695   {
1696     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1697       break;
1698     image=GetImageFromList(images,i % GetImageListLength(images));
1699     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1700   }
1701   SetErrorHandler((ErrorHandler) NULL);
1702   SetWarningHandler((WarningHandler) NULL);
1703   argv[0]=DestroyString(argv[0]);
1704   (void) XCloseDisplay(display);
1705   XDestroyResourceInfo(&resource_info);
1706   if (exception->severity != UndefinedException)
1707     return(MagickFalse);
1708   return(MagickTrue);
1709 }
1710 \f
1711 /*
1712 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1713 %                                                                             %
1714 %                                                                             %
1715 %                                                                             %
1716 %   R e m o t e D i s p l a y C o m m a n d                                   %
1717 %                                                                             %
1718 %                                                                             %
1719 %                                                                             %
1720 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1721 %
1722 %  RemoteDisplayCommand() encourages a remote display program to display the
1723 %  specified image filename.
1724 %
1725 %  The format of the RemoteDisplayCommand method is:
1726 %
1727 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1728 %        const char *window,const char *filename,ExceptionInfo *exception)
1729 %
1730 %  A description of each parameter follows:
1731 %
1732 %    o image_info: the image info.
1733 %
1734 %    o window: Specifies the name or id of an X window.
1735 %
1736 %    o filename: the name of the image filename to display.
1737 %
1738 %    o exception: return any errors or warnings in this structure.
1739 %
1740 */
1741 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1742   const char *window,const char *filename,ExceptionInfo *exception)
1743 {
1744   Display
1745     *display;
1746
1747   MagickStatusType
1748     status;
1749
1750   assert(image_info != (const ImageInfo *) NULL);
1751   assert(image_info->signature == MagickSignature);
1752   assert(filename != (char *) NULL);
1753   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1754   display=XOpenDisplay(image_info->server_name);
1755   if (display == (Display *) NULL)
1756     {
1757       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1758         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1759       return(MagickFalse);
1760     }
1761   (void) XSetErrorHandler(XError);
1762   status=XRemoteCommand(display,window,filename);
1763   (void) XCloseDisplay(display);
1764   return(status != 0 ? MagickTrue : MagickFalse);
1765 }
1766 \f
1767 /*
1768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1769 %                                                                             %
1770 %                                                                             %
1771 %                                                                             %
1772 +   X A n n o t a t e E d i t I m a g e                                       %
1773 %                                                                             %
1774 %                                                                             %
1775 %                                                                             %
1776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1777 %
1778 %  XAnnotateEditImage() annotates the image with text.
1779 %
1780 %  The format of the XAnnotateEditImage method is:
1781 %
1782 %      MagickBooleanType XAnnotateEditImage(Display *display,
1783 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1784 %        ExceptionInfo *exception)
1785 %
1786 %  A description of each parameter follows:
1787 %
1788 %    o display: Specifies a connection to an X server;  returned from
1789 %      XOpenDisplay.
1790 %
1791 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1792 %
1793 %    o windows: Specifies a pointer to a XWindows structure.
1794 %
1795 %    o image: the image; returned from ReadImage.
1796 %
1797 */
1798
1799 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1800 {
1801   if (x > y)
1802     return(x);
1803   return(y);
1804 }
1805
1806 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1807 {
1808   if (x < y)
1809     return(x);
1810   return(y);
1811 }
1812
1813 static MagickBooleanType XAnnotateEditImage(Display *display,
1814   XResourceInfo *resource_info,XWindows *windows,Image *image,
1815   ExceptionInfo *exception)
1816 {
1817   static const char
1818     *AnnotateMenu[] =
1819     {
1820       "Font Name",
1821       "Font Color",
1822       "Box Color",
1823       "Rotate Text",
1824       "Help",
1825       "Dismiss",
1826       (char *) NULL
1827     },
1828     *TextMenu[] =
1829     {
1830       "Help",
1831       "Apply",
1832       (char *) NULL
1833     };
1834
1835   static const ModeType
1836     AnnotateCommands[] =
1837     {
1838       AnnotateNameCommand,
1839       AnnotateFontColorCommand,
1840       AnnotateBackgroundColorCommand,
1841       AnnotateRotateCommand,
1842       AnnotateHelpCommand,
1843       AnnotateDismissCommand
1844     },
1845     TextCommands[] =
1846     {
1847       TextHelpCommand,
1848       TextApplyCommand
1849     };
1850
1851   static MagickBooleanType
1852     transparent_box = MagickTrue,
1853     transparent_pen = MagickFalse;
1854
1855   static MagickRealType
1856     degrees = 0.0;
1857
1858   static unsigned int
1859     box_id = MaxNumberPens-2,
1860     font_id = 0,
1861     pen_id = 0;
1862
1863   char
1864     command[MaxTextExtent],
1865     text[MaxTextExtent];
1866
1867   const char
1868     *ColorMenu[MaxNumberPens+1];
1869
1870   Cursor
1871     cursor;
1872
1873   GC
1874     annotate_context;
1875
1876   int
1877     id,
1878     pen_number,
1879     status,
1880     x,
1881     y;
1882
1883   KeySym
1884     key_symbol;
1885
1886   register char
1887     *p;
1888
1889   register ssize_t
1890     i;
1891
1892   unsigned int
1893     height,
1894     width;
1895
1896   size_t
1897     state;
1898
1899   XAnnotateInfo
1900     *annotate_info,
1901     *previous_info;
1902
1903   XColor
1904     color;
1905
1906   XFontStruct
1907     *font_info;
1908
1909   XEvent
1910     event,
1911     text_event;
1912
1913   /*
1914     Map Command widget.
1915   */
1916   (void) CloneString(&windows->command.name,"Annotate");
1917   windows->command.data=4;
1918   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1919   (void) XMapRaised(display,windows->command.id);
1920   XClientMessage(display,windows->image.id,windows->im_protocols,
1921     windows->im_update_widget,CurrentTime);
1922   /*
1923     Track pointer until button 1 is pressed.
1924   */
1925   XQueryPosition(display,windows->image.id,&x,&y);
1926   (void) XSelectInput(display,windows->image.id,
1927     windows->image.attributes.event_mask | PointerMotionMask);
1928   cursor=XCreateFontCursor(display,XC_left_side);
1929   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1930   state=DefaultState;
1931   do
1932   {
1933     if (windows->info.mapped != MagickFalse)
1934       {
1935         /*
1936           Display pointer position.
1937         */
1938         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1939           x+windows->image.x,y+windows->image.y);
1940         XInfoWidget(display,windows,text);
1941       }
1942     /*
1943       Wait for next event.
1944     */
1945     XScreenEvent(display,windows,&event);
1946     if (event.xany.window == windows->command.id)
1947       {
1948         /*
1949           Select a command from the Command widget.
1950         */
1951         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1952         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1953         if (id < 0)
1954           continue;
1955         switch (AnnotateCommands[id])
1956         {
1957           case AnnotateNameCommand:
1958           {
1959             const char
1960               *FontMenu[MaxNumberFonts];
1961
1962             int
1963               font_number;
1964
1965             /*
1966               Initialize menu selections.
1967             */
1968             for (i=0; i < MaxNumberFonts; i++)
1969               FontMenu[i]=resource_info->font_name[i];
1970             FontMenu[MaxNumberFonts-2]="Browser...";
1971             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1972             /*
1973               Select a font name from the pop-up menu.
1974             */
1975             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1976               (const char **) FontMenu,command);
1977             if (font_number < 0)
1978               break;
1979             if (font_number == (MaxNumberFonts-2))
1980               {
1981                 static char
1982                   font_name[MaxTextExtent] = "fixed";
1983
1984                 /*
1985                   Select a font name from a browser.
1986                 */
1987                 resource_info->font_name[font_number]=font_name;
1988                 XFontBrowserWidget(display,windows,"Select",font_name);
1989                 if (*font_name == '\0')
1990                   break;
1991               }
1992             /*
1993               Initialize font info.
1994             */
1995             font_info=XLoadQueryFont(display,resource_info->font_name[
1996               font_number]);
1997             if (font_info == (XFontStruct *) NULL)
1998               {
1999                 XNoticeWidget(display,windows,"Unable to load font:",
2000                   resource_info->font_name[font_number]);
2001                 break;
2002               }
2003             font_id=(unsigned int) font_number;
2004             (void) XFreeFont(display,font_info);
2005             break;
2006           }
2007           case AnnotateFontColorCommand:
2008           {
2009             /*
2010               Initialize menu selections.
2011             */
2012             for (i=0; i < (int) (MaxNumberPens-2); i++)
2013               ColorMenu[i]=resource_info->pen_colors[i];
2014             ColorMenu[MaxNumberPens-2]="transparent";
2015             ColorMenu[MaxNumberPens-1]="Browser...";
2016             ColorMenu[MaxNumberPens]=(const char *) NULL;
2017             /*
2018               Select a pen color from the pop-up menu.
2019             */
2020             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2021               (const char **) ColorMenu,command);
2022             if (pen_number < 0)
2023               break;
2024             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2025               MagickFalse;
2026             if (transparent_pen != MagickFalse)
2027               break;
2028             if (pen_number == (MaxNumberPens-1))
2029               {
2030                 static char
2031                   color_name[MaxTextExtent] = "gray";
2032
2033                 /*
2034                   Select a pen color from a dialog.
2035                 */
2036                 resource_info->pen_colors[pen_number]=color_name;
2037                 XColorBrowserWidget(display,windows,"Select",color_name);
2038                 if (*color_name == '\0')
2039                   break;
2040               }
2041             /*
2042               Set pen color.
2043             */
2044             (void) XParseColor(display,windows->map_info->colormap,
2045               resource_info->pen_colors[pen_number],&color);
2046             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2047               (unsigned int) MaxColors,&color);
2048             windows->pixel_info->pen_colors[pen_number]=color;
2049             pen_id=(unsigned int) pen_number;
2050             break;
2051           }
2052           case AnnotateBackgroundColorCommand:
2053           {
2054             /*
2055               Initialize menu selections.
2056             */
2057             for (i=0; i < (int) (MaxNumberPens-2); i++)
2058               ColorMenu[i]=resource_info->pen_colors[i];
2059             ColorMenu[MaxNumberPens-2]="transparent";
2060             ColorMenu[MaxNumberPens-1]="Browser...";
2061             ColorMenu[MaxNumberPens]=(const char *) NULL;
2062             /*
2063               Select a pen color from the pop-up menu.
2064             */
2065             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2066               (const char **) ColorMenu,command);
2067             if (pen_number < 0)
2068               break;
2069             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2070               MagickFalse;
2071             if (transparent_box != MagickFalse)
2072               break;
2073             if (pen_number == (MaxNumberPens-1))
2074               {
2075                 static char
2076                   color_name[MaxTextExtent] = "gray";
2077
2078                 /*
2079                   Select a pen color from a dialog.
2080                 */
2081                 resource_info->pen_colors[pen_number]=color_name;
2082                 XColorBrowserWidget(display,windows,"Select",color_name);
2083                 if (*color_name == '\0')
2084                   break;
2085               }
2086             /*
2087               Set pen color.
2088             */
2089             (void) XParseColor(display,windows->map_info->colormap,
2090               resource_info->pen_colors[pen_number],&color);
2091             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2092               (unsigned int) MaxColors,&color);
2093             windows->pixel_info->pen_colors[pen_number]=color;
2094             box_id=(unsigned int) pen_number;
2095             break;
2096           }
2097           case AnnotateRotateCommand:
2098           {
2099             int
2100               entry;
2101
2102             static char
2103               angle[MaxTextExtent] = "30.0";
2104
2105             static const char
2106               *RotateMenu[] =
2107               {
2108                 "-90",
2109                 "-45",
2110                 "-30",
2111                 "0",
2112                 "30",
2113                 "45",
2114                 "90",
2115                 "180",
2116                 "Dialog...",
2117                 (char *) NULL,
2118               };
2119
2120             /*
2121               Select a command from the pop-up menu.
2122             */
2123             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2124               command);
2125             if (entry < 0)
2126               break;
2127             if (entry != 8)
2128               {
2129                 degrees=InterpretLocaleValue(RotateMenu[entry],(char **) NULL);
2130                 break;
2131               }
2132             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2133               angle);
2134             if (*angle == '\0')
2135               break;
2136             degrees=InterpretLocaleValue(angle,(char **) NULL);
2137             break;
2138           }
2139           case AnnotateHelpCommand:
2140           {
2141             XTextViewWidget(display,resource_info,windows,MagickFalse,
2142               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2143             break;
2144           }
2145           case AnnotateDismissCommand:
2146           {
2147             /*
2148               Prematurely exit.
2149             */
2150             state|=EscapeState;
2151             state|=ExitState;
2152             break;
2153           }
2154           default:
2155             break;
2156         }
2157         continue;
2158       }
2159     switch (event.type)
2160     {
2161       case ButtonPress:
2162       {
2163         if (event.xbutton.button != Button1)
2164           break;
2165         if (event.xbutton.window != windows->image.id)
2166           break;
2167         /*
2168           Change to text entering mode.
2169         */
2170         x=event.xbutton.x;
2171         y=event.xbutton.y;
2172         state|=ExitState;
2173         break;
2174       }
2175       case ButtonRelease:
2176         break;
2177       case Expose:
2178         break;
2179       case KeyPress:
2180       {
2181         if (event.xkey.window != windows->image.id)
2182           break;
2183         /*
2184           Respond to a user key press.
2185         */
2186         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2187           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2188         switch ((int) key_symbol)
2189         {
2190           case XK_Escape:
2191           case XK_F20:
2192           {
2193             /*
2194               Prematurely exit.
2195             */
2196             state|=EscapeState;
2197             state|=ExitState;
2198             break;
2199           }
2200           case XK_F1:
2201           case XK_Help:
2202           {
2203             XTextViewWidget(display,resource_info,windows,MagickFalse,
2204               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2205             break;
2206           }
2207           default:
2208           {
2209             (void) XBell(display,0);
2210             break;
2211           }
2212         }
2213         break;
2214       }
2215       case MotionNotify:
2216       {
2217         /*
2218           Map and unmap Info widget as cursor crosses its boundaries.
2219         */
2220         x=event.xmotion.x;
2221         y=event.xmotion.y;
2222         if (windows->info.mapped != MagickFalse)
2223           {
2224             if ((x < (int) (windows->info.x+windows->info.width)) &&
2225                 (y < (int) (windows->info.y+windows->info.height)))
2226               (void) XWithdrawWindow(display,windows->info.id,
2227                 windows->info.screen);
2228           }
2229         else
2230           if ((x > (int) (windows->info.x+windows->info.width)) ||
2231               (y > (int) (windows->info.y+windows->info.height)))
2232             (void) XMapWindow(display,windows->info.id);
2233         break;
2234       }
2235       default:
2236         break;
2237     }
2238   } while ((state & ExitState) == 0);
2239   (void) XSelectInput(display,windows->image.id,
2240     windows->image.attributes.event_mask);
2241   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2242   if ((state & EscapeState) != 0)
2243     return(MagickTrue);
2244   /*
2245     Set font info and check boundary conditions.
2246   */
2247   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2248   if (font_info == (XFontStruct *) NULL)
2249     {
2250       XNoticeWidget(display,windows,"Unable to load font:",
2251         resource_info->font_name[font_id]);
2252       font_info=windows->font_info;
2253     }
2254   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2255     x=(int) windows->image.width-font_info->max_bounds.width;
2256   if (y < (int) (font_info->ascent+font_info->descent))
2257     y=(int) font_info->ascent+font_info->descent;
2258   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2259       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2260     return(MagickFalse);
2261   /*
2262     Initialize annotate structure.
2263   */
2264   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2265   if (annotate_info == (XAnnotateInfo *) NULL)
2266     return(MagickFalse);
2267   XGetAnnotateInfo(annotate_info);
2268   annotate_info->x=x;
2269   annotate_info->y=y;
2270   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2271     annotate_info->stencil=OpaqueStencil;
2272   else
2273     if (transparent_box == MagickFalse)
2274       annotate_info->stencil=BackgroundStencil;
2275     else
2276       annotate_info->stencil=ForegroundStencil;
2277   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2278   annotate_info->degrees=degrees;
2279   annotate_info->font_info=font_info;
2280   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2281     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2282     sizeof(*annotate_info->text));
2283   if (annotate_info->text == (char *) NULL)
2284     return(MagickFalse);
2285   /*
2286     Create cursor and set graphic context.
2287   */
2288   cursor=XCreateFontCursor(display,XC_pencil);
2289   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2290   annotate_context=windows->image.annotate_context;
2291   (void) XSetFont(display,annotate_context,font_info->fid);
2292   (void) XSetBackground(display,annotate_context,
2293     windows->pixel_info->pen_colors[box_id].pixel);
2294   (void) XSetForeground(display,annotate_context,
2295     windows->pixel_info->pen_colors[pen_id].pixel);
2296   /*
2297     Begin annotating the image with text.
2298   */
2299   (void) CloneString(&windows->command.name,"Text");
2300   windows->command.data=0;
2301   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2302   state=DefaultState;
2303   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2304   text_event.xexpose.width=(int) font_info->max_bounds.width;
2305   text_event.xexpose.height=font_info->max_bounds.ascent+
2306     font_info->max_bounds.descent;
2307   p=annotate_info->text;
2308   do
2309   {
2310     /*
2311       Display text cursor.
2312     */
2313     *p='\0';
2314     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2315     /*
2316       Wait for next event.
2317     */
2318     XScreenEvent(display,windows,&event);
2319     if (event.xany.window == windows->command.id)
2320       {
2321         /*
2322           Select a command from the Command widget.
2323         */
2324         (void) XSetBackground(display,annotate_context,
2325           windows->pixel_info->background_color.pixel);
2326         (void) XSetForeground(display,annotate_context,
2327           windows->pixel_info->foreground_color.pixel);
2328         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2329         (void) XSetBackground(display,annotate_context,
2330           windows->pixel_info->pen_colors[box_id].pixel);
2331         (void) XSetForeground(display,annotate_context,
2332           windows->pixel_info->pen_colors[pen_id].pixel);
2333         if (id < 0)
2334           continue;
2335         switch (TextCommands[id])
2336         {
2337           case TextHelpCommand:
2338           {
2339             XTextViewWidget(display,resource_info,windows,MagickFalse,
2340               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2341             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2342             break;
2343           }
2344           case TextApplyCommand:
2345           {
2346             /*
2347               Finished annotating.
2348             */
2349             annotate_info->width=(unsigned int) XTextWidth(font_info,
2350               annotate_info->text,(int) strlen(annotate_info->text));
2351             XRefreshWindow(display,&windows->image,&text_event);
2352             state|=ExitState;
2353             break;
2354           }
2355           default:
2356             break;
2357         }
2358         continue;
2359       }
2360     /*
2361       Erase text cursor.
2362     */
2363     text_event.xexpose.x=x;
2364     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2365     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2366       (unsigned int) text_event.xexpose.width,(unsigned int)
2367       text_event.xexpose.height,MagickFalse);
2368     XRefreshWindow(display,&windows->image,&text_event);
2369     switch (event.type)
2370     {
2371       case ButtonPress:
2372       {
2373         if (event.xbutton.window != windows->image.id)
2374           break;
2375         if (event.xbutton.button == Button2)
2376           {
2377             /*
2378               Request primary selection.
2379             */
2380             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2381               windows->image.id,CurrentTime);
2382             break;
2383           }
2384         break;
2385       }
2386       case Expose:
2387       {
2388         if (event.xexpose.count == 0)
2389           {
2390             XAnnotateInfo
2391               *text_info;
2392
2393             /*
2394               Refresh Image window.
2395             */
2396             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2397             text_info=annotate_info;
2398             while (text_info != (XAnnotateInfo *) NULL)
2399             {
2400               if (annotate_info->stencil == ForegroundStencil)
2401                 (void) XDrawString(display,windows->image.id,annotate_context,
2402                   text_info->x,text_info->y,text_info->text,
2403                   (int) strlen(text_info->text));
2404               else
2405                 (void) XDrawImageString(display,windows->image.id,
2406                   annotate_context,text_info->x,text_info->y,text_info->text,
2407                   (int) strlen(text_info->text));
2408               text_info=text_info->previous;
2409             }
2410             (void) XDrawString(display,windows->image.id,annotate_context,
2411               x,y,"_",1);
2412           }
2413         break;
2414       }
2415       case KeyPress:
2416       {
2417         int
2418           length;
2419
2420         if (event.xkey.window != windows->image.id)
2421           break;
2422         /*
2423           Respond to a user key press.
2424         */
2425         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2426           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2427         *(command+length)='\0';
2428         if (((event.xkey.state & ControlMask) != 0) ||
2429             ((event.xkey.state & Mod1Mask) != 0))
2430           state|=ModifierState;
2431         if ((state & ModifierState) != 0)
2432           switch ((int) key_symbol)
2433           {
2434             case XK_u:
2435             case XK_U:
2436             {
2437               key_symbol=DeleteCommand;
2438               break;
2439             }
2440             default:
2441               break;
2442           }
2443         switch ((int) key_symbol)
2444         {
2445           case XK_BackSpace:
2446           {
2447             /*
2448               Erase one character.
2449             */
2450             if (p == annotate_info->text)
2451               {
2452                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2453                   break;
2454                 else
2455                   {
2456                     /*
2457                       Go to end of the previous line of text.
2458                     */
2459                     annotate_info=annotate_info->previous;
2460                     p=annotate_info->text;
2461                     x=annotate_info->x+annotate_info->width;
2462                     y=annotate_info->y;
2463                     if (annotate_info->width != 0)
2464                       p+=strlen(annotate_info->text);
2465                     break;
2466                   }
2467               }
2468             p--;
2469             x-=XTextWidth(font_info,p,1);
2470             text_event.xexpose.x=x;
2471             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2472             XRefreshWindow(display,&windows->image,&text_event);
2473             break;
2474           }
2475           case XK_bracketleft:
2476           {
2477             key_symbol=XK_Escape;
2478             break;
2479           }
2480           case DeleteCommand:
2481           {
2482             /*
2483               Erase the entire line of text.
2484             */
2485             while (p != annotate_info->text)
2486             {
2487               p--;
2488               x-=XTextWidth(font_info,p,1);
2489               text_event.xexpose.x=x;
2490               XRefreshWindow(display,&windows->image,&text_event);
2491             }
2492             break;
2493           }
2494           case XK_Escape:
2495           case XK_F20:
2496           {
2497             /*
2498               Finished annotating.
2499             */
2500             annotate_info->width=(unsigned int) XTextWidth(font_info,
2501               annotate_info->text,(int) strlen(annotate_info->text));
2502             XRefreshWindow(display,&windows->image,&text_event);
2503             state|=ExitState;
2504             break;
2505           }
2506           default:
2507           {
2508             /*
2509               Draw a single character on the Image window.
2510             */
2511             if ((state & ModifierState) != 0)
2512               break;
2513             if (*command == '\0')
2514               break;
2515             *p=(*command);
2516             if (annotate_info->stencil == ForegroundStencil)
2517               (void) XDrawString(display,windows->image.id,annotate_context,
2518                 x,y,p,1);
2519             else
2520               (void) XDrawImageString(display,windows->image.id,
2521                 annotate_context,x,y,p,1);
2522             x+=XTextWidth(font_info,p,1);
2523             p++;
2524             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2525               break;
2526           }
2527           case XK_Return:
2528           case XK_KP_Enter:
2529           {
2530             /*
2531               Advance to the next line of text.
2532             */
2533             *p='\0';
2534             annotate_info->width=(unsigned int) XTextWidth(font_info,
2535               annotate_info->text,(int) strlen(annotate_info->text));
2536             if (annotate_info->next != (XAnnotateInfo *) NULL)
2537               {
2538                 /*
2539                   Line of text already exists.
2540                 */
2541                 annotate_info=annotate_info->next;
2542                 x=annotate_info->x;
2543                 y=annotate_info->y;
2544                 p=annotate_info->text;
2545                 break;
2546               }
2547             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2548               sizeof(*annotate_info->next));
2549             if (annotate_info->next == (XAnnotateInfo *) NULL)
2550               return(MagickFalse);
2551             *annotate_info->next=(*annotate_info);
2552             annotate_info->next->previous=annotate_info;
2553             annotate_info=annotate_info->next;
2554             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2555               windows->image.width/MagickMax((ssize_t)
2556               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2557             if (annotate_info->text == (char *) NULL)
2558               return(MagickFalse);
2559             annotate_info->y+=annotate_info->height;
2560             if (annotate_info->y > (int) windows->image.height)
2561               annotate_info->y=(int) annotate_info->height;
2562             annotate_info->next=(XAnnotateInfo *) NULL;
2563             x=annotate_info->x;
2564             y=annotate_info->y;
2565             p=annotate_info->text;
2566             break;
2567           }
2568         }
2569         break;
2570       }
2571       case KeyRelease:
2572       {
2573         /*
2574           Respond to a user key release.
2575         */
2576         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2577           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2578         state&=(~ModifierState);
2579         break;
2580       }
2581       case SelectionNotify:
2582       {
2583         Atom
2584           type;
2585
2586         int
2587           format;
2588
2589         unsigned char
2590           *data;
2591
2592         unsigned long
2593           after,
2594           length;
2595
2596         /*
2597           Obtain response from primary selection.
2598         */
2599         if (event.xselection.property == (Atom) None)
2600           break;
2601         status=XGetWindowProperty(display,event.xselection.requestor,
2602           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2603           &type,&format,&length,&after,&data);
2604         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2605             (length == 0))
2606           break;
2607         /*
2608           Annotate Image window with primary selection.
2609         */
2610         for (i=0; i < (ssize_t) length; i++)
2611         {
2612           if ((char) data[i] != '\n')
2613             {
2614               /*
2615                 Draw a single character on the Image window.
2616               */
2617               *p=(char) data[i];
2618               (void) XDrawString(display,windows->image.id,annotate_context,
2619                 x,y,p,1);
2620               x+=XTextWidth(font_info,p,1);
2621               p++;
2622               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2623                 continue;
2624             }
2625           /*
2626             Advance to the next line of text.
2627           */
2628           *p='\0';
2629           annotate_info->width=(unsigned int) XTextWidth(font_info,
2630             annotate_info->text,(int) strlen(annotate_info->text));
2631           if (annotate_info->next != (XAnnotateInfo *) NULL)
2632             {
2633               /*
2634                 Line of text already exists.
2635               */
2636               annotate_info=annotate_info->next;
2637               x=annotate_info->x;
2638               y=annotate_info->y;
2639               p=annotate_info->text;
2640               continue;
2641             }
2642           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2643             sizeof(*annotate_info->next));
2644           if (annotate_info->next == (XAnnotateInfo *) NULL)
2645             return(MagickFalse);
2646           *annotate_info->next=(*annotate_info);
2647           annotate_info->next->previous=annotate_info;
2648           annotate_info=annotate_info->next;
2649           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2650             windows->image.width/MagickMax((ssize_t)
2651             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2652           if (annotate_info->text == (char *) NULL)
2653             return(MagickFalse);
2654           annotate_info->y+=annotate_info->height;
2655           if (annotate_info->y > (int) windows->image.height)
2656             annotate_info->y=(int) annotate_info->height;
2657           annotate_info->next=(XAnnotateInfo *) NULL;
2658           x=annotate_info->x;
2659           y=annotate_info->y;
2660           p=annotate_info->text;
2661         }
2662         (void) XFree((void *) data);
2663         break;
2664       }
2665       default:
2666         break;
2667     }
2668   } while ((state & ExitState) == 0);
2669   (void) XFreeCursor(display,cursor);
2670   /*
2671     Annotation is relative to image configuration.
2672   */
2673   width=(unsigned int) image->columns;
2674   height=(unsigned int) image->rows;
2675   x=0;
2676   y=0;
2677   if (windows->image.crop_geometry != (char *) NULL)
2678     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2679   /*
2680     Initialize annotated image.
2681   */
2682   XSetCursorState(display,windows,MagickTrue);
2683   XCheckRefreshWindows(display,windows);
2684   while (annotate_info != (XAnnotateInfo *) NULL)
2685   {
2686     if (annotate_info->width == 0)
2687       {
2688         /*
2689           No text on this line--  go to the next line of text.
2690         */
2691         previous_info=annotate_info->previous;
2692         annotate_info->text=(char *)
2693           RelinquishMagickMemory(annotate_info->text);
2694         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2695         annotate_info=previous_info;
2696         continue;
2697       }
2698     /*
2699       Determine pixel index for box and pen color.
2700     */
2701     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2702     if (windows->pixel_info->colors != 0)
2703       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2704         if (windows->pixel_info->pixels[i] ==
2705             windows->pixel_info->pen_colors[box_id].pixel)
2706           {
2707             windows->pixel_info->box_index=(unsigned short) i;
2708             break;
2709           }
2710     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2711     if (windows->pixel_info->colors != 0)
2712       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2713         if (windows->pixel_info->pixels[i] ==
2714             windows->pixel_info->pen_colors[pen_id].pixel)
2715           {
2716             windows->pixel_info->pen_index=(unsigned short) i;
2717             break;
2718           }
2719     /*
2720       Define the annotate geometry string.
2721     */
2722     annotate_info->x=(int)
2723       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2724     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2725       windows->image.y)/windows->image.ximage->height;
2726     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2727       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2728       height*annotate_info->height/windows->image.ximage->height,
2729       annotate_info->x+x,annotate_info->y+y);
2730     /*
2731       Annotate image with text.
2732     */
2733     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2734     if (status == 0)
2735       return(MagickFalse);
2736     /*
2737       Free up memory.
2738     */
2739     previous_info=annotate_info->previous;
2740     annotate_info->text=DestroyString(annotate_info->text);
2741     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2742     annotate_info=previous_info;
2743   }
2744   (void) XSetForeground(display,annotate_context,
2745     windows->pixel_info->foreground_color.pixel);
2746   (void) XSetBackground(display,annotate_context,
2747     windows->pixel_info->background_color.pixel);
2748   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2749   XSetCursorState(display,windows,MagickFalse);
2750   (void) XFreeFont(display,font_info);
2751   /*
2752     Update image configuration.
2753   */
2754   XConfigureImageColormap(display,resource_info,windows,image);
2755   (void) XConfigureImage(display,resource_info,windows,image,exception);
2756   return(MagickTrue);
2757 }
2758 \f
2759 /*
2760 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2761 %                                                                             %
2762 %                                                                             %
2763 %                                                                             %
2764 +   X B a c k g r o u n d I m a g e                                           %
2765 %                                                                             %
2766 %                                                                             %
2767 %                                                                             %
2768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2769 %
2770 %  XBackgroundImage() displays the image in the background of a window.
2771 %
2772 %  The format of the XBackgroundImage method is:
2773 %
2774 %      MagickBooleanType XBackgroundImage(Display *display,
2775 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2776 %        ExceptionInfo *exception)
2777 %
2778 %  A description of each parameter follows:
2779 %
2780 %    o display: Specifies a connection to an X server; returned from
2781 %      XOpenDisplay.
2782 %
2783 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2784 %
2785 %    o windows: Specifies a pointer to a XWindows structure.
2786 %
2787 %    o image: the image.
2788 %
2789 %    o exception: return any errors or warnings in this structure.
2790 %
2791 */
2792 static MagickBooleanType XBackgroundImage(Display *display,
2793   XResourceInfo *resource_info,XWindows *windows,Image **image,
2794   ExceptionInfo *exception)
2795 {
2796 #define BackgroundImageTag  "Background/Image"
2797
2798   int
2799     status;
2800
2801   static char
2802     window_id[MaxTextExtent] = "root";
2803
2804   XResourceInfo
2805     background_resources;
2806
2807   /*
2808     Put image in background.
2809   */
2810   status=XDialogWidget(display,windows,"Background",
2811     "Enter window id (id 0x00 selects window with pointer):",window_id);
2812   if (*window_id == '\0')
2813     return(MagickFalse);
2814   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2815     exception);
2816   XInfoWidget(display,windows,BackgroundImageTag);
2817   XSetCursorState(display,windows,MagickTrue);
2818   XCheckRefreshWindows(display,windows);
2819   background_resources=(*resource_info);
2820   background_resources.window_id=window_id;
2821   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2822   status=XDisplayBackgroundImage(display,&background_resources,*image,
2823     exception);
2824   if (status != MagickFalse)
2825     XClientMessage(display,windows->image.id,windows->im_protocols,
2826       windows->im_retain_colors,CurrentTime);
2827   XSetCursorState(display,windows,MagickFalse);
2828   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2829     exception);
2830   return(MagickTrue);
2831 }
2832 \f
2833 /*
2834 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2835 %                                                                             %
2836 %                                                                             %
2837 %                                                                             %
2838 +   X C h o p I m a g e                                                       %
2839 %                                                                             %
2840 %                                                                             %
2841 %                                                                             %
2842 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2843 %
2844 %  XChopImage() chops the X image.
2845 %
2846 %  The format of the XChopImage method is:
2847 %
2848 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2849 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2850 %
2851 %  A description of each parameter follows:
2852 %
2853 %    o display: Specifies a connection to an X server; returned from
2854 %      XOpenDisplay.
2855 %
2856 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2857 %
2858 %    o windows: Specifies a pointer to a XWindows structure.
2859 %
2860 %    o image: the image.
2861 %
2862 %    o exception: return any errors or warnings in this structure.
2863 %
2864 */
2865 static MagickBooleanType XChopImage(Display *display,
2866   XResourceInfo *resource_info,XWindows *windows,Image **image,
2867   ExceptionInfo *exception)
2868 {
2869   static const char
2870     *ChopMenu[] =
2871     {
2872       "Direction",
2873       "Help",
2874       "Dismiss",
2875       (char *) NULL
2876     };
2877
2878   static ModeType
2879     direction = HorizontalChopCommand;
2880
2881   static const ModeType
2882     ChopCommands[] =
2883     {
2884       ChopDirectionCommand,
2885       ChopHelpCommand,
2886       ChopDismissCommand
2887     },
2888     DirectionCommands[] =
2889     {
2890       HorizontalChopCommand,
2891       VerticalChopCommand
2892     };
2893
2894   char
2895     text[MaxTextExtent];
2896
2897   Image
2898     *chop_image;
2899
2900   int
2901     id,
2902     x,
2903     y;
2904
2905   MagickRealType
2906     scale_factor;
2907
2908   RectangleInfo
2909     chop_info;
2910
2911   unsigned int
2912     distance,
2913     height,
2914     width;
2915
2916   size_t
2917     state;
2918
2919   XEvent
2920     event;
2921
2922   XSegment
2923     segment_info;
2924
2925   /*
2926     Map Command widget.
2927   */
2928   (void) CloneString(&windows->command.name,"Chop");
2929   windows->command.data=1;
2930   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2931   (void) XMapRaised(display,windows->command.id);
2932   XClientMessage(display,windows->image.id,windows->im_protocols,
2933     windows->im_update_widget,CurrentTime);
2934   /*
2935     Track pointer until button 1 is pressed.
2936   */
2937   XQueryPosition(display,windows->image.id,&x,&y);
2938   (void) XSelectInput(display,windows->image.id,
2939     windows->image.attributes.event_mask | PointerMotionMask);
2940   state=DefaultState;
2941   do
2942   {
2943     if (windows->info.mapped != MagickFalse)
2944       {
2945         /*
2946           Display pointer position.
2947         */
2948         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2949           x+windows->image.x,y+windows->image.y);
2950         XInfoWidget(display,windows,text);
2951       }
2952     /*
2953       Wait for next event.
2954     */
2955     XScreenEvent(display,windows,&event);
2956     if (event.xany.window == windows->command.id)
2957       {
2958         /*
2959           Select a command from the Command widget.
2960         */
2961         id=XCommandWidget(display,windows,ChopMenu,&event);
2962         if (id < 0)
2963           continue;
2964         switch (ChopCommands[id])
2965         {
2966           case ChopDirectionCommand:
2967           {
2968             char
2969               command[MaxTextExtent];
2970
2971             static const char
2972               *Directions[] =
2973               {
2974                 "horizontal",
2975                 "vertical",
2976                 (char *) NULL,
2977               };
2978
2979             /*
2980               Select a command from the pop-up menu.
2981             */
2982             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2983             if (id >= 0)
2984               direction=DirectionCommands[id];
2985             break;
2986           }
2987           case ChopHelpCommand:
2988           {
2989             XTextViewWidget(display,resource_info,windows,MagickFalse,
2990               "Help Viewer - Image Chop",ImageChopHelp);
2991             break;
2992           }
2993           case ChopDismissCommand:
2994           {
2995             /*
2996               Prematurely exit.
2997             */
2998             state|=EscapeState;
2999             state|=ExitState;
3000             break;
3001           }
3002           default:
3003             break;
3004         }
3005         continue;
3006       }
3007     switch (event.type)
3008     {
3009       case ButtonPress:
3010       {
3011         if (event.xbutton.button != Button1)
3012           break;
3013         if (event.xbutton.window != windows->image.id)
3014           break;
3015         /*
3016           User has committed to start point of chopping line.
3017         */
3018         segment_info.x1=(short int) event.xbutton.x;
3019         segment_info.x2=(short int) event.xbutton.x;
3020         segment_info.y1=(short int) event.xbutton.y;
3021         segment_info.y2=(short int) event.xbutton.y;
3022         state|=ExitState;
3023         break;
3024       }
3025       case ButtonRelease:
3026         break;
3027       case Expose:
3028         break;
3029       case KeyPress:
3030       {
3031         char
3032           command[MaxTextExtent];
3033
3034         KeySym
3035           key_symbol;
3036
3037         if (event.xkey.window != windows->image.id)
3038           break;
3039         /*
3040           Respond to a user key press.
3041         */
3042         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3043           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3044         switch ((int) key_symbol)
3045         {
3046           case XK_Escape:
3047           case XK_F20:
3048           {
3049             /*
3050               Prematurely exit.
3051             */
3052             state|=EscapeState;
3053             state|=ExitState;
3054             break;
3055           }
3056           case XK_F1:
3057           case XK_Help:
3058           {
3059             (void) XSetFunction(display,windows->image.highlight_context,
3060               GXcopy);
3061             XTextViewWidget(display,resource_info,windows,MagickFalse,
3062               "Help Viewer - Image Chop",ImageChopHelp);
3063             (void) XSetFunction(display,windows->image.highlight_context,
3064               GXinvert);
3065             break;
3066           }
3067           default:
3068           {
3069             (void) XBell(display,0);
3070             break;
3071           }
3072         }
3073         break;
3074       }
3075       case MotionNotify:
3076       {
3077         /*
3078           Map and unmap Info widget as text cursor crosses its boundaries.
3079         */
3080         x=event.xmotion.x;
3081         y=event.xmotion.y;
3082         if (windows->info.mapped != MagickFalse)
3083           {
3084             if ((x < (int) (windows->info.x+windows->info.width)) &&
3085                 (y < (int) (windows->info.y+windows->info.height)))
3086               (void) XWithdrawWindow(display,windows->info.id,
3087                 windows->info.screen);
3088           }
3089         else
3090           if ((x > (int) (windows->info.x+windows->info.width)) ||
3091               (y > (int) (windows->info.y+windows->info.height)))
3092             (void) XMapWindow(display,windows->info.id);
3093       }
3094     }
3095   } while ((state & ExitState) == 0);
3096   (void) XSelectInput(display,windows->image.id,
3097     windows->image.attributes.event_mask);
3098   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3099   if ((state & EscapeState) != 0)
3100     return(MagickTrue);
3101   /*
3102     Draw line as pointer moves until the mouse button is released.
3103   */
3104   chop_info.width=0;
3105   chop_info.height=0;
3106   chop_info.x=0;
3107   chop_info.y=0;
3108   distance=0;
3109   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3110   state=DefaultState;
3111   do
3112   {
3113     if (distance > 9)
3114       {
3115         /*
3116           Display info and draw chopping line.
3117         */
3118         if (windows->info.mapped == MagickFalse)
3119           (void) XMapWindow(display,windows->info.id);
3120         (void) FormatLocaleString(text,MaxTextExtent,
3121           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3122           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3123         XInfoWidget(display,windows,text);
3124         XHighlightLine(display,windows->image.id,
3125           windows->image.highlight_context,&segment_info);
3126       }
3127     else
3128       if (windows->info.mapped != MagickFalse)
3129         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3130     /*
3131       Wait for next event.
3132     */
3133     XScreenEvent(display,windows,&event);
3134     if (distance > 9)
3135       XHighlightLine(display,windows->image.id,
3136         windows->image.highlight_context,&segment_info);
3137     switch (event.type)
3138     {
3139       case ButtonPress:
3140       {
3141         segment_info.x2=(short int) event.xmotion.x;
3142         segment_info.y2=(short int) event.xmotion.y;
3143         break;
3144       }
3145       case ButtonRelease:
3146       {
3147         /*
3148           User has committed to chopping line.
3149         */
3150         segment_info.x2=(short int) event.xbutton.x;
3151         segment_info.y2=(short int) event.xbutton.y;
3152         state|=ExitState;
3153         break;
3154       }
3155       case Expose:
3156         break;
3157       case MotionNotify:
3158       {
3159         segment_info.x2=(short int) event.xmotion.x;
3160         segment_info.y2=(short int) event.xmotion.y;
3161       }
3162       default:
3163         break;
3164     }
3165     /*
3166       Check boundary conditions.
3167     */
3168     if (segment_info.x2 < 0)
3169       segment_info.x2=0;
3170     else
3171       if (segment_info.x2 > windows->image.ximage->width)
3172         segment_info.x2=windows->image.ximage->width;
3173     if (segment_info.y2 < 0)
3174       segment_info.y2=0;
3175     else
3176       if (segment_info.y2 > windows->image.ximage->height)
3177         segment_info.y2=windows->image.ximage->height;
3178     distance=(unsigned int)
3179       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3180        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3181     /*
3182       Compute chopping geometry.
3183     */
3184     if (direction == HorizontalChopCommand)
3185       {
3186         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3187         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3188         chop_info.height=0;
3189         chop_info.y=0;
3190         if (segment_info.x1 > (int) segment_info.x2)
3191           {
3192             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3193             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3194           }
3195       }
3196     else
3197       {
3198         chop_info.width=0;
3199         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3200         chop_info.x=0;
3201         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3202         if (segment_info.y1 > segment_info.y2)
3203           {
3204             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3205             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3206           }
3207       }
3208   } while ((state & ExitState) == 0);
3209   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3210   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3211   if (distance <= 9)
3212     return(MagickTrue);
3213   /*
3214     Image chopping is relative to image configuration.
3215   */
3216   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3217     exception);
3218   XSetCursorState(display,windows,MagickTrue);
3219   XCheckRefreshWindows(display,windows);
3220   windows->image.window_changes.width=windows->image.ximage->width-
3221     (unsigned int) chop_info.width;
3222   windows->image.window_changes.height=windows->image.ximage->height-
3223     (unsigned int) chop_info.height;
3224   width=(unsigned int) (*image)->columns;
3225   height=(unsigned int) (*image)->rows;
3226   x=0;
3227   y=0;
3228   if (windows->image.crop_geometry != (char *) NULL)
3229     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3230   scale_factor=(MagickRealType) width/windows->image.ximage->width;
3231   chop_info.x+=x;
3232   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3233   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3234   scale_factor=(MagickRealType) height/windows->image.ximage->height;
3235   chop_info.y+=y;
3236   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3237   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3238   /*
3239     Chop image.
3240   */
3241   chop_image=ChopImage(*image,&chop_info,exception);
3242   XSetCursorState(display,windows,MagickFalse);
3243   if (chop_image == (Image *) NULL)
3244     return(MagickFalse);
3245   *image=DestroyImage(*image);
3246   *image=chop_image;
3247   /*
3248     Update image configuration.
3249   */
3250   XConfigureImageColormap(display,resource_info,windows,*image);
3251   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3252   return(MagickTrue);
3253 }
3254 \f
3255 /*
3256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3257 %                                                                             %
3258 %                                                                             %
3259 %                                                                             %
3260 +   X C o l o r E d i t I m a g e                                             %
3261 %                                                                             %
3262 %                                                                             %
3263 %                                                                             %
3264 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3265 %
3266 %  XColorEditImage() allows the user to interactively change the color of one
3267 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3268 %
3269 %  The format of the XColorEditImage method is:
3270 %
3271 %      MagickBooleanType XColorEditImage(Display *display,
3272 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3273 %          ExceptionInfo *exception)
3274 %
3275 %  A description of each parameter follows:
3276 %
3277 %    o display: Specifies a connection to an X server;  returned from
3278 %      XOpenDisplay.
3279 %
3280 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3281 %
3282 %    o windows: Specifies a pointer to a XWindows structure.
3283 %
3284 %    o image: the image; returned from ReadImage.
3285 %
3286 %    o exception: return any errors or warnings in this structure.
3287 %
3288 */
3289 static MagickBooleanType XColorEditImage(Display *display,
3290   XResourceInfo *resource_info,XWindows *windows,Image **image,
3291   ExceptionInfo *exception)
3292 {
3293   static const char
3294     *ColorEditMenu[] =
3295     {
3296       "Method",
3297       "Pixel Color",
3298       "Border Color",
3299       "Fuzz",
3300       "Undo",
3301       "Help",
3302       "Dismiss",
3303       (char *) NULL
3304     };
3305
3306   static const ModeType
3307     ColorEditCommands[] =
3308     {
3309       ColorEditMethodCommand,
3310       ColorEditColorCommand,
3311       ColorEditBorderCommand,
3312       ColorEditFuzzCommand,
3313       ColorEditUndoCommand,
3314       ColorEditHelpCommand,
3315       ColorEditDismissCommand
3316     };
3317
3318   static PaintMethod
3319     method = PointMethod;
3320
3321   static unsigned int
3322     pen_id = 0;
3323
3324   static XColor
3325     border_color = { 0, 0, 0, 0, 0, 0 };
3326
3327   char
3328     command[MaxTextExtent],
3329     text[MaxTextExtent];
3330
3331   Cursor
3332     cursor;
3333
3334   int
3335     entry,
3336     id,
3337     x,
3338     x_offset,
3339     y,
3340     y_offset;
3341
3342   register Quantum
3343     *q;
3344
3345   register ssize_t
3346     i;
3347
3348   unsigned int
3349     height,
3350     width;
3351
3352   size_t
3353     state;
3354
3355   XColor
3356     color;
3357
3358   XEvent
3359     event;
3360
3361   /*
3362     Map Command widget.
3363   */
3364   (void) CloneString(&windows->command.name,"Color Edit");
3365   windows->command.data=4;
3366   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3367   (void) XMapRaised(display,windows->command.id);
3368   XClientMessage(display,windows->image.id,windows->im_protocols,
3369     windows->im_update_widget,CurrentTime);
3370   /*
3371     Make cursor.
3372   */
3373   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3374     resource_info->background_color,resource_info->foreground_color);
3375   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3376   /*
3377     Track pointer until button 1 is pressed.
3378   */
3379   XQueryPosition(display,windows->image.id,&x,&y);
3380   (void) XSelectInput(display,windows->image.id,
3381     windows->image.attributes.event_mask | PointerMotionMask);
3382   state=DefaultState;
3383   do
3384   {
3385     if (windows->info.mapped != MagickFalse)
3386       {
3387         /*
3388           Display pointer position.
3389         */
3390         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3391           x+windows->image.x,y+windows->image.y);
3392         XInfoWidget(display,windows,text);
3393       }
3394     /*
3395       Wait for next event.
3396     */
3397     XScreenEvent(display,windows,&event);
3398     if (event.xany.window == windows->command.id)
3399       {
3400         /*
3401           Select a command from the Command widget.
3402         */
3403         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3404         if (id < 0)
3405           {
3406             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3407             continue;
3408           }
3409         switch (ColorEditCommands[id])
3410         {
3411           case ColorEditMethodCommand:
3412           {
3413             char
3414               **methods;
3415
3416             /*
3417               Select a method from the pop-up menu.
3418             */
3419             methods=(char **) GetCommandOptions(MagickMethodOptions);
3420             if (methods == (char **) NULL)
3421               break;
3422             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3423               (const char **) methods,command);
3424             if (entry >= 0)
3425               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3426                 MagickFalse,methods[entry]);
3427             methods=DestroyStringList(methods);
3428             break;
3429           }
3430           case ColorEditColorCommand:
3431           {
3432             const char
3433               *ColorMenu[MaxNumberPens];
3434
3435             int
3436               pen_number;
3437
3438             /*
3439               Initialize menu selections.
3440             */
3441             for (i=0; i < (int) (MaxNumberPens-2); i++)
3442               ColorMenu[i]=resource_info->pen_colors[i];
3443             ColorMenu[MaxNumberPens-2]="Browser...";
3444             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3445             /*
3446               Select a pen color from the pop-up menu.
3447             */
3448             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3449               (const char **) ColorMenu,command);
3450             if (pen_number < 0)
3451               break;
3452             if (pen_number == (MaxNumberPens-2))
3453               {
3454                 static char
3455                   color_name[MaxTextExtent] = "gray";
3456
3457                 /*
3458                   Select a pen color from a dialog.
3459                 */
3460                 resource_info->pen_colors[pen_number]=color_name;
3461                 XColorBrowserWidget(display,windows,"Select",color_name);
3462                 if (*color_name == '\0')
3463                   break;
3464               }
3465             /*
3466               Set pen color.
3467             */
3468             (void) XParseColor(display,windows->map_info->colormap,
3469               resource_info->pen_colors[pen_number],&color);
3470             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3471               (unsigned int) MaxColors,&color);
3472             windows->pixel_info->pen_colors[pen_number]=color;
3473             pen_id=(unsigned int) pen_number;
3474             break;
3475           }
3476           case ColorEditBorderCommand:
3477           {
3478             const char
3479               *ColorMenu[MaxNumberPens];
3480
3481             int
3482               pen_number;
3483
3484             /*
3485               Initialize menu selections.
3486             */
3487             for (i=0; i < (int) (MaxNumberPens-2); i++)
3488               ColorMenu[i]=resource_info->pen_colors[i];
3489             ColorMenu[MaxNumberPens-2]="Browser...";
3490             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3491             /*
3492               Select a pen color from the pop-up menu.
3493             */
3494             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3495               (const char **) ColorMenu,command);
3496             if (pen_number < 0)
3497               break;
3498             if (pen_number == (MaxNumberPens-2))
3499               {
3500                 static char
3501                   color_name[MaxTextExtent] = "gray";
3502
3503                 /*
3504                   Select a pen color from a dialog.
3505                 */
3506                 resource_info->pen_colors[pen_number]=color_name;
3507                 XColorBrowserWidget(display,windows,"Select",color_name);
3508                 if (*color_name == '\0')
3509                   break;
3510               }
3511             /*
3512               Set border color.
3513             */
3514             (void) XParseColor(display,windows->map_info->colormap,
3515               resource_info->pen_colors[pen_number],&border_color);
3516             break;
3517           }
3518           case ColorEditFuzzCommand:
3519           {
3520             static char
3521               fuzz[MaxTextExtent];
3522
3523             static const char
3524               *FuzzMenu[] =
3525               {
3526                 "0%",
3527                 "2%",
3528                 "5%",
3529                 "10%",
3530                 "15%",
3531                 "Dialog...",
3532                 (char *) NULL,
3533               };
3534
3535             /*
3536               Select a command from the pop-up menu.
3537             */
3538             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3539               command);
3540             if (entry < 0)
3541               break;
3542             if (entry != 5)
3543               {
3544                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
3545                   QuantumRange+1.0);
3546                 break;
3547               }
3548             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3549             (void) XDialogWidget(display,windows,"Ok",
3550               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3551             if (*fuzz == '\0')
3552               break;
3553             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3554             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
3555             break;
3556           }
3557           case ColorEditUndoCommand:
3558           {
3559             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3560               image,exception);
3561             break;
3562           }
3563           case ColorEditHelpCommand:
3564           default:
3565           {
3566             XTextViewWidget(display,resource_info,windows,MagickFalse,
3567               "Help Viewer - Image Annotation",ImageColorEditHelp);
3568             break;
3569           }
3570           case ColorEditDismissCommand:
3571           {
3572             /*
3573               Prematurely exit.
3574             */
3575             state|=EscapeState;
3576             state|=ExitState;
3577             break;
3578           }
3579         }
3580         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3581         continue;
3582       }
3583     switch (event.type)
3584     {
3585       case ButtonPress:
3586       {
3587         if (event.xbutton.button != Button1)
3588           break;
3589         if ((event.xbutton.window != windows->image.id) &&
3590             (event.xbutton.window != windows->magnify.id))
3591           break;
3592         /*
3593           exit loop.
3594         */
3595         x=event.xbutton.x;
3596         y=event.xbutton.y;
3597         (void) XMagickCommand(display,resource_info,windows,
3598           SaveToUndoBufferCommand,image,exception);
3599         state|=UpdateConfigurationState;
3600         break;
3601       }
3602       case ButtonRelease:
3603       {
3604         if (event.xbutton.button != Button1)
3605           break;
3606         if ((event.xbutton.window != windows->image.id) &&
3607             (event.xbutton.window != windows->magnify.id))
3608           break;
3609         /*
3610           Update colormap information.
3611         */
3612         x=event.xbutton.x;
3613         y=event.xbutton.y;
3614         XConfigureImageColormap(display,resource_info,windows,*image);
3615         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3616         XInfoWidget(display,windows,text);
3617         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3618         state&=(~UpdateConfigurationState);
3619         break;
3620       }
3621       case Expose:
3622         break;
3623       case KeyPress:
3624       {
3625         KeySym
3626           key_symbol;
3627
3628         if (event.xkey.window == windows->magnify.id)
3629           {
3630             Window
3631               window;
3632
3633             window=windows->magnify.id;
3634             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3635           }
3636         if (event.xkey.window != windows->image.id)
3637           break;
3638         /*
3639           Respond to a user key press.
3640         */
3641         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3642           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3643         switch ((int) key_symbol)
3644         {
3645           case XK_Escape:
3646           case XK_F20:
3647           {
3648             /*
3649               Prematurely exit.
3650             */
3651             state|=ExitState;
3652             break;
3653           }
3654           case XK_F1:
3655           case XK_Help:
3656           {
3657             XTextViewWidget(display,resource_info,windows,MagickFalse,
3658               "Help Viewer - Image Annotation",ImageColorEditHelp);
3659             break;
3660           }
3661           default:
3662           {
3663             (void) XBell(display,0);
3664             break;
3665           }
3666         }
3667         break;
3668       }
3669       case MotionNotify:
3670       {
3671         /*
3672           Map and unmap Info widget as cursor crosses its boundaries.
3673         */
3674         x=event.xmotion.x;
3675         y=event.xmotion.y;
3676         if (windows->info.mapped != MagickFalse)
3677           {
3678             if ((x < (int) (windows->info.x+windows->info.width)) &&
3679                 (y < (int) (windows->info.y+windows->info.height)))
3680               (void) XWithdrawWindow(display,windows->info.id,
3681                 windows->info.screen);
3682           }
3683         else
3684           if ((x > (int) (windows->info.x+windows->info.width)) ||
3685               (y > (int) (windows->info.y+windows->info.height)))
3686             (void) XMapWindow(display,windows->info.id);
3687         break;
3688       }
3689       default:
3690         break;
3691     }
3692     if (event.xany.window == windows->magnify.id)
3693       {
3694         x=windows->magnify.x-windows->image.x;
3695         y=windows->magnify.y-windows->image.y;
3696       }
3697     x_offset=x;
3698     y_offset=y;
3699     if ((state & UpdateConfigurationState) != 0)
3700       {
3701         CacheView
3702           *image_view;
3703
3704         int
3705           x,
3706           y;
3707
3708         /*
3709           Pixel edit is relative to image configuration.
3710         */
3711         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3712           MagickTrue);
3713         color=windows->pixel_info->pen_colors[pen_id];
3714         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3715         width=(unsigned int) (*image)->columns;
3716         height=(unsigned int) (*image)->rows;
3717         x=0;
3718         y=0;
3719         if (windows->image.crop_geometry != (char *) NULL)
3720           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3721             &width,&height);
3722         x_offset=(int)
3723           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3724         y_offset=(int)
3725           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3726         if ((x_offset < 0) || (y_offset < 0))
3727           continue;
3728         if ((x_offset >= (int) (*image)->columns) ||
3729             (y_offset >= (int) (*image)->rows))
3730           continue;
3731         image_view=AcquireCacheView(*image);
3732         switch (method)
3733         {
3734           case PointMethod:
3735           default:
3736           {
3737             /*
3738               Update color information using point algorithm.
3739             */
3740             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3741               return(MagickFalse);
3742             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3743               (ssize_t) y_offset,1,1,exception);
3744             if (q == (Quantum *) NULL)
3745               break;
3746             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3747             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3748             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3749             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3750             break;
3751           }
3752           case ReplaceMethod:
3753           {
3754             PixelPacket
3755               pixel,
3756               target;
3757
3758             /*
3759               Update color information using replace algorithm.
3760             */
3761             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3762               (ssize_t) y_offset,&target,exception);
3763             if ((*image)->storage_class == DirectClass)
3764               {
3765                 for (y=0; y < (int) (*image)->rows; y++)
3766                 {
3767                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3768                     (*image)->columns,1,exception);
3769                   if (q == (Quantum *) NULL)
3770                     break;
3771                   for (x=0; x < (int) (*image)->columns; x++)
3772                   {
3773                     GetPixelPacket(*image,q,&pixel);
3774                     if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
3775                       {
3776                         SetPixelRed(*image,ScaleShortToQuantum(
3777                           color.red),q);
3778                         SetPixelGreen(*image,ScaleShortToQuantum(
3779                           color.green),q);
3780                         SetPixelBlue(*image,ScaleShortToQuantum(
3781                           color.blue),q);
3782                       }
3783                     q+=GetPixelChannels(*image);
3784                   }
3785                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3786                     break;
3787                 }
3788               }
3789             else
3790               {
3791                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3792                   if (IsFuzzyEquivalencePixelPacket(*image,(*image)->colormap+i,&target))
3793                     {
3794                       (*image)->colormap[i].red=ScaleShortToQuantum(
3795                         color.red);
3796                       (*image)->colormap[i].green=ScaleShortToQuantum(
3797                         color.green);
3798                       (*image)->colormap[i].blue=ScaleShortToQuantum(
3799                         color.blue);
3800                     }
3801                 (void) SyncImage(*image);
3802               }
3803             break;
3804           }
3805           case FloodfillMethod:
3806           case FillToBorderMethod:
3807           {
3808             DrawInfo
3809               *draw_info;
3810
3811             PixelInfo
3812               target;
3813
3814             /*
3815               Update color information using floodfill algorithm.
3816             */
3817             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3818               (ssize_t) y_offset,&target,exception);
3819             if (method == FillToBorderMethod)
3820               {
3821                 target.red=(MagickRealType)
3822                   ScaleShortToQuantum(border_color.red);
3823                 target.green=(MagickRealType)
3824                   ScaleShortToQuantum(border_color.green);
3825                 target.blue=(MagickRealType)
3826                   ScaleShortToQuantum(border_color.blue);
3827               }
3828             draw_info=CloneDrawInfo(resource_info->image_info,
3829               (DrawInfo *) NULL);
3830             (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3831               &draw_info->fill,exception);
3832             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3833               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3834               MagickFalse : MagickTrue,exception);
3835             draw_info=DestroyDrawInfo(draw_info);
3836             break;
3837           }
3838           case ResetMethod:
3839           {
3840             /*
3841               Update color information using reset algorithm.
3842             */
3843             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3844               return(MagickFalse);
3845             for (y=0; y < (int) (*image)->rows; y++)
3846             {
3847               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3848                 (*image)->columns,1,exception);
3849               if (q == (Quantum *) NULL)
3850                 break;
3851               for (x=0; x < (int) (*image)->columns; x++)
3852               {
3853                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3854                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3855                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3856                 q+=GetPixelChannels(*image);
3857               }
3858               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3859                 break;
3860             }
3861             break;
3862           }
3863         }
3864         image_view=DestroyCacheView(image_view);
3865         state&=(~UpdateConfigurationState);
3866       }
3867   } while ((state & ExitState) == 0);
3868   (void) XSelectInput(display,windows->image.id,
3869     windows->image.attributes.event_mask);
3870   XSetCursorState(display,windows,MagickFalse);
3871   (void) XFreeCursor(display,cursor);
3872   return(MagickTrue);
3873 }
3874 \f
3875 /*
3876 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3877 %                                                                             %
3878 %                                                                             %
3879 %                                                                             %
3880 +   X C o m p o s i t e I m a g e                                             %
3881 %                                                                             %
3882 %                                                                             %
3883 %                                                                             %
3884 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3885 %
3886 %  XCompositeImage() requests an image name from the user, reads the image and
3887 %  composites it with the X window image at a location the user chooses with
3888 %  the pointer.
3889 %
3890 %  The format of the XCompositeImage method is:
3891 %
3892 %      MagickBooleanType XCompositeImage(Display *display,
3893 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3894 %        ExceptionInfo *exception)
3895 %
3896 %  A description of each parameter follows:
3897 %
3898 %    o display: Specifies a connection to an X server;  returned from
3899 %      XOpenDisplay.
3900 %
3901 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3902 %
3903 %    o windows: Specifies a pointer to a XWindows structure.
3904 %
3905 %    o image: the image; returned from ReadImage.
3906 %
3907 %    o exception: return any errors or warnings in this structure.
3908 %
3909 */
3910 static MagickBooleanType XCompositeImage(Display *display,
3911   XResourceInfo *resource_info,XWindows *windows,Image *image,
3912   ExceptionInfo *exception)
3913 {
3914   static char
3915     displacement_geometry[MaxTextExtent] = "30x30",
3916     filename[MaxTextExtent] = "\0";
3917
3918   static const char
3919     *CompositeMenu[] =
3920     {
3921       "Operators",
3922       "Dissolve",
3923       "Displace",
3924       "Help",
3925       "Dismiss",
3926       (char *) NULL
3927     };
3928
3929   static CompositeOperator
3930     compose = CopyCompositeOp;
3931
3932   static const ModeType
3933     CompositeCommands[] =
3934     {
3935       CompositeOperatorsCommand,
3936       CompositeDissolveCommand,
3937       CompositeDisplaceCommand,
3938       CompositeHelpCommand,
3939       CompositeDismissCommand
3940     };
3941
3942   char
3943     text[MaxTextExtent];
3944
3945   Cursor
3946     cursor;
3947
3948   Image
3949     *composite_image;
3950
3951   int
3952     entry,
3953     id,
3954     x,
3955     y;
3956
3957   MagickRealType
3958     blend,
3959     scale_factor;
3960
3961   RectangleInfo
3962     highlight_info,
3963     composite_info;
3964
3965   unsigned int
3966     height,
3967     width;
3968
3969   size_t
3970     state;
3971
3972   XEvent
3973     event;
3974
3975   /*
3976     Request image file name from user.
3977   */
3978   XFileBrowserWidget(display,windows,"Composite",filename);
3979   if (*filename == '\0')
3980     return(MagickTrue);
3981   /*
3982     Read image.
3983   */
3984   XSetCursorState(display,windows,MagickTrue);
3985   XCheckRefreshWindows(display,windows);
3986   (void) CopyMagickString(resource_info->image_info->filename,filename,
3987     MaxTextExtent);
3988   composite_image=ReadImage(resource_info->image_info,exception);
3989   CatchException(exception);
3990   XSetCursorState(display,windows,MagickFalse);
3991   if (composite_image == (Image *) NULL)
3992     return(MagickFalse);
3993   /*
3994     Map Command widget.
3995   */
3996   (void) CloneString(&windows->command.name,"Composite");
3997   windows->command.data=1;
3998   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3999   (void) XMapRaised(display,windows->command.id);
4000   XClientMessage(display,windows->image.id,windows->im_protocols,
4001     windows->im_update_widget,CurrentTime);
4002   /*
4003     Track pointer until button 1 is pressed.
4004   */
4005   XQueryPosition(display,windows->image.id,&x,&y);
4006   (void) XSelectInput(display,windows->image.id,
4007     windows->image.attributes.event_mask | PointerMotionMask);
4008   composite_info.x=(ssize_t) windows->image.x+x;
4009   composite_info.y=(ssize_t) windows->image.y+y;
4010   composite_info.width=0;
4011   composite_info.height=0;
4012   cursor=XCreateFontCursor(display,XC_ul_angle);
4013   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4014   blend=0.0;
4015   state=DefaultState;
4016   do
4017   {
4018     if (windows->info.mapped != MagickFalse)
4019       {
4020         /*
4021           Display pointer position.
4022         */
4023         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4024           (long) composite_info.x,(long) composite_info.y);
4025         XInfoWidget(display,windows,text);
4026       }
4027     highlight_info=composite_info;
4028     highlight_info.x=composite_info.x-windows->image.x;
4029     highlight_info.y=composite_info.y-windows->image.y;
4030     XHighlightRectangle(display,windows->image.id,
4031       windows->image.highlight_context,&highlight_info);
4032     /*
4033       Wait for next event.
4034     */
4035     XScreenEvent(display,windows,&event);
4036     XHighlightRectangle(display,windows->image.id,
4037       windows->image.highlight_context,&highlight_info);
4038     if (event.xany.window == windows->command.id)
4039       {
4040         /*
4041           Select a command from the Command widget.
4042         */
4043         id=XCommandWidget(display,windows,CompositeMenu,&event);
4044         if (id < 0)
4045           continue;
4046         switch (CompositeCommands[id])
4047         {
4048           case CompositeOperatorsCommand:
4049           {
4050             char
4051               command[MaxTextExtent],
4052               **operators;
4053
4054             /*
4055               Select a command from the pop-up menu.
4056             */
4057             operators=GetCommandOptions(MagickComposeOptions);
4058             if (operators == (char **) NULL)
4059               break;
4060             entry=XMenuWidget(display,windows,CompositeMenu[id],
4061               (const char **) operators,command);
4062             if (entry >= 0)
4063               compose=(CompositeOperator) ParseCommandOption(
4064                 MagickComposeOptions,MagickFalse,operators[entry]);
4065             operators=DestroyStringList(operators);
4066             break;
4067           }
4068           case CompositeDissolveCommand:
4069           {
4070             static char
4071               factor[MaxTextExtent] = "20.0";
4072
4073             /*
4074               Dissolve the two images a given percent.
4075             */
4076             (void) XSetFunction(display,windows->image.highlight_context,
4077               GXcopy);
4078             (void) XDialogWidget(display,windows,"Dissolve",
4079               "Enter the blend factor (0.0 - 99.9%):",factor);
4080             (void) XSetFunction(display,windows->image.highlight_context,
4081               GXinvert);
4082             if (*factor == '\0')
4083               break;
4084             blend=InterpretLocaleValue(factor,(char **) NULL);
4085             compose=DissolveCompositeOp;
4086             break;
4087           }
4088           case CompositeDisplaceCommand:
4089           {
4090             /*
4091               Get horizontal and vertical scale displacement geometry.
4092             */
4093             (void) XSetFunction(display,windows->image.highlight_context,
4094               GXcopy);
4095             (void) XDialogWidget(display,windows,"Displace",
4096               "Enter the horizontal and vertical scale:",displacement_geometry);
4097             (void) XSetFunction(display,windows->image.highlight_context,
4098               GXinvert);
4099             if (*displacement_geometry == '\0')
4100               break;
4101             compose=DisplaceCompositeOp;
4102             break;
4103           }
4104           case CompositeHelpCommand:
4105           {
4106             (void) XSetFunction(display,windows->image.highlight_context,
4107               GXcopy);
4108             XTextViewWidget(display,resource_info,windows,MagickFalse,
4109               "Help Viewer - Image Composite",ImageCompositeHelp);
4110             (void) XSetFunction(display,windows->image.highlight_context,
4111               GXinvert);
4112             break;
4113           }
4114           case CompositeDismissCommand:
4115           {
4116             /*
4117               Prematurely exit.
4118             */
4119             state|=EscapeState;
4120             state|=ExitState;
4121             break;
4122           }
4123           default:
4124             break;
4125         }
4126         continue;
4127       }
4128     switch (event.type)
4129     {
4130       case ButtonPress:
4131       {
4132         if (image->debug != MagickFalse)
4133           (void) LogMagickEvent(X11Event,GetMagickModule(),
4134             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4135             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4136         if (event.xbutton.button != Button1)
4137           break;
4138         if (event.xbutton.window != windows->image.id)
4139           break;
4140         /*
4141           Change cursor.
4142         */
4143         composite_info.width=composite_image->columns;
4144         composite_info.height=composite_image->rows;
4145         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4146         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4147         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4148         break;
4149       }
4150       case ButtonRelease:
4151       {
4152         if (image->debug != MagickFalse)
4153           (void) LogMagickEvent(X11Event,GetMagickModule(),
4154             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4155             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4156         if (event.xbutton.button != Button1)
4157           break;
4158         if (event.xbutton.window != windows->image.id)
4159           break;
4160         if ((composite_info.width != 0) && (composite_info.height != 0))
4161           {
4162             /*
4163               User has selected the location of the composite image.
4164             */
4165             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4166             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4167             state|=ExitState;
4168           }
4169         break;
4170       }
4171       case Expose:
4172         break;
4173       case KeyPress:
4174       {
4175         char
4176           command[MaxTextExtent];
4177
4178         KeySym
4179           key_symbol;
4180
4181         int
4182           length;
4183
4184         if (event.xkey.window != windows->image.id)
4185           break;
4186         /*
4187           Respond to a user key press.
4188         */
4189         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4190           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4191         *(command+length)='\0';
4192         if (image->debug != MagickFalse)
4193           (void) LogMagickEvent(X11Event,GetMagickModule(),
4194             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4195         switch ((int) key_symbol)
4196         {
4197           case XK_Escape:
4198           case XK_F20:
4199           {
4200             /*
4201               Prematurely exit.
4202             */
4203             composite_image=DestroyImage(composite_image);
4204             state|=EscapeState;
4205             state|=ExitState;
4206             break;
4207           }
4208           case XK_F1:
4209           case XK_Help:
4210           {
4211             (void) XSetFunction(display,windows->image.highlight_context,
4212               GXcopy);
4213             XTextViewWidget(display,resource_info,windows,MagickFalse,
4214               "Help Viewer - Image Composite",ImageCompositeHelp);
4215             (void) XSetFunction(display,windows->image.highlight_context,
4216               GXinvert);
4217             break;
4218           }
4219           default:
4220           {
4221             (void) XBell(display,0);
4222             break;
4223           }
4224         }
4225         break;
4226       }
4227       case MotionNotify:
4228       {
4229         /*
4230           Map and unmap Info widget as text cursor crosses its boundaries.
4231         */
4232         x=event.xmotion.x;
4233         y=event.xmotion.y;
4234         if (windows->info.mapped != MagickFalse)
4235           {
4236             if ((x < (int) (windows->info.x+windows->info.width)) &&
4237                 (y < (int) (windows->info.y+windows->info.height)))
4238               (void) XWithdrawWindow(display,windows->info.id,
4239                 windows->info.screen);
4240           }
4241         else
4242           if ((x > (int) (windows->info.x+windows->info.width)) ||
4243               (y > (int) (windows->info.y+windows->info.height)))
4244             (void) XMapWindow(display,windows->info.id);
4245         composite_info.x=(ssize_t) windows->image.x+x;
4246         composite_info.y=(ssize_t) windows->image.y+y;
4247         break;
4248       }
4249       default:
4250       {
4251         if (image->debug != MagickFalse)
4252           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4253             event.type);
4254         break;
4255       }
4256     }
4257   } while ((state & ExitState) == 0);
4258   (void) XSelectInput(display,windows->image.id,
4259     windows->image.attributes.event_mask);
4260   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4261   XSetCursorState(display,windows,MagickFalse);
4262   (void) XFreeCursor(display,cursor);
4263   if ((state & EscapeState) != 0)
4264     return(MagickTrue);
4265   /*
4266     Image compositing is relative to image configuration.
4267   */
4268   XSetCursorState(display,windows,MagickTrue);
4269   XCheckRefreshWindows(display,windows);
4270   width=(unsigned int) image->columns;
4271   height=(unsigned int) image->rows;
4272   x=0;
4273   y=0;
4274   if (windows->image.crop_geometry != (char *) NULL)
4275     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4276   scale_factor=(MagickRealType) width/windows->image.ximage->width;
4277   composite_info.x+=x;
4278   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4279   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4280   scale_factor=(MagickRealType) height/windows->image.ximage->height;
4281   composite_info.y+=y;
4282   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4283   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4284   if ((composite_info.width != composite_image->columns) ||
4285       (composite_info.height != composite_image->rows))
4286     {
4287       Image
4288         *resize_image;
4289
4290       /*
4291         Scale composite image.
4292       */
4293       resize_image=ResizeImage(composite_image,composite_info.width,
4294         composite_info.height,composite_image->filter,composite_image->blur,
4295         exception);
4296       composite_image=DestroyImage(composite_image);
4297       if (resize_image == (Image *) NULL)
4298         {
4299           XSetCursorState(display,windows,MagickFalse);
4300           return(MagickFalse);
4301         }
4302       composite_image=resize_image;
4303     }
4304   if (compose == DisplaceCompositeOp)
4305     (void) SetImageArtifact(composite_image,"compose:args",
4306       displacement_geometry);
4307   if (blend != 0.0)
4308     {
4309       CacheView
4310         *image_view;
4311
4312       int
4313         y;
4314
4315       Quantum
4316         opacity;
4317
4318       register int
4319         x;
4320
4321       register Quantum
4322         *q;
4323
4324       /*
4325         Create mattes for blending.
4326       */
4327       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4328       opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4329         ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4330       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4331         return(MagickFalse);
4332       image->matte=MagickTrue;
4333       image_view=AcquireCacheView(image);
4334       for (y=0; y < (int) image->rows; y++)
4335       {
4336         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4337           exception);
4338         if (q == (Quantum *) NULL)
4339           break;
4340         for (x=0; x < (int) image->columns; x++)
4341         {
4342           SetPixelAlpha(image,opacity,q);
4343           q+=GetPixelChannels(image);
4344         }
4345         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4346           break;
4347       }
4348       image_view=DestroyCacheView(image_view);
4349     }
4350   /*
4351     Composite image with X Image window.
4352   */
4353   (void) CompositeImage(image,compose,composite_image,composite_info.x,
4354     composite_info.y);
4355   composite_image=DestroyImage(composite_image);
4356   XSetCursorState(display,windows,MagickFalse);
4357   /*
4358     Update image configuration.
4359   */
4360   XConfigureImageColormap(display,resource_info,windows,image);
4361   (void) XConfigureImage(display,resource_info,windows,image,exception);
4362   return(MagickTrue);
4363 }
4364 \f
4365 /*
4366 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4367 %                                                                             %
4368 %                                                                             %
4369 %                                                                             %
4370 +   X C o n f i g u r e I m a g e                                             %
4371 %                                                                             %
4372 %                                                                             %
4373 %                                                                             %
4374 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4375 %
4376 %  XConfigureImage() creates a new X image.  It also notifies the window
4377 %  manager of the new image size and configures the transient widows.
4378 %
4379 %  The format of the XConfigureImage method is:
4380 %
4381 %      MagickBooleanType XConfigureImage(Display *display,
4382 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4383 %        ExceptionInfo *exception)
4384 %
4385 %  A description of each parameter follows:
4386 %
4387 %    o display: Specifies a connection to an X server; returned from
4388 %      XOpenDisplay.
4389 %
4390 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4391 %
4392 %    o windows: Specifies a pointer to a XWindows structure.
4393 %
4394 %    o image: the image.
4395 %
4396 %    o exception: return any errors or warnings in this structure.
4397 %
4398 %    o exception: return any errors or warnings in this structure.
4399 %
4400 */
4401 static MagickBooleanType XConfigureImage(Display *display,
4402   XResourceInfo *resource_info,XWindows *windows,Image *image,
4403   ExceptionInfo *exception)
4404 {
4405   char
4406     geometry[MaxTextExtent];
4407
4408   MagickStatusType
4409     status;
4410
4411   size_t
4412     mask,
4413     height,
4414     width;
4415
4416   ssize_t
4417     x,
4418     y;
4419
4420   XSizeHints
4421     *size_hints;
4422
4423   XWindowChanges
4424     window_changes;
4425
4426   /*
4427     Dismiss if window dimensions are zero.
4428   */
4429   width=(unsigned int) windows->image.window_changes.width;
4430   height=(unsigned int) windows->image.window_changes.height;
4431   if (image->debug != MagickFalse)
4432     (void) LogMagickEvent(X11Event,GetMagickModule(),
4433       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4434       windows->image.ximage->height,(double) width,(double) height);
4435   if ((width*height) == 0)
4436     return(MagickTrue);
4437   x=0;
4438   y=0;
4439   /*
4440     Resize image to fit Image window dimensions.
4441   */
4442   XSetCursorState(display,windows,MagickTrue);
4443   (void) XFlush(display);
4444   if (((int) width != windows->image.ximage->width) ||
4445       ((int) height != windows->image.ximage->height))
4446     image->taint=MagickTrue;
4447   windows->magnify.x=(int)
4448     width*windows->magnify.x/windows->image.ximage->width;
4449   windows->magnify.y=(int)
4450     height*windows->magnify.y/windows->image.ximage->height;
4451   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4452   windows->image.y=(int)
4453     (height*windows->image.y/windows->image.ximage->height);
4454   status=XMakeImage(display,resource_info,&windows->image,image,
4455     (unsigned int) width,(unsigned int) height,exception);
4456   if (status == MagickFalse)
4457     XNoticeWidget(display,windows,"Unable to configure X image:",
4458       windows->image.name);
4459   /*
4460     Notify window manager of the new configuration.
4461   */
4462   if (resource_info->image_geometry != (char *) NULL)
4463     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4464       resource_info->image_geometry);
4465   else
4466     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4467       XDisplayWidth(display,windows->image.screen),
4468       XDisplayHeight(display,windows->image.screen));
4469   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4470   window_changes.width=(int) width;
4471   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4472     window_changes.width=XDisplayWidth(display,windows->image.screen);
4473   window_changes.height=(int) height;
4474   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4475     window_changes.height=XDisplayHeight(display,windows->image.screen);
4476   mask=(size_t) (CWWidth | CWHeight);
4477   if (resource_info->backdrop)
4478     {
4479       mask|=CWX | CWY;
4480       window_changes.x=(int)
4481         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4482       window_changes.y=(int)
4483         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4484     }
4485   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4486     (unsigned int) mask,&window_changes);
4487   (void) XClearWindow(display,windows->image.id);
4488   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4489   /*
4490     Update Magnify window configuration.
4491   */
4492   if (windows->magnify.mapped != MagickFalse)
4493     XMakeMagnifyImage(display,windows);
4494   windows->pan.crop_geometry=windows->image.crop_geometry;
4495   XBestIconSize(display,&windows->pan,image);
4496   while (((windows->pan.width << 1) < MaxIconSize) &&
4497          ((windows->pan.height << 1) < MaxIconSize))
4498   {
4499     windows->pan.width<<=1;
4500     windows->pan.height<<=1;
4501   }
4502   if (windows->pan.geometry != (char *) NULL)
4503     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4504       &windows->pan.width,&windows->pan.height);
4505   window_changes.width=(int) windows->pan.width;
4506   window_changes.height=(int) windows->pan.height;
4507   size_hints=XAllocSizeHints();
4508   if (size_hints != (XSizeHints *) NULL)
4509     {
4510       /*
4511         Set new size hints.
4512       */
4513       size_hints->flags=PSize | PMinSize | PMaxSize;
4514       size_hints->width=window_changes.width;
4515       size_hints->height=window_changes.height;
4516       size_hints->min_width=size_hints->width;
4517       size_hints->min_height=size_hints->height;
4518       size_hints->max_width=size_hints->width;
4519       size_hints->max_height=size_hints->height;
4520       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4521       (void) XFree((void *) size_hints);
4522     }
4523   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4524     (unsigned int) (CWWidth | CWHeight),&window_changes);
4525   /*
4526     Update icon window configuration.
4527   */
4528   windows->icon.crop_geometry=windows->image.crop_geometry;
4529   XBestIconSize(display,&windows->icon,image);
4530   window_changes.width=(int) windows->icon.width;
4531   window_changes.height=(int) windows->icon.height;
4532   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4533     (unsigned int) (CWWidth | CWHeight),&window_changes);
4534   XSetCursorState(display,windows,MagickFalse);
4535   return(status != 0 ? MagickTrue : MagickFalse);
4536 }
4537 \f
4538 /*
4539 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4540 %                                                                             %
4541 %                                                                             %
4542 %                                                                             %
4543 +   X C r o p I m a g e                                                       %
4544 %                                                                             %
4545 %                                                                             %
4546 %                                                                             %
4547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4548 %
4549 %  XCropImage() allows the user to select a region of the image and crop, copy,
4550 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4551 %  the image with XPasteImage.
4552 %
4553 %  The format of the XCropImage method is:
4554 %
4555 %      MagickBooleanType XCropImage(Display *display,
4556 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4557 %        const ClipboardMode mode,ExceptionInfo *exception)
4558 %
4559 %  A description of each parameter follows:
4560 %
4561 %    o display: Specifies a connection to an X server; returned from
4562 %      XOpenDisplay.
4563 %
4564 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4565 %
4566 %    o windows: Specifies a pointer to a XWindows structure.
4567 %
4568 %    o image: the image; returned from ReadImage.
4569 %
4570 %    o mode: This unsigned value specified whether the image should be
4571 %      cropped, copied, or cut.
4572 %
4573 %    o exception: return any errors or warnings in this structure.
4574 %
4575 */
4576 static MagickBooleanType XCropImage(Display *display,
4577   XResourceInfo *resource_info,XWindows *windows,Image *image,
4578   const ClipboardMode mode,ExceptionInfo *exception)
4579 {
4580   static const char
4581     *CropModeMenu[] =
4582     {
4583       "Help",
4584       "Dismiss",
4585       (char *) NULL
4586     },
4587     *RectifyModeMenu[] =
4588     {
4589       "Crop",
4590       "Help",
4591       "Dismiss",
4592       (char *) NULL
4593     };
4594
4595   static const ModeType
4596     CropCommands[] =
4597     {
4598       CropHelpCommand,
4599       CropDismissCommand
4600     },
4601     RectifyCommands[] =
4602     {
4603       RectifyCopyCommand,
4604       RectifyHelpCommand,
4605       RectifyDismissCommand
4606     };
4607
4608   CacheView
4609     *image_view;
4610
4611   char
4612     command[MaxTextExtent],
4613     text[MaxTextExtent];
4614
4615   Cursor
4616     cursor;
4617
4618   int
4619     id,
4620     x,
4621     y;
4622
4623   KeySym
4624     key_symbol;
4625
4626   Image
4627     *crop_image;
4628
4629   MagickRealType
4630     scale_factor;
4631
4632   RectangleInfo
4633     crop_info,
4634     highlight_info;
4635
4636   register Quantum
4637     *q;
4638
4639   unsigned int
4640     height,
4641     width;
4642
4643   size_t
4644     state;
4645
4646   XEvent
4647     event;
4648
4649   /*
4650     Map Command widget.
4651   */
4652   switch (mode)
4653   {
4654     case CopyMode:
4655     {
4656       (void) CloneString(&windows->command.name,"Copy");
4657       break;
4658     }
4659     case CropMode:
4660     {
4661       (void) CloneString(&windows->command.name,"Crop");
4662       break;
4663     }
4664     case CutMode:
4665     {
4666       (void) CloneString(&windows->command.name,"Cut");
4667       break;
4668     }
4669   }
4670   RectifyModeMenu[0]=windows->command.name;
4671   windows->command.data=0;
4672   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4673   (void) XMapRaised(display,windows->command.id);
4674   XClientMessage(display,windows->image.id,windows->im_protocols,
4675     windows->im_update_widget,CurrentTime);
4676   /*
4677     Track pointer until button 1 is pressed.
4678   */
4679   XQueryPosition(display,windows->image.id,&x,&y);
4680   (void) XSelectInput(display,windows->image.id,
4681     windows->image.attributes.event_mask | PointerMotionMask);
4682   crop_info.x=(ssize_t) windows->image.x+x;
4683   crop_info.y=(ssize_t) windows->image.y+y;
4684   crop_info.width=0;
4685   crop_info.height=0;
4686   cursor=XCreateFontCursor(display,XC_fleur);
4687   state=DefaultState;
4688   do
4689   {
4690     if (windows->info.mapped != MagickFalse)
4691       {
4692         /*
4693           Display pointer position.
4694         */
4695         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4696           (long) crop_info.x,(long) crop_info.y);
4697         XInfoWidget(display,windows,text);
4698       }
4699     /*
4700       Wait for next event.
4701     */
4702     XScreenEvent(display,windows,&event);
4703     if (event.xany.window == windows->command.id)
4704       {
4705         /*
4706           Select a command from the Command widget.
4707         */
4708         id=XCommandWidget(display,windows,CropModeMenu,&event);
4709         if (id < 0)
4710           continue;
4711         switch (CropCommands[id])
4712         {
4713           case CropHelpCommand:
4714           {
4715             switch (mode)
4716             {
4717               case CopyMode:
4718               {
4719                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4720                   "Help Viewer - Image Copy",ImageCopyHelp);
4721                 break;
4722               }
4723               case CropMode:
4724               {
4725                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4726                   "Help Viewer - Image Crop",ImageCropHelp);
4727                 break;
4728               }
4729               case CutMode:
4730               {
4731                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4732                   "Help Viewer - Image Cut",ImageCutHelp);
4733                 break;
4734               }
4735             }
4736             break;
4737           }
4738           case CropDismissCommand:
4739           {
4740             /*
4741               Prematurely exit.
4742             */
4743             state|=EscapeState;
4744             state|=ExitState;
4745             break;
4746           }
4747           default:
4748             break;
4749         }
4750         continue;
4751       }
4752     switch (event.type)
4753     {
4754       case ButtonPress:
4755       {
4756         if (event.xbutton.button != Button1)
4757           break;
4758         if (event.xbutton.window != windows->image.id)
4759           break;
4760         /*
4761           Note first corner of cropping rectangle-- exit loop.
4762         */
4763         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4764         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4765         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4766         state|=ExitState;
4767         break;
4768       }
4769       case ButtonRelease:
4770         break;
4771       case Expose:
4772         break;
4773       case KeyPress:
4774       {
4775         if (event.xkey.window != windows->image.id)
4776           break;
4777         /*
4778           Respond to a user key press.
4779         */
4780         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4781           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4782         switch ((int) key_symbol)
4783         {
4784           case XK_Escape:
4785           case XK_F20:
4786           {
4787             /*
4788               Prematurely exit.
4789             */
4790             state|=EscapeState;
4791             state|=ExitState;
4792             break;
4793           }
4794           case XK_F1:
4795           case XK_Help:
4796           {
4797             switch (mode)
4798             {
4799               case CopyMode:
4800               {
4801                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4802                   "Help Viewer - Image Copy",ImageCopyHelp);
4803                 break;
4804               }
4805               case CropMode:
4806               {
4807                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4808                   "Help Viewer - Image Crop",ImageCropHelp);
4809                 break;
4810               }
4811               case CutMode:
4812               {
4813                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4814                   "Help Viewer - Image Cut",ImageCutHelp);
4815                 break;
4816               }
4817             }
4818             break;
4819           }
4820           default:
4821           {
4822             (void) XBell(display,0);
4823             break;
4824           }
4825         }
4826         break;
4827       }
4828       case MotionNotify:
4829       {
4830         if (event.xmotion.window != windows->image.id)
4831           break;
4832         /*
4833           Map and unmap Info widget as text cursor crosses its boundaries.
4834         */
4835         x=event.xmotion.x;
4836         y=event.xmotion.y;
4837         if (windows->info.mapped != MagickFalse)
4838           {
4839             if ((x < (int) (windows->info.x+windows->info.width)) &&
4840                 (y < (int) (windows->info.y+windows->info.height)))
4841               (void) XWithdrawWindow(display,windows->info.id,
4842                 windows->info.screen);
4843           }
4844         else
4845           if ((x > (int) (windows->info.x+windows->info.width)) ||
4846               (y > (int) (windows->info.y+windows->info.height)))
4847             (void) XMapWindow(display,windows->info.id);
4848         crop_info.x=(ssize_t) windows->image.x+x;
4849         crop_info.y=(ssize_t) windows->image.y+y;
4850         break;
4851       }
4852       default:
4853         break;
4854     }
4855   } while ((state & ExitState) == 0);
4856   (void) XSelectInput(display,windows->image.id,
4857     windows->image.attributes.event_mask);
4858   if ((state & EscapeState) != 0)
4859     {
4860       /*
4861         User want to exit without cropping.
4862       */
4863       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4864       (void) XFreeCursor(display,cursor);
4865       return(MagickTrue);
4866     }
4867   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4868   do
4869   {
4870     /*
4871       Size rectangle as pointer moves until the mouse button is released.
4872     */
4873     x=(int) crop_info.x;
4874     y=(int) crop_info.y;
4875     crop_info.width=0;
4876     crop_info.height=0;
4877     state=DefaultState;
4878     do
4879     {
4880       highlight_info=crop_info;
4881       highlight_info.x=crop_info.x-windows->image.x;
4882       highlight_info.y=crop_info.y-windows->image.y;
4883       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4884         {
4885           /*
4886             Display info and draw cropping rectangle.
4887           */
4888           if (windows->info.mapped == MagickFalse)
4889             (void) XMapWindow(display,windows->info.id);
4890           (void) FormatLocaleString(text,MaxTextExtent,
4891             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4892             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4893           XInfoWidget(display,windows,text);
4894           XHighlightRectangle(display,windows->image.id,
4895             windows->image.highlight_context,&highlight_info);
4896         }
4897       else
4898         if (windows->info.mapped != MagickFalse)
4899           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4900       /*
4901         Wait for next event.
4902       */
4903       XScreenEvent(display,windows,&event);
4904       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4905         XHighlightRectangle(display,windows->image.id,
4906           windows->image.highlight_context,&highlight_info);
4907       switch (event.type)
4908       {
4909         case ButtonPress:
4910         {
4911           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4912           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4913           break;
4914         }
4915         case ButtonRelease:
4916         {
4917           /*
4918             User has committed to cropping rectangle.
4919           */
4920           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4921           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4922           XSetCursorState(display,windows,MagickFalse);
4923           state|=ExitState;
4924           windows->command.data=0;
4925           (void) XCommandWidget(display,windows,RectifyModeMenu,
4926             (XEvent *) NULL);
4927           break;
4928         }
4929         case Expose:
4930           break;
4931         case MotionNotify:
4932         {
4933           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4934           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4935         }
4936         default:
4937           break;
4938       }
4939       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4940           ((state & ExitState) != 0))
4941         {
4942           /*
4943             Check boundary conditions.
4944           */
4945           if (crop_info.x < 0)
4946             crop_info.x=0;
4947           else
4948             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4949               crop_info.x=(ssize_t) windows->image.ximage->width;
4950           if ((int) crop_info.x < x)
4951             crop_info.width=(unsigned int) (x-crop_info.x);
4952           else
4953             {
4954               crop_info.width=(unsigned int) (crop_info.x-x);
4955               crop_info.x=(ssize_t) x;
4956             }
4957           if (crop_info.y < 0)
4958             crop_info.y=0;
4959           else
4960             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4961               crop_info.y=(ssize_t) windows->image.ximage->height;
4962           if ((int) crop_info.y < y)
4963             crop_info.height=(unsigned int) (y-crop_info.y);
4964           else
4965             {
4966               crop_info.height=(unsigned int) (crop_info.y-y);
4967               crop_info.y=(ssize_t) y;
4968             }
4969         }
4970     } while ((state & ExitState) == 0);
4971     /*
4972       Wait for user to grab a corner of the rectangle or press return.
4973     */
4974     state=DefaultState;
4975     (void) XMapWindow(display,windows->info.id);
4976     do
4977     {
4978       if (windows->info.mapped != MagickFalse)
4979         {
4980           /*
4981             Display pointer position.
4982           */
4983           (void) FormatLocaleString(text,MaxTextExtent,
4984             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4985             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4986           XInfoWidget(display,windows,text);
4987         }
4988       highlight_info=crop_info;
4989       highlight_info.x=crop_info.x-windows->image.x;
4990       highlight_info.y=crop_info.y-windows->image.y;
4991       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4992         {
4993           state|=EscapeState;
4994           state|=ExitState;
4995           break;
4996         }
4997       XHighlightRectangle(display,windows->image.id,
4998         windows->image.highlight_context,&highlight_info);
4999       XScreenEvent(display,windows,&event);
5000       if (event.xany.window == windows->command.id)
5001         {
5002           /*
5003             Select a command from the Command widget.
5004           */
5005           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5006           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5007           (void) XSetFunction(display,windows->image.highlight_context,
5008             GXinvert);
5009           XHighlightRectangle(display,windows->image.id,
5010             windows->image.highlight_context,&highlight_info);
5011           if (id >= 0)
5012             switch (RectifyCommands[id])
5013             {
5014               case RectifyCopyCommand:
5015               {
5016                 state|=ExitState;
5017                 break;
5018               }
5019               case RectifyHelpCommand:
5020               {
5021                 (void) XSetFunction(display,windows->image.highlight_context,
5022                   GXcopy);
5023                 switch (mode)
5024                 {
5025                   case CopyMode:
5026                   {
5027                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5028                       "Help Viewer - Image Copy",ImageCopyHelp);
5029                     break;
5030                   }
5031                   case CropMode:
5032                   {
5033                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5034                       "Help Viewer - Image Crop",ImageCropHelp);
5035                     break;
5036                   }
5037                   case CutMode:
5038                   {
5039                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5040                       "Help Viewer - Image Cut",ImageCutHelp);
5041                     break;
5042                   }
5043                 }
5044                 (void) XSetFunction(display,windows->image.highlight_context,
5045                   GXinvert);
5046                 break;
5047               }
5048               case RectifyDismissCommand:
5049               {
5050                 /*
5051                   Prematurely exit.
5052                 */
5053                 state|=EscapeState;
5054                 state|=ExitState;
5055                 break;
5056               }
5057               default:
5058                 break;
5059             }
5060           continue;
5061         }
5062       XHighlightRectangle(display,windows->image.id,
5063         windows->image.highlight_context,&highlight_info);
5064       switch (event.type)
5065       {
5066         case ButtonPress:
5067         {
5068           if (event.xbutton.button != Button1)
5069             break;
5070           if (event.xbutton.window != windows->image.id)
5071             break;
5072           x=windows->image.x+event.xbutton.x;
5073           y=windows->image.y+event.xbutton.y;
5074           if ((x < (int) (crop_info.x+RoiDelta)) &&
5075               (x > (int) (crop_info.x-RoiDelta)) &&
5076               (y < (int) (crop_info.y+RoiDelta)) &&
5077               (y > (int) (crop_info.y-RoiDelta)))
5078             {
5079               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5080               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5081               state|=UpdateConfigurationState;
5082               break;
5083             }
5084           if ((x < (int) (crop_info.x+RoiDelta)) &&
5085               (x > (int) (crop_info.x-RoiDelta)) &&
5086               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5087               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5088             {
5089               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5090               state|=UpdateConfigurationState;
5091               break;
5092             }
5093           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5094               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5095               (y < (int) (crop_info.y+RoiDelta)) &&
5096               (y > (int) (crop_info.y-RoiDelta)))
5097             {
5098               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5099               state|=UpdateConfigurationState;
5100               break;
5101             }
5102           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5103               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5104               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5105               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5106             {
5107               state|=UpdateConfigurationState;
5108               break;
5109             }
5110         }
5111         case ButtonRelease:
5112         {
5113           if (event.xbutton.window == windows->pan.id)
5114             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5115                 (highlight_info.y != crop_info.y-windows->image.y))
5116               XHighlightRectangle(display,windows->image.id,
5117                 windows->image.highlight_context,&highlight_info);
5118           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5119             event.xbutton.time);
5120           break;
5121         }
5122         case Expose:
5123         {
5124           if (event.xexpose.window == windows->image.id)
5125             if (event.xexpose.count == 0)
5126               {
5127                 event.xexpose.x=(int) highlight_info.x;
5128                 event.xexpose.y=(int) highlight_info.y;
5129                 event.xexpose.width=(int) highlight_info.width;
5130                 event.xexpose.height=(int) highlight_info.height;
5131                 XRefreshWindow(display,&windows->image,&event);
5132               }
5133           if (event.xexpose.window == windows->info.id)
5134             if (event.xexpose.count == 0)
5135               XInfoWidget(display,windows,text);
5136           break;
5137         }
5138         case KeyPress:
5139         {
5140           if (event.xkey.window != windows->image.id)
5141             break;
5142           /*
5143             Respond to a user key press.
5144           */
5145           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5146             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5147           switch ((int) key_symbol)
5148           {
5149             case XK_Escape:
5150             case XK_F20:
5151               state|=EscapeState;
5152             case XK_Return:
5153             {
5154               state|=ExitState;
5155               break;
5156             }
5157             case XK_Home:
5158             case XK_KP_Home:
5159             {
5160               crop_info.x=(ssize_t) (windows->image.width/2L-
5161                 crop_info.width/2L);
5162               crop_info.y=(ssize_t) (windows->image.height/2L-
5163                 crop_info.height/2L);
5164               break;
5165             }
5166             case XK_Left:
5167             case XK_KP_Left:
5168             {
5169               crop_info.x--;
5170               break;
5171             }
5172             case XK_Up:
5173             case XK_KP_Up:
5174             case XK_Next:
5175             {
5176               crop_info.y--;
5177               break;
5178             }
5179             case XK_Right:
5180             case XK_KP_Right:
5181             {
5182               crop_info.x++;
5183               break;
5184             }
5185             case XK_Prior:
5186             case XK_Down:
5187             case XK_KP_Down:
5188             {
5189               crop_info.y++;
5190               break;
5191             }
5192             case XK_F1:
5193             case XK_Help:
5194             {
5195               (void) XSetFunction(display,windows->image.highlight_context,
5196                 GXcopy);
5197               switch (mode)
5198               {
5199                 case CopyMode:
5200                 {
5201                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5202                     "Help Viewer - Image Copy",ImageCopyHelp);
5203                   break;
5204                 }
5205                 case CropMode:
5206                 {
5207                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5208                     "Help Viewer - Image Cropg",ImageCropHelp);
5209                   break;
5210                 }
5211                 case CutMode:
5212                 {
5213                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5214                     "Help Viewer - Image Cutg",ImageCutHelp);
5215                   break;
5216                 }
5217               }
5218               (void) XSetFunction(display,windows->image.highlight_context,
5219                 GXinvert);
5220               break;
5221             }
5222             default:
5223             {
5224               (void) XBell(display,0);
5225               break;
5226             }
5227           }
5228           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5229             event.xkey.time);
5230           break;
5231         }
5232         case KeyRelease:
5233           break;
5234         case MotionNotify:
5235         {
5236           if (event.xmotion.window != windows->image.id)
5237             break;
5238           /*
5239             Map and unmap Info widget as text cursor crosses its boundaries.
5240           */
5241           x=event.xmotion.x;
5242           y=event.xmotion.y;
5243           if (windows->info.mapped != MagickFalse)
5244             {
5245               if ((x < (int) (windows->info.x+windows->info.width)) &&
5246                   (y < (int) (windows->info.y+windows->info.height)))
5247                 (void) XWithdrawWindow(display,windows->info.id,
5248                   windows->info.screen);
5249             }
5250           else
5251             if ((x > (int) (windows->info.x+windows->info.width)) ||
5252                 (y > (int) (windows->info.y+windows->info.height)))
5253               (void) XMapWindow(display,windows->info.id);
5254           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5255           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5256           break;
5257         }
5258         case SelectionRequest:
5259         {
5260           XSelectionEvent
5261             notify;
5262
5263           XSelectionRequestEvent
5264             *request;
5265
5266           /*
5267             Set primary selection.
5268           */
5269           (void) FormatLocaleString(text,MaxTextExtent,
5270             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5271             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5272           request=(&(event.xselectionrequest));
5273           (void) XChangeProperty(request->display,request->requestor,
5274             request->property,request->target,8,PropModeReplace,
5275             (unsigned char *) text,(int) strlen(text));
5276           notify.type=SelectionNotify;
5277           notify.display=request->display;
5278           notify.requestor=request->requestor;
5279           notify.selection=request->selection;
5280           notify.target=request->target;
5281           notify.time=request->time;
5282           if (request->property == None)
5283             notify.property=request->target;
5284           else
5285             notify.property=request->property;
5286           (void) XSendEvent(request->display,request->requestor,False,0,
5287             (XEvent *) &notify);
5288         }
5289         default:
5290           break;
5291       }
5292       if ((state & UpdateConfigurationState) != 0)
5293         {
5294           (void) XPutBackEvent(display,&event);
5295           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5296           break;
5297         }
5298     } while ((state & ExitState) == 0);
5299   } while ((state & ExitState) == 0);
5300   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5301   XSetCursorState(display,windows,MagickFalse);
5302   if ((state & EscapeState) != 0)
5303     return(MagickTrue);
5304   if (mode == CropMode)
5305     if (((int) crop_info.width != windows->image.ximage->width) ||
5306         ((int) crop_info.height != windows->image.ximage->height))
5307       {
5308         /*
5309           Reconfigure Image window as defined by cropping rectangle.
5310         */
5311         XSetCropGeometry(display,windows,&crop_info,image);
5312         windows->image.window_changes.width=(int) crop_info.width;
5313         windows->image.window_changes.height=(int) crop_info.height;
5314         (void) XConfigureImage(display,resource_info,windows,image,exception);
5315         return(MagickTrue);
5316       }
5317   /*
5318     Copy image before applying image transforms.
5319   */
5320   XSetCursorState(display,windows,MagickTrue);
5321   XCheckRefreshWindows(display,windows);
5322   width=(unsigned int) image->columns;
5323   height=(unsigned int) image->rows;
5324   x=0;
5325   y=0;
5326   if (windows->image.crop_geometry != (char *) NULL)
5327     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5328   scale_factor=(MagickRealType) width/windows->image.ximage->width;
5329   crop_info.x+=x;
5330   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5331   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5332   scale_factor=(MagickRealType) height/windows->image.ximage->height;
5333   crop_info.y+=y;
5334   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5335   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5336   crop_image=CropImage(image,&crop_info,exception);
5337   XSetCursorState(display,windows,MagickFalse);
5338   if (crop_image == (Image *) NULL)
5339     return(MagickFalse);
5340   if (resource_info->copy_image != (Image *) NULL)
5341     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5342   resource_info->copy_image=crop_image;
5343   if (mode == CopyMode)
5344     {
5345       (void) XConfigureImage(display,resource_info,windows,image,exception);
5346       return(MagickTrue);
5347     }
5348   /*
5349     Cut image.
5350   */
5351   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5352     return(MagickFalse);
5353   image->matte=MagickTrue;
5354   image_view=AcquireCacheView(image);
5355   for (y=0; y < (int) crop_info.height; y++)
5356   {
5357     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5358       crop_info.width,1,exception);
5359     if (q == (Quantum *) NULL)
5360       break;
5361     for (x=0; x < (int) crop_info.width; x++)
5362     {
5363       SetPixelAlpha(image,TransparentAlpha,q);
5364       q+=GetPixelChannels(image);
5365     }
5366     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5367       break;
5368   }
5369   image_view=DestroyCacheView(image_view);
5370   /*
5371     Update image configuration.
5372   */
5373   XConfigureImageColormap(display,resource_info,windows,image);
5374   (void) XConfigureImage(display,resource_info,windows,image,exception);
5375   return(MagickTrue);
5376 }
5377 \f
5378 /*
5379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5380 %                                                                             %
5381 %                                                                             %
5382 %                                                                             %
5383 +   X D r a w I m a g e                                                       %
5384 %                                                                             %
5385 %                                                                             %
5386 %                                                                             %
5387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5388 %
5389 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5390 %  the image.
5391 %
5392 %  The format of the XDrawEditImage method is:
5393 %
5394 %      MagickBooleanType XDrawEditImage(Display *display,
5395 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5396 %        ExceptionInfo *exception)
5397 %
5398 %  A description of each parameter follows:
5399 %
5400 %    o display: Specifies a connection to an X server; returned from
5401 %      XOpenDisplay.
5402 %
5403 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5404 %
5405 %    o windows: Specifies a pointer to a XWindows structure.
5406 %
5407 %    o image: the image.
5408 %
5409 %    o exception: return any errors or warnings in this structure.
5410 %
5411 */
5412 static MagickBooleanType XDrawEditImage(Display *display,
5413   XResourceInfo *resource_info,XWindows *windows,Image **image,
5414   ExceptionInfo *exception)
5415 {
5416   static const char
5417     *DrawMenu[] =
5418     {
5419       "Element",
5420       "Color",
5421       "Stipple",
5422       "Width",
5423       "Undo",
5424       "Help",
5425       "Dismiss",
5426       (char *) NULL
5427     };
5428
5429   static ElementType
5430     element = PointElement;
5431
5432   static const ModeType
5433     DrawCommands[] =
5434     {
5435       DrawElementCommand,
5436       DrawColorCommand,
5437       DrawStippleCommand,
5438       DrawWidthCommand,
5439       DrawUndoCommand,
5440       DrawHelpCommand,
5441       DrawDismissCommand
5442     };
5443
5444   static Pixmap
5445     stipple = (Pixmap) NULL;
5446
5447   static unsigned int
5448     pen_id = 0,
5449     line_width = 1;
5450
5451   char
5452     command[MaxTextExtent],
5453     text[MaxTextExtent];
5454
5455   Cursor
5456     cursor;
5457
5458   int
5459     entry,
5460     id,
5461     number_coordinates,
5462     x,
5463     y;
5464
5465   MagickRealType
5466     degrees;
5467
5468   MagickStatusType
5469     status;
5470
5471   RectangleInfo
5472     rectangle_info;
5473
5474   register int
5475     i;
5476
5477   unsigned int
5478     distance,
5479     height,
5480     max_coordinates,
5481     width;
5482
5483   size_t
5484     state;
5485
5486   Window
5487     root_window;
5488
5489   XDrawInfo
5490     draw_info;
5491
5492   XEvent
5493     event;
5494
5495   XPoint
5496     *coordinate_info;
5497
5498   XSegment
5499     line_info;
5500
5501   /*
5502     Allocate polygon info.
5503   */
5504   max_coordinates=2048;
5505   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5506     sizeof(*coordinate_info));
5507   if (coordinate_info == (XPoint *) NULL)
5508     {
5509       (void) ThrowMagickException(exception,GetMagickModule(),
5510         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5511       return(MagickFalse);
5512     }
5513   /*
5514     Map Command widget.
5515   */
5516   (void) CloneString(&windows->command.name,"Draw");
5517   windows->command.data=4;
5518   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5519   (void) XMapRaised(display,windows->command.id);
5520   XClientMessage(display,windows->image.id,windows->im_protocols,
5521     windows->im_update_widget,CurrentTime);
5522   /*
5523     Wait for first button press.
5524   */
5525   root_window=XRootWindow(display,XDefaultScreen(display));
5526   draw_info.stencil=OpaqueStencil;
5527   status=MagickTrue;
5528   cursor=XCreateFontCursor(display,XC_tcross);
5529   for ( ; ; )
5530   {
5531     XQueryPosition(display,windows->image.id,&x,&y);
5532     (void) XSelectInput(display,windows->image.id,
5533       windows->image.attributes.event_mask | PointerMotionMask);
5534     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5535     state=DefaultState;
5536     do
5537     {
5538       if (windows->info.mapped != MagickFalse)
5539         {
5540           /*
5541             Display pointer position.
5542           */
5543           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5544             x+windows->image.x,y+windows->image.y);
5545           XInfoWidget(display,windows,text);
5546         }
5547       /*
5548         Wait for next event.
5549       */
5550       XScreenEvent(display,windows,&event);
5551       if (event.xany.window == windows->command.id)
5552         {
5553           /*
5554             Select a command from the Command widget.
5555           */
5556           id=XCommandWidget(display,windows,DrawMenu,&event);
5557           if (id < 0)
5558             continue;
5559           switch (DrawCommands[id])
5560           {
5561             case DrawElementCommand:
5562             {
5563               static const char
5564                 *Elements[] =
5565                 {
5566                   "point",
5567                   "line",
5568                   "rectangle",
5569                   "fill rectangle",
5570                   "circle",
5571                   "fill circle",
5572                   "ellipse",
5573                   "fill ellipse",
5574                   "polygon",
5575                   "fill polygon",
5576                   (char *) NULL,
5577                 };
5578
5579               /*
5580                 Select a command from the pop-up menu.
5581               */
5582               element=(ElementType) (XMenuWidget(display,windows,
5583                 DrawMenu[id],Elements,command)+1);
5584               break;
5585             }
5586             case DrawColorCommand:
5587             {
5588               const char
5589                 *ColorMenu[MaxNumberPens+1];
5590
5591               int
5592                 pen_number;
5593
5594               MagickBooleanType
5595                 transparent;
5596
5597               XColor
5598                 color;
5599
5600               /*
5601                 Initialize menu selections.
5602               */
5603               for (i=0; i < (int) (MaxNumberPens-2); i++)
5604                 ColorMenu[i]=resource_info->pen_colors[i];
5605               ColorMenu[MaxNumberPens-2]="transparent";
5606               ColorMenu[MaxNumberPens-1]="Browser...";
5607               ColorMenu[MaxNumberPens]=(char *) NULL;
5608               /*
5609                 Select a pen color from the pop-up menu.
5610               */
5611               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5612                 (const char **) ColorMenu,command);
5613               if (pen_number < 0)
5614                 break;
5615               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5616                 MagickFalse;
5617               if (transparent != MagickFalse)
5618                 {
5619                   draw_info.stencil=TransparentStencil;
5620                   break;
5621                 }
5622               if (pen_number == (MaxNumberPens-1))
5623                 {
5624                   static char
5625                     color_name[MaxTextExtent] = "gray";
5626
5627                   /*
5628                     Select a pen color from a dialog.
5629                   */
5630                   resource_info->pen_colors[pen_number]=color_name;
5631                   XColorBrowserWidget(display,windows,"Select",color_name);
5632                   if (*color_name == '\0')
5633                     break;
5634                 }
5635               /*
5636                 Set pen color.
5637               */
5638               (void) XParseColor(display,windows->map_info->colormap,
5639                 resource_info->pen_colors[pen_number],&color);
5640               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5641                 (unsigned int) MaxColors,&color);
5642               windows->pixel_info->pen_colors[pen_number]=color;
5643               pen_id=(unsigned int) pen_number;
5644               draw_info.stencil=OpaqueStencil;
5645               break;
5646             }
5647             case DrawStippleCommand:
5648             {
5649               Image
5650                 *stipple_image;
5651
5652               ImageInfo
5653                 *image_info;
5654
5655               int
5656                 status;
5657
5658               static char
5659                 filename[MaxTextExtent] = "\0";
5660
5661               static const char
5662                 *StipplesMenu[] =
5663                 {
5664                   "Brick",
5665                   "Diagonal",
5666                   "Scales",
5667                   "Vertical",
5668                   "Wavy",
5669                   "Translucent",
5670                   "Opaque",
5671                   (char *) NULL,
5672                   (char *) NULL,
5673                 };
5674
5675               /*
5676                 Select a command from the pop-up menu.
5677               */
5678               StipplesMenu[7]="Open...";
5679               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5680                 command);
5681               if (entry < 0)
5682                 break;
5683               if (stipple != (Pixmap) NULL)
5684                 (void) XFreePixmap(display,stipple);
5685               stipple=(Pixmap) NULL;
5686               if (entry != 7)
5687                 {
5688                   switch (entry)
5689                   {
5690                     case 0:
5691                     {
5692                       stipple=XCreateBitmapFromData(display,root_window,
5693                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5694                       break;
5695                     }
5696                     case 1:
5697                     {
5698                       stipple=XCreateBitmapFromData(display,root_window,
5699                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5700                       break;
5701                     }
5702                     case 2:
5703                     {
5704                       stipple=XCreateBitmapFromData(display,root_window,
5705                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5706                       break;
5707                     }
5708                     case 3:
5709                     {
5710                       stipple=XCreateBitmapFromData(display,root_window,
5711                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5712                       break;
5713                     }
5714                     case 4:
5715                     {
5716                       stipple=XCreateBitmapFromData(display,root_window,
5717                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5718                       break;
5719                     }
5720                     case 5:
5721                     {
5722                       stipple=XCreateBitmapFromData(display,root_window,
5723                         (char *) HighlightBitmap,HighlightWidth,
5724                         HighlightHeight);
5725                       break;
5726                     }
5727                     case 6:
5728                     default:
5729                     {
5730                       stipple=XCreateBitmapFromData(display,root_window,
5731                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5732                       break;
5733                     }
5734                   }
5735                   break;
5736                 }
5737               XFileBrowserWidget(display,windows,"Stipple",filename);
5738               if (*filename == '\0')
5739                 break;
5740               /*
5741                 Read image.
5742               */
5743               XSetCursorState(display,windows,MagickTrue);
5744               XCheckRefreshWindows(display,windows);
5745               image_info=AcquireImageInfo();
5746               (void) CopyMagickString(image_info->filename,filename,
5747                 MaxTextExtent);
5748               stipple_image=ReadImage(image_info,exception);
5749               CatchException(exception);
5750               XSetCursorState(display,windows,MagickFalse);
5751               if (stipple_image == (Image *) NULL)
5752                 break;
5753               (void) AcquireUniqueFileResource(filename);
5754               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5755                 "xbm:%s",filename);
5756               (void) WriteImage(image_info,stipple_image,exception);
5757               stipple_image=DestroyImage(stipple_image);
5758               image_info=DestroyImageInfo(image_info);
5759               status=XReadBitmapFile(display,root_window,filename,&width,
5760                 &height,&stipple,&x,&y);
5761               (void) RelinquishUniqueFileResource(filename);
5762               if ((status != BitmapSuccess) != 0)
5763                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5764                   filename);
5765               break;
5766             }
5767             case DrawWidthCommand:
5768             {
5769               static char
5770                 width[MaxTextExtent] = "0";
5771
5772               static const char
5773                 *WidthsMenu[] =
5774                 {
5775                   "1",
5776                   "2",
5777                   "4",
5778                   "8",
5779                   "16",
5780                   "Dialog...",
5781                   (char *) NULL,
5782                 };
5783
5784               /*
5785                 Select a command from the pop-up menu.
5786               */
5787               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5788                 command);
5789               if (entry < 0)
5790                 break;
5791               if (entry != 5)
5792                 {
5793                   line_width=(unsigned int) StringToUnsignedLong(
5794                     WidthsMenu[entry]);
5795                   break;
5796                 }
5797               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5798                 width);
5799               if (*width == '\0')
5800                 break;
5801               line_width=(unsigned int) StringToUnsignedLong(width);
5802               break;
5803             }
5804             case DrawUndoCommand:
5805             {
5806               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5807                 image,exception);
5808               break;
5809             }
5810             case DrawHelpCommand:
5811             {
5812               XTextViewWidget(display,resource_info,windows,MagickFalse,
5813                 "Help Viewer - Image Rotation",ImageDrawHelp);
5814               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5815               break;
5816             }
5817             case DrawDismissCommand:
5818             {
5819               /*
5820                 Prematurely exit.
5821               */
5822               state|=EscapeState;
5823               state|=ExitState;
5824               break;
5825             }
5826             default:
5827               break;
5828           }
5829           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5830           continue;
5831         }
5832       switch (event.type)
5833       {
5834         case ButtonPress:
5835         {
5836           if (event.xbutton.button != Button1)
5837             break;
5838           if (event.xbutton.window != windows->image.id)
5839             break;
5840           /*
5841             exit loop.
5842           */
5843           x=event.xbutton.x;
5844           y=event.xbutton.y;
5845           state|=ExitState;
5846           break;
5847         }
5848         case ButtonRelease:
5849           break;
5850         case Expose:
5851           break;
5852         case KeyPress:
5853         {
5854           KeySym
5855             key_symbol;
5856
5857           if (event.xkey.window != windows->image.id)
5858             break;
5859           /*
5860             Respond to a user key press.
5861           */
5862           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5863             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5864           switch ((int) key_symbol)
5865           {
5866             case XK_Escape:
5867             case XK_F20:
5868             {
5869               /*
5870                 Prematurely exit.
5871               */
5872               state|=EscapeState;
5873               state|=ExitState;
5874               break;
5875             }
5876             case XK_F1:
5877             case XK_Help:
5878             {
5879               XTextViewWidget(display,resource_info,windows,MagickFalse,
5880                 "Help Viewer - Image Rotation",ImageDrawHelp);
5881               break;
5882             }
5883             default:
5884             {
5885               (void) XBell(display,0);
5886               break;
5887             }
5888           }
5889           break;
5890         }
5891         case MotionNotify:
5892         {
5893           /*
5894             Map and unmap Info widget as text cursor crosses its boundaries.
5895           */
5896           x=event.xmotion.x;
5897           y=event.xmotion.y;
5898           if (windows->info.mapped != MagickFalse)
5899             {
5900               if ((x < (int) (windows->info.x+windows->info.width)) &&
5901                   (y < (int) (windows->info.y+windows->info.height)))
5902                 (void) XWithdrawWindow(display,windows->info.id,
5903                   windows->info.screen);
5904             }
5905           else
5906             if ((x > (int) (windows->info.x+windows->info.width)) ||
5907                 (y > (int) (windows->info.y+windows->info.height)))
5908               (void) XMapWindow(display,windows->info.id);
5909           break;
5910         }
5911       }
5912     } while ((state & ExitState) == 0);
5913     (void) XSelectInput(display,windows->image.id,
5914       windows->image.attributes.event_mask);
5915     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5916     if ((state & EscapeState) != 0)
5917       break;
5918     /*
5919       Draw element as pointer moves until the button is released.
5920     */
5921     distance=0;
5922     degrees=0.0;
5923     line_info.x1=x;
5924     line_info.y1=y;
5925     line_info.x2=x;
5926     line_info.y2=y;
5927     rectangle_info.x=(ssize_t) x;
5928     rectangle_info.y=(ssize_t) y;
5929     rectangle_info.width=0;
5930     rectangle_info.height=0;
5931     number_coordinates=1;
5932     coordinate_info->x=x;
5933     coordinate_info->y=y;
5934     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5935     state=DefaultState;
5936     do
5937     {
5938       switch (element)
5939       {
5940         case PointElement:
5941         default:
5942         {
5943           if (number_coordinates > 1)
5944             {
5945               (void) XDrawLines(display,windows->image.id,
5946                 windows->image.highlight_context,coordinate_info,
5947                 number_coordinates,CoordModeOrigin);
5948               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5949                 coordinate_info[number_coordinates-1].x,
5950                 coordinate_info[number_coordinates-1].y);
5951               XInfoWidget(display,windows,text);
5952             }
5953           break;
5954         }
5955         case LineElement:
5956         {
5957           if (distance > 9)
5958             {
5959               /*
5960                 Display angle of the line.
5961               */
5962               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5963                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5964               (void) FormatLocaleString(text,MaxTextExtent," %g",
5965                 (double) degrees);
5966               XInfoWidget(display,windows,text);
5967               XHighlightLine(display,windows->image.id,
5968                 windows->image.highlight_context,&line_info);
5969             }
5970           else
5971             if (windows->info.mapped != MagickFalse)
5972               (void) XWithdrawWindow(display,windows->info.id,
5973                 windows->info.screen);
5974           break;
5975         }
5976         case RectangleElement:
5977         case FillRectangleElement:
5978         {
5979           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5980             {
5981               /*
5982                 Display info and draw drawing rectangle.
5983               */
5984               (void) FormatLocaleString(text,MaxTextExtent,
5985                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5986                 (double) rectangle_info.height,(double) rectangle_info.x,
5987                 (double) rectangle_info.y);
5988               XInfoWidget(display,windows,text);
5989               XHighlightRectangle(display,windows->image.id,
5990                 windows->image.highlight_context,&rectangle_info);
5991             }
5992           else
5993             if (windows->info.mapped != MagickFalse)
5994               (void) XWithdrawWindow(display,windows->info.id,
5995                 windows->info.screen);
5996           break;
5997         }
5998         case CircleElement:
5999         case FillCircleElement:
6000         case EllipseElement:
6001         case FillEllipseElement:
6002         {
6003           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6004             {
6005               /*
6006                 Display info and draw drawing rectangle.
6007               */
6008               (void) FormatLocaleString(text,MaxTextExtent,
6009                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6010                 (double) rectangle_info.height,(double) rectangle_info.x,
6011                 (double) rectangle_info.y);
6012               XInfoWidget(display,windows,text);
6013               XHighlightEllipse(display,windows->image.id,
6014                 windows->image.highlight_context,&rectangle_info);
6015             }
6016           else
6017             if (windows->info.mapped != MagickFalse)
6018               (void) XWithdrawWindow(display,windows->info.id,
6019                 windows->info.screen);
6020           break;
6021         }
6022         case PolygonElement:
6023         case FillPolygonElement:
6024         {
6025           if (number_coordinates > 1)
6026             (void) XDrawLines(display,windows->image.id,
6027               windows->image.highlight_context,coordinate_info,
6028               number_coordinates,CoordModeOrigin);
6029           if (distance > 9)
6030             {
6031               /*
6032                 Display angle of the line.
6033               */
6034               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6035                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6036               (void) FormatLocaleString(text,MaxTextExtent," %g",
6037                 (double) degrees);
6038               XInfoWidget(display,windows,text);
6039               XHighlightLine(display,windows->image.id,
6040                 windows->image.highlight_context,&line_info);
6041             }
6042           else
6043             if (windows->info.mapped != MagickFalse)
6044               (void) XWithdrawWindow(display,windows->info.id,
6045                 windows->info.screen);
6046           break;
6047         }
6048       }
6049       /*
6050         Wait for next event.
6051       */
6052       XScreenEvent(display,windows,&event);
6053       switch (element)
6054       {
6055         case PointElement:
6056         default:
6057         {
6058           if (number_coordinates > 1)
6059             (void) XDrawLines(display,windows->image.id,
6060               windows->image.highlight_context,coordinate_info,
6061               number_coordinates,CoordModeOrigin);
6062           break;
6063         }
6064         case LineElement:
6065         {
6066           if (distance > 9)
6067             XHighlightLine(display,windows->image.id,
6068               windows->image.highlight_context,&line_info);
6069           break;
6070         }
6071         case RectangleElement:
6072         case FillRectangleElement:
6073         {
6074           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6075             XHighlightRectangle(display,windows->image.id,
6076               windows->image.highlight_context,&rectangle_info);
6077           break;
6078         }
6079         case CircleElement:
6080         case FillCircleElement:
6081         case EllipseElement:
6082         case FillEllipseElement:
6083         {
6084           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6085             XHighlightEllipse(display,windows->image.id,
6086               windows->image.highlight_context,&rectangle_info);
6087           break;
6088         }
6089         case PolygonElement:
6090         case FillPolygonElement:
6091         {
6092           if (number_coordinates > 1)
6093             (void) XDrawLines(display,windows->image.id,
6094               windows->image.highlight_context,coordinate_info,
6095               number_coordinates,CoordModeOrigin);
6096           if (distance > 9)
6097             XHighlightLine(display,windows->image.id,
6098               windows->image.highlight_context,&line_info);
6099           break;
6100         }
6101       }
6102       switch (event.type)
6103       {
6104         case ButtonPress:
6105           break;
6106         case ButtonRelease:
6107         {
6108           /*
6109             User has committed to element.
6110           */
6111           line_info.x2=event.xbutton.x;
6112           line_info.y2=event.xbutton.y;
6113           rectangle_info.x=(ssize_t) event.xbutton.x;
6114           rectangle_info.y=(ssize_t) event.xbutton.y;
6115           coordinate_info[number_coordinates].x=event.xbutton.x;
6116           coordinate_info[number_coordinates].y=event.xbutton.y;
6117           if (((element != PolygonElement) &&
6118                (element != FillPolygonElement)) || (distance <= 9))
6119             {
6120               state|=ExitState;
6121               break;
6122             }
6123           number_coordinates++;
6124           if (number_coordinates < (int) max_coordinates)
6125             {
6126               line_info.x1=event.xbutton.x;
6127               line_info.y1=event.xbutton.y;
6128               break;
6129             }
6130           max_coordinates<<=1;
6131           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6132             max_coordinates,sizeof(*coordinate_info));
6133           if (coordinate_info == (XPoint *) NULL)
6134             (void) ThrowMagickException(exception,GetMagickModule(),
6135               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6136           break;
6137         }
6138         case Expose:
6139           break;
6140         case MotionNotify:
6141         {
6142           if (event.xmotion.window != windows->image.id)
6143             break;
6144           if (element != PointElement)
6145             {
6146               line_info.x2=event.xmotion.x;
6147               line_info.y2=event.xmotion.y;
6148               rectangle_info.x=(ssize_t) event.xmotion.x;
6149               rectangle_info.y=(ssize_t) event.xmotion.y;
6150               break;
6151             }
6152           coordinate_info[number_coordinates].x=event.xbutton.x;
6153           coordinate_info[number_coordinates].y=event.xbutton.y;
6154           number_coordinates++;
6155           if (number_coordinates < (int) max_coordinates)
6156             break;
6157           max_coordinates<<=1;
6158           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6159             max_coordinates,sizeof(*coordinate_info));
6160           if (coordinate_info == (XPoint *) NULL)
6161             (void) ThrowMagickException(exception,GetMagickModule(),
6162               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6163           break;
6164         }
6165         default:
6166           break;
6167       }
6168       /*
6169         Check boundary conditions.
6170       */
6171       if (line_info.x2 < 0)
6172         line_info.x2=0;
6173       else
6174         if (line_info.x2 > (int) windows->image.width)
6175           line_info.x2=(short) windows->image.width;
6176       if (line_info.y2 < 0)
6177         line_info.y2=0;
6178       else
6179         if (line_info.y2 > (int) windows->image.height)
6180           line_info.y2=(short) windows->image.height;
6181       distance=(unsigned int)
6182         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6183          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6184       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6185           ((state & ExitState) != 0))
6186         {
6187           if (rectangle_info.x < 0)
6188             rectangle_info.x=0;
6189           else
6190             if (rectangle_info.x > (ssize_t) windows->image.width)
6191               rectangle_info.x=(ssize_t) windows->image.width;
6192           if ((int) rectangle_info.x < x)
6193             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6194           else
6195             {
6196               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6197               rectangle_info.x=(ssize_t) x;
6198             }
6199           if (rectangle_info.y < 0)
6200             rectangle_info.y=0;
6201           else
6202             if (rectangle_info.y > (ssize_t) windows->image.height)
6203               rectangle_info.y=(ssize_t) windows->image.height;
6204           if ((int) rectangle_info.y < y)
6205             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6206           else
6207             {
6208               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6209               rectangle_info.y=(ssize_t) y;
6210             }
6211         }
6212     } while ((state & ExitState) == 0);
6213     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6214     if ((element == PointElement) || (element == PolygonElement) ||
6215         (element == FillPolygonElement))
6216       {
6217         /*
6218           Determine polygon bounding box.
6219         */
6220         rectangle_info.x=(ssize_t) coordinate_info->x;
6221         rectangle_info.y=(ssize_t) coordinate_info->y;
6222         x=coordinate_info->x;
6223         y=coordinate_info->y;
6224         for (i=1; i < number_coordinates; i++)
6225         {
6226           if (coordinate_info[i].x > x)
6227             x=coordinate_info[i].x;
6228           if (coordinate_info[i].y > y)
6229             y=coordinate_info[i].y;
6230           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6231             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6232           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6233             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6234         }
6235         rectangle_info.width=(size_t) (x-rectangle_info.x);
6236         rectangle_info.height=(size_t) (y-rectangle_info.y);
6237         for (i=0; i < number_coordinates; i++)
6238         {
6239           coordinate_info[i].x-=rectangle_info.x;
6240           coordinate_info[i].y-=rectangle_info.y;
6241         }
6242       }
6243     else
6244       if (distance <= 9)
6245         continue;
6246       else
6247         if ((element == RectangleElement) ||
6248             (element == CircleElement) || (element == EllipseElement))
6249           {
6250             rectangle_info.width--;
6251             rectangle_info.height--;
6252           }
6253     /*
6254       Drawing is relative to image configuration.
6255     */
6256     draw_info.x=(int) rectangle_info.x;
6257     draw_info.y=(int) rectangle_info.y;
6258     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6259       image,exception);
6260     width=(unsigned int) (*image)->columns;
6261     height=(unsigned int) (*image)->rows;
6262     x=0;
6263     y=0;
6264     if (windows->image.crop_geometry != (char *) NULL)
6265       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6266     draw_info.x+=windows->image.x-(line_width/2);
6267     if (draw_info.x < 0)
6268       draw_info.x=0;
6269     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6270     draw_info.y+=windows->image.y-(line_width/2);
6271     if (draw_info.y < 0)
6272       draw_info.y=0;
6273     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6274     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6275     if (draw_info.width > (unsigned int) (*image)->columns)
6276       draw_info.width=(unsigned int) (*image)->columns;
6277     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6278     if (draw_info.height > (unsigned int) (*image)->rows)
6279       draw_info.height=(unsigned int) (*image)->rows;
6280     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6281       width*draw_info.width/windows->image.ximage->width,
6282       height*draw_info.height/windows->image.ximage->height,
6283       draw_info.x+x,draw_info.y+y);
6284     /*
6285       Initialize drawing attributes.
6286     */
6287     draw_info.degrees=0.0;
6288     draw_info.element=element;
6289     draw_info.stipple=stipple;
6290     draw_info.line_width=line_width;
6291     draw_info.line_info=line_info;
6292     if (line_info.x1 > (int) (line_width/2))
6293       draw_info.line_info.x1=(short) line_width/2;
6294     if (line_info.y1 > (int) (line_width/2))
6295       draw_info.line_info.y1=(short) line_width/2;
6296     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6297     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6298     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6299       {
6300         draw_info.line_info.x2=(-draw_info.line_info.x2);
6301         draw_info.line_info.y2=(-draw_info.line_info.y2);
6302       }
6303     if (draw_info.line_info.x2 < 0)
6304       {
6305         draw_info.line_info.x2=(-draw_info.line_info.x2);
6306         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6307       }
6308     if (draw_info.line_info.y2 < 0)
6309       {
6310         draw_info.line_info.y2=(-draw_info.line_info.y2);
6311         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6312       }
6313     draw_info.rectangle_info=rectangle_info;
6314     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6315       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6316     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6317       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6318     draw_info.number_coordinates=(unsigned int) number_coordinates;
6319     draw_info.coordinate_info=coordinate_info;
6320     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6321     /*
6322       Draw element on image.
6323     */
6324     XSetCursorState(display,windows,MagickTrue);
6325     XCheckRefreshWindows(display,windows);
6326     status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6327     XSetCursorState(display,windows,MagickFalse);
6328     /*
6329       Update image colormap and return to image drawing.
6330     */
6331     XConfigureImageColormap(display,resource_info,windows,*image);
6332     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6333   }
6334   XSetCursorState(display,windows,MagickFalse);
6335   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6336   return(status != 0 ? MagickTrue : MagickFalse);
6337 }
6338 \f
6339 /*
6340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6341 %                                                                             %
6342 %                                                                             %
6343 %                                                                             %
6344 +   X D r a w P a n R e c t a n g l e                                         %
6345 %                                                                             %
6346 %                                                                             %
6347 %                                                                             %
6348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6349 %
6350 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6351 %  displays a zoom image and the rectangle shows which portion of the image is
6352 %  displayed in the Image window.
6353 %
6354 %  The format of the XDrawPanRectangle method is:
6355 %
6356 %      XDrawPanRectangle(Display *display,XWindows *windows)
6357 %
6358 %  A description of each parameter follows:
6359 %
6360 %    o display: Specifies a connection to an X server;  returned from
6361 %      XOpenDisplay.
6362 %
6363 %    o windows: Specifies a pointer to a XWindows structure.
6364 %
6365 */
6366 static void XDrawPanRectangle(Display *display,XWindows *windows)
6367 {
6368   MagickRealType
6369     scale_factor;
6370
6371   RectangleInfo
6372     highlight_info;
6373
6374   /*
6375     Determine dimensions of the panning rectangle.
6376   */
6377   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6378   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6379   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6380   scale_factor=(MagickRealType)
6381     windows->pan.height/windows->image.ximage->height;
6382   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6383   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6384   /*
6385     Display the panning rectangle.
6386   */
6387   (void) XClearWindow(display,windows->pan.id);
6388   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6389     &highlight_info);
6390 }
6391 \f
6392 /*
6393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6394 %                                                                             %
6395 %                                                                             %
6396 %                                                                             %
6397 +   X I m a g e C a c h e                                                     %
6398 %                                                                             %
6399 %                                                                             %
6400 %                                                                             %
6401 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6402 %
6403 %  XImageCache() handles the creation, manipulation, and destruction of the
6404 %  image cache (undo and redo buffers).
6405 %
6406 %  The format of the XImageCache method is:
6407 %
6408 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6409 %        XWindows *windows,const CommandType command,Image **image,
6410 %        ExceptionInfo *exception)
6411 %
6412 %  A description of each parameter follows:
6413 %
6414 %    o display: Specifies a connection to an X server; returned from
6415 %      XOpenDisplay.
6416 %
6417 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6418 %
6419 %    o windows: Specifies a pointer to a XWindows structure.
6420 %
6421 %    o command: Specifies a command to perform.
6422 %
6423 %    o image: the image;  XImageCache may transform the image and return a new
6424 %      image pointer.
6425 %
6426 %    o exception: return any errors or warnings in this structure.
6427 %
6428 */
6429 static void XImageCache(Display *display,XResourceInfo *resource_info,
6430   XWindows *windows,const CommandType command,Image **image,
6431   ExceptionInfo *exception)
6432 {
6433   Image
6434     *cache_image;
6435
6436   static Image
6437     *redo_image = (Image *) NULL,
6438     *undo_image = (Image *) NULL;
6439
6440   switch (command)
6441   {
6442     case FreeBuffersCommand:
6443     {
6444       /*
6445         Free memory from the undo and redo cache.
6446       */
6447       while (undo_image != (Image *) NULL)
6448       {
6449         cache_image=undo_image;
6450         undo_image=GetPreviousImageInList(undo_image);
6451         cache_image->list=DestroyImage(cache_image->list);
6452         cache_image=DestroyImage(cache_image);
6453       }
6454       undo_image=NewImageList();
6455       if (redo_image != (Image *) NULL)
6456         redo_image=DestroyImage(redo_image);
6457       redo_image=NewImageList();
6458       return;
6459     }
6460     case UndoCommand:
6461     {
6462       char
6463         image_geometry[MaxTextExtent];
6464
6465       /*
6466         Undo the last image transformation.
6467       */
6468       if (undo_image == (Image *) NULL)
6469         {
6470           (void) XBell(display,0);
6471           return;
6472         }
6473       cache_image=undo_image;
6474       undo_image=GetPreviousImageInList(undo_image);
6475       windows->image.window_changes.width=(int) cache_image->columns;
6476       windows->image.window_changes.height=(int) cache_image->rows;
6477       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6478         windows->image.ximage->width,windows->image.ximage->height);
6479       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6480       if (windows->image.crop_geometry != (char *) NULL)
6481         windows->image.crop_geometry=(char *)
6482           RelinquishMagickMemory(windows->image.crop_geometry);
6483       windows->image.crop_geometry=cache_image->geometry;
6484       if (redo_image != (Image *) NULL)
6485         redo_image=DestroyImage(redo_image);
6486       redo_image=(*image);
6487       *image=cache_image->list;
6488       cache_image=DestroyImage(cache_image);
6489       if (windows->image.orphan != MagickFalse)
6490         return;
6491       XConfigureImageColormap(display,resource_info,windows,*image);
6492       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6493       return;
6494     }
6495     case CutCommand:
6496     case PasteCommand:
6497     case ApplyCommand:
6498     case HalfSizeCommand:
6499     case OriginalSizeCommand:
6500     case DoubleSizeCommand:
6501     case ResizeCommand:
6502     case TrimCommand:
6503     case CropCommand:
6504     case ChopCommand:
6505     case FlipCommand:
6506     case FlopCommand:
6507     case RotateRightCommand:
6508     case RotateLeftCommand:
6509     case RotateCommand:
6510     case ShearCommand:
6511     case RollCommand:
6512     case NegateCommand:
6513     case ContrastStretchCommand:
6514     case SigmoidalContrastCommand:
6515     case NormalizeCommand:
6516     case EqualizeCommand:
6517     case HueCommand:
6518     case SaturationCommand:
6519     case BrightnessCommand:
6520     case GammaCommand:
6521     case SpiffCommand:
6522     case DullCommand:
6523     case GrayscaleCommand:
6524     case MapCommand:
6525     case QuantizeCommand:
6526     case DespeckleCommand:
6527     case EmbossCommand:
6528     case ReduceNoiseCommand:
6529     case AddNoiseCommand:
6530     case SharpenCommand:
6531     case BlurCommand:
6532     case ThresholdCommand:
6533     case EdgeDetectCommand:
6534     case SpreadCommand:
6535     case ShadeCommand:
6536     case RaiseCommand:
6537     case SegmentCommand:
6538     case SolarizeCommand:
6539     case SepiaToneCommand:
6540     case SwirlCommand:
6541     case ImplodeCommand:
6542     case VignetteCommand:
6543     case WaveCommand:
6544     case OilPaintCommand:
6545     case CharcoalDrawCommand:
6546     case AnnotateCommand:
6547     case AddBorderCommand:
6548     case AddFrameCommand:
6549     case CompositeCommand:
6550     case CommentCommand:
6551     case LaunchCommand:
6552     case RegionofInterestCommand:
6553     case SaveToUndoBufferCommand:
6554     case RedoCommand:
6555     {
6556       Image
6557         *previous_image;
6558
6559       ssize_t
6560         bytes;
6561
6562       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6563       if (undo_image != (Image *) NULL)
6564         {
6565           /*
6566             Ensure the undo cache has enough memory available.
6567           */
6568           previous_image=undo_image;
6569           while (previous_image != (Image *) NULL)
6570           {
6571             bytes+=previous_image->list->columns*previous_image->list->rows*
6572               sizeof(PixelPacket);
6573             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6574               {
6575                 previous_image=GetPreviousImageInList(previous_image);
6576                 continue;
6577               }
6578             bytes-=previous_image->list->columns*previous_image->list->rows*
6579               sizeof(PixelPacket);
6580             if (previous_image == undo_image)
6581               undo_image=NewImageList();
6582             else
6583               previous_image->next->previous=NewImageList();
6584             break;
6585           }
6586           while (previous_image != (Image *) NULL)
6587           {
6588             /*
6589               Delete any excess memory from undo cache.
6590             */
6591             cache_image=previous_image;
6592             previous_image=GetPreviousImageInList(previous_image);
6593             cache_image->list=DestroyImage(cache_image->list);
6594             cache_image=DestroyImage(cache_image);
6595           }
6596         }
6597       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6598         break;
6599       /*
6600         Save image before transformations are applied.
6601       */
6602       cache_image=AcquireImage((ImageInfo *) NULL);
6603       if (cache_image == (Image *) NULL)
6604         break;
6605       XSetCursorState(display,windows,MagickTrue);
6606       XCheckRefreshWindows(display,windows);
6607       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6608       XSetCursorState(display,windows,MagickFalse);
6609       if (cache_image->list == (Image *) NULL)
6610         {
6611           cache_image=DestroyImage(cache_image);
6612           break;
6613         }
6614       cache_image->columns=(size_t) windows->image.ximage->width;
6615       cache_image->rows=(size_t) windows->image.ximage->height;
6616       cache_image->geometry=windows->image.crop_geometry;
6617       if (windows->image.crop_geometry != (char *) NULL)
6618         {
6619           cache_image->geometry=AcquireString((char *) NULL);
6620           (void) CopyMagickString(cache_image->geometry,
6621             windows->image.crop_geometry,MaxTextExtent);
6622         }
6623       if (undo_image == (Image *) NULL)
6624         {
6625           undo_image=cache_image;
6626           break;
6627         }
6628       undo_image->next=cache_image;
6629       undo_image->next->previous=undo_image;
6630       undo_image=undo_image->next;
6631       break;
6632     }
6633     default:
6634       break;
6635   }
6636   if (command == RedoCommand)
6637     {
6638       /*
6639         Redo the last image transformation.
6640       */
6641       if (redo_image == (Image *) NULL)
6642         {
6643           (void) XBell(display,0);
6644           return;
6645         }
6646       windows->image.window_changes.width=(int) redo_image->columns;
6647       windows->image.window_changes.height=(int) redo_image->rows;
6648       if (windows->image.crop_geometry != (char *) NULL)
6649         windows->image.crop_geometry=(char *)
6650           RelinquishMagickMemory(windows->image.crop_geometry);
6651       windows->image.crop_geometry=redo_image->geometry;
6652       *image=DestroyImage(*image);
6653       *image=redo_image;
6654       redo_image=NewImageList();
6655       if (windows->image.orphan != MagickFalse)
6656         return;
6657       XConfigureImageColormap(display,resource_info,windows,*image);
6658       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6659       return;
6660     }
6661   if (command != InfoCommand)
6662     return;
6663   /*
6664     Display image info.
6665   */
6666   XSetCursorState(display,windows,MagickTrue);
6667   XCheckRefreshWindows(display,windows);
6668   XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6669   XSetCursorState(display,windows,MagickFalse);
6670 }
6671 \f
6672 /*
6673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6674 %                                                                             %
6675 %                                                                             %
6676 %                                                                             %
6677 +   X I m a g e W i n d o w C o m m a n d                                     %
6678 %                                                                             %
6679 %                                                                             %
6680 %                                                                             %
6681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6682 %
6683 %  XImageWindowCommand() makes a transform to the image or Image window as
6684 %  specified by a user menu button or keyboard command.
6685 %
6686 %  The format of the XImageWindowCommand method is:
6687 %
6688 %      CommandType XImageWindowCommand(Display *display,
6689 %        XResourceInfo *resource_info,XWindows *windows,
6690 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6691 %        ExceptionInfo *exception)
6692 %
6693 %  A description of each parameter follows:
6694 %
6695 %    o nexus:  Method XImageWindowCommand returns an image when the
6696 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6697 %      image is returned.
6698 %
6699 %    o display: Specifies a connection to an X server; returned from
6700 %      XOpenDisplay.
6701 %
6702 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6703 %
6704 %    o windows: Specifies a pointer to a XWindows structure.
6705 %
6706 %    o state: key mask.
6707 %
6708 %    o key_symbol: Specifies a command to perform.
6709 %
6710 %    o image: the image;  XImageWIndowCommand may transform the image and
6711 %      return a new image pointer.
6712 %
6713 %    o exception: return any errors or warnings in this structure.
6714 %
6715 */
6716 static CommandType XImageWindowCommand(Display *display,
6717   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6718   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6719 {
6720   static char
6721     delta[MaxTextExtent] = "";
6722
6723   static const char
6724     Digits[] = "01234567890";
6725
6726   static KeySym
6727     last_symbol = XK_0;
6728
6729   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6730     {
6731       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6732         {
6733           *delta='\0';
6734           resource_info->quantum=1;
6735         }
6736       last_symbol=key_symbol;
6737       delta[strlen(delta)+1]='\0';
6738       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6739       resource_info->quantum=StringToLong(delta);
6740       return(NullCommand);
6741     }
6742   last_symbol=key_symbol;
6743   if (resource_info->immutable)
6744     {
6745       /*
6746         Virtual image window has a restricted command set.
6747       */
6748       switch (key_symbol)
6749       {
6750         case XK_question:
6751           return(InfoCommand);
6752         case XK_p:
6753         case XK_Print:
6754           return(PrintCommand);
6755         case XK_space:
6756           return(NextCommand);
6757         case XK_q:
6758         case XK_Escape:
6759           return(QuitCommand);
6760         default:
6761           break;
6762       }
6763       return(NullCommand);
6764     }
6765   switch ((int) key_symbol)
6766   {
6767     case XK_o:
6768     {
6769       if ((state & ControlMask) == 0)
6770         break;
6771       return(OpenCommand);
6772     }
6773     case XK_space:
6774       return(NextCommand);
6775     case XK_BackSpace:
6776       return(FormerCommand);
6777     case XK_s:
6778     {
6779       if ((state & Mod1Mask) != 0)
6780         return(SwirlCommand);
6781       if ((state & ControlMask) == 0)
6782         return(ShearCommand);
6783       return(SaveCommand);
6784     }
6785     case XK_p:
6786     case XK_Print:
6787     {
6788       if ((state & Mod1Mask) != 0)
6789         return(OilPaintCommand);
6790       if ((state & Mod4Mask) != 0)
6791         return(ColorCommand);
6792       if ((state & ControlMask) == 0)
6793         return(NullCommand);
6794       return(PrintCommand);
6795     }
6796     case XK_d:
6797     {
6798       if ((state & Mod4Mask) != 0)
6799         return(DrawCommand);
6800       if ((state & ControlMask) == 0)
6801         return(NullCommand);
6802       return(DeleteCommand);
6803     }
6804     case XK_Select:
6805     {
6806       if ((state & ControlMask) == 0)
6807         return(NullCommand);
6808       return(SelectCommand);
6809     }
6810     case XK_n:
6811     {
6812       if ((state & ControlMask) == 0)
6813         return(NullCommand);
6814       return(NewCommand);
6815     }
6816     case XK_q:
6817     case XK_Escape:
6818       return(QuitCommand);
6819     case XK_z:
6820     case XK_Undo:
6821     {
6822       if ((state & ControlMask) == 0)
6823         return(NullCommand);
6824       return(UndoCommand);
6825     }
6826     case XK_r:
6827     case XK_Redo:
6828     {
6829       if ((state & ControlMask) == 0)
6830         return(RollCommand);
6831       return(RedoCommand);
6832     }
6833     case XK_x:
6834     {
6835       if ((state & ControlMask) == 0)
6836         return(NullCommand);
6837       return(CutCommand);
6838     }
6839     case XK_c:
6840     {
6841       if ((state & Mod1Mask) != 0)
6842         return(CharcoalDrawCommand);
6843       if ((state & ControlMask) == 0)
6844         return(CropCommand);
6845       return(CopyCommand);
6846     }
6847     case XK_v:
6848     case XK_Insert:
6849     {
6850       if ((state & Mod4Mask) != 0)
6851         return(CompositeCommand);
6852       if ((state & ControlMask) == 0)
6853         return(FlipCommand);
6854       return(PasteCommand);
6855     }
6856     case XK_less:
6857       return(HalfSizeCommand);
6858     case XK_minus:
6859       return(OriginalSizeCommand);
6860     case XK_greater:
6861       return(DoubleSizeCommand);
6862     case XK_percent:
6863       return(ResizeCommand);
6864     case XK_at:
6865       return(RefreshCommand);
6866     case XK_bracketleft:
6867       return(ChopCommand);
6868     case XK_h:
6869       return(FlopCommand);
6870     case XK_slash:
6871       return(RotateRightCommand);
6872     case XK_backslash:
6873       return(RotateLeftCommand);
6874     case XK_asterisk:
6875       return(RotateCommand);
6876     case XK_t:
6877       return(TrimCommand);
6878     case XK_H:
6879       return(HueCommand);
6880     case XK_S:
6881       return(SaturationCommand);
6882     case XK_L:
6883       return(BrightnessCommand);
6884     case XK_G:
6885       return(GammaCommand);
6886     case XK_C:
6887       return(SpiffCommand);
6888     case XK_Z:
6889       return(DullCommand);
6890     case XK_N:
6891       return(NormalizeCommand);
6892     case XK_equal:
6893       return(EqualizeCommand);
6894     case XK_asciitilde:
6895       return(NegateCommand);
6896     case XK_period:
6897       return(GrayscaleCommand);
6898     case XK_numbersign:
6899       return(QuantizeCommand);
6900     case XK_F2:
6901       return(DespeckleCommand);
6902     case XK_F3:
6903       return(EmbossCommand);
6904     case XK_F4:
6905       return(ReduceNoiseCommand);
6906     case XK_F5:
6907       return(AddNoiseCommand);
6908     case XK_F6:
6909       return(SharpenCommand);
6910     case XK_F7:
6911       return(BlurCommand);
6912     case XK_F8:
6913       return(ThresholdCommand);
6914     case XK_F9:
6915       return(EdgeDetectCommand);
6916     case XK_F10:
6917       return(SpreadCommand);
6918     case XK_F11:
6919       return(ShadeCommand);
6920     case XK_F12:
6921       return(RaiseCommand);
6922     case XK_F13:
6923       return(SegmentCommand);
6924     case XK_i:
6925     {
6926       if ((state & Mod1Mask) == 0)
6927         return(NullCommand);
6928       return(ImplodeCommand);
6929     }
6930     case XK_w:
6931     {
6932       if ((state & Mod1Mask) == 0)
6933         return(NullCommand);
6934       return(WaveCommand);
6935     }
6936     case XK_m:
6937     {
6938       if ((state & Mod4Mask) == 0)
6939         return(NullCommand);
6940       return(MatteCommand);
6941     }
6942     case XK_b:
6943     {
6944       if ((state & Mod4Mask) == 0)
6945         return(NullCommand);
6946       return(AddBorderCommand);
6947     }
6948     case XK_f:
6949     {
6950       if ((state & Mod4Mask) == 0)
6951         return(NullCommand);
6952       return(AddFrameCommand);
6953     }
6954     case XK_exclam:
6955     {
6956       if ((state & Mod4Mask) == 0)
6957         return(NullCommand);
6958       return(CommentCommand);
6959     }
6960     case XK_a:
6961     {
6962       if ((state & Mod1Mask) != 0)
6963         return(ApplyCommand);
6964       if ((state & Mod4Mask) != 0)
6965         return(AnnotateCommand);
6966       if ((state & ControlMask) == 0)
6967         return(NullCommand);
6968       return(RegionofInterestCommand);
6969     }
6970     case XK_question:
6971       return(InfoCommand);
6972     case XK_plus:
6973       return(ZoomCommand);
6974     case XK_P:
6975     {
6976       if ((state & ShiftMask) == 0)
6977         return(NullCommand);
6978       return(ShowPreviewCommand);
6979     }
6980     case XK_Execute:
6981       return(LaunchCommand);
6982     case XK_F1:
6983       return(HelpCommand);
6984     case XK_Find:
6985       return(BrowseDocumentationCommand);
6986     case XK_Menu:
6987     {
6988       (void) XMapRaised(display,windows->command.id);
6989       return(NullCommand);
6990     }
6991     case XK_Next:
6992     case XK_Prior:
6993     case XK_Home:
6994     case XK_KP_Home:
6995     {
6996       XTranslateImage(display,windows,*image,key_symbol);
6997       return(NullCommand);
6998     }
6999     case XK_Up:
7000     case XK_KP_Up:
7001     case XK_Down:
7002     case XK_KP_Down:
7003     case XK_Left:
7004     case XK_KP_Left:
7005     case XK_Right:
7006     case XK_KP_Right:
7007     {
7008       if ((state & Mod1Mask) != 0)
7009         {
7010           RectangleInfo
7011             crop_info;
7012
7013           /*
7014             Trim one pixel from edge of image.
7015           */
7016           crop_info.x=0;
7017           crop_info.y=0;
7018           crop_info.width=(size_t) windows->image.ximage->width;
7019           crop_info.height=(size_t) windows->image.ximage->height;
7020           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7021             {
7022               if (resource_info->quantum >= (int) crop_info.height)
7023                 resource_info->quantum=(int) crop_info.height-1;
7024               crop_info.height-=resource_info->quantum;
7025             }
7026           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7027             {
7028               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7029                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7030               crop_info.y+=resource_info->quantum;
7031               crop_info.height-=resource_info->quantum;
7032             }
7033           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7034             {
7035               if (resource_info->quantum >= (int) crop_info.width)
7036                 resource_info->quantum=(int) crop_info.width-1;
7037               crop_info.width-=resource_info->quantum;
7038             }
7039           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7040             {
7041               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7042                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7043               crop_info.x+=resource_info->quantum;
7044               crop_info.width-=resource_info->quantum;
7045             }
7046           if ((int) (windows->image.x+windows->image.width) >
7047               (int) crop_info.width)
7048             windows->image.x=(int) (crop_info.width-windows->image.width);
7049           if ((int) (windows->image.y+windows->image.height) >
7050               (int) crop_info.height)
7051             windows->image.y=(int) (crop_info.height-windows->image.height);
7052           XSetCropGeometry(display,windows,&crop_info,*image);
7053           windows->image.window_changes.width=(int) crop_info.width;
7054           windows->image.window_changes.height=(int) crop_info.height;
7055           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7056           (void) XConfigureImage(display,resource_info,windows,*image,
7057             exception);
7058           return(NullCommand);
7059         }
7060       XTranslateImage(display,windows,*image,key_symbol);
7061       return(NullCommand);
7062     }
7063     default:
7064       return(NullCommand);
7065   }
7066   return(NullCommand);
7067 }
7068 \f
7069 /*
7070 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7071 %                                                                             %
7072 %                                                                             %
7073 %                                                                             %
7074 +   X M a g i c k C o m m a n d                                               %
7075 %                                                                             %
7076 %                                                                             %
7077 %                                                                             %
7078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7079 %
7080 %  XMagickCommand() makes a transform to the image or Image window as
7081 %  specified by a user menu button or keyboard command.
7082 %
7083 %  The format of the XMagickCommand method is:
7084 %
7085 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7086 %        XWindows *windows,const CommandType command,Image **image,
7087 %        ExceptionInfo *exception)
7088 %
7089 %  A description of each parameter follows:
7090 %
7091 %    o display: Specifies a connection to an X server; returned from
7092 %      XOpenDisplay.
7093 %
7094 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7095 %
7096 %    o windows: Specifies a pointer to a XWindows structure.
7097 %
7098 %    o command: Specifies a command to perform.
7099 %
7100 %    o image: the image;  XMagickCommand may transform the image and return a
7101 %      new image pointer.
7102 %
7103 %    o exception: return any errors or warnings in this structure.
7104 %
7105 */
7106 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7107   XWindows *windows,const CommandType command,Image **image,
7108   ExceptionInfo *exception)
7109 {
7110   char
7111     filename[MaxTextExtent],
7112     geometry[MaxTextExtent],
7113     modulate_factors[MaxTextExtent];
7114
7115   GeometryInfo
7116     geometry_info;
7117
7118   Image
7119     *nexus;
7120
7121   ImageInfo
7122     *image_info;
7123
7124   int
7125     x,
7126     y;
7127
7128   MagickStatusType
7129     flags,
7130     status;
7131
7132   QuantizeInfo
7133     quantize_info;
7134
7135   RectangleInfo
7136     page_geometry;
7137
7138   register int
7139     i;
7140
7141   static char
7142     color[MaxTextExtent] = "gray";
7143
7144   unsigned int
7145     height,
7146     width;
7147
7148   /*
7149     Process user command.
7150   */
7151   XCheckRefreshWindows(display,windows);
7152   XImageCache(display,resource_info,windows,command,image,exception);
7153   nexus=NewImageList();
7154   windows->image.window_changes.width=windows->image.ximage->width;
7155   windows->image.window_changes.height=windows->image.ximage->height;
7156   image_info=CloneImageInfo(resource_info->image_info);
7157   SetGeometryInfo(&geometry_info);
7158   GetQuantizeInfo(&quantize_info);
7159   switch (command)
7160   {
7161     case OpenCommand:
7162     {
7163       /*
7164         Load image.
7165       */
7166       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7167       break;
7168     }
7169     case NextCommand:
7170     {
7171       /*
7172         Display next image.
7173       */
7174       for (i=0; i < resource_info->quantum; i++)
7175         XClientMessage(display,windows->image.id,windows->im_protocols,
7176           windows->im_next_image,CurrentTime);
7177       break;
7178     }
7179     case FormerCommand:
7180     {
7181       /*
7182         Display former image.
7183       */
7184       for (i=0; i < resource_info->quantum; i++)
7185         XClientMessage(display,windows->image.id,windows->im_protocols,
7186           windows->im_former_image,CurrentTime);
7187       break;
7188     }
7189     case SelectCommand:
7190     {
7191       int
7192         status;
7193
7194       /*
7195         Select image.
7196       */
7197       status=chdir(resource_info->home_directory);
7198       if (status == -1)
7199         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7200           "UnableToOpenFile","%s",resource_info->home_directory);
7201       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7202       break;
7203     }
7204     case SaveCommand:
7205     {
7206       /*
7207         Save image.
7208       */
7209       status=XSaveImage(display,resource_info,windows,*image,exception);
7210       if (status == MagickFalse)
7211         {
7212           char
7213             message[MaxTextExtent];
7214
7215           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7216             exception->reason != (char *) NULL ? exception->reason : "",
7217             exception->description != (char *) NULL ? exception->description :
7218             "");
7219           XNoticeWidget(display,windows,"Unable to save file:",message);
7220           break;
7221         }
7222       break;
7223     }
7224     case PrintCommand:
7225     {
7226       /*
7227         Print image.
7228       */
7229       status=XPrintImage(display,resource_info,windows,*image,exception);
7230       if (status == MagickFalse)
7231         {
7232           char
7233             message[MaxTextExtent];
7234
7235           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7236             exception->reason != (char *) NULL ? exception->reason : "",
7237             exception->description != (char *) NULL ? exception->description :
7238             "");
7239           XNoticeWidget(display,windows,"Unable to print file:",message);
7240           break;
7241         }
7242       break;
7243     }
7244     case DeleteCommand:
7245     {
7246       static char
7247         filename[MaxTextExtent] = "\0";
7248
7249       /*
7250         Delete image file.
7251       */
7252       XFileBrowserWidget(display,windows,"Delete",filename);
7253       if (*filename == '\0')
7254         break;
7255       status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
7256       if (status != MagickFalse)
7257         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7258       break;
7259     }
7260     case NewCommand:
7261     {
7262       int
7263         status;
7264
7265       static char
7266         color[MaxTextExtent] = "gray",
7267         geometry[MaxTextExtent] = "640x480";
7268
7269       static const char
7270         *format = "gradient";
7271
7272       /*
7273         Query user for canvas geometry.
7274       */
7275       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7276         geometry);
7277       if (*geometry == '\0')
7278         break;
7279       if (status == 0)
7280         format="xc";
7281       XColorBrowserWidget(display,windows,"Select",color);
7282       if (*color == '\0')
7283         break;
7284       /*
7285         Create canvas.
7286       */
7287       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7288         "%s:%s",format,color);
7289       (void) CloneString(&image_info->size,geometry);
7290       nexus=ReadImage(image_info,exception);
7291       CatchException(exception);
7292       XClientMessage(display,windows->image.id,windows->im_protocols,
7293         windows->im_next_image,CurrentTime);
7294       break;
7295     }
7296     case VisualDirectoryCommand:
7297     {
7298       /*
7299         Visual Image directory.
7300       */
7301       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7302       break;
7303     }
7304     case QuitCommand:
7305     {
7306       /*
7307         exit program.
7308       */
7309       if (resource_info->confirm_exit == MagickFalse)
7310         XClientMessage(display,windows->image.id,windows->im_protocols,
7311           windows->im_exit,CurrentTime);
7312       else
7313         {
7314           int
7315             status;
7316
7317           /*
7318             Confirm program exit.
7319           */
7320           status=XConfirmWidget(display,windows,"Do you really want to exit",
7321             resource_info->client_name);
7322           if (status > 0)
7323             XClientMessage(display,windows->image.id,windows->im_protocols,
7324               windows->im_exit,CurrentTime);
7325         }
7326       break;
7327     }
7328     case CutCommand:
7329     {
7330       /*
7331         Cut image.
7332       */
7333       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7334       break;
7335     }
7336     case CopyCommand:
7337     {
7338       /*
7339         Copy image.
7340       */
7341       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7342         exception);
7343       break;
7344     }
7345     case PasteCommand:
7346     {
7347       /*
7348         Paste image.
7349       */
7350       status=XPasteImage(display,resource_info,windows,*image,exception);
7351       if (status == MagickFalse)
7352         {
7353           XNoticeWidget(display,windows,"Unable to paste X image",
7354             (*image)->filename);
7355           break;
7356         }
7357       break;
7358     }
7359     case HalfSizeCommand:
7360     {
7361       /*
7362         Half image size.
7363       */
7364       windows->image.window_changes.width=windows->image.ximage->width/2;
7365       windows->image.window_changes.height=windows->image.ximage->height/2;
7366       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7367       break;
7368     }
7369     case OriginalSizeCommand:
7370     {
7371       /*
7372         Original image size.
7373       */
7374       windows->image.window_changes.width=(int) (*image)->columns;
7375       windows->image.window_changes.height=(int) (*image)->rows;
7376       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7377       break;
7378     }
7379     case DoubleSizeCommand:
7380     {
7381       /*
7382         Double the image size.
7383       */
7384       windows->image.window_changes.width=windows->image.ximage->width << 1;
7385       windows->image.window_changes.height=windows->image.ximage->height << 1;
7386       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7387       break;
7388     }
7389     case ResizeCommand:
7390     {
7391       int
7392         status;
7393
7394       size_t
7395         height,
7396         width;
7397
7398       ssize_t
7399         x,
7400         y;
7401
7402       /*
7403         Resize image.
7404       */
7405       width=(size_t) windows->image.ximage->width;
7406       height=(size_t) windows->image.ximage->height;
7407       x=0;
7408       y=0;
7409       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7410         (double) width,(double) height);
7411       status=XDialogWidget(display,windows,"Resize",
7412         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7413       if (*geometry == '\0')
7414         break;
7415       if (status == 0)
7416         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7417       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7418       windows->image.window_changes.width=(int) width;
7419       windows->image.window_changes.height=(int) height;
7420       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7421       break;
7422     }
7423     case ApplyCommand:
7424     {
7425       char
7426         image_geometry[MaxTextExtent];
7427
7428       if ((windows->image.crop_geometry == (char *) NULL) &&
7429           ((int) (*image)->columns == windows->image.ximage->width) &&
7430           ((int) (*image)->rows == windows->image.ximage->height))
7431         break;
7432       /*
7433         Apply size transforms to image.
7434       */
7435       XSetCursorState(display,windows,MagickTrue);
7436       XCheckRefreshWindows(display,windows);
7437       /*
7438         Crop and/or scale displayed image.
7439       */
7440       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7441         windows->image.ximage->width,windows->image.ximage->height);
7442       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7443       if (windows->image.crop_geometry != (char *) NULL)
7444         windows->image.crop_geometry=(char *)
7445           RelinquishMagickMemory(windows->image.crop_geometry);
7446       windows->image.x=0;
7447       windows->image.y=0;
7448       XConfigureImageColormap(display,resource_info,windows,*image);
7449       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7450       break;
7451     }
7452     case RefreshCommand:
7453     {
7454       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7455       break;
7456     }
7457     case RestoreCommand:
7458     {
7459       /*
7460         Restore Image window to its original size.
7461       */
7462       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7463           (windows->image.height == (unsigned int) (*image)->rows) &&
7464           (windows->image.crop_geometry == (char *) NULL))
7465         {
7466           (void) XBell(display,0);
7467           break;
7468         }
7469       windows->image.window_changes.width=(int) (*image)->columns;
7470       windows->image.window_changes.height=(int) (*image)->rows;
7471       if (windows->image.crop_geometry != (char *) NULL)
7472         {
7473           windows->image.crop_geometry=(char *)
7474             RelinquishMagickMemory(windows->image.crop_geometry);
7475           windows->image.crop_geometry=(char *) NULL;
7476           windows->image.x=0;
7477           windows->image.y=0;
7478         }
7479       XConfigureImageColormap(display,resource_info,windows,*image);
7480       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7481       break;
7482     }
7483     case CropCommand:
7484     {
7485       /*
7486         Crop image.
7487       */
7488       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7489         exception);
7490       break;
7491     }
7492     case ChopCommand:
7493     {
7494       /*
7495         Chop image.
7496       */
7497       status=XChopImage(display,resource_info,windows,image,exception);
7498       if (status == MagickFalse)
7499         {
7500           XNoticeWidget(display,windows,"Unable to cut X image",
7501             (*image)->filename);
7502           break;
7503         }
7504       break;
7505     }
7506     case FlopCommand:
7507     {
7508       Image
7509         *flop_image;
7510
7511       /*
7512         Flop image scanlines.
7513       */
7514       XSetCursorState(display,windows,MagickTrue);
7515       XCheckRefreshWindows(display,windows);
7516       flop_image=FlopImage(*image,exception);
7517       if (flop_image != (Image *) NULL)
7518         {
7519           *image=DestroyImage(*image);
7520           *image=flop_image;
7521         }
7522       CatchException(exception);
7523       XSetCursorState(display,windows,MagickFalse);
7524       if (windows->image.crop_geometry != (char *) NULL)
7525         {
7526           /*
7527             Flop crop geometry.
7528           */
7529           width=(unsigned int) (*image)->columns;
7530           height=(unsigned int) (*image)->rows;
7531           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7532             &width,&height);
7533           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7534             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7535         }
7536       if (windows->image.orphan != MagickFalse)
7537         break;
7538       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7539       break;
7540     }
7541     case FlipCommand:
7542     {
7543       Image
7544         *flip_image;
7545
7546       /*
7547         Flip image scanlines.
7548       */
7549       XSetCursorState(display,windows,MagickTrue);
7550       XCheckRefreshWindows(display,windows);
7551       flip_image=FlipImage(*image,exception);
7552       if (flip_image != (Image *) NULL)
7553         {
7554           *image=DestroyImage(*image);
7555           *image=flip_image;
7556         }
7557       CatchException(exception);
7558       XSetCursorState(display,windows,MagickFalse);
7559       if (windows->image.crop_geometry != (char *) NULL)
7560         {
7561           /*
7562             Flip crop geometry.
7563           */
7564           width=(unsigned int) (*image)->columns;
7565           height=(unsigned int) (*image)->rows;
7566           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7567             &width,&height);
7568           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7569             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7570         }
7571       if (windows->image.orphan != MagickFalse)
7572         break;
7573       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7574       break;
7575     }
7576     case RotateRightCommand:
7577     {
7578       /*
7579         Rotate image 90 degrees clockwise.
7580       */
7581       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7582       if (status == MagickFalse)
7583         {
7584           XNoticeWidget(display,windows,"Unable to rotate X image",
7585             (*image)->filename);
7586           break;
7587         }
7588       break;
7589     }
7590     case RotateLeftCommand:
7591     {
7592       /*
7593         Rotate image 90 degrees counter-clockwise.
7594       */
7595       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7596       if (status == MagickFalse)
7597         {
7598           XNoticeWidget(display,windows,"Unable to rotate X image",
7599             (*image)->filename);
7600           break;
7601         }
7602       break;
7603     }
7604     case RotateCommand:
7605     {
7606       /*
7607         Rotate image.
7608       */
7609       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7610       if (status == MagickFalse)
7611         {
7612           XNoticeWidget(display,windows,"Unable to rotate X image",
7613             (*image)->filename);
7614           break;
7615         }
7616       break;
7617     }
7618     case ShearCommand:
7619     {
7620       Image
7621         *shear_image;
7622
7623       static char
7624         geometry[MaxTextExtent] = "45.0x45.0";
7625
7626       /*
7627         Query user for shear color and geometry.
7628       */
7629       XColorBrowserWidget(display,windows,"Select",color);
7630       if (*color == '\0')
7631         break;
7632       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7633         geometry);
7634       if (*geometry == '\0')
7635         break;
7636       /*
7637         Shear image.
7638       */
7639       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7640         exception);
7641       XSetCursorState(display,windows,MagickTrue);
7642       XCheckRefreshWindows(display,windows);
7643       (void) QueryColorDatabase(color,&(*image)->background_color,
7644         exception);
7645       flags=ParseGeometry(geometry,&geometry_info);
7646       if ((flags & SigmaValue) == 0)
7647         geometry_info.sigma=geometry_info.rho;
7648       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7649         exception);
7650       if (shear_image != (Image *) NULL)
7651         {
7652           *image=DestroyImage(*image);
7653           *image=shear_image;
7654         }
7655       CatchException(exception);
7656       XSetCursorState(display,windows,MagickFalse);
7657       if (windows->image.orphan != MagickFalse)
7658         break;
7659       windows->image.window_changes.width=(int) (*image)->columns;
7660       windows->image.window_changes.height=(int) (*image)->rows;
7661       XConfigureImageColormap(display,resource_info,windows,*image);
7662       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7663       break;
7664     }
7665     case RollCommand:
7666     {
7667       Image
7668         *roll_image;
7669
7670       static char
7671         geometry[MaxTextExtent] = "+2+2";
7672
7673       /*
7674         Query user for the roll geometry.
7675       */
7676       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7677         geometry);
7678       if (*geometry == '\0')
7679         break;
7680       /*
7681         Roll image.
7682       */
7683       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7684         exception);
7685       XSetCursorState(display,windows,MagickTrue);
7686       XCheckRefreshWindows(display,windows);
7687       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7688         exception);
7689       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7690         exception);
7691       if (roll_image != (Image *) NULL)
7692         {
7693           *image=DestroyImage(*image);
7694           *image=roll_image;
7695         }
7696       CatchException(exception);
7697       XSetCursorState(display,windows,MagickFalse);
7698       if (windows->image.orphan != MagickFalse)
7699         break;
7700       windows->image.window_changes.width=(int) (*image)->columns;
7701       windows->image.window_changes.height=(int) (*image)->rows;
7702       XConfigureImageColormap(display,resource_info,windows,*image);
7703       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7704       break;
7705     }
7706     case TrimCommand:
7707     {
7708       static char
7709         fuzz[MaxTextExtent];
7710
7711       /*
7712         Query user for the fuzz factor.
7713       */
7714       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7715         (*image)->fuzz/(QuantumRange+1.0));
7716       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7717       if (*fuzz == '\0')
7718         break;
7719       (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7720       /*
7721         Trim image.
7722       */
7723       status=XTrimImage(display,resource_info,windows,*image,exception);
7724       if (status == MagickFalse)
7725         {
7726           XNoticeWidget(display,windows,"Unable to trim X image",
7727             (*image)->filename);
7728           break;
7729         }
7730       break;
7731     }
7732     case HueCommand:
7733     {
7734       static char
7735         hue_percent[MaxTextExtent] = "110";
7736
7737       /*
7738         Query user for percent hue change.
7739       */
7740       (void) XDialogWidget(display,windows,"Apply",
7741         "Enter percent change in image hue (0-200):",hue_percent);
7742       if (*hue_percent == '\0')
7743         break;
7744       /*
7745         Vary the image hue.
7746       */
7747       XSetCursorState(display,windows,MagickTrue);
7748       XCheckRefreshWindows(display,windows);
7749       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7750       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7751         MaxTextExtent);
7752       (void) ModulateImage(*image,modulate_factors,exception);
7753       XSetCursorState(display,windows,MagickFalse);
7754       if (windows->image.orphan != MagickFalse)
7755         break;
7756       XConfigureImageColormap(display,resource_info,windows,*image);
7757       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7758       break;
7759     }
7760     case SaturationCommand:
7761     {
7762       static char
7763         saturation_percent[MaxTextExtent] = "110";
7764
7765       /*
7766         Query user for percent saturation change.
7767       */
7768       (void) XDialogWidget(display,windows,"Apply",
7769         "Enter percent change in color saturation (0-200):",saturation_percent);
7770       if (*saturation_percent == '\0')
7771         break;
7772       /*
7773         Vary color saturation.
7774       */
7775       XSetCursorState(display,windows,MagickTrue);
7776       XCheckRefreshWindows(display,windows);
7777       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7778       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7779         MaxTextExtent);
7780       (void) ModulateImage(*image,modulate_factors,exception);
7781       XSetCursorState(display,windows,MagickFalse);
7782       if (windows->image.orphan != MagickFalse)
7783         break;
7784       XConfigureImageColormap(display,resource_info,windows,*image);
7785       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7786       break;
7787     }
7788     case BrightnessCommand:
7789     {
7790       static char
7791         brightness_percent[MaxTextExtent] = "110";
7792
7793       /*
7794         Query user for percent brightness change.
7795       */
7796       (void) XDialogWidget(display,windows,"Apply",
7797         "Enter percent change in color brightness (0-200):",brightness_percent);
7798       if (*brightness_percent == '\0')
7799         break;
7800       /*
7801         Vary the color brightness.
7802       */
7803       XSetCursorState(display,windows,MagickTrue);
7804       XCheckRefreshWindows(display,windows);
7805       (void) CopyMagickString(modulate_factors,brightness_percent,
7806         MaxTextExtent);
7807       (void) ModulateImage(*image,modulate_factors,exception);
7808       XSetCursorState(display,windows,MagickFalse);
7809       if (windows->image.orphan != MagickFalse)
7810         break;
7811       XConfigureImageColormap(display,resource_info,windows,*image);
7812       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7813       break;
7814     }
7815     case GammaCommand:
7816     {
7817       static char
7818         factor[MaxTextExtent] = "1.6";
7819
7820       /*
7821         Query user for gamma value.
7822       */
7823       (void) XDialogWidget(display,windows,"Gamma",
7824         "Enter gamma value (e.g. 1.2):",factor);
7825       if (*factor == '\0')
7826         break;
7827       /*
7828         Gamma correct image.
7829       */
7830       XSetCursorState(display,windows,MagickTrue);
7831       XCheckRefreshWindows(display,windows);
7832       (void) GammaImage(*image,atof(factor),exception);
7833       XSetCursorState(display,windows,MagickFalse);
7834       if (windows->image.orphan != MagickFalse)
7835         break;
7836       XConfigureImageColormap(display,resource_info,windows,*image);
7837       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7838       break;
7839     }
7840     case SpiffCommand:
7841     {
7842       /*
7843         Sharpen the image contrast.
7844       */
7845       XSetCursorState(display,windows,MagickTrue);
7846       XCheckRefreshWindows(display,windows);
7847       (void) ContrastImage(*image,MagickTrue,exception);
7848       XSetCursorState(display,windows,MagickFalse);
7849       if (windows->image.orphan != MagickFalse)
7850         break;
7851       XConfigureImageColormap(display,resource_info,windows,*image);
7852       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7853       break;
7854     }
7855     case DullCommand:
7856     {
7857       /*
7858         Dull the image contrast.
7859       */
7860       XSetCursorState(display,windows,MagickTrue);
7861       XCheckRefreshWindows(display,windows);
7862       (void) ContrastImage(*image,MagickFalse,exception);
7863       XSetCursorState(display,windows,MagickFalse);
7864       if (windows->image.orphan != MagickFalse)
7865         break;
7866       XConfigureImageColormap(display,resource_info,windows,*image);
7867       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7868       break;
7869     }
7870     case ContrastStretchCommand:
7871     {
7872       double
7873         black_point,
7874         white_point;
7875
7876       static char
7877         levels[MaxTextExtent] = "1%";
7878
7879       /*
7880         Query user for gamma value.
7881       */
7882       (void) XDialogWidget(display,windows,"Contrast Stretch",
7883         "Enter black and white points:",levels);
7884       if (*levels == '\0')
7885         break;
7886       /*
7887         Contrast stretch image.
7888       */
7889       XSetCursorState(display,windows,MagickTrue);
7890       XCheckRefreshWindows(display,windows);
7891       flags=ParseGeometry(levels,&geometry_info);
7892       black_point=geometry_info.rho;
7893       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7894       if ((flags & PercentValue) != 0)
7895         {
7896           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7897           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7898         }
7899       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7900       (void) ContrastStretchImage(*image,black_point,white_point,
7901         exception);
7902       XSetCursorState(display,windows,MagickFalse);
7903       if (windows->image.orphan != MagickFalse)
7904         break;
7905       XConfigureImageColormap(display,resource_info,windows,*image);
7906       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7907       break;
7908     }
7909     case SigmoidalContrastCommand:
7910     {
7911       GeometryInfo
7912         geometry_info;
7913
7914       MagickStatusType
7915         flags;
7916
7917       static char
7918         levels[MaxTextExtent] = "3x50%";
7919
7920       /*
7921         Query user for gamma value.
7922       */
7923       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7924         "Enter contrast and midpoint:",levels);
7925       if (*levels == '\0')
7926         break;
7927       /*
7928         Contrast stretch image.
7929       */
7930       XSetCursorState(display,windows,MagickTrue);
7931       XCheckRefreshWindows(display,windows);
7932       flags=ParseGeometry(levels,&geometry_info);
7933       if ((flags & SigmaValue) == 0)
7934         geometry_info.sigma=1.0*QuantumRange/2.0;
7935       if ((flags & PercentValue) != 0)
7936         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7937       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7938         geometry_info.sigma,exception);
7939       XSetCursorState(display,windows,MagickFalse);
7940       if (windows->image.orphan != MagickFalse)
7941         break;
7942       XConfigureImageColormap(display,resource_info,windows,*image);
7943       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7944       break;
7945     }
7946     case NormalizeCommand:
7947     {
7948       /*
7949         Perform histogram normalization on the image.
7950       */
7951       XSetCursorState(display,windows,MagickTrue);
7952       XCheckRefreshWindows(display,windows);
7953       (void) NormalizeImage(*image,exception);
7954       XSetCursorState(display,windows,MagickFalse);
7955       if (windows->image.orphan != MagickFalse)
7956         break;
7957       XConfigureImageColormap(display,resource_info,windows,*image);
7958       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7959       break;
7960     }
7961     case EqualizeCommand:
7962     {
7963       /*
7964         Perform histogram equalization on the image.
7965       */
7966       XSetCursorState(display,windows,MagickTrue);
7967       XCheckRefreshWindows(display,windows);
7968       (void) EqualizeImage(*image,exception);
7969       XSetCursorState(display,windows,MagickFalse);
7970       if (windows->image.orphan != MagickFalse)
7971         break;
7972       XConfigureImageColormap(display,resource_info,windows,*image);
7973       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7974       break;
7975     }
7976     case NegateCommand:
7977     {
7978       /*
7979         Negate colors in image.
7980       */
7981       XSetCursorState(display,windows,MagickTrue);
7982       XCheckRefreshWindows(display,windows);
7983       (void) NegateImage(*image,MagickFalse,exception);
7984       XSetCursorState(display,windows,MagickFalse);
7985       if (windows->image.orphan != MagickFalse)
7986         break;
7987       XConfigureImageColormap(display,resource_info,windows,*image);
7988       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7989       break;
7990     }
7991     case GrayscaleCommand:
7992     {
7993       /*
7994         Convert image to grayscale.
7995       */
7996       XSetCursorState(display,windows,MagickTrue);
7997       XCheckRefreshWindows(display,windows);
7998       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7999         GrayscaleType : GrayscaleMatteType,exception);
8000       XSetCursorState(display,windows,MagickFalse);
8001       if (windows->image.orphan != MagickFalse)
8002         break;
8003       XConfigureImageColormap(display,resource_info,windows,*image);
8004       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8005       break;
8006     }
8007     case MapCommand:
8008     {
8009       Image
8010         *affinity_image;
8011
8012       static char
8013         filename[MaxTextExtent] = "\0";
8014
8015       /*
8016         Request image file name from user.
8017       */
8018       XFileBrowserWidget(display,windows,"Map",filename);
8019       if (*filename == '\0')
8020         break;
8021       /*
8022         Map image.
8023       */
8024       XSetCursorState(display,windows,MagickTrue);
8025       XCheckRefreshWindows(display,windows);
8026       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8027       affinity_image=ReadImage(image_info,exception);
8028       if (affinity_image != (Image *) NULL)
8029         {
8030           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8031           affinity_image=DestroyImage(affinity_image);
8032         }
8033       CatchException(exception);
8034       XSetCursorState(display,windows,MagickFalse);
8035       if (windows->image.orphan != MagickFalse)
8036         break;
8037       XConfigureImageColormap(display,resource_info,windows,*image);
8038       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8039       break;
8040     }
8041     case QuantizeCommand:
8042     {
8043       int
8044         status;
8045
8046       static char
8047         colors[MaxTextExtent] = "256";
8048
8049       /*
8050         Query user for maximum number of colors.
8051       */
8052       status=XDialogWidget(display,windows,"Quantize",
8053         "Maximum number of colors:",colors);
8054       if (*colors == '\0')
8055         break;
8056       /*
8057         Color reduce the image.
8058       */
8059       XSetCursorState(display,windows,MagickTrue);
8060       XCheckRefreshWindows(display,windows);
8061       quantize_info.number_colors=StringToUnsignedLong(colors);
8062       quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
8063       (void) QuantizeImage(&quantize_info,*image,exception);
8064       XSetCursorState(display,windows,MagickFalse);
8065       if (windows->image.orphan != MagickFalse)
8066         break;
8067       XConfigureImageColormap(display,resource_info,windows,*image);
8068       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8069       break;
8070     }
8071     case DespeckleCommand:
8072     {
8073       Image
8074         *despeckle_image;
8075
8076       /*
8077         Despeckle image.
8078       */
8079       XSetCursorState(display,windows,MagickTrue);
8080       XCheckRefreshWindows(display,windows);
8081       despeckle_image=DespeckleImage(*image,exception);
8082       if (despeckle_image != (Image *) NULL)
8083         {
8084           *image=DestroyImage(*image);
8085           *image=despeckle_image;
8086         }
8087       CatchException(exception);
8088       XSetCursorState(display,windows,MagickFalse);
8089       if (windows->image.orphan != MagickFalse)
8090         break;
8091       XConfigureImageColormap(display,resource_info,windows,*image);
8092       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8093       break;
8094     }
8095     case EmbossCommand:
8096     {
8097       Image
8098         *emboss_image;
8099
8100       static char
8101         radius[MaxTextExtent] = "0.0x1.0";
8102
8103       /*
8104         Query user for emboss radius.
8105       */
8106       (void) XDialogWidget(display,windows,"Emboss",
8107         "Enter the emboss radius and standard deviation:",radius);
8108       if (*radius == '\0')
8109         break;
8110       /*
8111         Reduce noise in the image.
8112       */
8113       XSetCursorState(display,windows,MagickTrue);
8114       XCheckRefreshWindows(display,windows);
8115       flags=ParseGeometry(radius,&geometry_info);
8116       if ((flags & SigmaValue) == 0)
8117         geometry_info.sigma=1.0;
8118       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8119         exception);
8120       if (emboss_image != (Image *) NULL)
8121         {
8122           *image=DestroyImage(*image);
8123           *image=emboss_image;
8124         }
8125       CatchException(exception);
8126       XSetCursorState(display,windows,MagickFalse);
8127       if (windows->image.orphan != MagickFalse)
8128         break;
8129       XConfigureImageColormap(display,resource_info,windows,*image);
8130       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8131       break;
8132     }
8133     case ReduceNoiseCommand:
8134     {
8135       Image
8136         *noise_image;
8137
8138       static char
8139         radius[MaxTextExtent] = "0";
8140
8141       /*
8142         Query user for noise radius.
8143       */
8144       (void) XDialogWidget(display,windows,"Reduce Noise",
8145         "Enter the noise radius:",radius);
8146       if (*radius == '\0')
8147         break;
8148       /*
8149         Reduce noise in the image.
8150       */
8151       XSetCursorState(display,windows,MagickTrue);
8152       XCheckRefreshWindows(display,windows);
8153       flags=ParseGeometry(radius,&geometry_info);
8154       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8155         geometry_info.rho,(size_t) geometry_info.rho,exception);
8156       if (noise_image != (Image *) NULL)
8157         {
8158           *image=DestroyImage(*image);
8159           *image=noise_image;
8160         }
8161       CatchException(exception);
8162       XSetCursorState(display,windows,MagickFalse);
8163       if (windows->image.orphan != MagickFalse)
8164         break;
8165       XConfigureImageColormap(display,resource_info,windows,*image);
8166       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8167       break;
8168     }
8169     case AddNoiseCommand:
8170     {
8171       char
8172         **noises;
8173
8174       Image
8175         *noise_image;
8176
8177       static char
8178         noise_type[MaxTextExtent] = "Gaussian";
8179
8180       /*
8181         Add noise to the image.
8182       */
8183       noises=GetCommandOptions(MagickNoiseOptions);
8184       if (noises == (char **) NULL)
8185         break;
8186       XListBrowserWidget(display,windows,&windows->widget,
8187         (const char **) noises,"Add Noise",
8188         "Select a type of noise to add to your image:",noise_type);
8189       noises=DestroyStringList(noises);
8190       if (*noise_type == '\0')
8191         break;
8192       XSetCursorState(display,windows,MagickTrue);
8193       XCheckRefreshWindows(display,windows);
8194       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8195         MagickNoiseOptions,MagickFalse,noise_type),exception);
8196       if (noise_image != (Image *) NULL)
8197         {
8198           *image=DestroyImage(*image);
8199           *image=noise_image;
8200         }
8201       CatchException(exception);
8202       XSetCursorState(display,windows,MagickFalse);
8203       if (windows->image.orphan != MagickFalse)
8204         break;
8205       XConfigureImageColormap(display,resource_info,windows,*image);
8206       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8207       break;
8208     }
8209     case SharpenCommand:
8210     {
8211       Image
8212         *sharp_image;
8213
8214       static char
8215         radius[MaxTextExtent] = "0.0x1.0";
8216
8217       /*
8218         Query user for sharpen radius.
8219       */
8220       (void) XDialogWidget(display,windows,"Sharpen",
8221         "Enter the sharpen radius and standard deviation:",radius);
8222       if (*radius == '\0')
8223         break;
8224       /*
8225         Sharpen image scanlines.
8226       */
8227       XSetCursorState(display,windows,MagickTrue);
8228       XCheckRefreshWindows(display,windows);
8229       flags=ParseGeometry(radius,&geometry_info);
8230       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8231         geometry_info.xi,exception);
8232       if (sharp_image != (Image *) NULL)
8233         {
8234           *image=DestroyImage(*image);
8235           *image=sharp_image;
8236         }
8237       CatchException(exception);
8238       XSetCursorState(display,windows,MagickFalse);
8239       if (windows->image.orphan != MagickFalse)
8240         break;
8241       XConfigureImageColormap(display,resource_info,windows,*image);
8242       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8243       break;
8244     }
8245     case BlurCommand:
8246     {
8247       Image
8248         *blur_image;
8249
8250       static char
8251         radius[MaxTextExtent] = "0.0x1.0";
8252
8253       /*
8254         Query user for blur radius.
8255       */
8256       (void) XDialogWidget(display,windows,"Blur",
8257         "Enter the blur radius and standard deviation:",radius);
8258       if (*radius == '\0')
8259         break;
8260       /*
8261         Blur an image.
8262       */
8263       XSetCursorState(display,windows,MagickTrue);
8264       XCheckRefreshWindows(display,windows);
8265       flags=ParseGeometry(radius,&geometry_info);
8266       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8267         geometry_info.xi,exception);
8268       if (blur_image != (Image *) NULL)
8269         {
8270           *image=DestroyImage(*image);
8271           *image=blur_image;
8272         }
8273       CatchException(exception);
8274       XSetCursorState(display,windows,MagickFalse);
8275       if (windows->image.orphan != MagickFalse)
8276         break;
8277       XConfigureImageColormap(display,resource_info,windows,*image);
8278       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8279       break;
8280     }
8281     case ThresholdCommand:
8282     {
8283       double
8284         threshold;
8285
8286       static char
8287         factor[MaxTextExtent] = "128";
8288
8289       /*
8290         Query user for threshold value.
8291       */
8292       (void) XDialogWidget(display,windows,"Threshold",
8293         "Enter threshold value:",factor);
8294       if (*factor == '\0')
8295         break;
8296       /*
8297         Gamma correct image.
8298       */
8299       XSetCursorState(display,windows,MagickTrue);
8300       XCheckRefreshWindows(display,windows);
8301       threshold=SiPrefixToDouble(factor,QuantumRange);
8302       (void) BilevelImage(*image,threshold);
8303       XSetCursorState(display,windows,MagickFalse);
8304       if (windows->image.orphan != MagickFalse)
8305         break;
8306       XConfigureImageColormap(display,resource_info,windows,*image);
8307       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8308       break;
8309     }
8310     case EdgeDetectCommand:
8311     {
8312       Image
8313         *edge_image;
8314
8315       static char
8316         radius[MaxTextExtent] = "0";
8317
8318       /*
8319         Query user for edge factor.
8320       */
8321       (void) XDialogWidget(display,windows,"Detect Edges",
8322         "Enter the edge detect radius:",radius);
8323       if (*radius == '\0')
8324         break;
8325       /*
8326         Detect edge in image.
8327       */
8328       XSetCursorState(display,windows,MagickTrue);
8329       XCheckRefreshWindows(display,windows);
8330       flags=ParseGeometry(radius,&geometry_info);
8331       edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8332         exception);
8333       if (edge_image != (Image *) NULL)
8334         {
8335           *image=DestroyImage(*image);
8336           *image=edge_image;
8337         }
8338       CatchException(exception);
8339       XSetCursorState(display,windows,MagickFalse);
8340       if (windows->image.orphan != MagickFalse)
8341         break;
8342       XConfigureImageColormap(display,resource_info,windows,*image);
8343       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8344       break;
8345     }
8346     case SpreadCommand:
8347     {
8348       Image
8349         *spread_image;
8350
8351       static char
8352         amount[MaxTextExtent] = "2";
8353
8354       /*
8355         Query user for spread amount.
8356       */
8357       (void) XDialogWidget(display,windows,"Spread",
8358         "Enter the displacement amount:",amount);
8359       if (*amount == '\0')
8360         break;
8361       /*
8362         Displace image pixels by a random amount.
8363       */
8364       XSetCursorState(display,windows,MagickTrue);
8365       XCheckRefreshWindows(display,windows);
8366       flags=ParseGeometry(amount,&geometry_info);
8367       spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8368         exception);
8369       if (spread_image != (Image *) NULL)
8370         {
8371           *image=DestroyImage(*image);
8372           *image=spread_image;
8373         }
8374       CatchException(exception);
8375       XSetCursorState(display,windows,MagickFalse);
8376       if (windows->image.orphan != MagickFalse)
8377         break;
8378       XConfigureImageColormap(display,resource_info,windows,*image);
8379       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8380       break;
8381     }
8382     case ShadeCommand:
8383     {
8384       Image
8385         *shade_image;
8386
8387       int
8388         status;
8389
8390       static char
8391         geometry[MaxTextExtent] = "30x30";
8392
8393       /*
8394         Query user for the shade geometry.
8395       */
8396       status=XDialogWidget(display,windows,"Shade",
8397         "Enter the azimuth and elevation of the light source:",geometry);
8398       if (*geometry == '\0')
8399         break;
8400       /*
8401         Shade image pixels.
8402       */
8403       XSetCursorState(display,windows,MagickTrue);
8404       XCheckRefreshWindows(display,windows);
8405       flags=ParseGeometry(geometry,&geometry_info);
8406       if ((flags & SigmaValue) == 0)
8407         geometry_info.sigma=1.0;
8408       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8409         geometry_info.rho,geometry_info.sigma,exception);
8410       if (shade_image != (Image *) NULL)
8411         {
8412           *image=DestroyImage(*image);
8413           *image=shade_image;
8414         }
8415       CatchException(exception);
8416       XSetCursorState(display,windows,MagickFalse);
8417       if (windows->image.orphan != MagickFalse)
8418         break;
8419       XConfigureImageColormap(display,resource_info,windows,*image);
8420       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8421       break;
8422     }
8423     case RaiseCommand:
8424     {
8425       static char
8426         bevel_width[MaxTextExtent] = "10";
8427
8428       /*
8429         Query user for bevel width.
8430       */
8431       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8432       if (*bevel_width == '\0')
8433         break;
8434       /*
8435         Raise an image.
8436       */
8437       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8438         exception);
8439       XSetCursorState(display,windows,MagickTrue);
8440       XCheckRefreshWindows(display,windows);
8441       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8442         exception);
8443       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8444       XSetCursorState(display,windows,MagickFalse);
8445       if (windows->image.orphan != MagickFalse)
8446         break;
8447       XConfigureImageColormap(display,resource_info,windows,*image);
8448       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8449       break;
8450     }
8451     case SegmentCommand:
8452     {
8453       static char
8454         threshold[MaxTextExtent] = "1.0x1.5";
8455
8456       /*
8457         Query user for smoothing threshold.
8458       */
8459       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8460         threshold);
8461       if (*threshold == '\0')
8462         break;
8463       /*
8464         Segment an image.
8465       */
8466       XSetCursorState(display,windows,MagickTrue);
8467       XCheckRefreshWindows(display,windows);
8468       flags=ParseGeometry(threshold,&geometry_info);
8469       if ((flags & SigmaValue) == 0)
8470         geometry_info.sigma=1.0;
8471       (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8472         geometry_info.sigma,exception);
8473       XSetCursorState(display,windows,MagickFalse);
8474       if (windows->image.orphan != MagickFalse)
8475         break;
8476       XConfigureImageColormap(display,resource_info,windows,*image);
8477       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8478       break;
8479     }
8480     case SepiaToneCommand:
8481     {
8482       double
8483         threshold;
8484
8485       Image
8486         *sepia_image;
8487
8488       static char
8489         factor[MaxTextExtent] = "80%";
8490
8491       /*
8492         Query user for sepia-tone factor.
8493       */
8494       (void) XDialogWidget(display,windows,"Sepia Tone",
8495         "Enter the sepia tone factor (0 - 99.9%):",factor);
8496       if (*factor == '\0')
8497         break;
8498       /*
8499         Sepia tone image pixels.
8500       */
8501       XSetCursorState(display,windows,MagickTrue);
8502       XCheckRefreshWindows(display,windows);
8503       threshold=SiPrefixToDouble(factor,QuantumRange);
8504       sepia_image=SepiaToneImage(*image,threshold,exception);
8505       if (sepia_image != (Image *) NULL)
8506         {
8507           *image=DestroyImage(*image);
8508           *image=sepia_image;
8509         }
8510       CatchException(exception);
8511       XSetCursorState(display,windows,MagickFalse);
8512       if (windows->image.orphan != MagickFalse)
8513         break;
8514       XConfigureImageColormap(display,resource_info,windows,*image);
8515       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8516       break;
8517     }
8518     case SolarizeCommand:
8519     {
8520       double
8521         threshold;
8522
8523       static char
8524         factor[MaxTextExtent] = "60%";
8525
8526       /*
8527         Query user for solarize factor.
8528       */
8529       (void) XDialogWidget(display,windows,"Solarize",
8530         "Enter the solarize factor (0 - 99.9%):",factor);
8531       if (*factor == '\0')
8532         break;
8533       /*
8534         Solarize image pixels.
8535       */
8536       XSetCursorState(display,windows,MagickTrue);
8537       XCheckRefreshWindows(display,windows);
8538       threshold=SiPrefixToDouble(factor,QuantumRange);
8539       (void) SolarizeImage(*image,threshold,exception);
8540       XSetCursorState(display,windows,MagickFalse);
8541       if (windows->image.orphan != MagickFalse)
8542         break;
8543       XConfigureImageColormap(display,resource_info,windows,*image);
8544       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8545       break;
8546     }
8547     case SwirlCommand:
8548     {
8549       Image
8550         *swirl_image;
8551
8552       static char
8553         degrees[MaxTextExtent] = "60";
8554
8555       /*
8556         Query user for swirl angle.
8557       */
8558       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8559         degrees);
8560       if (*degrees == '\0')
8561         break;
8562       /*
8563         Swirl image pixels about the center.
8564       */
8565       XSetCursorState(display,windows,MagickTrue);
8566       XCheckRefreshWindows(display,windows);
8567       flags=ParseGeometry(degrees,&geometry_info);
8568       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8569         exception);
8570       if (swirl_image != (Image *) NULL)
8571         {
8572           *image=DestroyImage(*image);
8573           *image=swirl_image;
8574         }
8575       CatchException(exception);
8576       XSetCursorState(display,windows,MagickFalse);
8577       if (windows->image.orphan != MagickFalse)
8578         break;
8579       XConfigureImageColormap(display,resource_info,windows,*image);
8580       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8581       break;
8582     }
8583     case ImplodeCommand:
8584     {
8585       Image
8586         *implode_image;
8587
8588       static char
8589         factor[MaxTextExtent] = "0.3";
8590
8591       /*
8592         Query user for implode factor.
8593       */
8594       (void) XDialogWidget(display,windows,"Implode",
8595         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8596       if (*factor == '\0')
8597         break;
8598       /*
8599         Implode image pixels about the center.
8600       */
8601       XSetCursorState(display,windows,MagickTrue);
8602       XCheckRefreshWindows(display,windows);
8603       flags=ParseGeometry(factor,&geometry_info);
8604       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8605         exception);
8606       if (implode_image != (Image *) NULL)
8607         {
8608           *image=DestroyImage(*image);
8609           *image=implode_image;
8610         }
8611       CatchException(exception);
8612       XSetCursorState(display,windows,MagickFalse);
8613       if (windows->image.orphan != MagickFalse)
8614         break;
8615       XConfigureImageColormap(display,resource_info,windows,*image);
8616       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8617       break;
8618     }
8619     case VignetteCommand:
8620     {
8621       Image
8622         *vignette_image;
8623
8624       static char
8625         geometry[MaxTextExtent] = "0x20";
8626
8627       /*
8628         Query user for the vignette geometry.
8629       */
8630       (void) XDialogWidget(display,windows,"Vignette",
8631         "Enter the radius, sigma, and x and y offsets:",geometry);
8632       if (*geometry == '\0')
8633         break;
8634       /*
8635         Soften the edges of the image in vignette style
8636       */
8637       XSetCursorState(display,windows,MagickTrue);
8638       XCheckRefreshWindows(display,windows);
8639       flags=ParseGeometry(geometry,&geometry_info);
8640       if ((flags & SigmaValue) == 0)
8641         geometry_info.sigma=1.0;
8642       if ((flags & XiValue) == 0)
8643         geometry_info.xi=0.1*(*image)->columns;
8644       if ((flags & PsiValue) == 0)
8645         geometry_info.psi=0.1*(*image)->rows;
8646       vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8647         (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8648         0.5),exception);
8649       if (vignette_image != (Image *) NULL)
8650         {
8651           *image=DestroyImage(*image);
8652           *image=vignette_image;
8653         }
8654       CatchException(exception);
8655       XSetCursorState(display,windows,MagickFalse);
8656       if (windows->image.orphan != MagickFalse)
8657         break;
8658       XConfigureImageColormap(display,resource_info,windows,*image);
8659       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8660       break;
8661     }
8662     case WaveCommand:
8663     {
8664       Image
8665         *wave_image;
8666
8667       static char
8668         geometry[MaxTextExtent] = "25x150";
8669
8670       /*
8671         Query user for the wave geometry.
8672       */
8673       (void) XDialogWidget(display,windows,"Wave",
8674         "Enter the amplitude and length of the wave:",geometry);
8675       if (*geometry == '\0')
8676         break;
8677       /*
8678         Alter an image along a sine wave.
8679       */
8680       XSetCursorState(display,windows,MagickTrue);
8681       XCheckRefreshWindows(display,windows);
8682       flags=ParseGeometry(geometry,&geometry_info);
8683       if ((flags & SigmaValue) == 0)
8684         geometry_info.sigma=1.0;
8685       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8686         (*image)->interpolate,exception);
8687       if (wave_image != (Image *) NULL)
8688         {
8689           *image=DestroyImage(*image);
8690           *image=wave_image;
8691         }
8692       CatchException(exception);
8693       XSetCursorState(display,windows,MagickFalse);
8694       if (windows->image.orphan != MagickFalse)
8695         break;
8696       XConfigureImageColormap(display,resource_info,windows,*image);
8697       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8698       break;
8699     }
8700     case OilPaintCommand:
8701     {
8702       Image
8703         *paint_image;
8704
8705       static char
8706         radius[MaxTextExtent] = "0";
8707
8708       /*
8709         Query user for circular neighborhood radius.
8710       */
8711       (void) XDialogWidget(display,windows,"Oil Paint",
8712         "Enter the mask radius:",radius);
8713       if (*radius == '\0')
8714         break;
8715       /*
8716         OilPaint image scanlines.
8717       */
8718       XSetCursorState(display,windows,MagickTrue);
8719       XCheckRefreshWindows(display,windows);
8720       flags=ParseGeometry(radius,&geometry_info);
8721       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8722         exception);
8723       if (paint_image != (Image *) NULL)
8724         {
8725           *image=DestroyImage(*image);
8726           *image=paint_image;
8727         }
8728       CatchException(exception);
8729       XSetCursorState(display,windows,MagickFalse);
8730       if (windows->image.orphan != MagickFalse)
8731         break;
8732       XConfigureImageColormap(display,resource_info,windows,*image);
8733       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8734       break;
8735     }
8736     case CharcoalDrawCommand:
8737     {
8738       Image
8739         *charcoal_image;
8740
8741       static char
8742         radius[MaxTextExtent] = "0x1";
8743
8744       /*
8745         Query user for charcoal radius.
8746       */
8747       (void) XDialogWidget(display,windows,"Charcoal Draw",
8748         "Enter the charcoal radius and sigma:",radius);
8749       if (*radius == '\0')
8750         break;
8751       /*
8752         Charcoal the image.
8753       */
8754       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8755         exception);
8756       XSetCursorState(display,windows,MagickTrue);
8757       XCheckRefreshWindows(display,windows);
8758       flags=ParseGeometry(radius,&geometry_info);
8759       if ((flags & SigmaValue) == 0)
8760         geometry_info.sigma=geometry_info.rho;
8761       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8762         geometry_info.xi,exception);
8763       if (charcoal_image != (Image *) NULL)
8764         {
8765           *image=DestroyImage(*image);
8766           *image=charcoal_image;
8767         }
8768       CatchException(exception);
8769       XSetCursorState(display,windows,MagickFalse);
8770       if (windows->image.orphan != MagickFalse)
8771         break;
8772       XConfigureImageColormap(display,resource_info,windows,*image);
8773       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8774       break;
8775     }
8776     case AnnotateCommand:
8777     {
8778       /*
8779         Annotate the image with text.
8780       */
8781       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8782       if (status == MagickFalse)
8783         {
8784           XNoticeWidget(display,windows,"Unable to annotate X image",
8785             (*image)->filename);
8786           break;
8787         }
8788       break;
8789     }
8790     case DrawCommand:
8791     {
8792       /*
8793         Draw image.
8794       */
8795       status=XDrawEditImage(display,resource_info,windows,image,exception);
8796       if (status == MagickFalse)
8797         {
8798           XNoticeWidget(display,windows,"Unable to draw on the X image",
8799             (*image)->filename);
8800           break;
8801         }
8802       break;
8803     }
8804     case ColorCommand:
8805     {
8806       /*
8807         Color edit.
8808       */
8809       status=XColorEditImage(display,resource_info,windows,image,exception);
8810       if (status == MagickFalse)
8811         {
8812           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8813             (*image)->filename);
8814           break;
8815         }
8816       break;
8817     }
8818     case MatteCommand:
8819     {
8820       /*
8821         Matte edit.
8822       */
8823       status=XMatteEditImage(display,resource_info,windows,image,exception);
8824       if (status == MagickFalse)
8825         {
8826           XNoticeWidget(display,windows,"Unable to matte edit X image",
8827             (*image)->filename);
8828           break;
8829         }
8830       break;
8831     }
8832     case CompositeCommand:
8833     {
8834       /*
8835         Composite image.
8836       */
8837       status=XCompositeImage(display,resource_info,windows,*image,
8838         exception);
8839       if (status == MagickFalse)
8840         {
8841           XNoticeWidget(display,windows,"Unable to composite X image",
8842             (*image)->filename);
8843           break;
8844         }
8845       break;
8846     }
8847     case AddBorderCommand:
8848     {
8849       Image
8850         *border_image;
8851
8852       static char
8853         geometry[MaxTextExtent] = "6x6";
8854
8855       /*
8856         Query user for border color and geometry.
8857       */
8858       XColorBrowserWidget(display,windows,"Select",color);
8859       if (*color == '\0')
8860         break;
8861       (void) XDialogWidget(display,windows,"Add Border",
8862         "Enter border geometry:",geometry);
8863       if (*geometry == '\0')
8864         break;
8865       /*
8866         Add a border to the image.
8867       */
8868       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8869         exception);
8870       XSetCursorState(display,windows,MagickTrue);
8871       XCheckRefreshWindows(display,windows);
8872       (void) QueryColorDatabase(color,&(*image)->border_color,
8873         exception);
8874       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8875         exception);
8876       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8877         exception);
8878       if (border_image != (Image *) NULL)
8879         {
8880           *image=DestroyImage(*image);
8881           *image=border_image;
8882         }
8883       CatchException(exception);
8884       XSetCursorState(display,windows,MagickFalse);
8885       if (windows->image.orphan != MagickFalse)
8886         break;
8887       windows->image.window_changes.width=(int) (*image)->columns;
8888       windows->image.window_changes.height=(int) (*image)->rows;
8889       XConfigureImageColormap(display,resource_info,windows,*image);
8890       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8891       break;
8892     }
8893     case AddFrameCommand:
8894     {
8895       FrameInfo
8896         frame_info;
8897
8898       Image
8899         *frame_image;
8900
8901       static char
8902         geometry[MaxTextExtent] = "6x6";
8903
8904       /*
8905         Query user for frame color and geometry.
8906       */
8907       XColorBrowserWidget(display,windows,"Select",color);
8908       if (*color == '\0')
8909         break;
8910       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8911         geometry);
8912       if (*geometry == '\0')
8913         break;
8914       /*
8915         Surround image with an ornamental border.
8916       */
8917       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8918         exception);
8919       XSetCursorState(display,windows,MagickTrue);
8920       XCheckRefreshWindows(display,windows);
8921       (void) QueryColorDatabase(color,&(*image)->matte_color,
8922         exception);
8923       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8924         exception);
8925       frame_info.width=page_geometry.width;
8926       frame_info.height=page_geometry.height;
8927       frame_info.outer_bevel=page_geometry.x;
8928       frame_info.inner_bevel=page_geometry.y;
8929       frame_info.x=(ssize_t) frame_info.width;
8930       frame_info.y=(ssize_t) frame_info.height;
8931       frame_info.width=(*image)->columns+2*frame_info.width;
8932       frame_info.height=(*image)->rows+2*frame_info.height;
8933       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8934       if (frame_image != (Image *) NULL)
8935         {
8936           *image=DestroyImage(*image);
8937           *image=frame_image;
8938         }
8939       CatchException(exception);
8940       XSetCursorState(display,windows,MagickFalse);
8941       if (windows->image.orphan != MagickFalse)
8942         break;
8943       windows->image.window_changes.width=(int) (*image)->columns;
8944       windows->image.window_changes.height=(int) (*image)->rows;
8945       XConfigureImageColormap(display,resource_info,windows,*image);
8946       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8947       break;
8948     }
8949     case CommentCommand:
8950     {
8951       const char
8952         *value;
8953
8954       FILE
8955         *file;
8956
8957       int
8958         unique_file;
8959
8960       /*
8961         Edit image comment.
8962       */
8963       unique_file=AcquireUniqueFileResource(image_info->filename);
8964       if (unique_file == -1)
8965         XNoticeWidget(display,windows,"Unable to edit image comment",
8966           image_info->filename);
8967       value=GetImageProperty(*image,"comment");
8968       if (value == (char *) NULL)
8969         unique_file=close(unique_file)-1;
8970       else
8971         {
8972           register const char
8973             *p;
8974
8975           file=fdopen(unique_file,"w");
8976           if (file == (FILE *) NULL)
8977             {
8978               XNoticeWidget(display,windows,"Unable to edit image comment",
8979                 image_info->filename);
8980               break;
8981             }
8982           for (p=value; *p != '\0'; p++)
8983             (void) fputc((int) *p,file);
8984           (void) fputc('\n',file);
8985           (void) fclose(file);
8986         }
8987       XSetCursorState(display,windows,MagickTrue);
8988       XCheckRefreshWindows(display,windows);
8989       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8990         exception);
8991       if (status == MagickFalse)
8992         XNoticeWidget(display,windows,"Unable to edit image comment",
8993           (char *) NULL);
8994       else
8995         {
8996           char
8997             *comment;
8998
8999           comment=FileToString(image_info->filename,~0UL,exception);
9000           if (comment != (char *) NULL)
9001             {
9002               (void) SetImageProperty(*image,"comment",comment);
9003               (*image)->taint=MagickTrue;
9004             }
9005         }
9006       (void) RelinquishUniqueFileResource(image_info->filename);
9007       XSetCursorState(display,windows,MagickFalse);
9008       break;
9009     }
9010     case LaunchCommand:
9011     {
9012       /*
9013         Launch program.
9014       */
9015       XSetCursorState(display,windows,MagickTrue);
9016       XCheckRefreshWindows(display,windows);
9017       (void) AcquireUniqueFilename(filename);
9018       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9019         filename);
9020       status=WriteImage(image_info,*image,exception);
9021       if (status == MagickFalse)
9022         XNoticeWidget(display,windows,"Unable to launch image editor",
9023           (char *) NULL);
9024       else
9025         {
9026           nexus=ReadImage(resource_info->image_info,exception);
9027           CatchException(exception);
9028           XClientMessage(display,windows->image.id,windows->im_protocols,
9029             windows->im_next_image,CurrentTime);
9030         }
9031       (void) RelinquishUniqueFileResource(filename);
9032       XSetCursorState(display,windows,MagickFalse);
9033       break;
9034     }
9035     case RegionofInterestCommand:
9036     {
9037       /*
9038         Apply an image processing technique to a region of interest.
9039       */
9040       (void) XROIImage(display,resource_info,windows,image,exception);
9041       break;
9042     }
9043     case InfoCommand:
9044       break;
9045     case ZoomCommand:
9046     {
9047       /*
9048         Zoom image.
9049       */
9050       if (windows->magnify.mapped != MagickFalse)
9051         (void) XRaiseWindow(display,windows->magnify.id);
9052       else
9053         {
9054           /*
9055             Make magnify image.
9056           */
9057           XSetCursorState(display,windows,MagickTrue);
9058           (void) XMapRaised(display,windows->magnify.id);
9059           XSetCursorState(display,windows,MagickFalse);
9060         }
9061       break;
9062     }
9063     case ShowPreviewCommand:
9064     {
9065       char
9066         **previews;
9067
9068       Image
9069         *preview_image;
9070
9071       static char
9072         preview_type[MaxTextExtent] = "Gamma";
9073
9074       /*
9075         Select preview type from menu.
9076       */
9077       previews=GetCommandOptions(MagickPreviewOptions);
9078       if (previews == (char **) NULL)
9079         break;
9080       XListBrowserWidget(display,windows,&windows->widget,
9081         (const char **) previews,"Preview",
9082         "Select an enhancement, effect, or F/X:",preview_type);
9083       previews=DestroyStringList(previews);
9084       if (*preview_type == '\0')
9085         break;
9086       /*
9087         Show image preview.
9088       */
9089       XSetCursorState(display,windows,MagickTrue);
9090       XCheckRefreshWindows(display,windows);
9091       image_info->preview_type=(PreviewType)
9092         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9093       image_info->group=(ssize_t) windows->image.id;
9094       (void) DeleteImageProperty(*image,"label");
9095       (void) SetImageProperty(*image,"label","Preview");
9096       (void) AcquireUniqueFilename(filename);
9097       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9098         filename);
9099       status=WriteImage(image_info,*image,exception);
9100       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9101       preview_image=ReadImage(image_info,exception);
9102       (void) RelinquishUniqueFileResource(filename);
9103       if (preview_image == (Image *) NULL)
9104         break;
9105       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9106         filename);
9107       status=WriteImage(image_info,preview_image,exception);
9108       preview_image=DestroyImage(preview_image);
9109       if (status == MagickFalse)
9110         XNoticeWidget(display,windows,"Unable to show image preview",
9111           (*image)->filename);
9112       XDelay(display,1500);
9113       XSetCursorState(display,windows,MagickFalse);
9114       break;
9115     }
9116     case ShowHistogramCommand:
9117     {
9118       Image
9119         *histogram_image;
9120
9121       /*
9122         Show image histogram.
9123       */
9124       XSetCursorState(display,windows,MagickTrue);
9125       XCheckRefreshWindows(display,windows);
9126       image_info->group=(ssize_t) windows->image.id;
9127       (void) DeleteImageProperty(*image,"label");
9128       (void) SetImageProperty(*image,"label","Histogram");
9129       (void) AcquireUniqueFilename(filename);
9130       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9131         filename);
9132       status=WriteImage(image_info,*image,exception);
9133       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9134       histogram_image=ReadImage(image_info,exception);
9135       (void) RelinquishUniqueFileResource(filename);
9136       if (histogram_image == (Image *) NULL)
9137         break;
9138       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9139         "show:%s",filename);
9140       status=WriteImage(image_info,histogram_image,exception);
9141       histogram_image=DestroyImage(histogram_image);
9142       if (status == MagickFalse)
9143         XNoticeWidget(display,windows,"Unable to show histogram",
9144           (*image)->filename);
9145       XDelay(display,1500);
9146       XSetCursorState(display,windows,MagickFalse);
9147       break;
9148     }
9149     case ShowMatteCommand:
9150     {
9151       Image
9152         *matte_image;
9153
9154       if ((*image)->matte == MagickFalse)
9155         {
9156           XNoticeWidget(display,windows,
9157             "Image does not have any matte information",(*image)->filename);
9158           break;
9159         }
9160       /*
9161         Show image matte.
9162       */
9163       XSetCursorState(display,windows,MagickTrue);
9164       XCheckRefreshWindows(display,windows);
9165       image_info->group=(ssize_t) windows->image.id;
9166       (void) DeleteImageProperty(*image,"label");
9167       (void) SetImageProperty(*image,"label","Matte");
9168       (void) AcquireUniqueFilename(filename);
9169       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9170         filename);
9171       status=WriteImage(image_info,*image,exception);
9172       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9173       matte_image=ReadImage(image_info,exception);
9174       (void) RelinquishUniqueFileResource(filename);
9175       if (matte_image == (Image *) NULL)
9176         break;
9177       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9178         filename);
9179       status=WriteImage(image_info,matte_image,exception);
9180       matte_image=DestroyImage(matte_image);
9181       if (status == MagickFalse)
9182         XNoticeWidget(display,windows,"Unable to show matte",
9183           (*image)->filename);
9184       XDelay(display,1500);
9185       XSetCursorState(display,windows,MagickFalse);
9186       break;
9187     }
9188     case BackgroundCommand:
9189     {
9190       /*
9191         Background image.
9192       */
9193       status=XBackgroundImage(display,resource_info,windows,image,exception);
9194       if (status == MagickFalse)
9195         break;
9196       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9197       if (nexus != (Image *) NULL)
9198         XClientMessage(display,windows->image.id,windows->im_protocols,
9199           windows->im_next_image,CurrentTime);
9200       break;
9201     }
9202     case SlideShowCommand:
9203     {
9204       static char
9205         delay[MaxTextExtent] = "5";
9206
9207       /*
9208         Display next image after pausing.
9209       */
9210       (void) XDialogWidget(display,windows,"Slide Show",
9211         "Pause how many 1/100ths of a second between images:",delay);
9212       if (*delay == '\0')
9213         break;
9214       resource_info->delay=StringToUnsignedLong(delay);
9215       XClientMessage(display,windows->image.id,windows->im_protocols,
9216         windows->im_next_image,CurrentTime);
9217       break;
9218     }
9219     case PreferencesCommand:
9220     {
9221       /*
9222         Set user preferences.
9223       */
9224       status=XPreferencesWidget(display,resource_info,windows);
9225       if (status == MagickFalse)
9226         break;
9227       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9228       if (nexus != (Image *) NULL)
9229         XClientMessage(display,windows->image.id,windows->im_protocols,
9230           windows->im_next_image,CurrentTime);
9231       break;
9232     }
9233     case HelpCommand:
9234     {
9235       /*
9236         User requested help.
9237       */
9238       XTextViewWidget(display,resource_info,windows,MagickFalse,
9239         "Help Viewer - Display",DisplayHelp);
9240       break;
9241     }
9242     case BrowseDocumentationCommand:
9243     {
9244       Atom
9245         mozilla_atom;
9246
9247       Window
9248         mozilla_window,
9249         root_window;
9250
9251       /*
9252         Browse the ImageMagick documentation.
9253       */
9254       root_window=XRootWindow(display,XDefaultScreen(display));
9255       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9256       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9257       if (mozilla_window != (Window) NULL)
9258         {
9259           char
9260             command[MaxTextExtent],
9261             *url;
9262
9263           /*
9264             Display documentation using Netscape remote control.
9265           */
9266           url=GetMagickHomeURL();
9267           (void) FormatLocaleString(command,MaxTextExtent,
9268             "openurl(%s,new-tab)",url);
9269           url=DestroyString(url);
9270           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9271           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9272             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9273           XSetCursorState(display,windows,MagickFalse);
9274           break;
9275         }
9276       XSetCursorState(display,windows,MagickTrue);
9277       XCheckRefreshWindows(display,windows);
9278       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9279         exception);
9280       if (status == MagickFalse)
9281         XNoticeWidget(display,windows,"Unable to browse documentation",
9282           (char *) NULL);
9283       XDelay(display,1500);
9284       XSetCursorState(display,windows,MagickFalse);
9285       break;
9286     }
9287     case VersionCommand:
9288     {
9289       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9290         GetMagickCopyright());
9291       break;
9292     }
9293     case SaveToUndoBufferCommand:
9294       break;
9295     default:
9296     {
9297       (void) XBell(display,0);
9298       break;
9299     }
9300   }
9301   image_info=DestroyImageInfo(image_info);
9302   return(nexus);
9303 }
9304 \f
9305 /*
9306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9307 %                                                                             %
9308 %                                                                             %
9309 %                                                                             %
9310 +   X M a g n i f y I m a g e                                                 %
9311 %                                                                             %
9312 %                                                                             %
9313 %                                                                             %
9314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9315 %
9316 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9317 %  The magnified portion is displayed in a separate window.
9318 %
9319 %  The format of the XMagnifyImage method is:
9320 %
9321 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9322 %
9323 %  A description of each parameter follows:
9324 %
9325 %    o display: Specifies a connection to an X server;  returned from
9326 %      XOpenDisplay.
9327 %
9328 %    o windows: Specifies a pointer to a XWindows structure.
9329 %
9330 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9331 %      the entire image is refreshed.
9332 %
9333 */
9334 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9335 {
9336   char
9337     text[MaxTextExtent];
9338
9339   register int
9340     x,
9341     y;
9342
9343   size_t
9344     state;
9345
9346   /*
9347     Update magnified image until the mouse button is released.
9348   */
9349   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9350   state=DefaultState;
9351   x=event->xbutton.x;
9352   y=event->xbutton.y;
9353   windows->magnify.x=(int) windows->image.x+x;
9354   windows->magnify.y=(int) windows->image.y+y;
9355   do
9356   {
9357     /*
9358       Map and unmap Info widget as text cursor crosses its boundaries.
9359     */
9360     if (windows->info.mapped != MagickFalse)
9361       {
9362         if ((x < (int) (windows->info.x+windows->info.width)) &&
9363             (y < (int) (windows->info.y+windows->info.height)))
9364           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9365       }
9366     else
9367       if ((x > (int) (windows->info.x+windows->info.width)) ||
9368           (y > (int) (windows->info.y+windows->info.height)))
9369         (void) XMapWindow(display,windows->info.id);
9370     if (windows->info.mapped != MagickFalse)
9371       {
9372         /*
9373           Display pointer position.
9374         */
9375         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9376           windows->magnify.x,windows->magnify.y);
9377         XInfoWidget(display,windows,text);
9378       }
9379     /*
9380       Wait for next event.
9381     */
9382     XScreenEvent(display,windows,event);
9383     switch (event->type)
9384     {
9385       case ButtonPress:
9386         break;
9387       case ButtonRelease:
9388       {
9389         /*
9390           User has finished magnifying image.
9391         */
9392         x=event->xbutton.x;
9393         y=event->xbutton.y;
9394         state|=ExitState;
9395         break;
9396       }
9397       case Expose:
9398         break;
9399       case MotionNotify:
9400       {
9401         x=event->xmotion.x;
9402         y=event->xmotion.y;
9403         break;
9404       }
9405       default:
9406         break;
9407     }
9408     /*
9409       Check boundary conditions.
9410     */
9411     if (x < 0)
9412       x=0;
9413     else
9414       if (x >= (int) windows->image.width)
9415         x=(int) windows->image.width-1;
9416     if (y < 0)
9417       y=0;
9418     else
9419      if (y >= (int) windows->image.height)
9420        y=(int) windows->image.height-1;
9421   } while ((state & ExitState) == 0);
9422   /*
9423     Display magnified image.
9424   */
9425   XSetCursorState(display,windows,MagickFalse);
9426 }
9427 \f
9428 /*
9429 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9430 %                                                                             %
9431 %                                                                             %
9432 %                                                                             %
9433 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9434 %                                                                             %
9435 %                                                                             %
9436 %                                                                             %
9437 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9438 %
9439 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9440 %  pixel as specified by the key symbol.
9441 %
9442 %  The format of the XMagnifyWindowCommand method is:
9443 %
9444 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9445 %        const MagickStatusType state,const KeySym key_symbol)
9446 %
9447 %  A description of each parameter follows:
9448 %
9449 %    o display: Specifies a connection to an X server; returned from
9450 %      XOpenDisplay.
9451 %
9452 %    o windows: Specifies a pointer to a XWindows structure.
9453 %
9454 %    o state: key mask.
9455 %
9456 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9457 %      to trim.
9458 %
9459 */
9460 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9461   const MagickStatusType state,const KeySym key_symbol)
9462 {
9463   unsigned int
9464     quantum;
9465
9466   /*
9467     User specified a magnify factor or position.
9468   */
9469   quantum=1;
9470   if ((state & Mod1Mask) != 0)
9471     quantum=10;
9472   switch ((int) key_symbol)
9473   {
9474     case QuitCommand:
9475     {
9476       (void) XWithdrawWindow(display,windows->magnify.id,
9477         windows->magnify.screen);
9478       break;
9479     }
9480     case XK_Home:
9481     case XK_KP_Home:
9482     {
9483       windows->magnify.x=(int) windows->image.width/2;
9484       windows->magnify.y=(int) windows->image.height/2;
9485       break;
9486     }
9487     case XK_Left:
9488     case XK_KP_Left:
9489     {
9490       if (windows->magnify.x > 0)
9491         windows->magnify.x-=quantum;
9492       break;
9493     }
9494     case XK_Up:
9495     case XK_KP_Up:
9496     {
9497       if (windows->magnify.y > 0)
9498         windows->magnify.y-=quantum;
9499       break;
9500     }
9501     case XK_Right:
9502     case XK_KP_Right:
9503     {
9504       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9505         windows->magnify.x+=quantum;
9506       break;
9507     }
9508     case XK_Down:
9509     case XK_KP_Down:
9510     {
9511       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9512         windows->magnify.y+=quantum;
9513       break;
9514     }
9515     case XK_0:
9516     case XK_1:
9517     case XK_2:
9518     case XK_3:
9519     case XK_4:
9520     case XK_5:
9521     case XK_6:
9522     case XK_7:
9523     case XK_8:
9524     case XK_9:
9525     {
9526       windows->magnify.data=(key_symbol-XK_0);
9527       break;
9528     }
9529     case XK_KP_0:
9530     case XK_KP_1:
9531     case XK_KP_2:
9532     case XK_KP_3:
9533     case XK_KP_4:
9534     case XK_KP_5:
9535     case XK_KP_6:
9536     case XK_KP_7:
9537     case XK_KP_8:
9538     case XK_KP_9:
9539     {
9540       windows->magnify.data=(key_symbol-XK_KP_0);
9541       break;
9542     }
9543     default:
9544       break;
9545   }
9546   XMakeMagnifyImage(display,windows);
9547 }
9548 \f
9549 /*
9550 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9551 %                                                                             %
9552 %                                                                             %
9553 %                                                                             %
9554 +   X M a k e P a n I m a g e                                                 %
9555 %                                                                             %
9556 %                                                                             %
9557 %                                                                             %
9558 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9559 %
9560 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9561 %  icon window.
9562 %
9563 %  The format of the XMakePanImage method is:
9564 %
9565 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9566 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9567 %
9568 %  A description of each parameter follows:
9569 %
9570 %    o display: Specifies a connection to an X server;  returned from
9571 %      XOpenDisplay.
9572 %
9573 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9574 %
9575 %    o windows: Specifies a pointer to a XWindows structure.
9576 %
9577 %    o image: the image.
9578 %
9579 %    o exception: return any errors or warnings in this structure.
9580 %
9581 */
9582 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9583   XWindows *windows,Image *image,ExceptionInfo *exception)
9584 {
9585   MagickStatusType
9586     status;
9587
9588   /*
9589     Create and display image for panning icon.
9590   */
9591   XSetCursorState(display,windows,MagickTrue);
9592   XCheckRefreshWindows(display,windows);
9593   windows->pan.x=(int) windows->image.x;
9594   windows->pan.y=(int) windows->image.y;
9595   status=XMakeImage(display,resource_info,&windows->pan,image,
9596     windows->pan.width,windows->pan.height,exception);
9597   if (status == MagickFalse)
9598     ThrowXWindowFatalException(ResourceLimitError,
9599      "MemoryAllocationFailed",image->filename);
9600   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9601     windows->pan.pixmap);
9602   (void) XClearWindow(display,windows->pan.id);
9603   XDrawPanRectangle(display,windows);
9604   XSetCursorState(display,windows,MagickFalse);
9605 }
9606 \f
9607 /*
9608 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9609 %                                                                             %
9610 %                                                                             %
9611 %                                                                             %
9612 +   X M a t t a E d i t I m a g e                                             %
9613 %                                                                             %
9614 %                                                                             %
9615 %                                                                             %
9616 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9617 %
9618 %  XMatteEditImage() allows the user to interactively change the Matte channel
9619 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9620 %  before the matte information is stored.
9621 %
9622 %  The format of the XMatteEditImage method is:
9623 %
9624 %      MagickBooleanType XMatteEditImage(Display *display,
9625 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9626 %        ExceptionInfo *exception)
9627 %
9628 %  A description of each parameter follows:
9629 %
9630 %    o display: Specifies a connection to an X server;  returned from
9631 %      XOpenDisplay.
9632 %
9633 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9634 %
9635 %    o windows: Specifies a pointer to a XWindows structure.
9636 %
9637 %    o image: the image; returned from ReadImage.
9638 %
9639 %    o exception: return any errors or warnings in this structure.
9640 %
9641 */
9642 static MagickBooleanType XMatteEditImage(Display *display,
9643   XResourceInfo *resource_info,XWindows *windows,Image **image,
9644   ExceptionInfo *exception)
9645 {
9646   static char
9647     matte[MaxTextExtent] = "0";
9648
9649   static const char
9650     *MatteEditMenu[] =
9651     {
9652       "Method",
9653       "Border Color",
9654       "Fuzz",
9655       "Matte Value",
9656       "Undo",
9657       "Help",
9658       "Dismiss",
9659       (char *) NULL
9660     };
9661
9662   static const ModeType
9663     MatteEditCommands[] =
9664     {
9665       MatteEditMethod,
9666       MatteEditBorderCommand,
9667       MatteEditFuzzCommand,
9668       MatteEditValueCommand,
9669       MatteEditUndoCommand,
9670       MatteEditHelpCommand,
9671       MatteEditDismissCommand
9672     };
9673
9674   static PaintMethod
9675     method = PointMethod;
9676
9677   static XColor
9678     border_color = { 0, 0, 0, 0, 0, 0 };
9679
9680   char
9681     command[MaxTextExtent],
9682     text[MaxTextExtent];
9683
9684   Cursor
9685     cursor;
9686
9687   int
9688     entry,
9689     id,
9690     x,
9691     x_offset,
9692     y,
9693     y_offset;
9694
9695   register int
9696     i;
9697
9698   register Quantum
9699     *q;
9700
9701   unsigned int
9702     height,
9703     width;
9704
9705   size_t
9706     state;
9707
9708   XEvent
9709     event;
9710
9711   /*
9712     Map Command widget.
9713   */
9714   (void) CloneString(&windows->command.name,"Matte Edit");
9715   windows->command.data=4;
9716   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9717   (void) XMapRaised(display,windows->command.id);
9718   XClientMessage(display,windows->image.id,windows->im_protocols,
9719     windows->im_update_widget,CurrentTime);
9720   /*
9721     Make cursor.
9722   */
9723   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9724     resource_info->background_color,resource_info->foreground_color);
9725   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9726   /*
9727     Track pointer until button 1 is pressed.
9728   */
9729   XQueryPosition(display,windows->image.id,&x,&y);
9730   (void) XSelectInput(display,windows->image.id,
9731     windows->image.attributes.event_mask | PointerMotionMask);
9732   state=DefaultState;
9733   do
9734   {
9735     if (windows->info.mapped != MagickFalse)
9736       {
9737         /*
9738           Display pointer position.
9739         */
9740         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9741           x+windows->image.x,y+windows->image.y);
9742         XInfoWidget(display,windows,text);
9743       }
9744     /*
9745       Wait for next event.
9746     */
9747     XScreenEvent(display,windows,&event);
9748     if (event.xany.window == windows->command.id)
9749       {
9750         /*
9751           Select a command from the Command widget.
9752         */
9753         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9754         if (id < 0)
9755           {
9756             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9757             continue;
9758           }
9759         switch (MatteEditCommands[id])
9760         {
9761           case MatteEditMethod:
9762           {
9763             char
9764               **methods;
9765
9766             /*
9767               Select a method from the pop-up menu.
9768             */
9769             methods=GetCommandOptions(MagickMethodOptions);
9770             if (methods == (char **) NULL)
9771               break;
9772             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9773               (const char **) methods,command);
9774             if (entry >= 0)
9775               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9776                 MagickFalse,methods[entry]);
9777             methods=DestroyStringList(methods);
9778             break;
9779           }
9780           case MatteEditBorderCommand:
9781           {
9782             const char
9783               *ColorMenu[MaxNumberPens];
9784
9785             int
9786               pen_number;
9787
9788             /*
9789               Initialize menu selections.
9790             */
9791             for (i=0; i < (int) (MaxNumberPens-2); i++)
9792               ColorMenu[i]=resource_info->pen_colors[i];
9793             ColorMenu[MaxNumberPens-2]="Browser...";
9794             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9795             /*
9796               Select a pen color from the pop-up menu.
9797             */
9798             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9799               (const char **) ColorMenu,command);
9800             if (pen_number < 0)
9801               break;
9802             if (pen_number == (MaxNumberPens-2))
9803               {
9804                 static char
9805                   color_name[MaxTextExtent] = "gray";
9806
9807                 /*
9808                   Select a pen color from a dialog.
9809                 */
9810                 resource_info->pen_colors[pen_number]=color_name;
9811                 XColorBrowserWidget(display,windows,"Select",color_name);
9812                 if (*color_name == '\0')
9813                   break;
9814               }
9815             /*
9816               Set border color.
9817             */
9818             (void) XParseColor(display,windows->map_info->colormap,
9819               resource_info->pen_colors[pen_number],&border_color);
9820             break;
9821           }
9822           case MatteEditFuzzCommand:
9823           {
9824             static char
9825               fuzz[MaxTextExtent];
9826
9827             static const char
9828               *FuzzMenu[] =
9829               {
9830                 "0%",
9831                 "2%",
9832                 "5%",
9833                 "10%",
9834                 "15%",
9835                 "Dialog...",
9836                 (char *) NULL,
9837               };
9838
9839             /*
9840               Select a command from the pop-up menu.
9841             */
9842             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9843               command);
9844             if (entry < 0)
9845               break;
9846             if (entry != 5)
9847               {
9848                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
9849                   QuantumRange+1.0);
9850                 break;
9851               }
9852             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9853             (void) XDialogWidget(display,windows,"Ok",
9854               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9855             if (*fuzz == '\0')
9856               break;
9857             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9858             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9859             break;
9860           }
9861           case MatteEditValueCommand:
9862           {
9863             static char
9864               message[MaxTextExtent];
9865
9866             static const char
9867               *MatteMenu[] =
9868               {
9869                 "Opaque",
9870                 "Transparent",
9871                 "Dialog...",
9872                 (char *) NULL,
9873               };
9874
9875             /*
9876               Select a command from the pop-up menu.
9877             */
9878             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9879               command);
9880             if (entry < 0)
9881               break;
9882             if (entry != 2)
9883               {
9884                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9885                   OpaqueAlpha);
9886                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9887                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9888                     (Quantum) TransparentAlpha);
9889                 break;
9890               }
9891             (void) FormatLocaleString(message,MaxTextExtent,
9892               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9893               QuantumRange);
9894             (void) XDialogWidget(display,windows,"Matte",message,matte);
9895             if (*matte == '\0')
9896               break;
9897             break;
9898           }
9899           case MatteEditUndoCommand:
9900           {
9901             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9902               image,exception);
9903             break;
9904           }
9905           case MatteEditHelpCommand:
9906           {
9907             XTextViewWidget(display,resource_info,windows,MagickFalse,
9908               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9909             break;
9910           }
9911           case MatteEditDismissCommand:
9912           {
9913             /*
9914               Prematurely exit.
9915             */
9916             state|=EscapeState;
9917             state|=ExitState;
9918             break;
9919           }
9920           default:
9921             break;
9922         }
9923         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9924         continue;
9925       }
9926     switch (event.type)
9927     {
9928       case ButtonPress:
9929       {
9930         if (event.xbutton.button != Button1)
9931           break;
9932         if ((event.xbutton.window != windows->image.id) &&
9933             (event.xbutton.window != windows->magnify.id))
9934           break;
9935         /*
9936           Update matte data.
9937         */
9938         x=event.xbutton.x;
9939         y=event.xbutton.y;
9940         (void) XMagickCommand(display,resource_info,windows,
9941           SaveToUndoBufferCommand,image,exception);
9942         state|=UpdateConfigurationState;
9943         break;
9944       }
9945       case ButtonRelease:
9946       {
9947         if (event.xbutton.button != Button1)
9948           break;
9949         if ((event.xbutton.window != windows->image.id) &&
9950             (event.xbutton.window != windows->magnify.id))
9951           break;
9952         /*
9953           Update colormap information.
9954         */
9955         x=event.xbutton.x;
9956         y=event.xbutton.y;
9957         XConfigureImageColormap(display,resource_info,windows,*image);
9958         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9959         XInfoWidget(display,windows,text);
9960         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9961         state&=(~UpdateConfigurationState);
9962         break;
9963       }
9964       case Expose:
9965         break;
9966       case KeyPress:
9967       {
9968         char
9969           command[MaxTextExtent];
9970
9971         KeySym
9972           key_symbol;
9973
9974         if (event.xkey.window == windows->magnify.id)
9975           {
9976             Window
9977               window;
9978
9979             window=windows->magnify.id;
9980             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9981           }
9982         if (event.xkey.window != windows->image.id)
9983           break;
9984         /*
9985           Respond to a user key press.
9986         */
9987         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9988           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9989         switch ((int) key_symbol)
9990         {
9991           case XK_Escape:
9992           case XK_F20:
9993           {
9994             /*
9995               Prematurely exit.
9996             */
9997             state|=ExitState;
9998             break;
9999           }
10000           case XK_F1:
10001           case XK_Help:
10002           {
10003             XTextViewWidget(display,resource_info,windows,MagickFalse,
10004               "Help Viewer - Matte Edit",ImageMatteEditHelp);
10005             break;
10006           }
10007           default:
10008           {
10009             (void) XBell(display,0);
10010             break;
10011           }
10012         }
10013         break;
10014       }
10015       case MotionNotify:
10016       {
10017         /*
10018           Map and unmap Info widget as cursor crosses its boundaries.
10019         */
10020         x=event.xmotion.x;
10021         y=event.xmotion.y;
10022         if (windows->info.mapped != MagickFalse)
10023           {
10024             if ((x < (int) (windows->info.x+windows->info.width)) &&
10025                 (y < (int) (windows->info.y+windows->info.height)))
10026               (void) XWithdrawWindow(display,windows->info.id,
10027                 windows->info.screen);
10028           }
10029         else
10030           if ((x > (int) (windows->info.x+windows->info.width)) ||
10031               (y > (int) (windows->info.y+windows->info.height)))
10032             (void) XMapWindow(display,windows->info.id);
10033         break;
10034       }
10035       default:
10036         break;
10037     }
10038     if (event.xany.window == windows->magnify.id)
10039       {
10040         x=windows->magnify.x-windows->image.x;
10041         y=windows->magnify.y-windows->image.y;
10042       }
10043     x_offset=x;
10044     y_offset=y;
10045     if ((state & UpdateConfigurationState) != 0)
10046       {
10047         CacheView
10048           *image_view;
10049
10050         int
10051           x,
10052           y;
10053
10054         /*
10055           Matte edit is relative to image configuration.
10056         */
10057         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10058           MagickTrue);
10059         XPutPixel(windows->image.ximage,x_offset,y_offset,
10060           windows->pixel_info->background_color.pixel);
10061         width=(unsigned int) (*image)->columns;
10062         height=(unsigned int) (*image)->rows;
10063         x=0;
10064         y=0;
10065         if (windows->image.crop_geometry != (char *) NULL)
10066           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10067             &height);
10068         x_offset=(int) (width*(windows->image.x+x_offset)/
10069           windows->image.ximage->width+x);
10070         y_offset=(int) (height*(windows->image.y+y_offset)/
10071           windows->image.ximage->height+y);
10072         if ((x_offset < 0) || (y_offset < 0))
10073           continue;
10074         if ((x_offset >= (int) (*image)->columns) ||
10075             (y_offset >= (int) (*image)->rows))
10076           continue;
10077         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10078           return(MagickFalse);
10079         (*image)->matte=MagickTrue;
10080         image_view=AcquireCacheView(*image);
10081         switch (method)
10082         {
10083           case PointMethod:
10084           default:
10085           {
10086             /*
10087               Update matte information using point algorithm.
10088             */
10089             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10090               (ssize_t) y_offset,1,1,exception);
10091             if (q == (Quantum *) NULL)
10092               break;
10093             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10094             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10095             break;
10096           }
10097           case ReplaceMethod:
10098           {
10099             PixelPacket
10100               pixel,
10101               target;
10102
10103             /*
10104               Update matte information using replace algorithm.
10105             */
10106             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10107               (ssize_t) y_offset,&target,exception);
10108             for (y=0; y < (int) (*image)->rows; y++)
10109             {
10110               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10111                 (*image)->columns,1,exception);
10112               if (q == (Quantum *) NULL)
10113                 break;
10114               for (x=0; x < (int) (*image)->columns; x++)
10115               {
10116                 GetPixelPacket(*image,q,&pixel);
10117                 if (IsFuzzyEquivalencePixelPacket(*image,&pixel,&target))
10118                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10119                 q+=GetPixelChannels(*image);
10120               }
10121               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10122                 break;
10123             }
10124             break;
10125           }
10126           case FloodfillMethod:
10127           case FillToBorderMethod:
10128           {
10129             ChannelType
10130               channel_mask;
10131
10132             DrawInfo
10133               *draw_info;
10134
10135             PixelInfo
10136               target;
10137
10138             /*
10139               Update matte information using floodfill algorithm.
10140             */
10141             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10142               (ssize_t) y_offset,&target,exception);
10143             if (method == FillToBorderMethod)
10144               {
10145                 target.red=(MagickRealType) ScaleShortToQuantum(
10146                   border_color.red);
10147                 target.green=(MagickRealType) ScaleShortToQuantum(
10148                   border_color.green);
10149                 target.blue=(MagickRealType) ScaleShortToQuantum(
10150                   border_color.blue);
10151               }
10152             draw_info=CloneDrawInfo(resource_info->image_info,
10153               (DrawInfo *) NULL);
10154             draw_info->fill.alpha=ClampToQuantum(InterpretLocaleValue(matte,
10155               (char **) NULL));
10156             channel_mask=SetPixelChannelMask(*image,AlphaChannel); 
10157             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10158               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10159               MagickFalse : MagickTrue,exception);
10160             (void) SetPixelChannelMap(*image,channel_mask);
10161             draw_info=DestroyDrawInfo(draw_info);
10162             break;
10163           }
10164           case ResetMethod:
10165           {
10166             /*
10167               Update matte information using reset algorithm.
10168             */
10169             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10170               return(MagickFalse);
10171             for (y=0; y < (int) (*image)->rows; y++)
10172             {
10173               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10174                 (*image)->columns,1,exception);
10175               if (q == (Quantum *) NULL)
10176                 break;
10177               for (x=0; x < (int) (*image)->columns; x++)
10178               {
10179                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10180                 q+=GetPixelChannels(*image);
10181               }
10182               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10183                 break;
10184             }
10185             if (StringToLong(matte) == (long) OpaqueAlpha)
10186               (*image)->matte=MagickFalse;
10187             break;
10188           }
10189         }
10190         image_view=DestroyCacheView(image_view);
10191         state&=(~UpdateConfigurationState);
10192       }
10193   } while ((state & ExitState) == 0);
10194   (void) XSelectInput(display,windows->image.id,
10195     windows->image.attributes.event_mask);
10196   XSetCursorState(display,windows,MagickFalse);
10197   (void) XFreeCursor(display,cursor);
10198   return(MagickTrue);
10199 }
10200 \f
10201 /*
10202 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10203 %                                                                             %
10204 %                                                                             %
10205 %                                                                             %
10206 +   X O p e n I m a g e                                                       %
10207 %                                                                             %
10208 %                                                                             %
10209 %                                                                             %
10210 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10211 %
10212 %  XOpenImage() loads an image from a file.
10213 %
10214 %  The format of the XOpenImage method is:
10215 %
10216 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10217 %       XWindows *windows,const unsigned int command)
10218 %
10219 %  A description of each parameter follows:
10220 %
10221 %    o display: Specifies a connection to an X server; returned from
10222 %      XOpenDisplay.
10223 %
10224 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10225 %
10226 %    o windows: Specifies a pointer to a XWindows structure.
10227 %
10228 %    o command: A value other than zero indicates that the file is selected
10229 %      from the command line argument list.
10230 %
10231 */
10232 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10233   XWindows *windows,const MagickBooleanType command)
10234 {
10235   const MagickInfo
10236     *magick_info;
10237
10238   ExceptionInfo
10239     *exception;
10240
10241   Image
10242     *nexus;
10243
10244   ImageInfo
10245     *image_info;
10246
10247   static char
10248     filename[MaxTextExtent] = "\0";
10249
10250   /*
10251     Request file name from user.
10252   */
10253   if (command == MagickFalse)
10254     XFileBrowserWidget(display,windows,"Open",filename);
10255   else
10256     {
10257       char
10258         **filelist,
10259         **files;
10260
10261       int
10262         count,
10263         status;
10264
10265       register int
10266         i,
10267         j;
10268
10269       /*
10270         Select next image from the command line.
10271       */
10272       status=XGetCommand(display,windows->image.id,&files,&count);
10273       if (status == 0)
10274         {
10275           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10276           return((Image *) NULL);
10277         }
10278       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10279       if (filelist == (char **) NULL)
10280         {
10281           ThrowXWindowFatalException(ResourceLimitError,
10282             "MemoryAllocationFailed","...");
10283           (void) XFreeStringList(files);
10284           return((Image *) NULL);
10285         }
10286       j=0;
10287       for (i=1; i < count; i++)
10288         if (*files[i] != '-')
10289           filelist[j++]=files[i];
10290       filelist[j]=(char *) NULL;
10291       XListBrowserWidget(display,windows,&windows->widget,
10292         (const char **) filelist,"Load","Select Image to Load:",filename);
10293       filelist=(char **) RelinquishMagickMemory(filelist);
10294       (void) XFreeStringList(files);
10295     }
10296   if (*filename == '\0')
10297     return((Image *) NULL);
10298   image_info=CloneImageInfo(resource_info->image_info);
10299   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10300     (void *) NULL);
10301   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10302   exception=AcquireExceptionInfo();
10303   (void) SetImageInfo(image_info,0,exception);
10304   if (LocaleCompare(image_info->magick,"X") == 0)
10305     {
10306       char
10307         seconds[MaxTextExtent];
10308
10309       /*
10310         User may want to delay the X server screen grab.
10311       */
10312       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10313       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10314         seconds);
10315       if (*seconds == '\0')
10316         return((Image *) NULL);
10317       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10318     }
10319   magick_info=GetMagickInfo(image_info->magick,exception);
10320   if ((magick_info != (const MagickInfo *) NULL) &&
10321       (magick_info->raw != MagickFalse))
10322     {
10323       char
10324         geometry[MaxTextExtent];
10325
10326       /*
10327         Request image size from the user.
10328       */
10329       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10330       if (image_info->size != (char *) NULL)
10331         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10332       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10333         geometry);
10334       (void) CloneString(&image_info->size,geometry);
10335     }
10336   /*
10337     Load the image.
10338   */
10339   XSetCursorState(display,windows,MagickTrue);
10340   XCheckRefreshWindows(display,windows);
10341   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10342   nexus=ReadImage(image_info,exception);
10343   CatchException(exception);
10344   XSetCursorState(display,windows,MagickFalse);
10345   if (nexus != (Image *) NULL)
10346     XClientMessage(display,windows->image.id,windows->im_protocols,
10347       windows->im_next_image,CurrentTime);
10348   else
10349     {
10350       char
10351         *text,
10352         **textlist;
10353
10354       /*
10355         Unknown image format.
10356       */
10357       text=FileToString(filename,~0,exception);
10358       if (text == (char *) NULL)
10359         return((Image *) NULL);
10360       textlist=StringToList(text);
10361       if (textlist != (char **) NULL)
10362         {
10363           char
10364             title[MaxTextExtent];
10365
10366           register int
10367             i;
10368
10369           (void) FormatLocaleString(title,MaxTextExtent,
10370             "Unknown format: %s",filename);
10371           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10372             (const char **) textlist);
10373           for (i=0; textlist[i] != (char *) NULL; i++)
10374             textlist[i]=DestroyString(textlist[i]);
10375           textlist=(char **) RelinquishMagickMemory(textlist);
10376         }
10377       text=DestroyString(text);
10378     }
10379   exception=DestroyExceptionInfo(exception);
10380   image_info=DestroyImageInfo(image_info);
10381   return(nexus);
10382 }
10383 \f
10384 /*
10385 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10386 %                                                                             %
10387 %                                                                             %
10388 %                                                                             %
10389 +   X P a n I m a g e                                                         %
10390 %                                                                             %
10391 %                                                                             %
10392 %                                                                             %
10393 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10394 %
10395 %  XPanImage() pans the image until the mouse button is released.
10396 %
10397 %  The format of the XPanImage method is:
10398 %
10399 %      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10400 %
10401 %  A description of each parameter follows:
10402 %
10403 %    o display: Specifies a connection to an X server;  returned from
10404 %      XOpenDisplay.
10405 %
10406 %    o windows: Specifies a pointer to a XWindows structure.
10407 %
10408 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10409 %      the entire image is refreshed.
10410 %
10411 */
10412 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10413 {
10414   char
10415     text[MaxTextExtent];
10416
10417   Cursor
10418     cursor;
10419
10420   MagickRealType
10421     x_factor,
10422     y_factor;
10423
10424   RectangleInfo
10425     pan_info;
10426
10427   size_t
10428     state;
10429
10430   /*
10431     Define cursor.
10432   */
10433   if ((windows->image.ximage->width > (int) windows->image.width) &&
10434       (windows->image.ximage->height > (int) windows->image.height))
10435     cursor=XCreateFontCursor(display,XC_fleur);
10436   else
10437     if (windows->image.ximage->width > (int) windows->image.width)
10438       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10439     else
10440       if (windows->image.ximage->height > (int) windows->image.height)
10441         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10442       else
10443         cursor=XCreateFontCursor(display,XC_arrow);
10444   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10445   /*
10446     Pan image as pointer moves until the mouse button is released.
10447   */
10448   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10449   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10450   pan_info.width=windows->pan.width*windows->image.width/
10451     windows->image.ximage->width;
10452   pan_info.height=windows->pan.height*windows->image.height/
10453     windows->image.ximage->height;
10454   pan_info.x=0;
10455   pan_info.y=0;
10456   state=UpdateConfigurationState;
10457   do
10458   {
10459     switch (event->type)
10460     {
10461       case ButtonPress:
10462       {
10463         /*
10464           User choose an initial pan location.
10465         */
10466         pan_info.x=(ssize_t) event->xbutton.x;
10467         pan_info.y=(ssize_t) event->xbutton.y;
10468         state|=UpdateConfigurationState;
10469         break;
10470       }
10471       case ButtonRelease:
10472       {
10473         /*
10474           User has finished panning the image.
10475         */
10476         pan_info.x=(ssize_t) event->xbutton.x;
10477         pan_info.y=(ssize_t) event->xbutton.y;
10478         state|=UpdateConfigurationState | ExitState;
10479         break;
10480       }
10481       case MotionNotify:
10482       {
10483         pan_info.x=(ssize_t) event->xmotion.x;
10484         pan_info.y=(ssize_t) event->xmotion.y;
10485         state|=UpdateConfigurationState;
10486       }
10487       default:
10488         break;
10489     }
10490     if ((state & UpdateConfigurationState) != 0)
10491       {
10492         /*
10493           Check boundary conditions.
10494         */
10495         if (pan_info.x < (ssize_t) (pan_info.width/2))
10496           pan_info.x=0;
10497         else
10498           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10499         if (pan_info.x < 0)
10500           pan_info.x=0;
10501         else
10502           if ((int) (pan_info.x+windows->image.width) >
10503               windows->image.ximage->width)
10504             pan_info.x=(ssize_t)
10505               (windows->image.ximage->width-windows->image.width);
10506         if (pan_info.y < (ssize_t) (pan_info.height/2))
10507           pan_info.y=0;
10508         else
10509           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10510         if (pan_info.y < 0)
10511           pan_info.y=0;
10512         else
10513           if ((int) (pan_info.y+windows->image.height) >
10514               windows->image.ximage->height)
10515             pan_info.y=(ssize_t)
10516               (windows->image.ximage->height-windows->image.height);
10517         if ((windows->image.x != (int) pan_info.x) ||
10518             (windows->image.y != (int) pan_info.y))
10519           {
10520             /*
10521               Display image pan offset.
10522             */
10523             windows->image.x=(int) pan_info.x;
10524             windows->image.y=(int) pan_info.y;
10525             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10526               windows->image.width,windows->image.height,windows->image.x,
10527               windows->image.y);
10528             XInfoWidget(display,windows,text);
10529             /*
10530               Refresh Image window.
10531             */
10532             XDrawPanRectangle(display,windows);
10533             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10534           }
10535         state&=(~UpdateConfigurationState);
10536       }
10537     /*
10538       Wait for next event.
10539     */
10540     if ((state & ExitState) == 0)
10541       XScreenEvent(display,windows,event);
10542   } while ((state & ExitState) == 0);
10543   /*
10544     Restore cursor.
10545   */
10546   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10547   (void) XFreeCursor(display,cursor);
10548   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10549 }
10550 \f
10551 /*
10552 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10553 %                                                                             %
10554 %                                                                             %
10555 %                                                                             %
10556 +   X P a s t e I m a g e                                                     %
10557 %                                                                             %
10558 %                                                                             %
10559 %                                                                             %
10560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10561 %
10562 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10563 %  window image at a location the user chooses with the pointer.
10564 %
10565 %  The format of the XPasteImage method is:
10566 %
10567 %      MagickBooleanType XPasteImage(Display *display,
10568 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10569 %        ExceptionInfo *exception)
10570 %
10571 %  A description of each parameter follows:
10572 %
10573 %    o display: Specifies a connection to an X server;  returned from
10574 %      XOpenDisplay.
10575 %
10576 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10577 %
10578 %    o windows: Specifies a pointer to a XWindows structure.
10579 %
10580 %    o image: the image; returned from ReadImage.
10581 %
10582 %    o exception: return any errors or warnings in this structure.
10583 %
10584 */
10585 static MagickBooleanType XPasteImage(Display *display,
10586   XResourceInfo *resource_info,XWindows *windows,Image *image,
10587   ExceptionInfo *exception)
10588 {
10589   static const char
10590     *PasteMenu[] =
10591     {
10592       "Operator",
10593       "Help",
10594       "Dismiss",
10595       (char *) NULL
10596     };
10597
10598   static const ModeType
10599     PasteCommands[] =
10600     {
10601       PasteOperatorsCommand,
10602       PasteHelpCommand,
10603       PasteDismissCommand
10604     };
10605
10606   static CompositeOperator
10607     compose = CopyCompositeOp;
10608
10609   char
10610     text[MaxTextExtent];
10611
10612   Cursor
10613     cursor;
10614
10615   Image
10616     *paste_image;
10617
10618   int
10619     entry,
10620     id,
10621     x,
10622     y;
10623
10624   MagickRealType
10625     scale_factor;
10626
10627   RectangleInfo
10628     highlight_info,
10629     paste_info;
10630
10631   unsigned int
10632     height,
10633     width;
10634
10635   size_t
10636     state;
10637
10638   XEvent
10639     event;
10640
10641   /*
10642     Copy image.
10643   */
10644   if (resource_info->copy_image == (Image *) NULL)
10645     return(MagickFalse);
10646   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10647   /*
10648     Map Command widget.
10649   */
10650   (void) CloneString(&windows->command.name,"Paste");
10651   windows->command.data=1;
10652   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10653   (void) XMapRaised(display,windows->command.id);
10654   XClientMessage(display,windows->image.id,windows->im_protocols,
10655     windows->im_update_widget,CurrentTime);
10656   /*
10657     Track pointer until button 1 is pressed.
10658   */
10659   XSetCursorState(display,windows,MagickFalse);
10660   XQueryPosition(display,windows->image.id,&x,&y);
10661   (void) XSelectInput(display,windows->image.id,
10662     windows->image.attributes.event_mask | PointerMotionMask);
10663   paste_info.x=(ssize_t) windows->image.x+x;
10664   paste_info.y=(ssize_t) windows->image.y+y;
10665   paste_info.width=0;
10666   paste_info.height=0;
10667   cursor=XCreateFontCursor(display,XC_ul_angle);
10668   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10669   state=DefaultState;
10670   do
10671   {
10672     if (windows->info.mapped != MagickFalse)
10673       {
10674         /*
10675           Display pointer position.
10676         */
10677         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10678           (long) paste_info.x,(long) paste_info.y);
10679         XInfoWidget(display,windows,text);
10680       }
10681     highlight_info=paste_info;
10682     highlight_info.x=paste_info.x-windows->image.x;
10683     highlight_info.y=paste_info.y-windows->image.y;
10684     XHighlightRectangle(display,windows->image.id,
10685       windows->image.highlight_context,&highlight_info);
10686     /*
10687       Wait for next event.
10688     */
10689     XScreenEvent(display,windows,&event);
10690     XHighlightRectangle(display,windows->image.id,
10691       windows->image.highlight_context,&highlight_info);
10692     if (event.xany.window == windows->command.id)
10693       {
10694         /*
10695           Select a command from the Command widget.
10696         */
10697         id=XCommandWidget(display,windows,PasteMenu,&event);
10698         if (id < 0)
10699           continue;
10700         switch (PasteCommands[id])
10701         {
10702           case PasteOperatorsCommand:
10703           {
10704             char
10705               command[MaxTextExtent],
10706               **operators;
10707
10708             /*
10709               Select a command from the pop-up menu.
10710             */
10711             operators=GetCommandOptions(MagickComposeOptions);
10712             if (operators == (char **) NULL)
10713               break;
10714             entry=XMenuWidget(display,windows,PasteMenu[id],
10715               (const char **) operators,command);
10716             if (entry >= 0)
10717               compose=(CompositeOperator) ParseCommandOption(
10718                 MagickComposeOptions,MagickFalse,operators[entry]);
10719             operators=DestroyStringList(operators);
10720             break;
10721           }
10722           case PasteHelpCommand:
10723           {
10724             XTextViewWidget(display,resource_info,windows,MagickFalse,
10725               "Help Viewer - Image Composite",ImagePasteHelp);
10726             break;
10727           }
10728           case PasteDismissCommand:
10729           {
10730             /*
10731               Prematurely exit.
10732             */
10733             state|=EscapeState;
10734             state|=ExitState;
10735             break;
10736           }
10737           default:
10738             break;
10739         }
10740         continue;
10741       }
10742     switch (event.type)
10743     {
10744       case ButtonPress:
10745       {
10746         if (image->debug != MagickFalse)
10747           (void) LogMagickEvent(X11Event,GetMagickModule(),
10748             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10749             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10750         if (event.xbutton.button != Button1)
10751           break;
10752         if (event.xbutton.window != windows->image.id)
10753           break;
10754         /*
10755           Paste rectangle is relative to image configuration.
10756         */
10757         width=(unsigned int) image->columns;
10758         height=(unsigned int) image->rows;
10759         x=0;
10760         y=0;
10761         if (windows->image.crop_geometry != (char *) NULL)
10762           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10763             &width,&height);
10764         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10765         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10766         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10767         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10768         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10769         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10770         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10771         break;
10772       }
10773       case ButtonRelease:
10774       {
10775         if (image->debug != MagickFalse)
10776           (void) LogMagickEvent(X11Event,GetMagickModule(),
10777             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10778             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10779         if (event.xbutton.button != Button1)
10780           break;
10781         if (event.xbutton.window != windows->image.id)
10782           break;
10783         if ((paste_info.width != 0) && (paste_info.height != 0))
10784           {
10785             /*
10786               User has selected the location of the paste image.
10787             */
10788             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10789             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10790             state|=ExitState;
10791           }
10792         break;
10793       }
10794       case Expose:
10795         break;
10796       case KeyPress:
10797       {
10798         char
10799           command[MaxTextExtent];
10800
10801         KeySym
10802           key_symbol;
10803
10804         int
10805           length;
10806
10807         if (event.xkey.window != windows->image.id)
10808           break;
10809         /*
10810           Respond to a user key press.
10811         */
10812         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10813           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10814         *(command+length)='\0';
10815         if (image->debug != MagickFalse)
10816           (void) LogMagickEvent(X11Event,GetMagickModule(),
10817             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10818         switch ((int) key_symbol)
10819         {
10820           case XK_Escape:
10821           case XK_F20:
10822           {
10823             /*
10824               Prematurely exit.
10825             */
10826             paste_image=DestroyImage(paste_image);
10827             state|=EscapeState;
10828             state|=ExitState;
10829             break;
10830           }
10831           case XK_F1:
10832           case XK_Help:
10833           {
10834             (void) XSetFunction(display,windows->image.highlight_context,
10835               GXcopy);
10836             XTextViewWidget(display,resource_info,windows,MagickFalse,
10837               "Help Viewer - Image Composite",ImagePasteHelp);
10838             (void) XSetFunction(display,windows->image.highlight_context,
10839               GXinvert);
10840             break;
10841           }
10842           default:
10843           {
10844             (void) XBell(display,0);
10845             break;
10846           }
10847         }
10848         break;
10849       }
10850       case MotionNotify:
10851       {
10852         /*
10853           Map and unmap Info widget as text cursor crosses its boundaries.
10854         */
10855         x=event.xmotion.x;
10856         y=event.xmotion.y;
10857         if (windows->info.mapped != MagickFalse)
10858           {
10859             if ((x < (int) (windows->info.x+windows->info.width)) &&
10860                 (y < (int) (windows->info.y+windows->info.height)))
10861               (void) XWithdrawWindow(display,windows->info.id,
10862                 windows->info.screen);
10863           }
10864         else
10865           if ((x > (int) (windows->info.x+windows->info.width)) ||
10866               (y > (int) (windows->info.y+windows->info.height)))
10867             (void) XMapWindow(display,windows->info.id);
10868         paste_info.x=(ssize_t) windows->image.x+x;
10869         paste_info.y=(ssize_t) windows->image.y+y;
10870         break;
10871       }
10872       default:
10873       {
10874         if (image->debug != MagickFalse)
10875           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10876             event.type);
10877         break;
10878       }
10879     }
10880   } while ((state & ExitState) == 0);
10881   (void) XSelectInput(display,windows->image.id,
10882     windows->image.attributes.event_mask);
10883   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10884   XSetCursorState(display,windows,MagickFalse);
10885   (void) XFreeCursor(display,cursor);
10886   if ((state & EscapeState) != 0)
10887     return(MagickTrue);
10888   /*
10889     Image pasting is relative to image configuration.
10890   */
10891   XSetCursorState(display,windows,MagickTrue);
10892   XCheckRefreshWindows(display,windows);
10893   width=(unsigned int) image->columns;
10894   height=(unsigned int) image->rows;
10895   x=0;
10896   y=0;
10897   if (windows->image.crop_geometry != (char *) NULL)
10898     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10899   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10900   paste_info.x+=x;
10901   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10902   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10903   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10904   paste_info.y+=y;
10905   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10906   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10907   /*
10908     Paste image with X Image window.
10909   */
10910   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10911   paste_image=DestroyImage(paste_image);
10912   XSetCursorState(display,windows,MagickFalse);
10913   /*
10914     Update image colormap.
10915   */
10916   XConfigureImageColormap(display,resource_info,windows,image);
10917   (void) XConfigureImage(display,resource_info,windows,image,exception);
10918   return(MagickTrue);
10919 }
10920 \f
10921 /*
10922 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10923 %                                                                             %
10924 %                                                                             %
10925 %                                                                             %
10926 +   X P r i n t I m a g e                                                     %
10927 %                                                                             %
10928 %                                                                             %
10929 %                                                                             %
10930 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10931 %
10932 %  XPrintImage() prints an image to a Postscript printer.
10933 %
10934 %  The format of the XPrintImage method is:
10935 %
10936 %      MagickBooleanType XPrintImage(Display *display,
10937 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10938 %        ExceptionInfo *exception)
10939 %
10940 %  A description of each parameter follows:
10941 %
10942 %    o display: Specifies a connection to an X server; returned from
10943 %      XOpenDisplay.
10944 %
10945 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10946 %
10947 %    o windows: Specifies a pointer to a XWindows structure.
10948 %
10949 %    o image: the image.
10950 %
10951 %    o exception: return any errors or warnings in this structure.
10952 %
10953 */
10954 static MagickBooleanType XPrintImage(Display *display,
10955   XResourceInfo *resource_info,XWindows *windows,Image *image,
10956   ExceptionInfo *exception)
10957 {
10958   char
10959     filename[MaxTextExtent],
10960     geometry[MaxTextExtent];
10961
10962   Image
10963     *print_image;
10964
10965   ImageInfo
10966     *image_info;
10967
10968   MagickStatusType
10969     status;
10970
10971   /*
10972     Request Postscript page geometry from user.
10973   */
10974   image_info=CloneImageInfo(resource_info->image_info);
10975   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10976   if (image_info->page != (char *) NULL)
10977     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10978   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10979     "Select Postscript Page Geometry:",geometry);
10980   if (*geometry == '\0')
10981     return(MagickTrue);
10982   image_info->page=GetPageGeometry(geometry);
10983   /*
10984     Apply image transforms.
10985   */
10986   XSetCursorState(display,windows,MagickTrue);
10987   XCheckRefreshWindows(display,windows);
10988   print_image=CloneImage(image,0,0,MagickTrue,exception);
10989   if (print_image == (Image *) NULL)
10990     return(MagickFalse);
10991   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10992     windows->image.ximage->width,windows->image.ximage->height);
10993   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10994   /*
10995     Print image.
10996   */
10997   (void) AcquireUniqueFilename(filename);
10998   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10999     filename);
11000   status=WriteImage(image_info,print_image,exception);
11001   (void) RelinquishUniqueFileResource(filename);
11002   print_image=DestroyImage(print_image);
11003   image_info=DestroyImageInfo(image_info);
11004   XSetCursorState(display,windows,MagickFalse);
11005   return(status != 0 ? MagickTrue : MagickFalse);
11006 }
11007 \f
11008 /*
11009 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11010 %                                                                             %
11011 %                                                                             %
11012 %                                                                             %
11013 +   X R O I I m a g e                                                         %
11014 %                                                                             %
11015 %                                                                             %
11016 %                                                                             %
11017 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11018 %
11019 %  XROIImage() applies an image processing technique to a region of interest.
11020 %
11021 %  The format of the XROIImage method is:
11022 %
11023 %      MagickBooleanType XROIImage(Display *display,
11024 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11025 %        ExceptionInfo *exception)
11026 %
11027 %  A description of each parameter follows:
11028 %
11029 %    o display: Specifies a connection to an X server; returned from
11030 %      XOpenDisplay.
11031 %
11032 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11033 %
11034 %    o windows: Specifies a pointer to a XWindows structure.
11035 %
11036 %    o image: the image; returned from ReadImage.
11037 %
11038 %    o exception: return any errors or warnings in this structure.
11039 %
11040 */
11041 static MagickBooleanType XROIImage(Display *display,
11042   XResourceInfo *resource_info,XWindows *windows,Image **image,
11043   ExceptionInfo *exception)
11044 {
11045 #define ApplyMenus  7
11046
11047   static const char
11048     *ROIMenu[] =
11049     {
11050       "Help",
11051       "Dismiss",
11052       (char *) NULL
11053     },
11054     *ApplyMenu[] =
11055     {
11056       "File",
11057       "Edit",
11058       "Transform",
11059       "Enhance",
11060       "Effects",
11061       "F/X",
11062       "Miscellany",
11063       "Help",
11064       "Dismiss",
11065       (char *) NULL
11066     },
11067     *FileMenu[] =
11068     {
11069       "Save...",
11070       "Print...",
11071       (char *) NULL
11072     },
11073     *EditMenu[] =
11074     {
11075       "Undo",
11076       "Redo",
11077       (char *) NULL
11078     },
11079     *TransformMenu[] =
11080     {
11081       "Flop",
11082       "Flip",
11083       "Rotate Right",
11084       "Rotate Left",
11085       (char *) NULL
11086     },
11087     *EnhanceMenu[] =
11088     {
11089       "Hue...",
11090       "Saturation...",
11091       "Brightness...",
11092       "Gamma...",
11093       "Spiff",
11094       "Dull",
11095       "Contrast Stretch...",
11096       "Sigmoidal Contrast...",
11097       "Normalize",
11098       "Equalize",
11099       "Negate",
11100       "Grayscale",
11101       "Map...",
11102       "Quantize...",
11103       (char *) NULL
11104     },
11105     *EffectsMenu[] =
11106     {
11107       "Despeckle",
11108       "Emboss",
11109       "Reduce Noise",
11110       "Add Noise",
11111       "Sharpen...",
11112       "Blur...",
11113       "Threshold...",
11114       "Edge Detect...",
11115       "Spread...",
11116       "Shade...",
11117       "Raise...",
11118       "Segment...",
11119       (char *) NULL
11120     },
11121     *FXMenu[] =
11122     {
11123       "Solarize...",
11124       "Sepia Tone...",
11125       "Swirl...",
11126       "Implode...",
11127       "Vignette...",
11128       "Wave...",
11129       "Oil Paint...",
11130       "Charcoal Draw...",
11131       (char *) NULL
11132     },
11133     *MiscellanyMenu[] =
11134     {
11135       "Image Info",
11136       "Zoom Image",
11137       "Show Preview...",
11138       "Show Histogram",
11139       "Show Matte",
11140       (char *) NULL
11141     };
11142
11143   static const char
11144     **Menus[ApplyMenus] =
11145     {
11146       FileMenu,
11147       EditMenu,
11148       TransformMenu,
11149       EnhanceMenu,
11150       EffectsMenu,
11151       FXMenu,
11152       MiscellanyMenu
11153     };
11154
11155   static const CommandType
11156     ApplyCommands[] =
11157     {
11158       NullCommand,
11159       NullCommand,
11160       NullCommand,
11161       NullCommand,
11162       NullCommand,
11163       NullCommand,
11164       NullCommand,
11165       HelpCommand,
11166       QuitCommand
11167     },
11168     FileCommands[] =
11169     {
11170       SaveCommand,
11171       PrintCommand
11172     },
11173     EditCommands[] =
11174     {
11175       UndoCommand,
11176       RedoCommand
11177     },
11178     TransformCommands[] =
11179     {
11180       FlopCommand,
11181       FlipCommand,
11182       RotateRightCommand,
11183       RotateLeftCommand
11184     },
11185     EnhanceCommands[] =
11186     {
11187       HueCommand,
11188       SaturationCommand,
11189       BrightnessCommand,
11190       GammaCommand,
11191       SpiffCommand,
11192       DullCommand,
11193       ContrastStretchCommand,
11194       SigmoidalContrastCommand,
11195       NormalizeCommand,
11196       EqualizeCommand,
11197       NegateCommand,
11198       GrayscaleCommand,
11199       MapCommand,
11200       QuantizeCommand
11201     },
11202     EffectsCommands[] =
11203     {
11204       DespeckleCommand,
11205       EmbossCommand,
11206       ReduceNoiseCommand,
11207       AddNoiseCommand,
11208       SharpenCommand,
11209       BlurCommand,
11210       EdgeDetectCommand,
11211       SpreadCommand,
11212       ShadeCommand,
11213       RaiseCommand,
11214       SegmentCommand
11215     },
11216     FXCommands[] =
11217     {
11218       SolarizeCommand,
11219       SepiaToneCommand,
11220       SwirlCommand,
11221       ImplodeCommand,
11222       VignetteCommand,
11223       WaveCommand,
11224       OilPaintCommand,
11225       CharcoalDrawCommand
11226     },
11227     MiscellanyCommands[] =
11228     {
11229       InfoCommand,
11230       ZoomCommand,
11231       ShowPreviewCommand,
11232       ShowHistogramCommand,
11233       ShowMatteCommand
11234     },
11235     ROICommands[] =
11236     {
11237       ROIHelpCommand,
11238       ROIDismissCommand
11239     };
11240
11241   static const CommandType
11242     *Commands[ApplyMenus] =
11243     {
11244       FileCommands,
11245       EditCommands,
11246       TransformCommands,
11247       EnhanceCommands,
11248       EffectsCommands,
11249       FXCommands,
11250       MiscellanyCommands
11251     };
11252
11253   char
11254     command[MaxTextExtent],
11255     text[MaxTextExtent];
11256
11257   CommandType
11258     command_type;
11259
11260   Cursor
11261     cursor;
11262
11263   Image
11264     *roi_image;
11265
11266   int
11267     entry,
11268     id,
11269     x,
11270     y;
11271
11272   MagickRealType
11273     scale_factor;
11274
11275   MagickProgressMonitor
11276     progress_monitor;
11277
11278   RectangleInfo
11279     crop_info,
11280     highlight_info,
11281     roi_info;
11282
11283   unsigned int
11284     height,
11285     width;
11286
11287   size_t
11288     state;
11289
11290   XEvent
11291     event;
11292
11293   /*
11294     Map Command widget.
11295   */
11296   (void) CloneString(&windows->command.name,"ROI");
11297   windows->command.data=0;
11298   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11299   (void) XMapRaised(display,windows->command.id);
11300   XClientMessage(display,windows->image.id,windows->im_protocols,
11301     windows->im_update_widget,CurrentTime);
11302   /*
11303     Track pointer until button 1 is pressed.
11304   */
11305   XQueryPosition(display,windows->image.id,&x,&y);
11306   (void) XSelectInput(display,windows->image.id,
11307     windows->image.attributes.event_mask | PointerMotionMask);
11308   roi_info.x=(ssize_t) windows->image.x+x;
11309   roi_info.y=(ssize_t) windows->image.y+y;
11310   roi_info.width=0;
11311   roi_info.height=0;
11312   cursor=XCreateFontCursor(display,XC_fleur);
11313   state=DefaultState;
11314   do
11315   {
11316     if (windows->info.mapped != MagickFalse)
11317       {
11318         /*
11319           Display pointer position.
11320         */
11321         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11322           (long) roi_info.x,(long) roi_info.y);
11323         XInfoWidget(display,windows,text);
11324       }
11325     /*
11326       Wait for next event.
11327     */
11328     XScreenEvent(display,windows,&event);
11329     if (event.xany.window == windows->command.id)
11330       {
11331         /*
11332           Select a command from the Command widget.
11333         */
11334         id=XCommandWidget(display,windows,ROIMenu,&event);
11335         if (id < 0)
11336           continue;
11337         switch (ROICommands[id])
11338         {
11339           case ROIHelpCommand:
11340           {
11341             XTextViewWidget(display,resource_info,windows,MagickFalse,
11342               "Help Viewer - Region of Interest",ImageROIHelp);
11343             break;
11344           }
11345           case ROIDismissCommand:
11346           {
11347             /*
11348               Prematurely exit.
11349             */
11350             state|=EscapeState;
11351             state|=ExitState;
11352             break;
11353           }
11354           default:
11355             break;
11356         }
11357         continue;
11358       }
11359     switch (event.type)
11360     {
11361       case ButtonPress:
11362       {
11363         if (event.xbutton.button != Button1)
11364           break;
11365         if (event.xbutton.window != windows->image.id)
11366           break;
11367         /*
11368           Note first corner of region of interest rectangle-- exit loop.
11369         */
11370         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11371         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11372         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11373         state|=ExitState;
11374         break;
11375       }
11376       case ButtonRelease:
11377         break;
11378       case Expose:
11379         break;
11380       case KeyPress:
11381       {
11382         KeySym
11383           key_symbol;
11384
11385         if (event.xkey.window != windows->image.id)
11386           break;
11387         /*
11388           Respond to a user key press.
11389         */
11390         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11391           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11392         switch ((int) key_symbol)
11393         {
11394           case XK_Escape:
11395           case XK_F20:
11396           {
11397             /*
11398               Prematurely exit.
11399             */
11400             state|=EscapeState;
11401             state|=ExitState;
11402             break;
11403           }
11404           case XK_F1:
11405           case XK_Help:
11406           {
11407             XTextViewWidget(display,resource_info,windows,MagickFalse,
11408               "Help Viewer - Region of Interest",ImageROIHelp);
11409             break;
11410           }
11411           default:
11412           {
11413             (void) XBell(display,0);
11414             break;
11415           }
11416         }
11417         break;
11418       }
11419       case MotionNotify:
11420       {
11421         /*
11422           Map and unmap Info widget as text cursor crosses its boundaries.
11423         */
11424         x=event.xmotion.x;
11425         y=event.xmotion.y;
11426         if (windows->info.mapped != MagickFalse)
11427           {
11428             if ((x < (int) (windows->info.x+windows->info.width)) &&
11429                 (y < (int) (windows->info.y+windows->info.height)))
11430               (void) XWithdrawWindow(display,windows->info.id,
11431                 windows->info.screen);
11432           }
11433         else
11434           if ((x > (int) (windows->info.x+windows->info.width)) ||
11435               (y > (int) (windows->info.y+windows->info.height)))
11436             (void) XMapWindow(display,windows->info.id);
11437         roi_info.x=(ssize_t) windows->image.x+x;
11438         roi_info.y=(ssize_t) windows->image.y+y;
11439         break;
11440       }
11441       default:
11442         break;
11443     }
11444   } while ((state & ExitState) == 0);
11445   (void) XSelectInput(display,windows->image.id,
11446     windows->image.attributes.event_mask);
11447   if ((state & EscapeState) != 0)
11448     {
11449       /*
11450         User want to exit without region of interest.
11451       */
11452       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11453       (void) XFreeCursor(display,cursor);
11454       return(MagickTrue);
11455     }
11456   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11457   do
11458   {
11459     /*
11460       Size rectangle as pointer moves until the mouse button is released.
11461     */
11462     x=(int) roi_info.x;
11463     y=(int) roi_info.y;
11464     roi_info.width=0;
11465     roi_info.height=0;
11466     state=DefaultState;
11467     do
11468     {
11469       highlight_info=roi_info;
11470       highlight_info.x=roi_info.x-windows->image.x;
11471       highlight_info.y=roi_info.y-windows->image.y;
11472       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11473         {
11474           /*
11475             Display info and draw region of interest rectangle.
11476           */
11477           if (windows->info.mapped == MagickFalse)
11478             (void) XMapWindow(display,windows->info.id);
11479           (void) FormatLocaleString(text,MaxTextExtent,
11480             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11481             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11482           XInfoWidget(display,windows,text);
11483           XHighlightRectangle(display,windows->image.id,
11484             windows->image.highlight_context,&highlight_info);
11485         }
11486       else
11487         if (windows->info.mapped != MagickFalse)
11488           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11489       /*
11490         Wait for next event.
11491       */
11492       XScreenEvent(display,windows,&event);
11493       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11494         XHighlightRectangle(display,windows->image.id,
11495           windows->image.highlight_context,&highlight_info);
11496       switch (event.type)
11497       {
11498         case ButtonPress:
11499         {
11500           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11501           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11502           break;
11503         }
11504         case ButtonRelease:
11505         {
11506           /*
11507             User has committed to region of interest rectangle.
11508           */
11509           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11510           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11511           XSetCursorState(display,windows,MagickFalse);
11512           state|=ExitState;
11513           if (LocaleCompare(windows->command.name,"Apply") == 0)
11514             break;
11515           (void) CloneString(&windows->command.name,"Apply");
11516           windows->command.data=ApplyMenus;
11517           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11518           break;
11519         }
11520         case Expose:
11521           break;
11522         case MotionNotify:
11523         {
11524           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11525           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11526         }
11527         default:
11528           break;
11529       }
11530       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11531           ((state & ExitState) != 0))
11532         {
11533           /*
11534             Check boundary conditions.
11535           */
11536           if (roi_info.x < 0)
11537             roi_info.x=0;
11538           else
11539             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11540               roi_info.x=(ssize_t) windows->image.ximage->width;
11541           if ((int) roi_info.x < x)
11542             roi_info.width=(unsigned int) (x-roi_info.x);
11543           else
11544             {
11545               roi_info.width=(unsigned int) (roi_info.x-x);
11546               roi_info.x=(ssize_t) x;
11547             }
11548           if (roi_info.y < 0)
11549             roi_info.y=0;
11550           else
11551             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11552               roi_info.y=(ssize_t) windows->image.ximage->height;
11553           if ((int) roi_info.y < y)
11554             roi_info.height=(unsigned int) (y-roi_info.y);
11555           else
11556             {
11557               roi_info.height=(unsigned int) (roi_info.y-y);
11558               roi_info.y=(ssize_t) y;
11559             }
11560         }
11561     } while ((state & ExitState) == 0);
11562     /*
11563       Wait for user to grab a corner of the rectangle or press return.
11564     */
11565     state=DefaultState;
11566     command_type=NullCommand;
11567     (void) XMapWindow(display,windows->info.id);
11568     do
11569     {
11570       if (windows->info.mapped != MagickFalse)
11571         {
11572           /*
11573             Display pointer position.
11574           */
11575           (void) FormatLocaleString(text,MaxTextExtent,
11576             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11577             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11578           XInfoWidget(display,windows,text);
11579         }
11580       highlight_info=roi_info;
11581       highlight_info.x=roi_info.x-windows->image.x;
11582       highlight_info.y=roi_info.y-windows->image.y;
11583       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11584         {
11585           state|=EscapeState;
11586           state|=ExitState;
11587           break;
11588         }
11589       if ((state & UpdateRegionState) != 0)
11590         {
11591           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11592           switch (command_type)
11593           {
11594             case UndoCommand:
11595             case RedoCommand:
11596             {
11597               (void) XMagickCommand(display,resource_info,windows,command_type,
11598                 image,exception);
11599               break;
11600             }
11601             default:
11602             {
11603               /*
11604                 Region of interest is relative to image configuration.
11605               */
11606               progress_monitor=SetImageProgressMonitor(*image,
11607                 (MagickProgressMonitor) NULL,(*image)->client_data);
11608               crop_info=roi_info;
11609               width=(unsigned int) (*image)->columns;
11610               height=(unsigned int) (*image)->rows;
11611               x=0;
11612               y=0;
11613               if (windows->image.crop_geometry != (char *) NULL)
11614                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11615                   &width,&height);
11616               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11617               crop_info.x+=x;
11618               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11619               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11620               scale_factor=(MagickRealType)
11621                 height/windows->image.ximage->height;
11622               crop_info.y+=y;
11623               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11624               crop_info.height=(unsigned int)
11625                 (scale_factor*crop_info.height+0.5);
11626               roi_image=CropImage(*image,&crop_info,exception);
11627               (void) SetImageProgressMonitor(*image,progress_monitor,
11628                 (*image)->client_data);
11629               if (roi_image == (Image *) NULL)
11630                 continue;
11631               /*
11632                 Apply image processing technique to the region of interest.
11633               */
11634               windows->image.orphan=MagickTrue;
11635               (void) XMagickCommand(display,resource_info,windows,command_type,
11636                 &roi_image,exception);
11637               progress_monitor=SetImageProgressMonitor(*image,
11638                 (MagickProgressMonitor) NULL,(*image)->client_data);
11639               (void) XMagickCommand(display,resource_info,windows,
11640                 SaveToUndoBufferCommand,image,exception);
11641               windows->image.orphan=MagickFalse;
11642               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11643                 crop_info.x,crop_info.y);
11644               roi_image=DestroyImage(roi_image);
11645               (void) SetImageProgressMonitor(*image,progress_monitor,
11646                 (*image)->client_data);
11647               break;
11648             }
11649           }
11650           if (command_type != InfoCommand)
11651             {
11652               XConfigureImageColormap(display,resource_info,windows,*image);
11653               (void) XConfigureImage(display,resource_info,windows,*image,exception);
11654             }
11655           XCheckRefreshWindows(display,windows);
11656           XInfoWidget(display,windows,text);
11657           (void) XSetFunction(display,windows->image.highlight_context,
11658             GXinvert);
11659           state&=(~UpdateRegionState);
11660         }
11661       XHighlightRectangle(display,windows->image.id,
11662         windows->image.highlight_context,&highlight_info);
11663       XScreenEvent(display,windows,&event);
11664       if (event.xany.window == windows->command.id)
11665         {
11666           /*
11667             Select a command from the Command widget.
11668           */
11669           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11670           command_type=NullCommand;
11671           id=XCommandWidget(display,windows,ApplyMenu,&event);
11672           if (id >= 0)
11673             {
11674               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11675               command_type=ApplyCommands[id];
11676               if (id < ApplyMenus)
11677                 {
11678                   /*
11679                     Select a command from a pop-up menu.
11680                   */
11681                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11682                     (const char **) Menus[id],command);
11683                   if (entry >= 0)
11684                     {
11685                       (void) CopyMagickString(command,Menus[id][entry],
11686                         MaxTextExtent);
11687                       command_type=Commands[id][entry];
11688                     }
11689                 }
11690             }
11691           (void) XSetFunction(display,windows->image.highlight_context,
11692             GXinvert);
11693           XHighlightRectangle(display,windows->image.id,
11694             windows->image.highlight_context,&highlight_info);
11695           if (command_type == HelpCommand)
11696             {
11697               (void) XSetFunction(display,windows->image.highlight_context,
11698                 GXcopy);
11699               XTextViewWidget(display,resource_info,windows,MagickFalse,
11700                 "Help Viewer - Region of Interest",ImageROIHelp);
11701               (void) XSetFunction(display,windows->image.highlight_context,
11702                 GXinvert);
11703               continue;
11704             }
11705           if (command_type == QuitCommand)
11706             {
11707               /*
11708                 exit.
11709               */
11710               state|=EscapeState;
11711               state|=ExitState;
11712               continue;
11713             }
11714           if (command_type != NullCommand)
11715             state|=UpdateRegionState;
11716           continue;
11717         }
11718       XHighlightRectangle(display,windows->image.id,
11719         windows->image.highlight_context,&highlight_info);
11720       switch (event.type)
11721       {
11722         case ButtonPress:
11723         {
11724           x=windows->image.x;
11725           y=windows->image.y;
11726           if (event.xbutton.button != Button1)
11727             break;
11728           if (event.xbutton.window != windows->image.id)
11729             break;
11730           x=windows->image.x+event.xbutton.x;
11731           y=windows->image.y+event.xbutton.y;
11732           if ((x < (int) (roi_info.x+RoiDelta)) &&
11733               (x > (int) (roi_info.x-RoiDelta)) &&
11734               (y < (int) (roi_info.y+RoiDelta)) &&
11735               (y > (int) (roi_info.y-RoiDelta)))
11736             {
11737               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11738               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11739               state|=UpdateConfigurationState;
11740               break;
11741             }
11742           if ((x < (int) (roi_info.x+RoiDelta)) &&
11743               (x > (int) (roi_info.x-RoiDelta)) &&
11744               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11745               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11746             {
11747               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11748               state|=UpdateConfigurationState;
11749               break;
11750             }
11751           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11752               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11753               (y < (int) (roi_info.y+RoiDelta)) &&
11754               (y > (int) (roi_info.y-RoiDelta)))
11755             {
11756               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11757               state|=UpdateConfigurationState;
11758               break;
11759             }
11760           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11761               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11762               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11763               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11764             {
11765               state|=UpdateConfigurationState;
11766               break;
11767             }
11768         }
11769         case ButtonRelease:
11770         {
11771           if (event.xbutton.window == windows->pan.id)
11772             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11773                 (highlight_info.y != crop_info.y-windows->image.y))
11774               XHighlightRectangle(display,windows->image.id,
11775                 windows->image.highlight_context,&highlight_info);
11776           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11777             event.xbutton.time);
11778           break;
11779         }
11780         case Expose:
11781         {
11782           if (event.xexpose.window == windows->image.id)
11783             if (event.xexpose.count == 0)
11784               {
11785                 event.xexpose.x=(int) highlight_info.x;
11786                 event.xexpose.y=(int) highlight_info.y;
11787                 event.xexpose.width=(int) highlight_info.width;
11788                 event.xexpose.height=(int) highlight_info.height;
11789                 XRefreshWindow(display,&windows->image,&event);
11790               }
11791           if (event.xexpose.window == windows->info.id)
11792             if (event.xexpose.count == 0)
11793               XInfoWidget(display,windows,text);
11794           break;
11795         }
11796         case KeyPress:
11797         {
11798           KeySym
11799             key_symbol;
11800
11801           if (event.xkey.window != windows->image.id)
11802             break;
11803           /*
11804             Respond to a user key press.
11805           */
11806           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11807             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11808           switch ((int) key_symbol)
11809           {
11810             case XK_Shift_L:
11811             case XK_Shift_R:
11812               break;
11813             case XK_Escape:
11814             case XK_F20:
11815               state|=EscapeState;
11816             case XK_Return:
11817             {
11818               state|=ExitState;
11819               break;
11820             }
11821             case XK_Home:
11822             case XK_KP_Home:
11823             {
11824               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11825               roi_info.y=(ssize_t) (windows->image.height/2L-
11826                 roi_info.height/2L);
11827               break;
11828             }
11829             case XK_Left:
11830             case XK_KP_Left:
11831             {
11832               roi_info.x--;
11833               break;
11834             }
11835             case XK_Up:
11836             case XK_KP_Up:
11837             case XK_Next:
11838             {
11839               roi_info.y--;
11840               break;
11841             }
11842             case XK_Right:
11843             case XK_KP_Right:
11844             {
11845               roi_info.x++;
11846               break;
11847             }
11848             case XK_Prior:
11849             case XK_Down:
11850             case XK_KP_Down:
11851             {
11852               roi_info.y++;
11853               break;
11854             }
11855             case XK_F1:
11856             case XK_Help:
11857             {
11858               (void) XSetFunction(display,windows->image.highlight_context,
11859                 GXcopy);
11860               XTextViewWidget(display,resource_info,windows,MagickFalse,
11861                 "Help Viewer - Region of Interest",ImageROIHelp);
11862               (void) XSetFunction(display,windows->image.highlight_context,
11863                 GXinvert);
11864               break;
11865             }
11866             default:
11867             {
11868               command_type=XImageWindowCommand(display,resource_info,windows,
11869                 event.xkey.state,key_symbol,image,exception);
11870               if (command_type != NullCommand)
11871                 state|=UpdateRegionState;
11872               break;
11873             }
11874           }
11875           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11876             event.xkey.time);
11877           break;
11878         }
11879         case KeyRelease:
11880           break;
11881         case MotionNotify:
11882         {
11883           if (event.xbutton.window != windows->image.id)
11884             break;
11885           /*
11886             Map and unmap Info widget as text cursor crosses its boundaries.
11887           */
11888           x=event.xmotion.x;
11889           y=event.xmotion.y;
11890           if (windows->info.mapped != MagickFalse)
11891             {
11892               if ((x < (int) (windows->info.x+windows->info.width)) &&
11893                   (y < (int) (windows->info.y+windows->info.height)))
11894                 (void) XWithdrawWindow(display,windows->info.id,
11895                   windows->info.screen);
11896             }
11897           else
11898             if ((x > (int) (windows->info.x+windows->info.width)) ||
11899                 (y > (int) (windows->info.y+windows->info.height)))
11900               (void) XMapWindow(display,windows->info.id);
11901           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11902           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11903           break;
11904         }
11905         case SelectionRequest:
11906         {
11907           XSelectionEvent
11908             notify;
11909
11910           XSelectionRequestEvent
11911             *request;
11912
11913           /*
11914             Set primary selection.
11915           */
11916           (void) FormatLocaleString(text,MaxTextExtent,
11917             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11918             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11919           request=(&(event.xselectionrequest));
11920           (void) XChangeProperty(request->display,request->requestor,
11921             request->property,request->target,8,PropModeReplace,
11922             (unsigned char *) text,(int) strlen(text));
11923           notify.type=SelectionNotify;
11924           notify.display=request->display;
11925           notify.requestor=request->requestor;
11926           notify.selection=request->selection;
11927           notify.target=request->target;
11928           notify.time=request->time;
11929           if (request->property == None)
11930             notify.property=request->target;
11931           else
11932             notify.property=request->property;
11933           (void) XSendEvent(request->display,request->requestor,False,0,
11934             (XEvent *) &notify);
11935         }
11936         default:
11937           break;
11938       }
11939       if ((state & UpdateConfigurationState) != 0)
11940         {
11941           (void) XPutBackEvent(display,&event);
11942           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11943           break;
11944         }
11945     } while ((state & ExitState) == 0);
11946   } while ((state & ExitState) == 0);
11947   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11948   XSetCursorState(display,windows,MagickFalse);
11949   if ((state & EscapeState) != 0)
11950     return(MagickTrue);
11951   return(MagickTrue);
11952 }
11953 \f
11954 /*
11955 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11956 %                                                                             %
11957 %                                                                             %
11958 %                                                                             %
11959 +   X R o t a t e I m a g e                                                   %
11960 %                                                                             %
11961 %                                                                             %
11962 %                                                                             %
11963 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11964 %
11965 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11966 %  rotation angle is computed from the slope of a line drawn by the user.
11967 %
11968 %  The format of the XRotateImage method is:
11969 %
11970 %      MagickBooleanType XRotateImage(Display *display,
11971 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11972 %        Image **image,ExceptionInfo *exception)
11973 %
11974 %  A description of each parameter follows:
11975 %
11976 %    o display: Specifies a connection to an X server; returned from
11977 %      XOpenDisplay.
11978 %
11979 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11980 %
11981 %    o windows: Specifies a pointer to a XWindows structure.
11982 %
11983 %    o degrees: Specifies the number of degrees to rotate the image.
11984 %
11985 %    o image: the image.
11986 %
11987 %    o exception: return any errors or warnings in this structure.
11988 %
11989 */
11990 static MagickBooleanType XRotateImage(Display *display,
11991   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
11992   ExceptionInfo *exception)
11993 {
11994   static const char
11995     *RotateMenu[] =
11996     {
11997       "Pixel Color",
11998       "Direction",
11999       "Help",
12000       "Dismiss",
12001       (char *) NULL
12002     };
12003
12004   static ModeType
12005     direction = HorizontalRotateCommand;
12006
12007   static const ModeType
12008     DirectionCommands[] =
12009     {
12010       HorizontalRotateCommand,
12011       VerticalRotateCommand
12012     },
12013     RotateCommands[] =
12014     {
12015       RotateColorCommand,
12016       RotateDirectionCommand,
12017       RotateHelpCommand,
12018       RotateDismissCommand
12019     };
12020
12021   static unsigned int
12022     pen_id = 0;
12023
12024   char
12025     command[MaxTextExtent],
12026     text[MaxTextExtent];
12027
12028   Image
12029     *rotate_image;
12030
12031   int
12032     id,
12033     x,
12034     y;
12035
12036   MagickRealType
12037     normalized_degrees;
12038
12039   register int
12040     i;
12041
12042   unsigned int
12043     height,
12044     rotations,
12045     width;
12046
12047   if (degrees == 0.0)
12048     {
12049       unsigned int
12050         distance;
12051
12052       size_t
12053         state;
12054
12055       XEvent
12056         event;
12057
12058       XSegment
12059         rotate_info;
12060
12061       /*
12062         Map Command widget.
12063       */
12064       (void) CloneString(&windows->command.name,"Rotate");
12065       windows->command.data=2;
12066       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12067       (void) XMapRaised(display,windows->command.id);
12068       XClientMessage(display,windows->image.id,windows->im_protocols,
12069         windows->im_update_widget,CurrentTime);
12070       /*
12071         Wait for first button press.
12072       */
12073       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12074       XQueryPosition(display,windows->image.id,&x,&y);
12075       rotate_info.x1=x;
12076       rotate_info.y1=y;
12077       rotate_info.x2=x;
12078       rotate_info.y2=y;
12079       state=DefaultState;
12080       do
12081       {
12082         XHighlightLine(display,windows->image.id,
12083           windows->image.highlight_context,&rotate_info);
12084         /*
12085           Wait for next event.
12086         */
12087         XScreenEvent(display,windows,&event);
12088         XHighlightLine(display,windows->image.id,
12089           windows->image.highlight_context,&rotate_info);
12090         if (event.xany.window == windows->command.id)
12091           {
12092             /*
12093               Select a command from the Command widget.
12094             */
12095             id=XCommandWidget(display,windows,RotateMenu,&event);
12096             if (id < 0)
12097               continue;
12098             (void) XSetFunction(display,windows->image.highlight_context,
12099               GXcopy);
12100             switch (RotateCommands[id])
12101             {
12102               case RotateColorCommand:
12103               {
12104                 const char
12105                   *ColorMenu[MaxNumberPens];
12106
12107                 int
12108                   pen_number;
12109
12110                 XColor
12111                   color;
12112
12113                 /*
12114                   Initialize menu selections.
12115                 */
12116                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12117                   ColorMenu[i]=resource_info->pen_colors[i];
12118                 ColorMenu[MaxNumberPens-2]="Browser...";
12119                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12120                 /*
12121                   Select a pen color from the pop-up menu.
12122                 */
12123                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12124                   (const char **) ColorMenu,command);
12125                 if (pen_number < 0)
12126                   break;
12127                 if (pen_number == (MaxNumberPens-2))
12128                   {
12129                     static char
12130                       color_name[MaxTextExtent] = "gray";
12131
12132                     /*
12133                       Select a pen color from a dialog.
12134                     */
12135                     resource_info->pen_colors[pen_number]=color_name;
12136                     XColorBrowserWidget(display,windows,"Select",color_name);
12137                     if (*color_name == '\0')
12138                       break;
12139                   }
12140                 /*
12141                   Set pen color.
12142                 */
12143                 (void) XParseColor(display,windows->map_info->colormap,
12144                   resource_info->pen_colors[pen_number],&color);
12145                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12146                   (unsigned int) MaxColors,&color);
12147                 windows->pixel_info->pen_colors[pen_number]=color;
12148                 pen_id=(unsigned int) pen_number;
12149                 break;
12150               }
12151               case RotateDirectionCommand:
12152               {
12153                 static const char
12154                   *Directions[] =
12155                   {
12156                     "horizontal",
12157                     "vertical",
12158                     (char *) NULL,
12159                   };
12160
12161                 /*
12162                   Select a command from the pop-up menu.
12163                 */
12164                 id=XMenuWidget(display,windows,RotateMenu[id],
12165                   Directions,command);
12166                 if (id >= 0)
12167                   direction=DirectionCommands[id];
12168                 break;
12169               }
12170               case RotateHelpCommand:
12171               {
12172                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12173                   "Help Viewer - Image Rotation",ImageRotateHelp);
12174                 break;
12175               }
12176               case RotateDismissCommand:
12177               {
12178                 /*
12179                   Prematurely exit.
12180                 */
12181                 state|=EscapeState;
12182                 state|=ExitState;
12183                 break;
12184               }
12185               default:
12186                 break;
12187             }
12188             (void) XSetFunction(display,windows->image.highlight_context,
12189               GXinvert);
12190             continue;
12191           }
12192         switch (event.type)
12193         {
12194           case ButtonPress:
12195           {
12196             if (event.xbutton.button != Button1)
12197               break;
12198             if (event.xbutton.window != windows->image.id)
12199               break;
12200             /*
12201               exit loop.
12202             */
12203             (void) XSetFunction(display,windows->image.highlight_context,
12204               GXcopy);
12205             rotate_info.x1=event.xbutton.x;
12206             rotate_info.y1=event.xbutton.y;
12207             state|=ExitState;
12208             break;
12209           }
12210           case ButtonRelease:
12211             break;
12212           case Expose:
12213             break;
12214           case KeyPress:
12215           {
12216             char
12217               command[MaxTextExtent];
12218
12219             KeySym
12220               key_symbol;
12221
12222             if (event.xkey.window != windows->image.id)
12223               break;
12224             /*
12225               Respond to a user key press.
12226             */
12227             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12228               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12229             switch ((int) key_symbol)
12230             {
12231               case XK_Escape:
12232               case XK_F20:
12233               {
12234                 /*
12235                   Prematurely exit.
12236                 */
12237                 state|=EscapeState;
12238                 state|=ExitState;
12239                 break;
12240               }
12241               case XK_F1:
12242               case XK_Help:
12243               {
12244                 (void) XSetFunction(display,windows->image.highlight_context,
12245                   GXcopy);
12246                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12247                   "Help Viewer - Image Rotation",ImageRotateHelp);
12248                 (void) XSetFunction(display,windows->image.highlight_context,
12249                   GXinvert);
12250                 break;
12251               }
12252               default:
12253               {
12254                 (void) XBell(display,0);
12255                 break;
12256               }
12257             }
12258             break;
12259           }
12260           case MotionNotify:
12261           {
12262             rotate_info.x1=event.xmotion.x;
12263             rotate_info.y1=event.xmotion.y;
12264           }
12265         }
12266         rotate_info.x2=rotate_info.x1;
12267         rotate_info.y2=rotate_info.y1;
12268         if (direction == HorizontalRotateCommand)
12269           rotate_info.x2+=32;
12270         else
12271           rotate_info.y2-=32;
12272       } while ((state & ExitState) == 0);
12273       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12274       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12275       if ((state & EscapeState) != 0)
12276         return(MagickTrue);
12277       /*
12278         Draw line as pointer moves until the mouse button is released.
12279       */
12280       distance=0;
12281       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12282       state=DefaultState;
12283       do
12284       {
12285         if (distance > 9)
12286           {
12287             /*
12288               Display info and draw rotation line.
12289             */
12290             if (windows->info.mapped == MagickFalse)
12291               (void) XMapWindow(display,windows->info.id);
12292             (void) FormatLocaleString(text,MaxTextExtent," %g",
12293               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12294             XInfoWidget(display,windows,text);
12295             XHighlightLine(display,windows->image.id,
12296               windows->image.highlight_context,&rotate_info);
12297           }
12298         else
12299           if (windows->info.mapped != MagickFalse)
12300             (void) XWithdrawWindow(display,windows->info.id,
12301               windows->info.screen);
12302         /*
12303           Wait for next event.
12304         */
12305         XScreenEvent(display,windows,&event);
12306         if (distance > 9)
12307           XHighlightLine(display,windows->image.id,
12308             windows->image.highlight_context,&rotate_info);
12309         switch (event.type)
12310         {
12311           case ButtonPress:
12312             break;
12313           case ButtonRelease:
12314           {
12315             /*
12316               User has committed to rotation line.
12317             */
12318             rotate_info.x2=event.xbutton.x;
12319             rotate_info.y2=event.xbutton.y;
12320             state|=ExitState;
12321             break;
12322           }
12323           case Expose:
12324             break;
12325           case MotionNotify:
12326           {
12327             rotate_info.x2=event.xmotion.x;
12328             rotate_info.y2=event.xmotion.y;
12329           }
12330           default:
12331             break;
12332         }
12333         /*
12334           Check boundary conditions.
12335         */
12336         if (rotate_info.x2 < 0)
12337           rotate_info.x2=0;
12338         else
12339           if (rotate_info.x2 > (int) windows->image.width)
12340             rotate_info.x2=(short) windows->image.width;
12341         if (rotate_info.y2 < 0)
12342           rotate_info.y2=0;
12343         else
12344           if (rotate_info.y2 > (int) windows->image.height)
12345             rotate_info.y2=(short) windows->image.height;
12346         /*
12347           Compute rotation angle from the slope of the line.
12348         */
12349         degrees=0.0;
12350         distance=(unsigned int)
12351           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12352           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12353         if (distance > 9)
12354           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12355             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12356       } while ((state & ExitState) == 0);
12357       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12358       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12359       if (distance <= 9)
12360         return(MagickTrue);
12361     }
12362   if (direction == VerticalRotateCommand)
12363     degrees-=90.0;
12364   if (degrees == 0.0)
12365     return(MagickTrue);
12366   /*
12367     Rotate image.
12368   */
12369   normalized_degrees=degrees;
12370   while (normalized_degrees < -45.0)
12371     normalized_degrees+=360.0;
12372   for (rotations=0; normalized_degrees > 45.0; rotations++)
12373     normalized_degrees-=90.0;
12374   if (normalized_degrees != 0.0)
12375     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12376       exception);
12377   XSetCursorState(display,windows,MagickTrue);
12378   XCheckRefreshWindows(display,windows);
12379   (*image)->background_color.red=ScaleShortToQuantum(
12380     windows->pixel_info->pen_colors[pen_id].red);
12381   (*image)->background_color.green=ScaleShortToQuantum(
12382     windows->pixel_info->pen_colors[pen_id].green);
12383   (*image)->background_color.blue=ScaleShortToQuantum(
12384     windows->pixel_info->pen_colors[pen_id].blue);
12385   rotate_image=RotateImage(*image,degrees,exception);
12386   XSetCursorState(display,windows,MagickFalse);
12387   if (rotate_image == (Image *) NULL)
12388     return(MagickFalse);
12389   *image=DestroyImage(*image);
12390   *image=rotate_image;
12391   if (windows->image.crop_geometry != (char *) NULL)
12392     {
12393       /*
12394         Rotate crop geometry.
12395       */
12396       width=(unsigned int) (*image)->columns;
12397       height=(unsigned int) (*image)->rows;
12398       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12399       switch (rotations % 4)
12400       {
12401         default:
12402         case 0:
12403           break;
12404         case 1:
12405         {
12406           /*
12407             Rotate 90 degrees.
12408           */
12409           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12410             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12411             (int) height-y,x);
12412           break;
12413         }
12414         case 2:
12415         {
12416           /*
12417             Rotate 180 degrees.
12418           */
12419           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12420             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12421           break;
12422         }
12423         case 3:
12424         {
12425           /*
12426             Rotate 270 degrees.
12427           */
12428           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12429             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12430           break;
12431         }
12432       }
12433     }
12434   if (windows->image.orphan != MagickFalse)
12435     return(MagickTrue);
12436   if (normalized_degrees != 0.0)
12437     {
12438       /*
12439         Update image colormap.
12440       */
12441       windows->image.window_changes.width=(int) (*image)->columns;
12442       windows->image.window_changes.height=(int) (*image)->rows;
12443       if (windows->image.crop_geometry != (char *) NULL)
12444         {
12445           /*
12446             Obtain dimensions of image from crop geometry.
12447           */
12448           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12449             &width,&height);
12450           windows->image.window_changes.width=(int) width;
12451           windows->image.window_changes.height=(int) height;
12452         }
12453       XConfigureImageColormap(display,resource_info,windows,*image);
12454     }
12455   else
12456     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12457       {
12458         windows->image.window_changes.width=windows->image.ximage->height;
12459         windows->image.window_changes.height=windows->image.ximage->width;
12460       }
12461   /*
12462     Update image configuration.
12463   */
12464   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12465   return(MagickTrue);
12466 }
12467 \f
12468 /*
12469 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12470 %                                                                             %
12471 %                                                                             %
12472 %                                                                             %
12473 +   X S a v e I m a g e                                                       %
12474 %                                                                             %
12475 %                                                                             %
12476 %                                                                             %
12477 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12478 %
12479 %  XSaveImage() saves an image to a file.
12480 %
12481 %  The format of the XSaveImage method is:
12482 %
12483 %      MagickBooleanType XSaveImage(Display *display,
12484 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12485 %        ExceptionInfo *exception)
12486 %
12487 %  A description of each parameter follows:
12488 %
12489 %    o display: Specifies a connection to an X server; returned from
12490 %      XOpenDisplay.
12491 %
12492 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12493 %
12494 %    o windows: Specifies a pointer to a XWindows structure.
12495 %
12496 %    o image: the image.
12497 %
12498 %    o exception: return any errors or warnings in this structure.
12499 %
12500 */
12501 static MagickBooleanType XSaveImage(Display *display,
12502   XResourceInfo *resource_info,XWindows *windows,Image *image,
12503   ExceptionInfo *exception)
12504 {
12505   char
12506     filename[MaxTextExtent],
12507     geometry[MaxTextExtent];
12508
12509   Image
12510     *save_image;
12511
12512   ImageInfo
12513     *image_info;
12514
12515   MagickStatusType
12516     status;
12517
12518   /*
12519     Request file name from user.
12520   */
12521   if (resource_info->write_filename != (char *) NULL)
12522     (void) CopyMagickString(filename,resource_info->write_filename,
12523       MaxTextExtent);
12524   else
12525     {
12526       char
12527         path[MaxTextExtent];
12528
12529       int
12530         status;
12531
12532       GetPathComponent(image->filename,HeadPath,path);
12533       GetPathComponent(image->filename,TailPath,filename);
12534       if (*path != '\0')
12535         {
12536           status=chdir(path);
12537           if (status == -1)
12538             (void) ThrowMagickException(exception,GetMagickModule(),
12539               FileOpenError,"UnableToOpenFile","%s",path);
12540         }
12541     }
12542   XFileBrowserWidget(display,windows,"Save",filename);
12543   if (*filename == '\0')
12544     return(MagickTrue);
12545   if (IsPathAccessible(filename) != MagickFalse)
12546     {
12547       int
12548         status;
12549
12550       /*
12551         File exists-- seek user's permission before overwriting.
12552       */
12553       status=XConfirmWidget(display,windows,"Overwrite",filename);
12554       if (status <= 0)
12555         return(MagickTrue);
12556     }
12557   image_info=CloneImageInfo(resource_info->image_info);
12558   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12559   (void) SetImageInfo(image_info,1,exception);
12560   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12561       (LocaleCompare(image_info->magick,"JPG") == 0))
12562     {
12563       char
12564         quality[MaxTextExtent];
12565
12566       int
12567         status;
12568
12569       /*
12570         Request JPEG quality from user.
12571       */
12572       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12573         image->quality);
12574       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12575         quality);
12576       if (*quality == '\0')
12577         return(MagickTrue);
12578       image->quality=StringToUnsignedLong(quality);
12579       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12580     }
12581   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12582       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12583       (LocaleCompare(image_info->magick,"PS") == 0) ||
12584       (LocaleCompare(image_info->magick,"PS2") == 0))
12585     {
12586       char
12587         geometry[MaxTextExtent];
12588
12589       /*
12590         Request page geometry from user.
12591       */
12592       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12593       if (LocaleCompare(image_info->magick,"PDF") == 0)
12594         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12595       if (image_info->page != (char *) NULL)
12596         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12597       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12598         "Select page geometry:",geometry);
12599       if (*geometry != '\0')
12600         image_info->page=GetPageGeometry(geometry);
12601     }
12602   /*
12603     Apply image transforms.
12604   */
12605   XSetCursorState(display,windows,MagickTrue);
12606   XCheckRefreshWindows(display,windows);
12607   save_image=CloneImage(image,0,0,MagickTrue,exception);
12608   if (save_image == (Image *) NULL)
12609     return(MagickFalse);
12610   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12611     windows->image.ximage->width,windows->image.ximage->height);
12612   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12613   /*
12614     Write image.
12615   */
12616   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12617   status=WriteImage(image_info,save_image,exception);
12618   if (status != MagickFalse)
12619     image->taint=MagickFalse;
12620   save_image=DestroyImage(save_image);
12621   image_info=DestroyImageInfo(image_info);
12622   XSetCursorState(display,windows,MagickFalse);
12623   return(status != 0 ? MagickTrue : MagickFalse);
12624 }
12625 \f
12626 /*
12627 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12628 %                                                                             %
12629 %                                                                             %
12630 %                                                                             %
12631 +   X S c r e e n E v e n t                                                   %
12632 %                                                                             %
12633 %                                                                             %
12634 %                                                                             %
12635 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12636 %
12637 %  XScreenEvent() handles global events associated with the Pan and Magnify
12638 %  windows.
12639 %
12640 %  The format of the XScreenEvent function is:
12641 %
12642 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12643 %
12644 %  A description of each parameter follows:
12645 %
12646 %    o display: Specifies a pointer to the Display structure;  returned from
12647 %      XOpenDisplay.
12648 %
12649 %    o windows: Specifies a pointer to a XWindows structure.
12650 %
12651 %    o event: Specifies a pointer to a X11 XEvent structure.
12652 %
12653 %
12654 */
12655
12656 #if defined(__cplusplus) || defined(c_plusplus)
12657 extern "C" {
12658 #endif
12659
12660 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12661 {
12662   register XWindows
12663     *windows;
12664
12665   windows=(XWindows *) data;
12666   if ((event->type == ClientMessage) &&
12667       (event->xclient.window == windows->image.id))
12668     return(MagickFalse);
12669   return(MagickTrue);
12670 }
12671
12672 #if defined(__cplusplus) || defined(c_plusplus)
12673 }
12674 #endif
12675
12676 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12677 {
12678   register int
12679     x,
12680     y;
12681
12682   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12683   if (event->xany.window == windows->command.id)
12684     return;
12685   switch (event->type)
12686   {
12687     case ButtonPress:
12688     case ButtonRelease:
12689     {
12690       if ((event->xbutton.button == Button3) &&
12691           (event->xbutton.state & Mod1Mask))
12692         {
12693           /*
12694             Convert Alt-Button3 to Button2.
12695           */
12696           event->xbutton.button=Button2;
12697           event->xbutton.state&=(~Mod1Mask);
12698         }
12699       if (event->xbutton.window == windows->backdrop.id)
12700         {
12701           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12702             event->xbutton.time);
12703           break;
12704         }
12705       if (event->xbutton.window == windows->pan.id)
12706         {
12707           XPanImage(display,windows,event);
12708           break;
12709         }
12710       if (event->xbutton.window == windows->image.id)
12711         if (event->xbutton.button == Button2)
12712           {
12713             /*
12714               Update magnified image.
12715             */
12716             x=event->xbutton.x;
12717             y=event->xbutton.y;
12718             if (x < 0)
12719               x=0;
12720             else
12721               if (x >= (int) windows->image.width)
12722                 x=(int) (windows->image.width-1);
12723             windows->magnify.x=(int) windows->image.x+x;
12724             if (y < 0)
12725               y=0;
12726             else
12727              if (y >= (int) windows->image.height)
12728                y=(int) (windows->image.height-1);
12729             windows->magnify.y=windows->image.y+y;
12730             if (windows->magnify.mapped == MagickFalse)
12731               (void) XMapRaised(display,windows->magnify.id);
12732             XMakeMagnifyImage(display,windows);
12733             if (event->type == ButtonRelease)
12734               (void) XWithdrawWindow(display,windows->info.id,
12735                 windows->info.screen);
12736             break;
12737           }
12738       break;
12739     }
12740     case ClientMessage:
12741     {
12742       /*
12743         If client window delete message, exit.
12744       */
12745       if (event->xclient.message_type != windows->wm_protocols)
12746         break;
12747       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12748         break;
12749       if (event->xclient.window == windows->magnify.id)
12750         {
12751           (void) XWithdrawWindow(display,windows->magnify.id,
12752             windows->magnify.screen);
12753           break;
12754         }
12755       break;
12756     }
12757     case ConfigureNotify:
12758     {
12759       if (event->xconfigure.window == windows->magnify.id)
12760         {
12761           unsigned int
12762             magnify;
12763
12764           /*
12765             Magnify window has a new configuration.
12766           */
12767           windows->magnify.width=(unsigned int) event->xconfigure.width;
12768           windows->magnify.height=(unsigned int) event->xconfigure.height;
12769           if (windows->magnify.mapped == MagickFalse)
12770             break;
12771           magnify=1;
12772           while ((int) magnify <= event->xconfigure.width)
12773             magnify<<=1;
12774           while ((int) magnify <= event->xconfigure.height)
12775             magnify<<=1;
12776           magnify>>=1;
12777           if (((int) magnify != event->xconfigure.width) ||
12778               ((int) magnify != event->xconfigure.height))
12779             {
12780               XWindowChanges
12781                 window_changes;
12782
12783               window_changes.width=(int) magnify;
12784               window_changes.height=(int) magnify;
12785               (void) XReconfigureWMWindow(display,windows->magnify.id,
12786                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12787                 &window_changes);
12788               break;
12789             }
12790           XMakeMagnifyImage(display,windows);
12791           break;
12792         }
12793       break;
12794     }
12795     case Expose:
12796     {
12797       if (event->xexpose.window == windows->image.id)
12798         {
12799           XRefreshWindow(display,&windows->image,event);
12800           break;
12801         }
12802       if (event->xexpose.window == windows->pan.id)
12803         if (event->xexpose.count == 0)
12804           {
12805             XDrawPanRectangle(display,windows);
12806             break;
12807           }
12808       if (event->xexpose.window == windows->magnify.id)
12809         if (event->xexpose.count == 0)
12810           {
12811             XMakeMagnifyImage(display,windows);
12812             break;
12813           }
12814       break;
12815     }
12816     case KeyPress:
12817     {
12818       char
12819         command[MaxTextExtent];
12820
12821       KeySym
12822         key_symbol;
12823
12824       if (event->xkey.window != windows->magnify.id)
12825         break;
12826       /*
12827         Respond to a user key press.
12828       */
12829       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12830         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12831       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12832       break;
12833     }
12834     case MapNotify:
12835     {
12836       if (event->xmap.window == windows->magnify.id)
12837         {
12838           windows->magnify.mapped=MagickTrue;
12839           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12840           break;
12841         }
12842       if (event->xmap.window == windows->info.id)
12843         {
12844           windows->info.mapped=MagickTrue;
12845           break;
12846         }
12847       break;
12848     }
12849     case MotionNotify:
12850     {
12851       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12852       if (event->xmotion.window == windows->image.id)
12853         if (windows->magnify.mapped != MagickFalse)
12854           {
12855             /*
12856               Update magnified image.
12857             */
12858             x=event->xmotion.x;
12859             y=event->xmotion.y;
12860             if (x < 0)
12861               x=0;
12862             else
12863               if (x >= (int) windows->image.width)
12864                 x=(int) (windows->image.width-1);
12865             windows->magnify.x=(int) windows->image.x+x;
12866             if (y < 0)
12867               y=0;
12868             else
12869              if (y >= (int) windows->image.height)
12870                y=(int) (windows->image.height-1);
12871             windows->magnify.y=windows->image.y+y;
12872             XMakeMagnifyImage(display,windows);
12873           }
12874       break;
12875     }
12876     case UnmapNotify:
12877     {
12878       if (event->xunmap.window == windows->magnify.id)
12879         {
12880           windows->magnify.mapped=MagickFalse;
12881           break;
12882         }
12883       if (event->xunmap.window == windows->info.id)
12884         {
12885           windows->info.mapped=MagickFalse;
12886           break;
12887         }
12888       break;
12889     }
12890     default:
12891       break;
12892   }
12893 }
12894 \f
12895 /*
12896 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12897 %                                                                             %
12898 %                                                                             %
12899 %                                                                             %
12900 +   X S e t C r o p G e o m e t r y                                           %
12901 %                                                                             %
12902 %                                                                             %
12903 %                                                                             %
12904 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12905 %
12906 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12907 %  and translates it to a cropping geometry relative to the image.
12908 %
12909 %  The format of the XSetCropGeometry method is:
12910 %
12911 %      void XSetCropGeometry(Display *display,XWindows *windows,
12912 %        RectangleInfo *crop_info,Image *image)
12913 %
12914 %  A description of each parameter follows:
12915 %
12916 %    o display: Specifies a connection to an X server; returned from
12917 %      XOpenDisplay.
12918 %
12919 %    o windows: Specifies a pointer to a XWindows structure.
12920 %
12921 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12922 %      Image window to crop.
12923 %
12924 %    o image: the image.
12925 %
12926 */
12927 static void XSetCropGeometry(Display *display,XWindows *windows,
12928   RectangleInfo *crop_info,Image *image)
12929 {
12930   char
12931     text[MaxTextExtent];
12932
12933   int
12934     x,
12935     y;
12936
12937   MagickRealType
12938     scale_factor;
12939
12940   unsigned int
12941     height,
12942     width;
12943
12944   if (windows->info.mapped != MagickFalse)
12945     {
12946       /*
12947         Display info on cropping rectangle.
12948       */
12949       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12950         (double) crop_info->width,(double) crop_info->height,(double)
12951         crop_info->x,(double) crop_info->y);
12952       XInfoWidget(display,windows,text);
12953     }
12954   /*
12955     Cropping geometry is relative to any previous crop geometry.
12956   */
12957   x=0;
12958   y=0;
12959   width=(unsigned int) image->columns;
12960   height=(unsigned int) image->rows;
12961   if (windows->image.crop_geometry != (char *) NULL)
12962     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12963   else
12964     windows->image.crop_geometry=AcquireString((char *) NULL);
12965   /*
12966     Define the crop geometry string from the cropping rectangle.
12967   */
12968   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12969   if (crop_info->x > 0)
12970     x+=(int) (scale_factor*crop_info->x+0.5);
12971   width=(unsigned int) (scale_factor*crop_info->width+0.5);
12972   if (width == 0)
12973     width=1;
12974   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12975   if (crop_info->y > 0)
12976     y+=(int) (scale_factor*crop_info->y+0.5);
12977   height=(unsigned int) (scale_factor*crop_info->height+0.5);
12978   if (height == 0)
12979     height=1;
12980   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12981     "%ux%u%+d%+d",width,height,x,y);
12982 }
12983 \f
12984 /*
12985 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12986 %                                                                             %
12987 %                                                                             %
12988 %                                                                             %
12989 +   X T i l e I m a g e                                                       %
12990 %                                                                             %
12991 %                                                                             %
12992 %                                                                             %
12993 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12994 %
12995 %  XTileImage() loads or deletes a selected tile from a visual image directory.
12996 %  The load or delete command is chosen from a menu.
12997 %
12998 %  The format of the XTileImage method is:
12999 %
13000 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13001 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13002 %
13003 %  A description of each parameter follows:
13004 %
13005 %    o tile_image:  XTileImage reads or deletes the tile image
13006 %      and returns it.  A null image is returned if an error occurs.
13007 %
13008 %    o display: Specifies a connection to an X server;  returned from
13009 %      XOpenDisplay.
13010 %
13011 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13012 %
13013 %    o windows: Specifies a pointer to a XWindows structure.
13014 %
13015 %    o image: the image; returned from ReadImage.
13016 %
13017 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13018 %      the entire image is refreshed.
13019 %
13020 %    o exception: return any errors or warnings in this structure.
13021 %
13022 */
13023 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13024   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13025 {
13026   static const char
13027     *VerbMenu[] =
13028     {
13029       "Load",
13030       "Next",
13031       "Former",
13032       "Delete",
13033       "Update",
13034       (char *) NULL,
13035     };
13036
13037   static const ModeType
13038     TileCommands[] =
13039     {
13040       TileLoadCommand,
13041       TileNextCommand,
13042       TileFormerCommand,
13043       TileDeleteCommand,
13044       TileUpdateCommand
13045     };
13046
13047   char
13048     command[MaxTextExtent],
13049     filename[MaxTextExtent];
13050
13051   Image
13052     *tile_image;
13053
13054   int
13055     id,
13056     status,
13057     tile,
13058     x,
13059     y;
13060
13061   MagickRealType
13062     scale_factor;
13063
13064   register char
13065     *p,
13066     *q;
13067
13068   register int
13069     i;
13070
13071   unsigned int
13072     height,
13073     width;
13074
13075   /*
13076     Tile image is relative to montage image configuration.
13077   */
13078   x=0;
13079   y=0;
13080   width=(unsigned int) image->columns;
13081   height=(unsigned int) image->rows;
13082   if (windows->image.crop_geometry != (char *) NULL)
13083     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13084   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13085   event->xbutton.x+=windows->image.x;
13086   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13087   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13088   event->xbutton.y+=windows->image.y;
13089   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13090   /*
13091     Determine size and location of each tile in the visual image directory.
13092   */
13093   width=(unsigned int) image->columns;
13094   height=(unsigned int) image->rows;
13095   x=0;
13096   y=0;
13097   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13098   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13099     (event->xbutton.x-x)/width;
13100   if (tile < 0)
13101     {
13102       /*
13103         Button press is outside any tile.
13104       */
13105       (void) XBell(display,0);
13106       return((Image *) NULL);
13107     }
13108   /*
13109     Determine file name from the tile directory.
13110   */
13111   p=image->directory;
13112   for (i=tile; (i != 0) && (*p != '\0'); )
13113   {
13114     if (*p == '\n')
13115       i--;
13116     p++;
13117   }
13118   if (*p == '\0')
13119     {
13120       /*
13121         Button press is outside any tile.
13122       */
13123       (void) XBell(display,0);
13124       return((Image *) NULL);
13125     }
13126   /*
13127     Select a command from the pop-up menu.
13128   */
13129   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13130   if (id < 0)
13131     return((Image *) NULL);
13132   q=p;
13133   while ((*q != '\n') && (*q != '\0'))
13134     q++;
13135   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13136   /*
13137     Perform command for the selected tile.
13138   */
13139   XSetCursorState(display,windows,MagickTrue);
13140   XCheckRefreshWindows(display,windows);
13141   tile_image=NewImageList();
13142   switch (TileCommands[id])
13143   {
13144     case TileLoadCommand:
13145     {
13146       /*
13147         Load tile image.
13148       */
13149       XCheckRefreshWindows(display,windows);
13150       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13151         MaxTextExtent);
13152       (void) CopyMagickString(resource_info->image_info->filename,filename,
13153         MaxTextExtent);
13154       tile_image=ReadImage(resource_info->image_info,exception);
13155       CatchException(exception);
13156       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13157       break;
13158     }
13159     case TileNextCommand:
13160     {
13161       /*
13162         Display next image.
13163       */
13164       XClientMessage(display,windows->image.id,windows->im_protocols,
13165         windows->im_next_image,CurrentTime);
13166       break;
13167     }
13168     case TileFormerCommand:
13169     {
13170       /*
13171         Display former image.
13172       */
13173       XClientMessage(display,windows->image.id,windows->im_protocols,
13174         windows->im_former_image,CurrentTime);
13175       break;
13176     }
13177     case TileDeleteCommand:
13178     {
13179       /*
13180         Delete tile image.
13181       */
13182       if (IsPathAccessible(filename) == MagickFalse)
13183         {
13184           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13185           break;
13186         }
13187       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13188       if (status <= 0)
13189         break;
13190       status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
13191       if (status != MagickFalse)
13192         {
13193           XNoticeWidget(display,windows,"Unable to delete image file:",
13194             filename);
13195           break;
13196         }
13197     }
13198     case TileUpdateCommand:
13199     {
13200       int
13201         x_offset,
13202         y_offset;
13203
13204       PixelPacket
13205         pixel;
13206
13207       register int
13208         j;
13209
13210       register Quantum
13211         *s;
13212
13213       /*
13214         Ensure all the images exist.
13215       */
13216       tile=0;
13217       for (p=image->directory; *p != '\0'; p++)
13218       {
13219         CacheView
13220           *image_view;
13221
13222         q=p;
13223         while ((*q != '\n') && (*q != '\0'))
13224           q++;
13225         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13226         p=q;
13227         if (IsPathAccessible(filename) != MagickFalse)
13228           {
13229             tile++;
13230             continue;
13231           }
13232         /*
13233           Overwrite tile with background color.
13234         */
13235         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13236         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13237         image_view=AcquireCacheView(image);
13238         (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13239         for (i=0; i < (int) height; i++)
13240         {
13241           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13242             y_offset+i,width,1,exception);
13243           if (s == (Quantum *) NULL)
13244             break;
13245           for (j=0; j < (int) width; j++)
13246           {
13247             SetPixelPacket(image,&pixel,s);
13248             s+=GetPixelChannels(image);
13249           }
13250           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13251             break;
13252         }
13253         image_view=DestroyCacheView(image_view);
13254         tile++;
13255       }
13256       windows->image.window_changes.width=(int) image->columns;
13257       windows->image.window_changes.height=(int) image->rows;
13258       XConfigureImageColormap(display,resource_info,windows,image);
13259       (void) XConfigureImage(display,resource_info,windows,image,exception);
13260       break;
13261     }
13262     default:
13263       break;
13264   }
13265   XSetCursorState(display,windows,MagickFalse);
13266   return(tile_image);
13267 }
13268 \f
13269 /*
13270 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13271 %                                                                             %
13272 %                                                                             %
13273 %                                                                             %
13274 +   X T r a n s l a t e I m a g e                                             %
13275 %                                                                             %
13276 %                                                                             %
13277 %                                                                             %
13278 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13279 %
13280 %  XTranslateImage() translates the image within an Image window by one pixel
13281 %  as specified by the key symbol.  If the image has a `montage string the
13282 %  translation is respect to the width and height contained within the string.
13283 %
13284 %  The format of the XTranslateImage method is:
13285 %
13286 %      void XTranslateImage(Display *display,XWindows *windows,
13287 %        Image *image,const KeySym key_symbol)
13288 %
13289 %  A description of each parameter follows:
13290 %
13291 %    o display: Specifies a connection to an X server; returned from
13292 %      XOpenDisplay.
13293 %
13294 %    o windows: Specifies a pointer to a XWindows structure.
13295 %
13296 %    o image: the image.
13297 %
13298 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13299 %      to trim.
13300 %
13301 */
13302 static void XTranslateImage(Display *display,XWindows *windows,
13303   Image *image,const KeySym key_symbol)
13304 {
13305   char
13306     text[MaxTextExtent];
13307
13308   int
13309     x,
13310     y;
13311
13312   unsigned int
13313     x_offset,
13314     y_offset;
13315
13316   /*
13317     User specified a pan position offset.
13318   */
13319   x_offset=windows->image.width;
13320   y_offset=windows->image.height;
13321   if (image->montage != (char *) NULL)
13322     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13323   switch ((int) key_symbol)
13324   {
13325     case XK_Home:
13326     case XK_KP_Home:
13327     {
13328       windows->image.x=(int) windows->image.width/2;
13329       windows->image.y=(int) windows->image.height/2;
13330       break;
13331     }
13332     case XK_Left:
13333     case XK_KP_Left:
13334     {
13335       windows->image.x-=x_offset;
13336       break;
13337     }
13338     case XK_Next:
13339     case XK_Up:
13340     case XK_KP_Up:
13341     {
13342       windows->image.y-=y_offset;
13343       break;
13344     }
13345     case XK_Right:
13346     case XK_KP_Right:
13347     {
13348       windows->image.x+=x_offset;
13349       break;
13350     }
13351     case XK_Prior:
13352     case XK_Down:
13353     case XK_KP_Down:
13354     {
13355       windows->image.y+=y_offset;
13356       break;
13357     }
13358     default:
13359       return;
13360   }
13361   /*
13362     Check boundary conditions.
13363   */
13364   if (windows->image.x < 0)
13365     windows->image.x=0;
13366   else
13367     if ((int) (windows->image.x+windows->image.width) >
13368         windows->image.ximage->width)
13369       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13370   if (windows->image.y < 0)
13371     windows->image.y=0;
13372   else
13373     if ((int) (windows->image.y+windows->image.height) >
13374         windows->image.ximage->height)
13375       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13376   /*
13377     Refresh Image window.
13378   */
13379   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13380     windows->image.width,windows->image.height,windows->image.x,
13381     windows->image.y);
13382   XInfoWidget(display,windows,text);
13383   XCheckRefreshWindows(display,windows);
13384   XDrawPanRectangle(display,windows);
13385   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13386   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13387 }
13388 \f
13389 /*
13390 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13391 %                                                                             %
13392 %                                                                             %
13393 %                                                                             %
13394 +   X T r i m I m a g e                                                       %
13395 %                                                                             %
13396 %                                                                             %
13397 %                                                                             %
13398 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13399 %
13400 %  XTrimImage() trims the edges from the Image window.
13401 %
13402 %  The format of the XTrimImage method is:
13403 %
13404 %      MagickBooleanType XTrimImage(Display *display,
13405 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13406 %        ExceptionInfo *exception)
13407 %
13408 %  A description of each parameter follows:
13409 %
13410 %    o display: Specifies a connection to an X server; returned from
13411 %      XOpenDisplay.
13412 %
13413 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13414 %
13415 %    o windows: Specifies a pointer to a XWindows structure.
13416 %
13417 %    o image: the image.
13418 %
13419 %    o exception: return any errors or warnings in this structure.
13420 %
13421 */
13422 static MagickBooleanType XTrimImage(Display *display,
13423   XResourceInfo *resource_info,XWindows *windows,Image *image,
13424   ExceptionInfo *exception)
13425 {
13426   RectangleInfo
13427     trim_info;
13428
13429   register int
13430     x,
13431     y;
13432
13433   size_t
13434     background,
13435     pixel;
13436
13437   /*
13438     Trim edges from image.
13439   */
13440   XSetCursorState(display,windows,MagickTrue);
13441   XCheckRefreshWindows(display,windows);
13442   /*
13443     Crop the left edge.
13444   */
13445   background=XGetPixel(windows->image.ximage,0,0);
13446   trim_info.width=(size_t) windows->image.ximage->width;
13447   for (x=0; x < windows->image.ximage->width; x++)
13448   {
13449     for (y=0; y < windows->image.ximage->height; y++)
13450     {
13451       pixel=XGetPixel(windows->image.ximage,x,y);
13452       if (pixel != background)
13453         break;
13454     }
13455     if (y < windows->image.ximage->height)
13456       break;
13457   }
13458   trim_info.x=(ssize_t) x;
13459   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13460     {
13461       XSetCursorState(display,windows,MagickFalse);
13462       return(MagickFalse);
13463     }
13464   /*
13465     Crop the right edge.
13466   */
13467   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13468   for (x=windows->image.ximage->width-1; x != 0; x--)
13469   {
13470     for (y=0; y < windows->image.ximage->height; y++)
13471     {
13472       pixel=XGetPixel(windows->image.ximage,x,y);
13473       if (pixel != background)
13474         break;
13475     }
13476     if (y < windows->image.ximage->height)
13477       break;
13478   }
13479   trim_info.width=(size_t) (x-trim_info.x+1);
13480   /*
13481     Crop the top edge.
13482   */
13483   background=XGetPixel(windows->image.ximage,0,0);
13484   trim_info.height=(size_t) windows->image.ximage->height;
13485   for (y=0; y < windows->image.ximage->height; y++)
13486   {
13487     for (x=0; x < windows->image.ximage->width; x++)
13488     {
13489       pixel=XGetPixel(windows->image.ximage,x,y);
13490       if (pixel != background)
13491         break;
13492     }
13493     if (x < windows->image.ximage->width)
13494       break;
13495   }
13496   trim_info.y=(ssize_t) y;
13497   /*
13498     Crop the bottom edge.
13499   */
13500   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13501   for (y=windows->image.ximage->height-1; y != 0; y--)
13502   {
13503     for (x=0; x < windows->image.ximage->width; x++)
13504     {
13505       pixel=XGetPixel(windows->image.ximage,x,y);
13506       if (pixel != background)
13507         break;
13508     }
13509     if (x < windows->image.ximage->width)
13510       break;
13511   }
13512   trim_info.height=(size_t) y-trim_info.y+1;
13513   if (((unsigned int) trim_info.width != windows->image.width) ||
13514       ((unsigned int) trim_info.height != windows->image.height))
13515     {
13516       /*
13517         Reconfigure Image window as defined by the trimming rectangle.
13518       */
13519       XSetCropGeometry(display,windows,&trim_info,image);
13520       windows->image.window_changes.width=(int) trim_info.width;
13521       windows->image.window_changes.height=(int) trim_info.height;
13522       (void) XConfigureImage(display,resource_info,windows,image,exception);
13523     }
13524   XSetCursorState(display,windows,MagickFalse);
13525   return(MagickTrue);
13526 }
13527 \f
13528 /*
13529 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13530 %                                                                             %
13531 %                                                                             %
13532 %                                                                             %
13533 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13534 %                                                                             %
13535 %                                                                             %
13536 %                                                                             %
13537 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13538 %
13539 %  XVisualDirectoryImage() creates a Visual Image Directory.
13540 %
13541 %  The format of the XVisualDirectoryImage method is:
13542 %
13543 %      Image *XVisualDirectoryImage(Display *display,
13544 %        XResourceInfo *resource_info,XWindows *windows,
13545 %        ExceptionInfo *exception)
13546 %
13547 %  A description of each parameter follows:
13548 %
13549 %    o display: Specifies a connection to an X server; returned from
13550 %      XOpenDisplay.
13551 %
13552 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13553 %
13554 %    o windows: Specifies a pointer to a XWindows structure.
13555 %
13556 %    o exception: return any errors or warnings in this structure.
13557 %
13558 */
13559 static Image *XVisualDirectoryImage(Display *display,
13560   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13561 {
13562 #define TileImageTag  "Scale/Image"
13563 #define XClientName  "montage"
13564
13565   char
13566     **filelist;
13567
13568   Image
13569     *images,
13570     *montage_image,
13571     *next_image,
13572     *thumbnail_image;
13573
13574   ImageInfo
13575     *read_info;
13576
13577   int
13578     number_files;
13579
13580   MagickBooleanType
13581     backdrop;
13582
13583   MagickStatusType
13584     status;
13585
13586   MontageInfo
13587     *montage_info;
13588
13589   RectangleInfo
13590     geometry;
13591
13592   register int
13593     i;
13594
13595   static char
13596     filename[MaxTextExtent] = "\0",
13597     filenames[MaxTextExtent] = "*";
13598
13599   XResourceInfo
13600     background_resources;
13601
13602   /*
13603     Request file name from user.
13604   */
13605   XFileBrowserWidget(display,windows,"Directory",filenames);
13606   if (*filenames == '\0')
13607     return((Image *) NULL);
13608   /*
13609     Expand the filenames.
13610   */
13611   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13612   if (filelist == (char **) NULL)
13613     {
13614       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13615         filenames);
13616       return((Image *) NULL);
13617     }
13618   number_files=1;
13619   filelist[0]=filenames;
13620   status=ExpandFilenames(&number_files,&filelist);
13621   if ((status == MagickFalse) || (number_files == 0))
13622     {
13623       if (number_files == 0)
13624         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13625       else
13626         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13627           filenames);
13628       return((Image *) NULL);
13629     }
13630   /*
13631     Set image background resources.
13632   */
13633   background_resources=(*resource_info);
13634   background_resources.window_id=AcquireString("");
13635   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13636     "0x%lx",windows->image.id);
13637   background_resources.backdrop=MagickTrue;
13638   /*
13639     Read each image and convert them to a tile.
13640   */
13641   backdrop=(windows->visual_info->klass == TrueColor) ||
13642     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13643   read_info=CloneImageInfo(resource_info->image_info);
13644   (void) SetImageOption(read_info,"jpeg:size","120x120");
13645   (void) CloneString(&read_info->size,DefaultTileGeometry);
13646   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13647     (void *) NULL);
13648   images=NewImageList();
13649   XSetCursorState(display,windows,MagickTrue);
13650   XCheckRefreshWindows(display,windows);
13651   for (i=0; i < (int) number_files; i++)
13652   {
13653     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13654     filelist[i]=DestroyString(filelist[i]);
13655     *read_info->magick='\0';
13656     next_image=ReadImage(read_info,exception);
13657     CatchException(exception);
13658     if (next_image != (Image *) NULL)
13659       {
13660         (void) DeleteImageProperty(next_image,"label");
13661         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13662           read_info,next_image,DefaultTileLabel,exception));
13663         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13664           exception);
13665         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13666           geometry.height,exception);
13667         if (thumbnail_image != (Image *) NULL)
13668           {
13669             next_image=DestroyImage(next_image);
13670             next_image=thumbnail_image;
13671           }
13672         if (backdrop)
13673           {
13674             (void) XDisplayBackgroundImage(display,&background_resources,
13675               next_image,exception);
13676             XSetCursorState(display,windows,MagickTrue);
13677           }
13678         AppendImageToList(&images,next_image);
13679         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13680           {
13681             MagickBooleanType
13682               proceed;
13683
13684             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13685               (MagickSizeType) number_files);
13686             if (proceed == MagickFalse)
13687               break;
13688           }
13689       }
13690   }
13691   filelist=(char **) RelinquishMagickMemory(filelist);
13692   if (images == (Image *) NULL)
13693     {
13694       read_info=DestroyImageInfo(read_info);
13695       XSetCursorState(display,windows,MagickFalse);
13696       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13697       return((Image *) NULL);
13698     }
13699   /*
13700     Create the Visual Image Directory.
13701   */
13702   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13703   montage_info->pointsize=10;
13704   if (resource_info->font != (char *) NULL)
13705     (void) CloneString(&montage_info->font,resource_info->font);
13706   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13707   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13708     images),exception);
13709   images=DestroyImageList(images);
13710   montage_info=DestroyMontageInfo(montage_info);
13711   read_info=DestroyImageInfo(read_info);
13712   XSetCursorState(display,windows,MagickFalse);
13713   if (montage_image == (Image *) NULL)
13714     return(montage_image);
13715   XClientMessage(display,windows->image.id,windows->im_protocols,
13716     windows->im_next_image,CurrentTime);
13717   return(montage_image);
13718 }
13719 \f
13720 /*
13721 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13722 %                                                                             %
13723 %                                                                             %
13724 %                                                                             %
13725 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13726 %                                                                             %
13727 %                                                                             %
13728 %                                                                             %
13729 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13730 %
13731 %  XDisplayBackgroundImage() displays an image in the background of a window.
13732 %
13733 %  The format of the XDisplayBackgroundImage method is:
13734 %
13735 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13736 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13737 %
13738 %  A description of each parameter follows:
13739 %
13740 %    o display: Specifies a connection to an X server;  returned from
13741 %      XOpenDisplay.
13742 %
13743 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13744 %
13745 %    o image: the image.
13746 %
13747 %    o exception: return any errors or warnings in this structure.
13748 %
13749 */
13750 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13751   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13752 {
13753   char
13754     geometry[MaxTextExtent],
13755     visual_type[MaxTextExtent];
13756
13757   int
13758     height,
13759     status,
13760     width;
13761
13762   RectangleInfo
13763     geometry_info;
13764
13765   static XPixelInfo
13766     pixel;
13767
13768   static XStandardColormap
13769     *map_info;
13770
13771   static XVisualInfo
13772     *visual_info = (XVisualInfo *) NULL;
13773
13774   static XWindowInfo
13775     window_info;
13776
13777   size_t
13778     delay;
13779
13780   Window
13781     root_window;
13782
13783   XGCValues
13784     context_values;
13785
13786   XResourceInfo
13787     resources;
13788
13789   XWindowAttributes
13790     window_attributes;
13791
13792   /*
13793     Determine target window.
13794   */
13795   assert(image != (Image *) NULL);
13796   assert(image->signature == MagickSignature);
13797   if (image->debug != MagickFalse)
13798     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13799   resources=(*resource_info);
13800   window_info.id=(Window) NULL;
13801   root_window=XRootWindow(display,XDefaultScreen(display));
13802   if (LocaleCompare(resources.window_id,"root") == 0)
13803     window_info.id=root_window;
13804   else
13805     {
13806       if (isdigit((unsigned char) *resources.window_id) != 0)
13807         window_info.id=XWindowByID(display,root_window,
13808           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13809       if (window_info.id == (Window) NULL)
13810         window_info.id=XWindowByName(display,root_window,resources.window_id);
13811     }
13812   if (window_info.id == (Window) NULL)
13813     {
13814       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13815         resources.window_id);
13816       return(MagickFalse);
13817     }
13818   /*
13819     Determine window visual id.
13820   */
13821   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13822   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13823   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13824   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13825   if (status != 0)
13826     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13827       XVisualIDFromVisual(window_attributes.visual));
13828   if (visual_info == (XVisualInfo *) NULL)
13829     {
13830       /*
13831         Allocate standard colormap.
13832       */
13833       map_info=XAllocStandardColormap();
13834       if (map_info == (XStandardColormap *) NULL)
13835         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13836           image->filename);
13837       map_info->colormap=(Colormap) NULL;
13838       pixel.pixels=(unsigned long *) NULL;
13839       /*
13840         Initialize visual info.
13841       */
13842       resources.map_type=(char *) NULL;
13843       resources.visual_type=visual_type;
13844       visual_info=XBestVisualInfo(display,map_info,&resources);
13845       if (visual_info == (XVisualInfo *) NULL)
13846         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13847           resources.visual_type);
13848       /*
13849         Initialize window info.
13850       */
13851       window_info.ximage=(XImage *) NULL;
13852       window_info.matte_image=(XImage *) NULL;
13853       window_info.pixmap=(Pixmap) NULL;
13854       window_info.matte_pixmap=(Pixmap) NULL;
13855     }
13856   /*
13857     Free previous root colors.
13858   */
13859   if (window_info.id == root_window)
13860     (void) XDestroyWindowColors(display,root_window);
13861   /*
13862     Initialize Standard Colormap.
13863   */
13864   resources.colormap=SharedColormap;
13865   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13866   /*
13867     Graphic context superclass.
13868   */
13869   context_values.background=pixel.background_color.pixel;
13870   context_values.foreground=pixel.foreground_color.pixel;
13871   pixel.annotate_context=XCreateGC(display,window_info.id,
13872     (size_t) (GCBackground | GCForeground),&context_values);
13873   if (pixel.annotate_context == (GC) NULL)
13874     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13875       image->filename);
13876   /*
13877     Initialize Image window attributes.
13878   */
13879   window_info.name=AcquireString("\0");
13880   window_info.icon_name=AcquireString("\0");
13881   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13882     &resources,&window_info);
13883   /*
13884     Create the X image.
13885   */
13886   window_info.width=(unsigned int) image->columns;
13887   window_info.height=(unsigned int) image->rows;
13888   if ((image->columns != window_info.width) ||
13889       (image->rows != window_info.height))
13890     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13891       image->filename);
13892   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13893     window_attributes.width,window_attributes.height);
13894   geometry_info.width=window_info.width;
13895   geometry_info.height=window_info.height;
13896   geometry_info.x=(ssize_t) window_info.x;
13897   geometry_info.y=(ssize_t) window_info.y;
13898   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13899     &geometry_info.width,&geometry_info.height);
13900   window_info.width=(unsigned int) geometry_info.width;
13901   window_info.height=(unsigned int) geometry_info.height;
13902   window_info.x=(int) geometry_info.x;
13903   window_info.y=(int) geometry_info.y;
13904   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13905     window_info.height,exception);
13906   if (status == MagickFalse)
13907     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13908       image->filename);
13909   window_info.x=0;
13910   window_info.y=0;
13911   if (image->debug != MagickFalse)
13912     {
13913       (void) LogMagickEvent(X11Event,GetMagickModule(),
13914         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13915         (double) image->columns,(double) image->rows);
13916       if (image->colors != 0)
13917         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13918           image->colors);
13919       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13920     }
13921   /*
13922     Adjust image dimensions as specified by backdrop or geometry options.
13923   */
13924   width=(int) window_info.width;
13925   height=(int) window_info.height;
13926   if (resources.backdrop != MagickFalse)
13927     {
13928       /*
13929         Center image on window.
13930       */
13931       window_info.x=(window_attributes.width/2)-
13932         (window_info.ximage->width/2);
13933       window_info.y=(window_attributes.height/2)-
13934         (window_info.ximage->height/2);
13935       width=window_attributes.width;
13936       height=window_attributes.height;
13937     }
13938   if ((resources.image_geometry != (char *) NULL) &&
13939       (*resources.image_geometry != '\0'))
13940     {
13941       char
13942         default_geometry[MaxTextExtent];
13943
13944       int
13945         flags,
13946         gravity;
13947
13948       XSizeHints
13949         *size_hints;
13950
13951       /*
13952         User specified geometry.
13953       */
13954       size_hints=XAllocSizeHints();
13955       if (size_hints == (XSizeHints *) NULL)
13956         ThrowXWindowFatalException(ResourceLimitFatalError,
13957           "MemoryAllocationFailed",image->filename);
13958       size_hints->flags=0L;
13959       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13960         width,height);
13961       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13962         default_geometry,window_info.border_width,size_hints,&window_info.x,
13963         &window_info.y,&width,&height,&gravity);
13964       if (flags & (XValue | YValue))
13965         {
13966           width=window_attributes.width;
13967           height=window_attributes.height;
13968         }
13969       (void) XFree((void *) size_hints);
13970     }
13971   /*
13972     Create the X pixmap.
13973   */
13974   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13975     (unsigned int) height,window_info.depth);
13976   if (window_info.pixmap == (Pixmap) NULL)
13977     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13978       image->filename);
13979   /*
13980     Display pixmap on the window.
13981   */
13982   if (((unsigned int) width > window_info.width) ||
13983       ((unsigned int) height > window_info.height))
13984     (void) XFillRectangle(display,window_info.pixmap,
13985       window_info.annotate_context,0,0,(unsigned int) width,
13986       (unsigned int) height);
13987   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13988     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13989     window_info.width,(unsigned int) window_info.height);
13990   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13991   (void) XClearWindow(display,window_info.id);
13992   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13993   XDelay(display,delay == 0UL ? 10UL : delay);
13994   (void) XSync(display,MagickFalse);
13995   return(window_info.id == root_window ? MagickTrue : MagickFalse);
13996 }
13997 \f
13998 /*
13999 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14000 %                                                                             %
14001 %                                                                             %
14002 %                                                                             %
14003 +   X D i s p l a y I m a g e                                                 %
14004 %                                                                             %
14005 %                                                                             %
14006 %                                                                             %
14007 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14008 %
14009 %  XDisplayImage() displays an image via X11.  A new image is created and
14010 %  returned if the user interactively transforms the displayed image.
14011 %
14012 %  The format of the XDisplayImage method is:
14013 %
14014 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14015 %        char **argv,int argc,Image **image,size_t *state,
14016 %        ExceptionInfo *exception)
14017 %
14018 %  A description of each parameter follows:
14019 %
14020 %    o nexus:  Method XDisplayImage returns an image when the
14021 %      user chooses 'Open Image' from the command menu or picks a tile
14022 %      from the image directory.  Otherwise a null image is returned.
14023 %
14024 %    o display: Specifies a connection to an X server;  returned from
14025 %      XOpenDisplay.
14026 %
14027 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14028 %
14029 %    o argv: Specifies the application's argument list.
14030 %
14031 %    o argc: Specifies the number of arguments.
14032 %
14033 %    o image: Specifies an address to an address of an Image structure;
14034 %
14035 %    o exception: return any errors or warnings in this structure.
14036 %
14037 */
14038 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14039   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14040 {
14041 #define MagnifySize  256  /* must be a power of 2 */
14042 #define MagickMenus  10
14043 #define MagickTitle  "Commands"
14044
14045   static const char
14046     *CommandMenu[] =
14047     {
14048       "File",
14049       "Edit",
14050       "View",
14051       "Transform",
14052       "Enhance",
14053       "Effects",
14054       "F/X",
14055       "Image Edit",
14056       "Miscellany",
14057       "Help",
14058       (char *) NULL
14059     },
14060     *FileMenu[] =
14061     {
14062       "Open...",
14063       "Next",
14064       "Former",
14065       "Select...",
14066       "Save...",
14067       "Print...",
14068       "Delete...",
14069       "New...",
14070       "Visual Directory...",
14071       "Quit",
14072       (char *) NULL
14073     },
14074     *EditMenu[] =
14075     {
14076       "Undo",
14077       "Redo",
14078       "Cut",
14079       "Copy",
14080       "Paste",
14081       (char *) NULL
14082     },
14083     *ViewMenu[] =
14084     {
14085       "Half Size",
14086       "Original Size",
14087       "Double Size",
14088       "Resize...",
14089       "Apply",
14090       "Refresh",
14091       "Restore",
14092       (char *) NULL
14093     },
14094     *TransformMenu[] =
14095     {
14096       "Crop",
14097       "Chop",
14098       "Flop",
14099       "Flip",
14100       "Rotate Right",
14101       "Rotate Left",
14102       "Rotate...",
14103       "Shear...",
14104       "Roll...",
14105       "Trim Edges",
14106       (char *) NULL
14107     },
14108     *EnhanceMenu[] =
14109     {
14110       "Hue...",
14111       "Saturation...",
14112       "Brightness...",
14113       "Gamma...",
14114       "Spiff",
14115       "Dull",
14116       "Contrast Stretch...",
14117       "Sigmoidal Contrast...",
14118       "Normalize",
14119       "Equalize",
14120       "Negate",
14121       "Grayscale",
14122       "Map...",
14123       "Quantize...",
14124       (char *) NULL
14125     },
14126     *EffectsMenu[] =
14127     {
14128       "Despeckle",
14129       "Emboss",
14130       "Reduce Noise",
14131       "Add Noise...",
14132       "Sharpen...",
14133       "Blur...",
14134       "Threshold...",
14135       "Edge Detect...",
14136       "Spread...",
14137       "Shade...",
14138       "Raise...",
14139       "Segment...",
14140       (char *) NULL
14141     },
14142     *FXMenu[] =
14143     {
14144       "Solarize...",
14145       "Sepia Tone...",
14146       "Swirl...",
14147       "Implode...",
14148       "Vignette...",
14149       "Wave...",
14150       "Oil Paint...",
14151       "Charcoal Draw...",
14152       (char *) NULL
14153     },
14154     *ImageEditMenu[] =
14155     {
14156       "Annotate...",
14157       "Draw...",
14158       "Color...",
14159       "Matte...",
14160       "Composite...",
14161       "Add Border...",
14162       "Add Frame...",
14163       "Comment...",
14164       "Launch...",
14165       "Region of Interest...",
14166       (char *) NULL
14167     },
14168     *MiscellanyMenu[] =
14169     {
14170       "Image Info",
14171       "Zoom Image",
14172       "Show Preview...",
14173       "Show Histogram",
14174       "Show Matte",
14175       "Background...",
14176       "Slide Show...",
14177       "Preferences...",
14178       (char *) NULL
14179     },
14180     *HelpMenu[] =
14181     {
14182       "Overview",
14183       "Browse Documentation",
14184       "About Display",
14185       (char *) NULL
14186     },
14187     *ShortCutsMenu[] =
14188     {
14189       "Next",
14190       "Former",
14191       "Open...",
14192       "Save...",
14193       "Print...",
14194       "Undo",
14195       "Restore",
14196       "Image Info",
14197       "Quit",
14198       (char *) NULL
14199     },
14200     *VirtualMenu[] =
14201     {
14202       "Image Info",
14203       "Print",
14204       "Next",
14205       "Quit",
14206       (char *) NULL
14207     };
14208
14209   static const char
14210     **Menus[MagickMenus] =
14211     {
14212       FileMenu,
14213       EditMenu,
14214       ViewMenu,
14215       TransformMenu,
14216       EnhanceMenu,
14217       EffectsMenu,
14218       FXMenu,
14219       ImageEditMenu,
14220       MiscellanyMenu,
14221       HelpMenu
14222     };
14223
14224   static CommandType
14225     CommandMenus[] =
14226     {
14227       NullCommand,
14228       NullCommand,
14229       NullCommand,
14230       NullCommand,
14231       NullCommand,
14232       NullCommand,
14233       NullCommand,
14234       NullCommand,
14235       NullCommand,
14236       NullCommand,
14237     },
14238     FileCommands[] =
14239     {
14240       OpenCommand,
14241       NextCommand,
14242       FormerCommand,
14243       SelectCommand,
14244       SaveCommand,
14245       PrintCommand,
14246       DeleteCommand,
14247       NewCommand,
14248       VisualDirectoryCommand,
14249       QuitCommand
14250     },
14251     EditCommands[] =
14252     {
14253       UndoCommand,
14254       RedoCommand,
14255       CutCommand,
14256       CopyCommand,
14257       PasteCommand
14258     },
14259     ViewCommands[] =
14260     {
14261       HalfSizeCommand,
14262       OriginalSizeCommand,
14263       DoubleSizeCommand,
14264       ResizeCommand,
14265       ApplyCommand,
14266       RefreshCommand,
14267       RestoreCommand
14268     },
14269     TransformCommands[] =
14270     {
14271       CropCommand,
14272       ChopCommand,
14273       FlopCommand,
14274       FlipCommand,
14275       RotateRightCommand,
14276       RotateLeftCommand,
14277       RotateCommand,
14278       ShearCommand,
14279       RollCommand,
14280       TrimCommand
14281     },
14282     EnhanceCommands[] =
14283     {
14284       HueCommand,
14285       SaturationCommand,
14286       BrightnessCommand,
14287       GammaCommand,
14288       SpiffCommand,
14289       DullCommand,
14290       ContrastStretchCommand,
14291       SigmoidalContrastCommand,
14292       NormalizeCommand,
14293       EqualizeCommand,
14294       NegateCommand,
14295       GrayscaleCommand,
14296       MapCommand,
14297       QuantizeCommand
14298     },
14299     EffectsCommands[] =
14300     {
14301       DespeckleCommand,
14302       EmbossCommand,
14303       ReduceNoiseCommand,
14304       AddNoiseCommand,
14305       SharpenCommand,
14306       BlurCommand,
14307       ThresholdCommand,
14308       EdgeDetectCommand,
14309       SpreadCommand,
14310       ShadeCommand,
14311       RaiseCommand,
14312       SegmentCommand
14313     },
14314     FXCommands[] =
14315     {
14316       SolarizeCommand,
14317       SepiaToneCommand,
14318       SwirlCommand,
14319       ImplodeCommand,
14320       VignetteCommand,
14321       WaveCommand,
14322       OilPaintCommand,
14323       CharcoalDrawCommand
14324     },
14325     ImageEditCommands[] =
14326     {
14327       AnnotateCommand,
14328       DrawCommand,
14329       ColorCommand,
14330       MatteCommand,
14331       CompositeCommand,
14332       AddBorderCommand,
14333       AddFrameCommand,
14334       CommentCommand,
14335       LaunchCommand,
14336       RegionofInterestCommand
14337     },
14338     MiscellanyCommands[] =
14339     {
14340       InfoCommand,
14341       ZoomCommand,
14342       ShowPreviewCommand,
14343       ShowHistogramCommand,
14344       ShowMatteCommand,
14345       BackgroundCommand,
14346       SlideShowCommand,
14347       PreferencesCommand
14348     },
14349     HelpCommands[] =
14350     {
14351       HelpCommand,
14352       BrowseDocumentationCommand,
14353       VersionCommand
14354     },
14355     ShortCutsCommands[] =
14356     {
14357       NextCommand,
14358       FormerCommand,
14359       OpenCommand,
14360       SaveCommand,
14361       PrintCommand,
14362       UndoCommand,
14363       RestoreCommand,
14364       InfoCommand,
14365       QuitCommand
14366     },
14367     VirtualCommands[] =
14368     {
14369       InfoCommand,
14370       PrintCommand,
14371       NextCommand,
14372       QuitCommand
14373     };
14374
14375   static CommandType
14376     *Commands[MagickMenus] =
14377     {
14378       FileCommands,
14379       EditCommands,
14380       ViewCommands,
14381       TransformCommands,
14382       EnhanceCommands,
14383       EffectsCommands,
14384       FXCommands,
14385       ImageEditCommands,
14386       MiscellanyCommands,
14387       HelpCommands
14388     };
14389
14390   char
14391     command[MaxTextExtent],
14392     *directory,
14393     geometry[MaxTextExtent],
14394     resource_name[MaxTextExtent];
14395
14396   CommandType
14397     command_type;
14398
14399   Image
14400     *display_image,
14401     *nexus;
14402
14403   int
14404     entry,
14405     id;
14406
14407   KeySym
14408     key_symbol;
14409
14410   MagickStatusType
14411     context_mask,
14412     status;
14413
14414   RectangleInfo
14415     geometry_info;
14416
14417   register int
14418     i;
14419
14420   static char
14421     working_directory[MaxTextExtent];
14422
14423   static XPoint
14424     vid_info;
14425
14426   static XWindowInfo
14427     *magick_windows[MaxXWindows];
14428
14429   static unsigned int
14430     number_windows;
14431
14432   struct stat
14433     attributes;
14434
14435   time_t
14436     timer,
14437     timestamp,
14438     update_time;
14439
14440   unsigned int
14441     height,
14442     width;
14443
14444   size_t
14445     delay;
14446
14447   WarningHandler
14448     warning_handler;
14449
14450   Window
14451     root_window;
14452
14453   XClassHint
14454     *class_hints;
14455
14456   XEvent
14457     event;
14458
14459   XFontStruct
14460     *font_info;
14461
14462   XGCValues
14463     context_values;
14464
14465   XPixelInfo
14466     *icon_pixel,
14467     *pixel;
14468
14469   XResourceInfo
14470     *icon_resources;
14471
14472   XStandardColormap
14473     *icon_map,
14474     *map_info;
14475
14476   XVisualInfo
14477     *icon_visual,
14478     *visual_info;
14479
14480   XWindowChanges
14481     window_changes;
14482
14483   XWindows
14484     *windows;
14485
14486   XWMHints
14487     *manager_hints;
14488
14489   assert(image != (Image **) NULL);
14490   assert((*image)->signature == MagickSignature);
14491   if ((*image)->debug != MagickFalse)
14492     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14493   display_image=(*image);
14494   warning_handler=(WarningHandler) NULL;
14495   windows=XSetWindows((XWindows *) ~0);
14496   if (windows != (XWindows *) NULL)
14497     {
14498       int
14499         status;
14500
14501       status=chdir(working_directory);
14502       if (status == -1)
14503         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14504           "UnableToOpenFile","%s",working_directory);
14505       warning_handler=resource_info->display_warnings ?
14506         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14507       warning_handler=resource_info->display_warnings ?
14508         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14509     }
14510   else
14511     {
14512       /*
14513         Allocate windows structure.
14514       */
14515       resource_info->colors=display_image->colors;
14516       windows=XSetWindows(XInitializeWindows(display,resource_info));
14517       if (windows == (XWindows *) NULL)
14518         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14519           (*image)->filename);
14520       /*
14521         Initialize window id's.
14522       */
14523       number_windows=0;
14524       magick_windows[number_windows++]=(&windows->icon);
14525       magick_windows[number_windows++]=(&windows->backdrop);
14526       magick_windows[number_windows++]=(&windows->image);
14527       magick_windows[number_windows++]=(&windows->info);
14528       magick_windows[number_windows++]=(&windows->command);
14529       magick_windows[number_windows++]=(&windows->widget);
14530       magick_windows[number_windows++]=(&windows->popup);
14531       magick_windows[number_windows++]=(&windows->magnify);
14532       magick_windows[number_windows++]=(&windows->pan);
14533       for (i=0; i < (int) number_windows; i++)
14534         magick_windows[i]->id=(Window) NULL;
14535       vid_info.x=0;
14536       vid_info.y=0;
14537     }
14538   /*
14539     Initialize font info.
14540   */
14541   if (windows->font_info != (XFontStruct *) NULL)
14542     (void) XFreeFont(display,windows->font_info);
14543   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14544   if (windows->font_info == (XFontStruct *) NULL)
14545     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14546       resource_info->font);
14547   /*
14548     Initialize Standard Colormap.
14549   */
14550   map_info=windows->map_info;
14551   icon_map=windows->icon_map;
14552   visual_info=windows->visual_info;
14553   icon_visual=windows->icon_visual;
14554   pixel=windows->pixel_info;
14555   icon_pixel=windows->icon_pixel;
14556   font_info=windows->font_info;
14557   icon_resources=windows->icon_resources;
14558   class_hints=windows->class_hints;
14559   manager_hints=windows->manager_hints;
14560   root_window=XRootWindow(display,visual_info->screen);
14561   nexus=NewImageList();
14562   if (display_image->debug != MagickFalse)
14563     {
14564       (void) LogMagickEvent(X11Event,GetMagickModule(),
14565         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14566         (double) display_image->scene,(double) display_image->columns,
14567         (double) display_image->rows);
14568       if (display_image->colors != 0)
14569         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14570           display_image->colors);
14571       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14572         display_image->magick);
14573     }
14574   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14575     map_info,pixel);
14576   display_image->taint=MagickFalse;
14577   /*
14578     Initialize graphic context.
14579   */
14580   windows->context.id=(Window) NULL;
14581   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14582     resource_info,&windows->context);
14583   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14584   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14585   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14586   manager_hints->flags=InputHint | StateHint;
14587   manager_hints->input=MagickFalse;
14588   manager_hints->initial_state=WithdrawnState;
14589   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14590     &windows->context);
14591   if (display_image->debug != MagickFalse)
14592     (void) LogMagickEvent(X11Event,GetMagickModule(),
14593       "Window id: 0x%lx (context)",windows->context.id);
14594   context_values.background=pixel->background_color.pixel;
14595   context_values.font=font_info->fid;
14596   context_values.foreground=pixel->foreground_color.pixel;
14597   context_values.graphics_exposures=MagickFalse;
14598   context_mask=(MagickStatusType)
14599     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14600   if (pixel->annotate_context != (GC) NULL)
14601     (void) XFreeGC(display,pixel->annotate_context);
14602   pixel->annotate_context=XCreateGC(display,windows->context.id,
14603     context_mask,&context_values);
14604   if (pixel->annotate_context == (GC) NULL)
14605     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14606       display_image->filename);
14607   context_values.background=pixel->depth_color.pixel;
14608   if (pixel->widget_context != (GC) NULL)
14609     (void) XFreeGC(display,pixel->widget_context);
14610   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14611     &context_values);
14612   if (pixel->widget_context == (GC) NULL)
14613     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14614       display_image->filename);
14615   context_values.background=pixel->foreground_color.pixel;
14616   context_values.foreground=pixel->background_color.pixel;
14617   context_values.plane_mask=context_values.background ^
14618     context_values.foreground;
14619   if (pixel->highlight_context != (GC) NULL)
14620     (void) XFreeGC(display,pixel->highlight_context);
14621   pixel->highlight_context=XCreateGC(display,windows->context.id,
14622     (size_t) (context_mask | GCPlaneMask),&context_values);
14623   if (pixel->highlight_context == (GC) NULL)
14624     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14625       display_image->filename);
14626   (void) XDestroyWindow(display,windows->context.id);
14627   /*
14628     Initialize icon window.
14629   */
14630   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14631     icon_resources,&windows->icon);
14632   windows->icon.geometry=resource_info->icon_geometry;
14633   XBestIconSize(display,&windows->icon,display_image);
14634   windows->icon.attributes.colormap=XDefaultColormap(display,
14635     icon_visual->screen);
14636   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14637   manager_hints->flags=InputHint | StateHint;
14638   manager_hints->input=MagickFalse;
14639   manager_hints->initial_state=IconicState;
14640   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14641     &windows->icon);
14642   if (display_image->debug != MagickFalse)
14643     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14644       windows->icon.id);
14645   /*
14646     Initialize graphic context for icon window.
14647   */
14648   if (icon_pixel->annotate_context != (GC) NULL)
14649     (void) XFreeGC(display,icon_pixel->annotate_context);
14650   context_values.background=icon_pixel->background_color.pixel;
14651   context_values.foreground=icon_pixel->foreground_color.pixel;
14652   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14653     (size_t) (GCBackground | GCForeground),&context_values);
14654   if (icon_pixel->annotate_context == (GC) NULL)
14655     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14656       display_image->filename);
14657   windows->icon.annotate_context=icon_pixel->annotate_context;
14658   /*
14659     Initialize Image window.
14660   */
14661   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14662     &windows->image);
14663   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14664   if (resource_info->use_shared_memory == MagickFalse)
14665     windows->image.shared_memory=MagickFalse;
14666   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14667     {
14668       char
14669         *title;
14670
14671       title=InterpretImageProperties(resource_info->image_info,display_image,
14672         resource_info->title,exception);
14673       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14674       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14675       title=DestroyString(title);
14676     }
14677   else
14678     {
14679       char
14680         filename[MaxTextExtent];
14681
14682       /*
14683         Window name is the base of the filename.
14684       */
14685       GetPathComponent(display_image->magick_filename,TailPath,filename);
14686       if (display_image->scene == 0)
14687         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14688           "%s: %s",MagickPackageName,filename);
14689       else
14690         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14691           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14692           (double) display_image->scene,(double) GetImageListLength(
14693           display_image));
14694       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14695     }
14696   if (resource_info->immutable)
14697     windows->image.immutable=MagickTrue;
14698   windows->image.use_pixmap=resource_info->use_pixmap;
14699   windows->image.geometry=resource_info->image_geometry;
14700   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14701     XDisplayWidth(display,visual_info->screen),
14702     XDisplayHeight(display,visual_info->screen));
14703   geometry_info.width=display_image->columns;
14704   geometry_info.height=display_image->rows;
14705   geometry_info.x=0;
14706   geometry_info.y=0;
14707   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14708     &geometry_info.width,&geometry_info.height);
14709   windows->image.width=(unsigned int) geometry_info.width;
14710   windows->image.height=(unsigned int) geometry_info.height;
14711   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14712     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14713     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14714     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14715   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14716     resource_info,&windows->backdrop);
14717   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14718     {
14719       /*
14720         Initialize backdrop window.
14721       */
14722       windows->backdrop.x=0;
14723       windows->backdrop.y=0;
14724       (void) CloneString(&windows->backdrop.name,"Backdrop");
14725       windows->backdrop.flags=(size_t) (USSize | USPosition);
14726       windows->backdrop.width=(unsigned int)
14727         XDisplayWidth(display,visual_info->screen);
14728       windows->backdrop.height=(unsigned int)
14729         XDisplayHeight(display,visual_info->screen);
14730       windows->backdrop.border_width=0;
14731       windows->backdrop.immutable=MagickTrue;
14732       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14733         ButtonReleaseMask;
14734       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14735         StructureNotifyMask;
14736       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14737       manager_hints->icon_window=windows->icon.id;
14738       manager_hints->input=MagickTrue;
14739       manager_hints->initial_state=resource_info->iconic ? IconicState :
14740         NormalState;
14741       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14742         &windows->backdrop);
14743       if (display_image->debug != MagickFalse)
14744         (void) LogMagickEvent(X11Event,GetMagickModule(),
14745           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14746       (void) XMapWindow(display,windows->backdrop.id);
14747       (void) XClearWindow(display,windows->backdrop.id);
14748       if (windows->image.id != (Window) NULL)
14749         {
14750           (void) XDestroyWindow(display,windows->image.id);
14751           windows->image.id=(Window) NULL;
14752         }
14753       /*
14754         Position image in the center the backdrop.
14755       */
14756       windows->image.flags|=USPosition;
14757       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14758         (windows->image.width/2);
14759       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14760         (windows->image.height/2);
14761     }
14762   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14763   manager_hints->icon_window=windows->icon.id;
14764   manager_hints->input=MagickTrue;
14765   manager_hints->initial_state=resource_info->iconic ? IconicState :
14766     NormalState;
14767   if (windows->group_leader.id != (Window) NULL)
14768     {
14769       /*
14770         Follow the leader.
14771       */
14772       manager_hints->flags|=WindowGroupHint;
14773       manager_hints->window_group=windows->group_leader.id;
14774       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14775       if (display_image->debug != MagickFalse)
14776         (void) LogMagickEvent(X11Event,GetMagickModule(),
14777           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14778     }
14779   XMakeWindow(display,
14780     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14781     argv,argc,class_hints,manager_hints,&windows->image);
14782   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14783     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14784   if (windows->group_leader.id != (Window) NULL)
14785     (void) XSetTransientForHint(display,windows->image.id,
14786       windows->group_leader.id);
14787   if (display_image->debug != MagickFalse)
14788     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14789       windows->image.id);
14790   /*
14791     Initialize Info widget.
14792   */
14793   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14794     &windows->info);
14795   (void) CloneString(&windows->info.name,"Info");
14796   (void) CloneString(&windows->info.icon_name,"Info");
14797   windows->info.border_width=1;
14798   windows->info.x=2;
14799   windows->info.y=2;
14800   windows->info.flags|=PPosition;
14801   windows->info.attributes.win_gravity=UnmapGravity;
14802   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14803     StructureNotifyMask;
14804   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14805   manager_hints->input=MagickFalse;
14806   manager_hints->initial_state=NormalState;
14807   manager_hints->window_group=windows->image.id;
14808   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14809     &windows->info);
14810   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14811     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14812   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14813     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14814   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14815   if (windows->image.mapped != MagickFalse)
14816     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14817   if (display_image->debug != MagickFalse)
14818     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14819       windows->info.id);
14820   /*
14821     Initialize Command widget.
14822   */
14823   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14824     resource_info,&windows->command);
14825   windows->command.data=MagickMenus;
14826   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14827   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14828     resource_info->client_name);
14829   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14830     resource_name,"geometry",(char *) NULL);
14831   (void) CloneString(&windows->command.name,MagickTitle);
14832   windows->command.border_width=0;
14833   windows->command.flags|=PPosition;
14834   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14835     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14836     OwnerGrabButtonMask | StructureNotifyMask;
14837   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14838   manager_hints->input=MagickTrue;
14839   manager_hints->initial_state=NormalState;
14840   manager_hints->window_group=windows->image.id;
14841   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14842     &windows->command);
14843   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14844     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14845     HighlightHeight);
14846   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14847     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14848   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14849   if (windows->command.mapped != MagickFalse)
14850     (void) XMapRaised(display,windows->command.id);
14851   if (display_image->debug != MagickFalse)
14852     (void) LogMagickEvent(X11Event,GetMagickModule(),
14853       "Window id: 0x%lx (command)",windows->command.id);
14854   /*
14855     Initialize Widget window.
14856   */
14857   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14858     resource_info,&windows->widget);
14859   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14860     resource_info->client_name);
14861   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14862     resource_name,"geometry",(char *) NULL);
14863   windows->widget.border_width=0;
14864   windows->widget.flags|=PPosition;
14865   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14866     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14867     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14868     StructureNotifyMask;
14869   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14870   manager_hints->input=MagickTrue;
14871   manager_hints->initial_state=NormalState;
14872   manager_hints->window_group=windows->image.id;
14873   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14874     &windows->widget);
14875   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14876     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14877   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14878     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14879   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14880   if (display_image->debug != MagickFalse)
14881     (void) LogMagickEvent(X11Event,GetMagickModule(),
14882       "Window id: 0x%lx (widget)",windows->widget.id);
14883   /*
14884     Initialize popup window.
14885   */
14886   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14887     resource_info,&windows->popup);
14888   windows->popup.border_width=0;
14889   windows->popup.flags|=PPosition;
14890   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14891     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14892     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14893   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14894   manager_hints->input=MagickTrue;
14895   manager_hints->initial_state=NormalState;
14896   manager_hints->window_group=windows->image.id;
14897   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14898     &windows->popup);
14899   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14900     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14901   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14902     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14903   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14904   if (display_image->debug != MagickFalse)
14905     (void) LogMagickEvent(X11Event,GetMagickModule(),
14906       "Window id: 0x%lx (pop up)",windows->popup.id);
14907   /*
14908     Initialize Magnify window and cursor.
14909   */
14910   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14911     resource_info,&windows->magnify);
14912   if (resource_info->use_shared_memory == MagickFalse)
14913     windows->magnify.shared_memory=MagickFalse;
14914   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14915     resource_info->client_name);
14916   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14917     resource_name,"geometry",(char *) NULL);
14918   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14919     resource_info->magnify);
14920   if (windows->magnify.cursor != (Cursor) NULL)
14921     (void) XFreeCursor(display,windows->magnify.cursor);
14922   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14923     map_info->colormap,resource_info->background_color,
14924     resource_info->foreground_color);
14925   if (windows->magnify.cursor == (Cursor) NULL)
14926     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14927       display_image->filename);
14928   windows->magnify.width=MagnifySize;
14929   windows->magnify.height=MagnifySize;
14930   windows->magnify.flags|=PPosition;
14931   windows->magnify.min_width=MagnifySize;
14932   windows->magnify.min_height=MagnifySize;
14933   windows->magnify.width_inc=MagnifySize;
14934   windows->magnify.height_inc=MagnifySize;
14935   windows->magnify.data=resource_info->magnify;
14936   windows->magnify.attributes.cursor=windows->magnify.cursor;
14937   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14938     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14939     StructureNotifyMask;
14940   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14941   manager_hints->input=MagickTrue;
14942   manager_hints->initial_state=NormalState;
14943   manager_hints->window_group=windows->image.id;
14944   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14945     &windows->magnify);
14946   if (display_image->debug != MagickFalse)
14947     (void) LogMagickEvent(X11Event,GetMagickModule(),
14948       "Window id: 0x%lx (magnify)",windows->magnify.id);
14949   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14950   /*
14951     Initialize panning window.
14952   */
14953   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14954     resource_info,&windows->pan);
14955   (void) CloneString(&windows->pan.name,"Pan Icon");
14956   windows->pan.width=windows->icon.width;
14957   windows->pan.height=windows->icon.height;
14958   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14959     resource_info->client_name);
14960   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14961     resource_name,"geometry",(char *) NULL);
14962   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14963     &windows->pan.width,&windows->pan.height);
14964   windows->pan.flags|=PPosition;
14965   windows->pan.immutable=MagickTrue;
14966   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14967     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14968     StructureNotifyMask;
14969   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14970   manager_hints->input=MagickFalse;
14971   manager_hints->initial_state=NormalState;
14972   manager_hints->window_group=windows->image.id;
14973   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14974     &windows->pan);
14975   if (display_image->debug != MagickFalse)
14976     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14977       windows->pan.id);
14978   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14979   if (windows->info.mapped != MagickFalse)
14980     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14981   if ((windows->image.mapped == MagickFalse) ||
14982       (windows->backdrop.id != (Window) NULL))
14983     (void) XMapWindow(display,windows->image.id);
14984   /*
14985     Set our progress monitor and warning handlers.
14986   */
14987   if (warning_handler == (WarningHandler) NULL)
14988     {
14989       warning_handler=resource_info->display_warnings ?
14990         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14991       warning_handler=resource_info->display_warnings ?
14992         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14993     }
14994   /*
14995     Initialize Image and Magnify X images.
14996   */
14997   windows->image.x=0;
14998   windows->image.y=0;
14999   windows->magnify.shape=MagickFalse;
15000   width=(unsigned int) display_image->columns;
15001   height=(unsigned int) display_image->rows;
15002   if ((display_image->columns != width) || (display_image->rows != height))
15003     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15004       display_image->filename);
15005   status=XMakeImage(display,resource_info,&windows->image,display_image,
15006     width,height,exception);
15007   if (status == MagickFalse)
15008     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15009       display_image->filename);
15010   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15011     windows->magnify.width,windows->magnify.height,exception);
15012   if (status == MagickFalse)
15013     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15014       display_image->filename);
15015   if (windows->magnify.mapped != MagickFalse)
15016     (void) XMapRaised(display,windows->magnify.id);
15017   if (windows->pan.mapped != MagickFalse)
15018     (void) XMapRaised(display,windows->pan.id);
15019   windows->image.window_changes.width=(int) display_image->columns;
15020   windows->image.window_changes.height=(int) display_image->rows;
15021   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15022   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15023   (void) XSync(display,MagickFalse);
15024   /*
15025     Respond to events.
15026   */
15027   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15028   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15029   update_time=0;
15030   if (resource_info->update != MagickFalse)
15031     {
15032       MagickBooleanType
15033         status;
15034
15035       /*
15036         Determine when file data was last modified.
15037       */
15038       status=GetPathAttributes(display_image->filename,&attributes);
15039       if (status != MagickFalse)
15040         update_time=attributes.st_mtime;
15041     }
15042   *state&=(~FormerImageState);
15043   *state&=(~MontageImageState);
15044   *state&=(~NextImageState);
15045   do
15046   {
15047     /*
15048       Handle a window event.
15049     */
15050     if (windows->image.mapped != MagickFalse)
15051       if ((display_image->delay != 0) || (resource_info->update != 0))
15052         {
15053           if (timer < time((time_t *) NULL))
15054             {
15055               if (resource_info->update == MagickFalse)
15056                 *state|=NextImageState | ExitState;
15057               else
15058                 {
15059                   MagickBooleanType
15060                     status;
15061
15062                   /*
15063                     Determine if image file was modified.
15064                   */
15065                   status=GetPathAttributes(display_image->filename,&attributes);
15066                   if (status != MagickFalse)
15067                     if (update_time != attributes.st_mtime)
15068                       {
15069                         /*
15070                           Redisplay image.
15071                         */
15072                         (void) FormatLocaleString(
15073                           resource_info->image_info->filename,MaxTextExtent,
15074                           "%s:%s",display_image->magick,
15075                           display_image->filename);
15076                         nexus=ReadImage(resource_info->image_info,
15077                           &display_image->exception);
15078                         if (nexus != (Image *) NULL)
15079                           {
15080                             nexus=DestroyImage(nexus);
15081                             *state|=NextImageState | ExitState;
15082                           }
15083                       }
15084                   delay=display_image->delay/MagickMax(
15085                     display_image->ticks_per_second,1L);
15086                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15087                 }
15088             }
15089           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15090             {
15091               /*
15092                 Do not block if delay > 0.
15093               */
15094               XDelay(display,SuspendTime << 2);
15095               continue;
15096             }
15097         }
15098     timestamp=time((time_t *) NULL);
15099     (void) XNextEvent(display,&event);
15100     if (windows->image.stasis == MagickFalse)
15101       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15102         MagickTrue : MagickFalse;
15103     if (windows->magnify.stasis == MagickFalse)
15104       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15105         MagickTrue : MagickFalse;
15106     if (event.xany.window == windows->command.id)
15107       {
15108         /*
15109           Select a command from the Command widget.
15110         */
15111         id=XCommandWidget(display,windows,CommandMenu,&event);
15112         if (id < 0)
15113           continue;
15114         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15115         command_type=CommandMenus[id];
15116         if (id < MagickMenus)
15117           {
15118             /*
15119               Select a command from a pop-up menu.
15120             */
15121             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15122               command);
15123             if (entry < 0)
15124               continue;
15125             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15126             command_type=Commands[id][entry];
15127           }
15128         if (command_type != NullCommand)
15129           nexus=XMagickCommand(display,resource_info,windows,command_type,
15130             &display_image,exception);
15131         continue;
15132       }
15133     switch (event.type)
15134     {
15135       case ButtonPress:
15136       {
15137         if (display_image->debug != MagickFalse)
15138           (void) LogMagickEvent(X11Event,GetMagickModule(),
15139             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15140             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15141         if ((event.xbutton.button == Button3) &&
15142             (event.xbutton.state & Mod1Mask))
15143           {
15144             /*
15145               Convert Alt-Button3 to Button2.
15146             */
15147             event.xbutton.button=Button2;
15148             event.xbutton.state&=(~Mod1Mask);
15149           }
15150         if (event.xbutton.window == windows->backdrop.id)
15151           {
15152             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15153               event.xbutton.time);
15154             break;
15155           }
15156         if (event.xbutton.window == windows->image.id)
15157           {
15158             switch (event.xbutton.button)
15159             {
15160               case Button1:
15161               {
15162                 if (resource_info->immutable)
15163                   {
15164                     /*
15165                       Select a command from the Virtual menu.
15166                     */
15167                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15168                       command);
15169                     if (entry >= 0)
15170                       nexus=XMagickCommand(display,resource_info,windows,
15171                         VirtualCommands[entry],&display_image,exception);
15172                     break;
15173                   }
15174                 /*
15175                   Map/unmap Command widget.
15176                 */
15177                 if (windows->command.mapped != MagickFalse)
15178                   (void) XWithdrawWindow(display,windows->command.id,
15179                     windows->command.screen);
15180                 else
15181                   {
15182                     (void) XCommandWidget(display,windows,CommandMenu,
15183                       (XEvent *) NULL);
15184                     (void) XMapRaised(display,windows->command.id);
15185                   }
15186                 break;
15187               }
15188               case Button2:
15189               {
15190                 /*
15191                   User pressed the image magnify button.
15192                 */
15193                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15194                   &display_image,exception);
15195                 XMagnifyImage(display,windows,&event);
15196                 break;
15197               }
15198               case Button3:
15199               {
15200                 if (resource_info->immutable)
15201                   {
15202                     /*
15203                       Select a command from the Virtual menu.
15204                     */
15205                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15206                       command);
15207                     if (entry >= 0)
15208                       nexus=XMagickCommand(display,resource_info,windows,
15209                         VirtualCommands[entry],&display_image,exception);
15210                     break;
15211                   }
15212                 if (display_image->montage != (char *) NULL)
15213                   {
15214                     /*
15215                       Open or delete a tile from a visual image directory.
15216                     */
15217                     nexus=XTileImage(display,resource_info,windows,
15218                       display_image,&event,exception);
15219                     if (nexus != (Image *) NULL)
15220                       *state|=MontageImageState | NextImageState | ExitState;
15221                     vid_info.x=(short int) windows->image.x;
15222                     vid_info.y=(short int) windows->image.y;
15223                     break;
15224                   }
15225                 /*
15226                   Select a command from the Short Cuts menu.
15227                 */
15228                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15229                   command);
15230                 if (entry >= 0)
15231                   nexus=XMagickCommand(display,resource_info,windows,
15232                     ShortCutsCommands[entry],&display_image,exception);
15233                 break;
15234               }
15235               case Button4:
15236               {
15237                 /*
15238                   Wheel up.
15239                 */
15240                 XTranslateImage(display,windows,*image,XK_Up);
15241                 break;
15242               }
15243               case Button5:
15244               {
15245                 /*
15246                   Wheel down.
15247                 */
15248                 XTranslateImage(display,windows,*image,XK_Down);
15249                 break;
15250               }
15251               default:
15252                 break;
15253             }
15254             break;
15255           }
15256         if (event.xbutton.window == windows->magnify.id)
15257           {
15258             int
15259               factor;
15260
15261             static const char
15262               *MagnifyMenu[] =
15263               {
15264                 "2",
15265                 "4",
15266                 "5",
15267                 "6",
15268                 "7",
15269                 "8",
15270                 "9",
15271                 "3",
15272                 (char *) NULL,
15273               };
15274
15275             static KeySym
15276               MagnifyCommands[] =
15277               {
15278                 XK_2,
15279                 XK_4,
15280                 XK_5,
15281                 XK_6,
15282                 XK_7,
15283                 XK_8,
15284                 XK_9,
15285                 XK_3
15286               };
15287
15288             /*
15289               Select a magnify factor from the pop-up menu.
15290             */
15291             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15292             if (factor >= 0)
15293               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15294             break;
15295           }
15296         if (event.xbutton.window == windows->pan.id)
15297           {
15298             switch (event.xbutton.button)
15299             {
15300               case Button4:
15301               {
15302                 /*
15303                   Wheel up.
15304                 */
15305                 XTranslateImage(display,windows,*image,XK_Up);
15306                 break;
15307               }
15308               case Button5:
15309               {
15310                 /*
15311                   Wheel down.
15312                 */
15313                 XTranslateImage(display,windows,*image,XK_Down);
15314                 break;
15315               }
15316               default:
15317               {
15318                 XPanImage(display,windows,&event);
15319                 break;
15320               }
15321             }
15322             break;
15323           }
15324         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15325           1L);
15326         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15327         break;
15328       }
15329       case ButtonRelease:
15330       {
15331         if (display_image->debug != MagickFalse)
15332           (void) LogMagickEvent(X11Event,GetMagickModule(),
15333             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15334             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15335         break;
15336       }
15337       case ClientMessage:
15338       {
15339         if (display_image->debug != MagickFalse)
15340           (void) LogMagickEvent(X11Event,GetMagickModule(),
15341             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15342             event.xclient.message_type,event.xclient.format,(unsigned long)
15343             event.xclient.data.l[0]);
15344         if (event.xclient.message_type == windows->im_protocols)
15345           {
15346             if (*event.xclient.data.l == (long) windows->im_update_widget)
15347               {
15348                 (void) CloneString(&windows->command.name,MagickTitle);
15349                 windows->command.data=MagickMenus;
15350                 (void) XCommandWidget(display,windows,CommandMenu,
15351                   (XEvent *) NULL);
15352                 break;
15353               }
15354             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15355               {
15356                 /*
15357                   Update graphic context and window colormap.
15358                 */
15359                 for (i=0; i < (int) number_windows; i++)
15360                 {
15361                   if (magick_windows[i]->id == windows->icon.id)
15362                     continue;
15363                   context_values.background=pixel->background_color.pixel;
15364                   context_values.foreground=pixel->foreground_color.pixel;
15365                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15366                     context_mask,&context_values);
15367                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15368                     context_mask,&context_values);
15369                   context_values.background=pixel->foreground_color.pixel;
15370                   context_values.foreground=pixel->background_color.pixel;
15371                   context_values.plane_mask=context_values.background ^
15372                     context_values.foreground;
15373                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15374                     (size_t) (context_mask | GCPlaneMask),
15375                     &context_values);
15376                   magick_windows[i]->attributes.background_pixel=
15377                     pixel->background_color.pixel;
15378                   magick_windows[i]->attributes.border_pixel=
15379                     pixel->border_color.pixel;
15380                   magick_windows[i]->attributes.colormap=map_info->colormap;
15381                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15382                     (unsigned long) magick_windows[i]->mask,
15383                     &magick_windows[i]->attributes);
15384                 }
15385                 if (windows->pan.mapped != MagickFalse)
15386                   {
15387                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15388                       windows->pan.pixmap);
15389                     (void) XClearWindow(display,windows->pan.id);
15390                     XDrawPanRectangle(display,windows);
15391                   }
15392                 if (windows->backdrop.id != (Window) NULL)
15393                   (void) XInstallColormap(display,map_info->colormap);
15394                 break;
15395               }
15396             if (*event.xclient.data.l == (long) windows->im_former_image)
15397               {
15398                 *state|=FormerImageState | ExitState;
15399                 break;
15400               }
15401             if (*event.xclient.data.l == (long) windows->im_next_image)
15402               {
15403                 *state|=NextImageState | ExitState;
15404                 break;
15405               }
15406             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15407               {
15408                 *state|=RetainColorsState;
15409                 break;
15410               }
15411             if (*event.xclient.data.l == (long) windows->im_exit)
15412               {
15413                 *state|=ExitState;
15414                 break;
15415               }
15416             break;
15417           }
15418         if (event.xclient.message_type == windows->dnd_protocols)
15419           {
15420             Atom
15421               selection,
15422               type;
15423
15424             int
15425               format,
15426               status;
15427
15428             unsigned char
15429               *data;
15430
15431             unsigned long
15432               after,
15433               length;
15434
15435             /*
15436               Display image named by the Drag-and-Drop selection.
15437             */
15438             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15439               break;
15440             selection=XInternAtom(display,"DndSelection",MagickFalse);
15441             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15442               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15443               &length,&after,&data);
15444             if ((status != Success) || (length == 0))
15445               break;
15446             if (*event.xclient.data.l == 2)
15447               {
15448                 /*
15449                   Offix DND.
15450                 */
15451                 (void) CopyMagickString(resource_info->image_info->filename,
15452                   (char *) data,MaxTextExtent);
15453               }
15454             else
15455               {
15456                 /*
15457                   XDND.
15458                 */
15459                 if (strncmp((char *) data, "file:", 5) != 0)
15460                   {
15461                     (void) XFree((void *) data);
15462                     break;
15463                   }
15464                 (void) CopyMagickString(resource_info->image_info->filename,
15465                   ((char *) data)+5,MaxTextExtent);
15466               }
15467             nexus=ReadImage(resource_info->image_info,
15468               &display_image->exception);
15469             CatchException(&display_image->exception);
15470             if (nexus != (Image *) NULL)
15471               *state|=NextImageState | ExitState;
15472             (void) XFree((void *) data);
15473             break;
15474           }
15475         /*
15476           If client window delete message, exit.
15477         */
15478         if (event.xclient.message_type != windows->wm_protocols)
15479           break;
15480         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15481           break;
15482         (void) XWithdrawWindow(display,event.xclient.window,
15483           visual_info->screen);
15484         if (event.xclient.window == windows->image.id)
15485           {
15486             *state|=ExitState;
15487             break;
15488           }
15489         if (event.xclient.window == windows->pan.id)
15490           {
15491             /*
15492               Restore original image size when pan window is deleted.
15493             */
15494             windows->image.window_changes.width=windows->image.ximage->width;
15495             windows->image.window_changes.height=windows->image.ximage->height;
15496             (void) XConfigureImage(display,resource_info,windows,
15497               display_image,exception);
15498           }
15499         break;
15500       }
15501       case ConfigureNotify:
15502       {
15503         if (display_image->debug != MagickFalse)
15504           (void) LogMagickEvent(X11Event,GetMagickModule(),
15505             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15506             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15507             event.xconfigure.y,event.xconfigure.send_event);
15508         if (event.xconfigure.window == windows->image.id)
15509           {
15510             /*
15511               Image window has a new configuration.
15512             */
15513             if (event.xconfigure.send_event != 0)
15514               {
15515                 XWindowChanges
15516                   window_changes;
15517
15518                 /*
15519                   Position the transient windows relative of the Image window.
15520                 */
15521                 if (windows->command.geometry == (char *) NULL)
15522                   if (windows->command.mapped == MagickFalse)
15523                     {
15524                       windows->command.x=event.xconfigure.x-
15525                         windows->command.width-25;
15526                       windows->command.y=event.xconfigure.y;
15527                       XConstrainWindowPosition(display,&windows->command);
15528                       window_changes.x=windows->command.x;
15529                       window_changes.y=windows->command.y;
15530                       (void) XReconfigureWMWindow(display,windows->command.id,
15531                         windows->command.screen,(unsigned int) (CWX | CWY),
15532                         &window_changes);
15533                     }
15534                 if (windows->widget.geometry == (char *) NULL)
15535                   if (windows->widget.mapped == MagickFalse)
15536                     {
15537                       windows->widget.x=event.xconfigure.x+
15538                         event.xconfigure.width/10;
15539                       windows->widget.y=event.xconfigure.y+
15540                         event.xconfigure.height/10;
15541                       XConstrainWindowPosition(display,&windows->widget);
15542                       window_changes.x=windows->widget.x;
15543                       window_changes.y=windows->widget.y;
15544                       (void) XReconfigureWMWindow(display,windows->widget.id,
15545                         windows->widget.screen,(unsigned int) (CWX | CWY),
15546                         &window_changes);
15547                     }
15548                 if (windows->magnify.geometry == (char *) NULL)
15549                   if (windows->magnify.mapped == MagickFalse)
15550                     {
15551                       windows->magnify.x=event.xconfigure.x+
15552                         event.xconfigure.width+25;
15553                       windows->magnify.y=event.xconfigure.y;
15554                       XConstrainWindowPosition(display,&windows->magnify);
15555                       window_changes.x=windows->magnify.x;
15556                       window_changes.y=windows->magnify.y;
15557                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15558                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15559                         &window_changes);
15560                     }
15561                 if (windows->pan.geometry == (char *) NULL)
15562                   if (windows->pan.mapped == MagickFalse)
15563                     {
15564                       windows->pan.x=event.xconfigure.x+
15565                         event.xconfigure.width+25;
15566                       windows->pan.y=event.xconfigure.y+
15567                         windows->magnify.height+50;
15568                       XConstrainWindowPosition(display,&windows->pan);
15569                       window_changes.x=windows->pan.x;
15570                       window_changes.y=windows->pan.y;
15571                       (void) XReconfigureWMWindow(display,windows->pan.id,
15572                         windows->pan.screen,(unsigned int) (CWX | CWY),
15573                         &window_changes);
15574                     }
15575               }
15576             if ((event.xconfigure.width == (int) windows->image.width) &&
15577                 (event.xconfigure.height == (int) windows->image.height))
15578               break;
15579             windows->image.width=(unsigned int) event.xconfigure.width;
15580             windows->image.height=(unsigned int) event.xconfigure.height;
15581             windows->image.x=0;
15582             windows->image.y=0;
15583             if (display_image->montage != (char *) NULL)
15584               {
15585                 windows->image.x=vid_info.x;
15586                 windows->image.y=vid_info.y;
15587               }
15588             if ((windows->image.mapped != MagickFalse) &&
15589                 (windows->image.stasis != MagickFalse))
15590               {
15591                 /*
15592                   Update image window configuration.
15593                 */
15594                 windows->image.window_changes.width=event.xconfigure.width;
15595                 windows->image.window_changes.height=event.xconfigure.height;
15596                 (void) XConfigureImage(display,resource_info,windows,
15597                   display_image,exception);
15598               }
15599             /*
15600               Update pan window configuration.
15601             */
15602             if ((event.xconfigure.width < windows->image.ximage->width) ||
15603                 (event.xconfigure.height < windows->image.ximage->height))
15604               {
15605                 (void) XMapRaised(display,windows->pan.id);
15606                 XDrawPanRectangle(display,windows);
15607               }
15608             else
15609               if (windows->pan.mapped != MagickFalse)
15610                 (void) XWithdrawWindow(display,windows->pan.id,
15611                   windows->pan.screen);
15612             break;
15613           }
15614         if (event.xconfigure.window == windows->magnify.id)
15615           {
15616             unsigned int
15617               magnify;
15618
15619             /*
15620               Magnify window has a new configuration.
15621             */
15622             windows->magnify.width=(unsigned int) event.xconfigure.width;
15623             windows->magnify.height=(unsigned int) event.xconfigure.height;
15624             if (windows->magnify.mapped == MagickFalse)
15625               break;
15626             magnify=1;
15627             while ((int) magnify <= event.xconfigure.width)
15628               magnify<<=1;
15629             while ((int) magnify <= event.xconfigure.height)
15630               magnify<<=1;
15631             magnify>>=1;
15632             if (((int) magnify != event.xconfigure.width) ||
15633                 ((int) magnify != event.xconfigure.height))
15634               {
15635                 window_changes.width=(int) magnify;
15636                 window_changes.height=(int) magnify;
15637                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15638                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15639                   &window_changes);
15640                 break;
15641               }
15642             if ((windows->magnify.mapped != MagickFalse) &&
15643                 (windows->magnify.stasis != MagickFalse))
15644               {
15645                 status=XMakeImage(display,resource_info,&windows->magnify,
15646                   display_image,windows->magnify.width,windows->magnify.height,
15647                   exception);
15648                 XMakeMagnifyImage(display,windows);
15649               }
15650             break;
15651           }
15652         if ((windows->magnify.mapped != MagickFalse) &&
15653             (event.xconfigure.window == windows->pan.id))
15654           {
15655             /*
15656               Pan icon window has a new configuration.
15657             */
15658             if (event.xconfigure.send_event != 0)
15659               {
15660                 windows->pan.x=event.xconfigure.x;
15661                 windows->pan.y=event.xconfigure.y;
15662               }
15663             windows->pan.width=(unsigned int) event.xconfigure.width;
15664             windows->pan.height=(unsigned int) event.xconfigure.height;
15665             break;
15666           }
15667         if (event.xconfigure.window == windows->icon.id)
15668           {
15669             /*
15670               Icon window has a new configuration.
15671             */
15672             windows->icon.width=(unsigned int) event.xconfigure.width;
15673             windows->icon.height=(unsigned int) event.xconfigure.height;
15674             break;
15675           }
15676         break;
15677       }
15678       case DestroyNotify:
15679       {
15680         /*
15681           Group leader has exited.
15682         */
15683         if (display_image->debug != MagickFalse)
15684           (void) LogMagickEvent(X11Event,GetMagickModule(),
15685             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15686         if (event.xdestroywindow.window == windows->group_leader.id)
15687           {
15688             *state|=ExitState;
15689             break;
15690           }
15691         break;
15692       }
15693       case EnterNotify:
15694       {
15695         /*
15696           Selectively install colormap.
15697         */
15698         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15699           if (event.xcrossing.mode != NotifyUngrab)
15700             XInstallColormap(display,map_info->colormap);
15701         break;
15702       }
15703       case Expose:
15704       {
15705         if (display_image->debug != MagickFalse)
15706           (void) LogMagickEvent(X11Event,GetMagickModule(),
15707             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15708             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15709             event.xexpose.y);
15710         /*
15711           Refresh windows that are now exposed.
15712         */
15713         if ((event.xexpose.window == windows->image.id) &&
15714             (windows->image.mapped != MagickFalse))
15715           {
15716             XRefreshWindow(display,&windows->image,&event);
15717             delay=display_image->delay/MagickMax(
15718               display_image->ticks_per_second,1L);
15719             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15720             break;
15721           }
15722         if ((event.xexpose.window == windows->magnify.id) &&
15723             (windows->magnify.mapped != MagickFalse))
15724           {
15725             XMakeMagnifyImage(display,windows);
15726             break;
15727           }
15728         if (event.xexpose.window == windows->pan.id)
15729           {
15730             XDrawPanRectangle(display,windows);
15731             break;
15732           }
15733         if (event.xexpose.window == windows->icon.id)
15734           {
15735             XRefreshWindow(display,&windows->icon,&event);
15736             break;
15737           }
15738         break;
15739       }
15740       case KeyPress:
15741       {
15742         int
15743           length;
15744
15745         /*
15746           Respond to a user key press.
15747         */
15748         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15749           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15750         *(command+length)='\0';
15751         if (display_image->debug != MagickFalse)
15752           (void) LogMagickEvent(X11Event,GetMagickModule(),
15753             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15754             key_symbol,command);
15755         if (event.xkey.window == windows->image.id)
15756           {
15757             command_type=XImageWindowCommand(display,resource_info,windows,
15758               event.xkey.state,key_symbol,&display_image,exception);
15759             if (command_type != NullCommand)
15760               nexus=XMagickCommand(display,resource_info,windows,command_type,
15761                 &display_image,exception);
15762           }
15763         if (event.xkey.window == windows->magnify.id)
15764           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15765         if (event.xkey.window == windows->pan.id)
15766           {
15767             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15768               (void) XWithdrawWindow(display,windows->pan.id,
15769                 windows->pan.screen);
15770             else
15771               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15772                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15773                   "Help Viewer - Image Pan",ImagePanHelp);
15774               else
15775                 XTranslateImage(display,windows,*image,key_symbol);
15776           }
15777         delay=display_image->delay/MagickMax(
15778           display_image->ticks_per_second,1L);
15779         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15780         break;
15781       }
15782       case KeyRelease:
15783       {
15784         /*
15785           Respond to a user key release.
15786         */
15787         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15788           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15789         if (display_image->debug != MagickFalse)
15790           (void) LogMagickEvent(X11Event,GetMagickModule(),
15791             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15792         break;
15793       }
15794       case LeaveNotify:
15795       {
15796         /*
15797           Selectively uninstall colormap.
15798         */
15799         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15800           if (event.xcrossing.mode != NotifyUngrab)
15801             XUninstallColormap(display,map_info->colormap);
15802         break;
15803       }
15804       case MapNotify:
15805       {
15806         if (display_image->debug != MagickFalse)
15807           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15808             event.xmap.window);
15809         if (event.xmap.window == windows->backdrop.id)
15810           {
15811             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15812               CurrentTime);
15813             windows->backdrop.mapped=MagickTrue;
15814             break;
15815           }
15816         if (event.xmap.window == windows->image.id)
15817           {
15818             if (windows->backdrop.id != (Window) NULL)
15819               (void) XInstallColormap(display,map_info->colormap);
15820             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15821               {
15822                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15823                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15824               }
15825             if (((int) windows->image.width < windows->image.ximage->width) ||
15826                 ((int) windows->image.height < windows->image.ximage->height))
15827               (void) XMapRaised(display,windows->pan.id);
15828             windows->image.mapped=MagickTrue;
15829             break;
15830           }
15831         if (event.xmap.window == windows->magnify.id)
15832           {
15833             XMakeMagnifyImage(display,windows);
15834             windows->magnify.mapped=MagickTrue;
15835             (void) XWithdrawWindow(display,windows->info.id,
15836               windows->info.screen);
15837             break;
15838           }
15839         if (event.xmap.window == windows->pan.id)
15840           {
15841             XMakePanImage(display,resource_info,windows,display_image,
15842               exception);
15843             windows->pan.mapped=MagickTrue;
15844             break;
15845           }
15846         if (event.xmap.window == windows->info.id)
15847           {
15848             windows->info.mapped=MagickTrue;
15849             break;
15850           }
15851         if (event.xmap.window == windows->icon.id)
15852           {
15853             MagickBooleanType
15854               taint;
15855
15856             /*
15857               Create an icon image.
15858             */
15859             taint=display_image->taint;
15860             XMakeStandardColormap(display,icon_visual,icon_resources,
15861               display_image,icon_map,icon_pixel);
15862             (void) XMakeImage(display,icon_resources,&windows->icon,
15863               display_image,windows->icon.width,windows->icon.height,
15864               exception);
15865             display_image->taint=taint;
15866             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15867               windows->icon.pixmap);
15868             (void) XClearWindow(display,windows->icon.id);
15869             (void) XWithdrawWindow(display,windows->info.id,
15870               windows->info.screen);
15871             windows->icon.mapped=MagickTrue;
15872             break;
15873           }
15874         if (event.xmap.window == windows->command.id)
15875           {
15876             windows->command.mapped=MagickTrue;
15877             break;
15878           }
15879         if (event.xmap.window == windows->popup.id)
15880           {
15881             windows->popup.mapped=MagickTrue;
15882             break;
15883           }
15884         if (event.xmap.window == windows->widget.id)
15885           {
15886             windows->widget.mapped=MagickTrue;
15887             break;
15888           }
15889         break;
15890       }
15891       case MappingNotify:
15892       {
15893         (void) XRefreshKeyboardMapping(&event.xmapping);
15894         break;
15895       }
15896       case NoExpose:
15897         break;
15898       case PropertyNotify:
15899       {
15900         Atom
15901           type;
15902
15903         int
15904           format,
15905           status;
15906
15907         unsigned char
15908           *data;
15909
15910         unsigned long
15911           after,
15912           length;
15913
15914         if (display_image->debug != MagickFalse)
15915           (void) LogMagickEvent(X11Event,GetMagickModule(),
15916             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15917             event.xproperty.atom,event.xproperty.state);
15918         if (event.xproperty.atom != windows->im_remote_command)
15919           break;
15920         /*
15921           Display image named by the remote command protocol.
15922         */
15923         status=XGetWindowProperty(display,event.xproperty.window,
15924           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15925           AnyPropertyType,&type,&format,&length,&after,&data);
15926         if ((status != Success) || (length == 0))
15927           break;
15928         if (LocaleCompare((char *) data,"-quit") == 0)
15929           {
15930             XClientMessage(display,windows->image.id,windows->im_protocols,
15931               windows->im_exit,CurrentTime);
15932             (void) XFree((void *) data);
15933             break;
15934           }
15935         (void) CopyMagickString(resource_info->image_info->filename,
15936           (char *) data,MaxTextExtent);
15937         (void) XFree((void *) data);
15938         nexus=ReadImage(resource_info->image_info,&display_image->exception);
15939         CatchException(&display_image->exception);
15940         if (nexus != (Image *) NULL)
15941           *state|=NextImageState | ExitState;
15942         break;
15943       }
15944       case ReparentNotify:
15945       {
15946         if (display_image->debug != MagickFalse)
15947           (void) LogMagickEvent(X11Event,GetMagickModule(),
15948             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15949             event.xreparent.window);
15950         break;
15951       }
15952       case UnmapNotify:
15953       {
15954         if (display_image->debug != MagickFalse)
15955           (void) LogMagickEvent(X11Event,GetMagickModule(),
15956             "Unmap Notify: 0x%lx",event.xunmap.window);
15957         if (event.xunmap.window == windows->backdrop.id)
15958           {
15959             windows->backdrop.mapped=MagickFalse;
15960             break;
15961           }
15962         if (event.xunmap.window == windows->image.id)
15963           {
15964             windows->image.mapped=MagickFalse;
15965             break;
15966           }
15967         if (event.xunmap.window == windows->magnify.id)
15968           {
15969             windows->magnify.mapped=MagickFalse;
15970             break;
15971           }
15972         if (event.xunmap.window == windows->pan.id)
15973           {
15974             windows->pan.mapped=MagickFalse;
15975             break;
15976           }
15977         if (event.xunmap.window == windows->info.id)
15978           {
15979             windows->info.mapped=MagickFalse;
15980             break;
15981           }
15982         if (event.xunmap.window == windows->icon.id)
15983           {
15984             if (map_info->colormap == icon_map->colormap)
15985               XConfigureImageColormap(display,resource_info,windows,
15986                 display_image);
15987             (void) XFreeStandardColormap(display,icon_visual,icon_map,
15988               icon_pixel);
15989             windows->icon.mapped=MagickFalse;
15990             break;
15991           }
15992         if (event.xunmap.window == windows->command.id)
15993           {
15994             windows->command.mapped=MagickFalse;
15995             break;
15996           }
15997         if (event.xunmap.window == windows->popup.id)
15998           {
15999             if (windows->backdrop.id != (Window) NULL)
16000               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16001                 CurrentTime);
16002             windows->popup.mapped=MagickFalse;
16003             break;
16004           }
16005         if (event.xunmap.window == windows->widget.id)
16006           {
16007             if (windows->backdrop.id != (Window) NULL)
16008               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16009                 CurrentTime);
16010             windows->widget.mapped=MagickFalse;
16011             break;
16012           }
16013         break;
16014       }
16015       default:
16016       {
16017         if (display_image->debug != MagickFalse)
16018           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16019             event.type);
16020         break;
16021       }
16022     }
16023   } while (!(*state & ExitState));
16024   if ((*state & ExitState) == 0)
16025     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16026       &display_image,exception);
16027   else
16028     if (resource_info->confirm_edit != MagickFalse)
16029       {
16030         /*
16031           Query user if image has changed.
16032         */
16033         if ((resource_info->immutable == MagickFalse) &&
16034             (display_image->taint != MagickFalse))
16035           {
16036             int
16037               status;
16038
16039             status=XConfirmWidget(display,windows,"Your image changed.",
16040               "Do you want to save it");
16041             if (status == 0)
16042               *state&=(~ExitState);
16043             else
16044               if (status > 0)
16045                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16046                   &display_image,exception);
16047           }
16048       }
16049   if ((windows->visual_info->klass == GrayScale) ||
16050       (windows->visual_info->klass == PseudoColor) ||
16051       (windows->visual_info->klass == DirectColor))
16052     {
16053       /*
16054         Withdraw pan and Magnify window.
16055       */
16056       if (windows->info.mapped != MagickFalse)
16057         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16058       if (windows->magnify.mapped != MagickFalse)
16059         (void) XWithdrawWindow(display,windows->magnify.id,
16060           windows->magnify.screen);
16061       if (windows->command.mapped != MagickFalse)
16062         (void) XWithdrawWindow(display,windows->command.id,
16063           windows->command.screen);
16064     }
16065   if (windows->pan.mapped != MagickFalse)
16066     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16067   if (resource_info->backdrop == MagickFalse)
16068     if (windows->backdrop.mapped)
16069       {
16070         (void) XWithdrawWindow(display,windows->backdrop.id,
16071           windows->backdrop.screen);
16072         (void) XDestroyWindow(display,windows->backdrop.id);
16073         windows->backdrop.id=(Window) NULL;
16074         (void) XWithdrawWindow(display,windows->image.id,
16075           windows->image.screen);
16076         (void) XDestroyWindow(display,windows->image.id);
16077         windows->image.id=(Window) NULL;
16078       }
16079   XSetCursorState(display,windows,MagickTrue);
16080   XCheckRefreshWindows(display,windows);
16081   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16082     *state&=(~ExitState);
16083   if (*state & ExitState)
16084     {
16085       /*
16086         Free Standard Colormap.
16087       */
16088       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16089       if (resource_info->map_type == (char *) NULL)
16090         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16091       /*
16092         Free X resources.
16093       */
16094       if (resource_info->copy_image != (Image *) NULL)
16095         {
16096           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16097           resource_info->copy_image=NewImageList();
16098         }
16099       DestroyXResources();
16100     }
16101   (void) XSync(display,MagickFalse);
16102   /*
16103     Restore our progress monitor and warning handlers.
16104   */
16105   (void) SetErrorHandler(warning_handler);
16106   (void) SetWarningHandler(warning_handler);
16107   /*
16108     Change to home directory.
16109   */
16110   directory=getcwd(working_directory,MaxTextExtent);
16111   (void) directory;
16112   {
16113     int
16114       status;
16115
16116     status=chdir(resource_info->home_directory);
16117     if (status == -1)
16118       (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
16119         FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
16120   }
16121   *image=display_image;
16122   return(nexus);
16123 }
16124 #else
16125 \f
16126 /*
16127 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16128 %                                                                             %
16129 %                                                                             %
16130 %                                                                             %
16131 +   D i s p l a y I m a g e s                                                 %
16132 %                                                                             %
16133 %                                                                             %
16134 %                                                                             %
16135 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16136 %
16137 %  DisplayImages() displays an image sequence to any X window screen.  It
16138 %  returns a value other than 0 if successful.  Check the exception member
16139 %  of image to determine the reason for any failure.
16140 %
16141 %  The format of the DisplayImages method is:
16142 %
16143 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16144 %        Image *images,ExceptionInfo *exception)
16145 %
16146 %  A description of each parameter follows:
16147 %
16148 %    o image_info: the image info.
16149 %
16150 %    o image: the image.
16151 %
16152 %    o exception: return any errors or warnings in this structure.
16153 %
16154 */
16155 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16156   Image *image,ExceptionInfo *exception)
16157 {
16158   assert(image_info != (const ImageInfo *) NULL);
16159   assert(image_info->signature == MagickSignature);
16160   assert(image != (Image *) NULL);
16161   assert(image->signature == MagickSignature);
16162   if (image->debug != MagickFalse)
16163     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16164   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16165     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image->filename);
16166   return(MagickFalse);
16167 }
16168 \f
16169 /*
16170 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16171 %                                                                             %
16172 %                                                                             %
16173 %                                                                             %
16174 +   R e m o t e D i s p l a y C o m m a n d                                   %
16175 %                                                                             %
16176 %                                                                             %
16177 %                                                                             %
16178 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16179 %
16180 %  RemoteDisplayCommand() encourages a remote display program to display the
16181 %  specified image filename.
16182 %
16183 %  The format of the RemoteDisplayCommand method is:
16184 %
16185 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16186 %        const char *window,const char *filename,ExceptionInfo *exception)
16187 %
16188 %  A description of each parameter follows:
16189 %
16190 %    o image_info: the image info.
16191 %
16192 %    o window: Specifies the name or id of an X window.
16193 %
16194 %    o filename: the name of the image filename to display.
16195 %
16196 %    o exception: return any errors or warnings in this structure.
16197 %
16198 */
16199 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16200   const char *window,const char *filename,ExceptionInfo *exception)
16201 {
16202   assert(image_info != (const ImageInfo *) NULL);
16203   assert(image_info->signature == MagickSignature);
16204   assert(filename != (char *) NULL);
16205   (void) window;
16206   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16207   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16208     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16209   return(MagickFalse);
16210 }
16211 #endif