]> granicus.if.org Git - imagemagick/blob - MagickCore/display.c
(no commit message)
[imagemagick] / MagickCore / display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7 %               D   D    I    SS     P   P  L      A   A   Y Y                %
8 %               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9 %               D   D    I       SS  P      L      A   A    Y                 %
10 %               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11 %                                                                             %
12 %                                                                             %
13 %        MagickCore Methods to Interactively Display and Edit an Image        %
14 %                                                                             %
15 %                             Software Design                                 %
16 %                               John Cristy                                   %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/cache.h"
46 #include "MagickCore/cache-private.h"
47 #include "MagickCore/client.h"
48 #include "MagickCore/color.h"
49 #include "MagickCore/colorspace.h"
50 #include "MagickCore/composite.h"
51 #include "MagickCore/constitute.h"
52 #include "MagickCore/decorate.h"
53 #include "MagickCore/delegate.h"
54 #include "MagickCore/display.h"
55 #include "MagickCore/display-private.h"
56 #include "MagickCore/distort.h"
57 #include "MagickCore/draw.h"
58 #include "MagickCore/effect.h"
59 #include "MagickCore/enhance.h"
60 #include "MagickCore/exception.h"
61 #include "MagickCore/exception-private.h"
62 #include "MagickCore/fx.h"
63 #include "MagickCore/geometry.h"
64 #include "MagickCore/image.h"
65 #include "MagickCore/image-private.h"
66 #include "MagickCore/list.h"
67 #include "MagickCore/log.h"
68 #include "MagickCore/magick.h"
69 #include "MagickCore/memory_.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/montage.h"
73 #include "MagickCore/option.h"
74 #include "MagickCore/paint.h"
75 #include "MagickCore/pixel.h"
76 #include "MagickCore/pixel-accessor.h"
77 #include "MagickCore/PreRvIcccm.h"
78 #include "MagickCore/property.h"
79 #include "MagickCore/quantum.h"
80 #include "MagickCore/quantum-private.h"
81 #include "MagickCore/resize.h"
82 #include "MagickCore/resource_.h"
83 #include "MagickCore/shear.h"
84 #include "MagickCore/segment.h"
85 #include "MagickCore/statistic.h"
86 #include "MagickCore/string_.h"
87 #include "MagickCore/string-private.h"
88 #include "MagickCore/transform.h"
89 #include "MagickCore/threshold.h"
90 #include "MagickCore/utility.h"
91 #include "MagickCore/utility-private.h"
92 #include "MagickCore/version.h"
93 #include "MagickCore/widget.h"
94 #include "MagickCore/widget-private.h"
95 #include "MagickCore/xwindow.h"
96 #include "MagickCore/xwindow-private.h"
97 \f
98 #if defined(MAGICKCORE_X11_DELEGATE)
99 /*
100   Define declarations.
101 */
102 #define MaxColors  MagickMin((ssize_t) windows->visual_info->colormap_size,256L)
103 \f
104 /*
105   Constant declarations.
106 */
107 static const unsigned char
108   HighlightBitmap[8] =
109   {
110     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
111   },
112   OpaqueBitmap[8] =
113   {
114     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
115   },
116   ShadowBitmap[8] =
117   {
118     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
119   };
120
121 static const char
122   *PageSizes[] =
123   {
124     "Letter",
125     "Tabloid",
126     "Ledger",
127     "Legal",
128     "Statement",
129     "Executive",
130     "A3",
131     "A4",
132     "A5",
133     "B4",
134     "B5",
135     "Folio",
136     "Quarto",
137     "10x14",
138     (char *) NULL
139   };
140 \f
141 /*
142   Help widget declarations.
143 */
144 static const char
145   *ImageAnnotateHelp[] =
146   {
147     "In annotate mode, the Command widget has these options:",
148     "",
149     "    Font Name",
150     "      fixed",
151     "      variable",
152     "      5x8",
153     "      6x10",
154     "      7x13bold",
155     "      8x13bold",
156     "      9x15bold",
157     "      10x20",
158     "      12x24",
159     "      Browser...",
160     "    Font Color",
161     "      black",
162     "      blue",
163     "      cyan",
164     "      green",
165     "      gray",
166     "      red",
167     "      magenta",
168     "      yellow",
169     "      white",
170     "      transparent",
171     "      Browser...",
172     "    Font Color",
173     "      black",
174     "      blue",
175     "      cyan",
176     "      green",
177     "      gray",
178     "      red",
179     "      magenta",
180     "      yellow",
181     "      white",
182     "      transparent",
183     "      Browser...",
184     "    Rotate Text",
185     "      -90",
186     "      -45",
187     "      -30",
188     "      0",
189     "      30",
190     "      45",
191     "      90",
192     "      180",
193     "      Dialog...",
194     "    Help",
195     "    Dismiss",
196     "",
197     "Choose a font name from the Font Name sub-menu.  Additional",
198     "font names can be specified with the font browser.  You can",
199     "change the menu names by setting the X resources font1",
200     "through font9.",
201     "",
202     "Choose a font color from the Font Color sub-menu.",
203     "Additional font colors can be specified with the color",
204     "browser.  You can change the menu colors by setting the X",
205     "resources pen1 through pen9.",
206     "",
207     "If you select the color browser and press Grab, you can",
208     "choose the font color by moving the pointer to the desired",
209     "color on the screen and press any button.",
210     "",
211     "If you choose to rotate the text, choose Rotate Text from the",
212     "menu and select an angle.  Typically you will only want to",
213     "rotate one line of text at a time.  Depending on the angle you",
214     "choose, subsequent lines may end up overwriting each other.",
215     "",
216     "Choosing a font and its color is optional.  The default font",
217     "is fixed and the default color is black.  However, you must",
218     "choose a location to begin entering text and press button 1.",
219     "An underscore character will appear at the location of the",
220     "pointer.  The cursor changes to a pencil to indicate you are",
221     "in text mode.  To exit immediately, press Dismiss.",
222     "",
223     "In text mode, any key presses will display the character at",
224     "the location of the underscore and advance the underscore",
225     "cursor.  Enter your text and once completed press Apply to",
226     "finish your image annotation.  To correct errors press BACK",
227     "SPACE.  To delete an entire line of text, press DELETE.  Any",
228     "text that exceeds the boundaries of the image window is",
229     "automagically continued onto the next line.",
230     "",
231     "The actual color you request for the font is saved in the",
232     "image.  However, the color that appears in your image window",
233     "may be different.  For example, on a monochrome screen the",
234     "text will appear black or white even if you choose the color",
235     "red as the font color.  However, the image saved to a file",
236     "with -write is written with red lettering.  To assure the",
237     "correct color text in the final image, any PseudoClass image",
238     "is promoted to DirectClass (see miff(5)).  To force a",
239     "PseudoClass image to remain PseudoClass, use -colors.",
240     (char *) NULL,
241   },
242   *ImageChopHelp[] =
243   {
244     "In chop mode, the Command widget has these options:",
245     "",
246     "    Direction",
247     "      horizontal",
248     "      vertical",
249     "    Help",
250     "    Dismiss",
251     "",
252     "If the you choose the horizontal direction (this the",
253     "default), the area of the image between the two horizontal",
254     "endpoints of the chop line is removed.  Otherwise, the area",
255     "of the image between the two vertical endpoints of the chop",
256     "line is removed.",
257     "",
258     "Select a location within the image window to begin your chop,",
259     "press and hold any button.  Next, move the pointer to",
260     "another location in the image.  As you move a line will",
261     "connect the initial location and the pointer.  When you",
262     "release the button, the area within the image to chop is",
263     "determined by which direction you choose from the Command",
264     "widget.",
265     "",
266     "To cancel the image chopping, move the pointer back to the",
267     "starting point of the line and release the button.",
268     (char *) NULL,
269   },
270   *ImageColorEditHelp[] =
271   {
272     "In color edit mode, the Command widget has these options:",
273     "",
274     "    Method",
275     "      point",
276     "      replace",
277     "      floodfill",
278     "      filltoborder",
279     "      reset",
280     "    Pixel Color",
281     "      black",
282     "      blue",
283     "      cyan",
284     "      green",
285     "      gray",
286     "      red",
287     "      magenta",
288     "      yellow",
289     "      white",
290     "      Browser...",
291     "    Border Color",
292     "      black",
293     "      blue",
294     "      cyan",
295     "      green",
296     "      gray",
297     "      red",
298     "      magenta",
299     "      yellow",
300     "      white",
301     "      Browser...",
302     "    Fuzz",
303     "      0%",
304     "      2%",
305     "      5%",
306     "      10%",
307     "      15%",
308     "      Dialog...",
309     "    Undo",
310     "    Help",
311     "    Dismiss",
312     "",
313     "Choose a color editing method from the Method sub-menu",
314     "of the Command widget.  The point method recolors any pixel",
315     "selected with the pointer until the button is released.  The",
316     "replace method recolors any pixel that matches the color of",
317     "the pixel you select with a button press.  Floodfill recolors",
318     "any pixel that matches the color of the pixel you select with",
319     "a button press and is a neighbor.  Whereas filltoborder recolors",
320     "any neighbor pixel that is not the border color.  Finally reset",
321     "changes the entire image to the designated color.",
322     "",
323     "Next, choose a pixel color from the Pixel Color sub-menu.",
324     "Additional pixel colors can be specified with the color",
325     "browser.  You can change the menu colors by setting the X",
326     "resources pen1 through pen9.",
327     "",
328     "Now press button 1 to select a pixel within the image window",
329     "to change its color.  Additional pixels may be recolored as",
330     "prescribed by the method you choose.",
331     "",
332     "If the Magnify widget is mapped, it can be helpful in positioning",
333     "your pointer within the image (refer to button 2).",
334     "",
335     "The actual color you request for the pixels is saved in the",
336     "image.  However, the color that appears in your image window",
337     "may be different.  For example, on a monochrome screen the",
338     "pixel will appear black or white even if you choose the",
339     "color red as the pixel color.  However, the image saved to a",
340     "file with -write is written with red pixels.  To assure the",
341     "correct color text in the final image, any PseudoClass image",
342     "is promoted to DirectClass (see miff(5)).  To force a",
343     "PseudoClass image to remain PseudoClass, use -colors.",
344     (char *) NULL,
345   },
346   *ImageCompositeHelp[] =
347   {
348     "First a widget window is displayed requesting you to enter an",
349     "image name. Press Composite, Grab or type a file name.",
350     "Press Cancel if you choose not to create a composite image.",
351     "When you choose Grab, move the pointer to the desired window",
352     "and press any button.",
353     "",
354     "If the Composite image does not have any matte information,",
355     "you are informed and the file browser is displayed again.",
356     "Enter the name of a mask image.  The image is typically",
357     "grayscale and the same size as the composite image.  If the",
358     "image is not grayscale, it is converted to grayscale and the",
359     "resulting intensities are used as matte information.",
360     "",
361     "A small window appears showing the location of the cursor in",
362     "the image window. You are now in composite mode.  To exit",
363     "immediately, press Dismiss.  In composite mode, the Command",
364     "widget has these options:",
365     "",
366     "    Operators",
367     "      Over",
368     "      In",
369     "      Out",
370     "      Atop",
371     "      Xor",
372     "      Plus",
373     "      Minus",
374     "      Add",
375     "      Subtract",
376     "      Difference",
377     "      Multiply",
378     "      Bumpmap",
379     "      Copy",
380     "      CopyRed",
381     "      CopyGreen",
382     "      CopyBlue",
383     "      CopyOpacity",
384     "      Clear",
385     "    Dissolve",
386     "    Displace",
387     "    Help",
388     "    Dismiss",
389     "",
390     "Choose a composite operation from the Operators sub-menu of",
391     "the Command widget.  How each operator behaves is described",
392     "below.  Image window is the image currently displayed on",
393     "your X server and image is the image obtained with the File",
394     "Browser widget.",
395     "",
396     "Over     The result is the union of the two image shapes,",
397     "         with image obscuring image window in the region of",
398     "         overlap.",
399     "",
400     "In       The result is simply image cut by the shape of",
401     "         image window.  None of the image data of image",
402     "         window is in the result.",
403     "",
404     "Out      The resulting image is image with the shape of",
405     "         image window cut out.",
406     "",
407     "Atop     The result is the same shape as image image window,",
408     "         with image obscuring image window where the image",
409     "         shapes overlap.  Note this differs from over",
410     "         because the portion of image outside image window's",
411     "         shape does not appear in the result.",
412     "",
413     "Xor      The result is the image data from both image and",
414     "         image window that is outside the overlap region.",
415     "         The overlap region is blank.",
416     "",
417     "Plus     The result is just the sum of the image data.",
418     "         Output values are cropped to QuantumRange (no overflow).",
419     "",
420     "Minus    The result of image - image window, with underflow",
421     "         cropped to zero.",
422     "",
423     "Add      The result of image + image window, with overflow",
424     "         wrapping around (mod 256).",
425     "",
426     "Subtract The result of image - image window, with underflow",
427     "         wrapping around (mod 256).  The add and subtract",
428     "         operators can be used to perform reversible",
429     "         transformations.",
430     "",
431     "Difference",
432     "         The result of abs(image - image window).  This",
433     "         useful for comparing two very similar images.",
434     "",
435     "Multiply",
436     "         The result of image * image window.  This",
437     "         useful for the creation of drop-shadows.",
438     "",
439     "Bumpmap  The result of surface normals from image * image",
440     "         window.",
441     "",
442     "Copy     The resulting image is image window replaced with",
443     "         image.  Here the matte information is ignored.",
444     "",
445     "CopyRed  The red layer of the image window is replace with",
446     "         the red layer of the image.  The other layers are",
447     "         untouched.",
448     "",
449     "CopyGreen",
450     "         The green layer of the image window is replace with",
451     "         the green layer of the image.  The other layers are",
452     "         untouched.",
453     "",
454     "CopyBlue The blue layer of the image window is replace with",
455     "         the blue layer of the image.  The other layers are",
456     "         untouched.",
457     "",
458     "CopyOpacity",
459     "         The matte layer of the image window is replace with",
460     "         the matte layer of the image.  The other layers are",
461     "         untouched.",
462     "",
463     "The image compositor requires a matte, or alpha channel in",
464     "the image for some operations.  This extra channel usually",
465     "defines a mask which represents a sort of a cookie-cutter",
466     "for the image.  This the case when matte is opaque (full",
467     "coverage) for pixels inside the shape, zero outside, and",
468     "between 0 and QuantumRange on the boundary.  If image does not",
469     "have a matte channel, it is initialized with 0 for any pixel",
470     "matching in color to pixel location (0,0), otherwise QuantumRange.",
471     "",
472     "If you choose Dissolve, the composite operator becomes Over.  The",
473     "image matte channel percent transparency is initialized to factor.",
474     "The image window is initialized to (100-factor). Where factor is the",
475     "value you specify in the Dialog widget.",
476     "",
477     "Displace shifts the image pixels as defined by a displacement",
478     "map.  With this option, image is used as a displacement map.",
479     "Black, within the displacement map, is a maximum positive",
480     "displacement.  White is a maximum negative displacement and",
481     "middle gray is neutral.  The displacement is scaled to determine",
482     "the pixel shift.  By default, the displacement applies in both the",
483     "horizontal and vertical directions.  However, if you specify a mask,",
484     "image is the horizontal X displacement and mask the vertical Y",
485     "displacement.",
486     "",
487     "Note that matte information for image window is not retained",
488     "for colormapped X server visuals (e.g. StaticColor,",
489     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
490     "behavior may require a TrueColor or DirectColor visual or a",
491     "Standard Colormap.",
492     "",
493     "Choosing a composite operator is optional.  The default",
494     "operator is replace.  However, you must choose a location to",
495     "composite your image and press button 1.  Press and hold the",
496     "button before releasing and an outline of the image will",
497     "appear to help you identify your location.",
498     "",
499     "The actual colors of the composite image is saved.  However,",
500     "the color that appears in image window may be different.",
501     "For example, on a monochrome screen image window will appear",
502     "black or white even though your composited image may have",
503     "many colors.  If the image is saved to a file it is written",
504     "with the correct colors.  To assure the correct colors are",
505     "saved in the final image, any PseudoClass image is promoted",
506     "to DirectClass (see miff(5)).  To force a PseudoClass image",
507     "to remain PseudoClass, use -colors.",
508     (char *) NULL,
509   },
510   *ImageCutHelp[] =
511   {
512     "In cut mode, the Command widget has these options:",
513     "",
514     "    Help",
515     "    Dismiss",
516     "",
517     "To define a cut region, press button 1 and drag.  The",
518     "cut region is defined by a highlighted rectangle that",
519     "expands or contracts as it follows the pointer.  Once you",
520     "are satisfied with the cut region, release the button.",
521     "You are now in rectify mode.  In rectify mode, the Command",
522     "widget has these options:",
523     "",
524     "    Cut",
525     "    Help",
526     "    Dismiss",
527     "",
528     "You can make adjustments by moving the pointer to one of the",
529     "cut rectangle corners, pressing a button, and dragging.",
530     "Finally, press Cut to commit your copy region.  To",
531     "exit without cutting the image, press Dismiss.",
532     (char *) NULL,
533   },
534   *ImageCopyHelp[] =
535   {
536     "In copy mode, the Command widget has these options:",
537     "",
538     "    Help",
539     "    Dismiss",
540     "",
541     "To define a copy region, press button 1 and drag.  The",
542     "copy region is defined by a highlighted rectangle that",
543     "expands or contracts as it follows the pointer.  Once you",
544     "are satisfied with the copy region, release the button.",
545     "You are now in rectify mode.  In rectify mode, the Command",
546     "widget has these options:",
547     "",
548     "    Copy",
549     "    Help",
550     "    Dismiss",
551     "",
552     "You can make adjustments by moving the pointer to one of the",
553     "copy rectangle corners, pressing a button, and dragging.",
554     "Finally, press Copy to commit your copy region.  To",
555     "exit without copying the image, press Dismiss.",
556     (char *) NULL,
557   },
558   *ImageCropHelp[] =
559   {
560     "In crop mode, the Command widget has these options:",
561     "",
562     "    Help",
563     "    Dismiss",
564     "",
565     "To define a cropping region, press button 1 and drag.  The",
566     "cropping region is defined by a highlighted rectangle that",
567     "expands or contracts as it follows the pointer.  Once you",
568     "are satisfied with the cropping region, release the button.",
569     "You are now in rectify mode.  In rectify mode, the Command",
570     "widget has these options:",
571     "",
572     "    Crop",
573     "    Help",
574     "    Dismiss",
575     "",
576     "You can make adjustments by moving the pointer to one of the",
577     "cropping rectangle corners, pressing a button, and dragging.",
578     "Finally, press Crop to commit your cropping region.  To",
579     "exit without cropping the image, press Dismiss.",
580     (char *) NULL,
581   },
582   *ImageDrawHelp[] =
583   {
584     "The cursor changes to a crosshair to indicate you are in",
585     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
586     "the Command widget has these options:",
587     "",
588     "    Element",
589     "      point",
590     "      line",
591     "      rectangle",
592     "      fill rectangle",
593     "      circle",
594     "      fill circle",
595     "      ellipse",
596     "      fill ellipse",
597     "      polygon",
598     "      fill polygon",
599     "    Color",
600     "      black",
601     "      blue",
602     "      cyan",
603     "      green",
604     "      gray",
605     "      red",
606     "      magenta",
607     "      yellow",
608     "      white",
609     "      transparent",
610     "      Browser...",
611     "    Stipple",
612     "      Brick",
613     "      Diagonal",
614     "      Scales",
615     "      Vertical",
616     "      Wavy",
617     "      Translucent",
618     "      Opaque",
619     "      Open...",
620     "    Width",
621     "      1",
622     "      2",
623     "      4",
624     "      8",
625     "      16",
626     "      Dialog...",
627     "    Undo",
628     "    Help",
629     "    Dismiss",
630     "",
631     "Choose a drawing primitive from the Element sub-menu.",
632     "",
633     "Choose a color from the Color sub-menu.  Additional",
634     "colors can be specified with the color browser.",
635     "",
636     "If you choose the color browser and press Grab, you can",
637     "select the color by moving the pointer to the desired",
638     "color on the screen and press any button.  The transparent",
639     "color updates the image matte channel and is useful for",
640     "image compositing.",
641     "",
642     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
643     "Additional stipples can be specified with the file browser.",
644     "Stipples obtained from the file browser must be on disk in the",
645     "X11 bitmap format.",
646     "",
647     "Choose a width, if appropriate, from the Width sub-menu.  To",
648     "choose a specific width select the Dialog widget.",
649     "",
650     "Choose a point in the Image window and press button 1 and",
651     "hold.  Next, move the pointer to another location in the",
652     "image.  As you move, a line connects the initial location and",
653     "the pointer.  When you release the button, the image is",
654     "updated with the primitive you just drew.  For polygons, the",
655     "image is updated when you press and release the button without",
656     "moving the pointer.",
657     "",
658     "To cancel image drawing, move the pointer back to the",
659     "starting point of the line and release the button.",
660     (char *) NULL,
661   },
662   *DisplayHelp[] =
663   {
664     "BUTTONS",
665     "  The effects of each button press is described below.  Three",
666     "  buttons are required.  If you have a two button mouse,",
667     "  button 1 and 3 are returned.  Press ALT and button 3 to",
668     "  simulate button 2.",
669     "",
670     "  1    Press this button to map or unmap the Command widget.",
671     "",
672     "  2    Press and drag to define a region of the image to",
673     "       magnify.",
674     "",
675     "  3    Press and drag to choose from a select set of commands.",
676     "       This button behaves differently if the image being",
677     "       displayed is a visual image directory.  Here, choose a",
678     "       particular tile of the directory and press this button and",
679     "       drag to select a command from a pop-up menu.  Choose from",
680     "       these menu items:",
681     "",
682     "           Open",
683     "           Next",
684     "           Former",
685     "           Delete",
686     "           Update",
687     "",
688     "       If you choose Open, the image represented by the tile is",
689     "       displayed.  To return to the visual image directory, choose",
690     "       Next from the Command widget.  Next and Former moves to the",
691     "       next or former image respectively.  Choose Delete to delete",
692     "       a particular image tile.  Finally, choose Update to",
693     "       synchronize all the image tiles with their respective",
694     "       images.",
695     "",
696     "COMMAND WIDGET",
697     "  The Command widget lists a number of sub-menus and commands.",
698     "  They are",
699     "",
700     "      File",
701     "        Open...",
702     "        Next",
703     "        Former",
704     "        Select...",
705     "        Save...",
706     "        Print...",
707     "        Delete...",
708     "        New...",
709     "        Visual Directory...",
710     "        Quit",
711     "      Edit",
712     "        Undo",
713     "        Redo",
714     "        Cut",
715     "        Copy",
716     "        Paste",
717     "      View",
718     "        Half Size",
719     "        Original Size",
720     "        Double Size",
721     "        Resize...",
722     "        Apply",
723     "        Refresh",
724     "        Restore",
725     "      Transform",
726     "        Crop",
727     "        Chop",
728     "        Flop",
729     "        Flip",
730     "        Rotate Right",
731     "        Rotate Left",
732     "        Rotate...",
733     "        Shear...",
734     "        Roll...",
735     "        Trim Edges",
736     "      Enhance",
737     "        Brightness...",
738     "        Saturation...",
739     "        Hue...",
740     "        Gamma...",
741     "        Sharpen...",
742     "        Dull",
743     "        Contrast Stretch...",
744     "        Sigmoidal Contrast...",
745     "        Normalize",
746     "        Equalize",
747     "        Negate",
748     "        Grayscale",
749     "        Map...",
750     "        Quantize...",
751     "      Effects",
752     "        Despeckle",
753     "        Emboss",
754     "        Reduce Noise",
755     "        Add Noise",
756     "        Sharpen...",
757     "        Blur...",
758     "        Threshold...",
759     "        Edge Detect...",
760     "        Spread...",
761     "        Shade...",
762     "        Painting...",
763     "        Segment...",
764     "      F/X",
765     "        Solarize...",
766     "        Sepia Tone...",
767     "        Swirl...",
768     "        Implode...",
769     "        Vignette...",
770     "        Wave...",
771     "        Oil Painting...",
772     "        Charcoal Drawing...",
773     "      Image Edit",
774     "        Annotate...",
775     "        Draw...",
776     "        Color...",
777     "        Matte...",
778     "        Composite...",
779     "        Add Border...",
780     "        Add Frame...",
781     "        Comment...",
782     "        Launch...",
783     "        Region of Interest...",
784     "      Miscellany",
785     "        Image Info",
786     "        Zoom Image",
787     "        Show Preview...",
788     "        Show Histogram",
789     "        Show Matte",
790     "        Background...",
791     "        Slide Show",
792     "        Preferences...",
793     "      Help",
794     "        Overview",
795     "        Browse Documentation",
796     "        About Display",
797     "",
798     "  Menu items with a indented triangle have a sub-menu.  They",
799     "  are represented above as the indented items.  To access a",
800     "  sub-menu item, move the pointer to the appropriate menu and",
801     "  press a button and drag.  When you find the desired sub-menu",
802     "  item, release the button and the command is executed.  Move",
803     "  the pointer away from the sub-menu if you decide not to",
804     "  execute a particular command.",
805     "",
806     "KEYBOARD ACCELERATORS",
807     "  Accelerators are one or two key presses that effect a",
808     "  particular command.  The keyboard accelerators that",
809     "  display(1) understands is:",
810     "",
811     "  Ctl+O     Press to open an image from a file.",
812     "",
813     "  space     Press to display the next image.",
814     "",
815     "            If the image is a multi-paged document such as a Postscript",
816     "            document, you can skip ahead several pages by preceding",
817     "            this command with a number.  For example to display the",
818     "            third page beyond the current page, press 3<space>.",
819     "",
820     "  backspace Press to display the former image.",
821     "",
822     "            If the image is a multi-paged document such as a Postscript",
823     "            document, you can skip behind several pages by preceding",
824     "            this command with a number.  For example to display the",
825     "            third page preceding the current page, press 3<backspace>.",
826     "",
827     "  Ctl+S     Press to write the image to a file.",
828     "",
829     "  Ctl+P     Press to print the image to a Postscript printer.",
830     "",
831     "  Ctl+D     Press to delete an image file.",
832     "",
833     "  Ctl+N     Press to create a blank canvas.",
834     "",
835     "  Ctl+Q     Press to discard all images and exit program.",
836     "",
837     "  Ctl+Z     Press to undo last image transformation.",
838     "",
839     "  Ctl+R     Press to redo last image transformation.",
840     "",
841     "  Ctl+X     Press to cut a region of the image.",
842     "",
843     "  Ctl+C     Press to copy a region of the image.",
844     "",
845     "  Ctl+V     Press to paste a region to the image.",
846     "",
847     "  <         Press to half the image size.",
848     "",
849     "  -         Press to return to the original image size.",
850     "",
851     "  >         Press to double the image size.",
852     "",
853     "  %         Press to resize the image to a width and height you",
854     "            specify.",
855     "",
856     "Cmd-A       Press to make any image transformations permanent."
857     "",
858     "            By default, any image size transformations are applied",
859     "            to the original image to create the image displayed on",
860     "            the X server.  However, the transformations are not",
861     "            permanent (i.e. the original image does not change",
862     "            size only the X image does).  For example, if you",
863     "            press > the X image will appear to double in size,",
864     "            but the original image will in fact remain the same size.",
865     "            To force the original image to double in size, press >",
866     "            followed by Cmd-A.",
867     "",
868     "  @         Press to refresh the image window.",
869     "",
870     "  C         Press to cut out a rectangular region of the image.",
871     "",
872     "  [         Press to chop the image.",
873     "",
874     "  H         Press to flop image in the horizontal direction.",
875     "",
876     "  V         Press to flip image in the vertical direction.",
877     "",
878     "  /         Press to rotate the image 90 degrees clockwise.",
879     "",
880     " \\         Press to rotate the image 90 degrees counter-clockwise.",
881     "",
882     "  *         Press to rotate the image the number of degrees you",
883     "            specify.",
884     "",
885     "  S         Press to shear the image the number of degrees you",
886     "            specify.",
887     "",
888     "  R         Press to roll the image.",
889     "",
890     "  T         Press to trim the image edges.",
891     "",
892     "  Shft-H    Press to vary the image hue.",
893     "",
894     "  Shft-S    Press to vary the color saturation.",
895     "",
896     "  Shft-L    Press to vary the color brightness.",
897     "",
898     "  Shft-G    Press to gamma correct the image.",
899     "",
900     "  Shft-C    Press to sharpen the image contrast.",
901     "",
902     "  Shft-Z    Press to dull the image contrast.",
903     "",
904     "  =         Press to perform histogram equalization on the image.",
905     "",
906     "  Shft-N    Press to perform histogram normalization on the image.",
907     "",
908     "  Shft-~    Press to negate the colors of the image.",
909     "",
910     "  .         Press to convert the image colors to gray.",
911     "",
912     "  Shft-#    Press to set the maximum number of unique colors in the",
913     "            image.",
914     "",
915     "  F2        Press to reduce the speckles in an image.",
916     "",
917     "  F3        Press to eliminate peak noise from an image.",
918     "",
919     "  F4        Press to add noise to an image.",
920     "",
921     "  F5        Press to sharpen an image.",
922     "",
923     "  F6        Press to delete an image file.",
924     "",
925     "  F7        Press to threshold the image.",
926     "",
927     "  F8        Press to detect edges within an image.",
928     "",
929     "  F9        Press to emboss an image.",
930     "",
931     "  F10       Press to displace pixels by a random amount.",
932     "",
933     "  F11       Press to negate all pixels above the threshold level.",
934     "",
935     "  F12       Press to shade the image using a distant light source.",
936     "",
937     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
938     "",
939     "  F14       Press to segment the image by color.",
940     "",
941     "  Meta-S    Press to swirl image pixels about the center.",
942     "",
943     "  Meta-I    Press to implode image pixels about the center.",
944     "",
945     "  Meta-W    Press to alter an image along a sine wave.",
946     "",
947     "  Meta-P    Press to simulate an oil painting.",
948     "",
949     "  Meta-C    Press to simulate a charcoal drawing.",
950     "",
951     "  Alt-A     Press to annotate the image with text.",
952     "",
953     "  Alt-D     Press to draw on an image.",
954     "",
955     "  Alt-P     Press to edit an image pixel color.",
956     "",
957     "  Alt-M     Press to edit the image matte information.",
958     "",
959     "  Alt-V     Press to composite the image with another.",
960     "",
961     "  Alt-B     Press to add a border to the image.",
962     "",
963     "  Alt-F     Press to add an ornamental border to the image.",
964     "",
965     "  Alt-Shft-!",
966     "            Press to add an image comment.",
967     "",
968     "  Ctl-A     Press to apply image processing techniques to a region",
969     "            of interest.",
970     "",
971     "  Shft-?    Press to display information about the image.",
972     "",
973     "  Shft-+    Press to map the zoom image window.",
974     "",
975     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
976     "",
977     "  F1        Press to display helpful information about display(1).",
978     "",
979     "  Find      Press to browse documentation about ImageMagick.",
980     "",
981     "  1-9       Press to change the level of magnification.",
982     "",
983     "  Use the arrow keys to move the image one pixel up, down,",
984     "  left, or right within the magnify window.  Be sure to first",
985     "  map the magnify window by pressing button 2.",
986     "",
987     "  Press ALT and one of the arrow keys to trim off one pixel",
988     "  from any side of the image.",
989     (char *) NULL,
990   },
991   *ImageMatteEditHelp[] =
992   {
993     "Matte information within an image is useful for some",
994     "operations such as image compositing (See IMAGE",
995     "COMPOSITING).  This extra channel usually defines a mask",
996     "which represents a sort of a cookie-cutter for the image.",
997     "This the case when matte is opaque (full coverage) for",
998     "pixels inside the shape, zero outside, and between 0 and",
999     "QuantumRange on the boundary.",
1000     "",
1001     "A small window appears showing the location of the cursor in",
1002     "the image window. You are now in matte edit mode.  To exit",
1003     "immediately, press Dismiss.  In matte edit mode, the Command",
1004     "widget has these options:",
1005     "",
1006     "    Method",
1007     "      point",
1008     "      replace",
1009     "      floodfill",
1010     "      filltoborder",
1011     "      reset",
1012     "    Border Color",
1013     "      black",
1014     "      blue",
1015     "      cyan",
1016     "      green",
1017     "      gray",
1018     "      red",
1019     "      magenta",
1020     "      yellow",
1021     "      white",
1022     "      Browser...",
1023     "    Fuzz",
1024     "      0%",
1025     "      2%",
1026     "      5%",
1027     "      10%",
1028     "      15%",
1029     "      Dialog...",
1030     "    Matte",
1031     "      Opaque",
1032     "      Transparent",
1033     "      Dialog...",
1034     "    Undo",
1035     "    Help",
1036     "    Dismiss",
1037     "",
1038     "Choose a matte editing method from the Method sub-menu of",
1039     "the Command widget.  The point method changes the matte value",
1040     "of any pixel selected with the pointer until the button is",
1041     "is released.  The replace method changes the matte value of",
1042     "any pixel that matches the color of the pixel you select with",
1043     "a button press.  Floodfill changes the matte value of any pixel",
1044     "that matches the color of the pixel you select with a button",
1045     "press and is a neighbor.  Whereas filltoborder changes the matte",
1046     "value any neighbor pixel that is not the border color.  Finally",
1047     "reset changes the entire image to the designated matte value.",
1048     "",
1049     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1050     "select the Dialog entry.  Here a dialog appears requesting a matte",
1051     "value.  The value you select is assigned as the opacity value of the",
1052     "selected pixel or pixels.",
1053     "",
1054     "Now, press any button to select a pixel within the image",
1055     "window to change its matte value.",
1056     "",
1057     "If the Magnify widget is mapped, it can be helpful in positioning",
1058     "your pointer within the image (refer to button 2).",
1059     "",
1060     "Matte information is only valid in a DirectClass image.",
1061     "Therefore, any PseudoClass image is promoted to DirectClass",
1062     "(see miff(5)).  Note that matte information for PseudoClass",
1063     "is not retained for colormapped X server visuals (e.g.",
1064     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1065     "immediately save your image to a file (refer to Write).",
1066     "Correct matte editing behavior may require a TrueColor or",
1067     "DirectColor visual or a Standard Colormap.",
1068     (char *) NULL,
1069   },
1070   *ImagePanHelp[] =
1071   {
1072     "When an image exceeds the width or height of the X server",
1073     "screen, display maps a small panning icon.  The rectangle",
1074     "within the panning icon shows the area that is currently",
1075     "displayed in the image window.  To pan about the image,",
1076     "press any button and drag the pointer within the panning",
1077     "icon.  The pan rectangle moves with the pointer and the",
1078     "image window is updated to reflect the location of the",
1079     "rectangle within the panning icon.  When you have selected",
1080     "the area of the image you wish to view, release the button.",
1081     "",
1082     "Use the arrow keys to pan the image one pixel up, down,",
1083     "left, or right within the image window.",
1084     "",
1085     "The panning icon is withdrawn if the image becomes smaller",
1086     "than the dimensions of the X server screen.",
1087     (char *) NULL,
1088   },
1089   *ImagePasteHelp[] =
1090   {
1091     "A small window appears showing the location of the cursor in",
1092     "the image window. You are now in paste mode.  To exit",
1093     "immediately, press Dismiss.  In paste mode, the Command",
1094     "widget has these options:",
1095     "",
1096     "    Operators",
1097     "      over",
1098     "      in",
1099     "      out",
1100     "      atop",
1101     "      xor",
1102     "      plus",
1103     "      minus",
1104     "      add",
1105     "      subtract",
1106     "      difference",
1107     "      replace",
1108     "    Help",
1109     "    Dismiss",
1110     "",
1111     "Choose a composite operation from the Operators sub-menu of",
1112     "the Command widget.  How each operator behaves is described",
1113     "below.  Image window is the image currently displayed on",
1114     "your X server and image is the image obtained with the File",
1115     "Browser widget.",
1116     "",
1117     "Over     The result is the union of the two image shapes,",
1118     "         with image obscuring image window in the region of",
1119     "         overlap.",
1120     "",
1121     "In       The result is simply image cut by the shape of",
1122     "         image window.  None of the image data of image",
1123     "         window is in the result.",
1124     "",
1125     "Out      The resulting image is image with the shape of",
1126     "         image window cut out.",
1127     "",
1128     "Atop     The result is the same shape as image image window,",
1129     "         with image obscuring image window where the image",
1130     "         shapes overlap.  Note this differs from over",
1131     "         because the portion of image outside image window's",
1132     "         shape does not appear in the result.",
1133     "",
1134     "Xor      The result is the image data from both image and",
1135     "         image window that is outside the overlap region.",
1136     "         The overlap region is blank.",
1137     "",
1138     "Plus     The result is just the sum of the image data.",
1139     "         Output values are cropped to QuantumRange (no overflow).",
1140     "         This operation is independent of the matte",
1141     "         channels.",
1142     "",
1143     "Minus    The result of image - image window, with underflow",
1144     "         cropped to zero.",
1145     "",
1146     "Add      The result of image + image window, with overflow",
1147     "         wrapping around (mod 256).",
1148     "",
1149     "Subtract The result of image - image window, with underflow",
1150     "         wrapping around (mod 256).  The add and subtract",
1151     "         operators can be used to perform reversible",
1152     "         transformations.",
1153     "",
1154     "Difference",
1155     "         The result of abs(image - image window).  This",
1156     "         useful for comparing two very similar images.",
1157     "",
1158     "Copy     The resulting image is image window replaced with",
1159     "         image.  Here the matte information is ignored.",
1160     "",
1161     "CopyRed  The red layer of the image window is replace with",
1162     "         the red layer of the image.  The other layers are",
1163     "         untouched.",
1164     "",
1165     "CopyGreen",
1166     "         The green layer of the image window is replace with",
1167     "         the green layer of the image.  The other layers are",
1168     "         untouched.",
1169     "",
1170     "CopyBlue The blue layer of the image window is replace with",
1171     "         the blue layer of the image.  The other layers are",
1172     "         untouched.",
1173     "",
1174     "CopyOpacity",
1175     "         The matte layer of the image window is replace with",
1176     "         the matte layer of the image.  The other layers are",
1177     "         untouched.",
1178     "",
1179     "The image compositor requires a matte, or alpha channel in",
1180     "the image for some operations.  This extra channel usually",
1181     "defines a mask which represents a sort of a cookie-cutter",
1182     "for the image.  This the case when matte is opaque (full",
1183     "coverage) for pixels inside the shape, zero outside, and",
1184     "between 0 and QuantumRange on the boundary.  If image does not",
1185     "have a matte channel, it is initialized with 0 for any pixel",
1186     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1187     "",
1188     "Note that matte information for image window is not retained",
1189     "for colormapped X server visuals (e.g. StaticColor,",
1190     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1191     "behavior may require a TrueColor or DirectColor visual or a",
1192     "Standard Colormap.",
1193     "",
1194     "Choosing a composite operator is optional.  The default",
1195     "operator is replace.  However, you must choose a location to",
1196     "paste your image and press button 1.  Press and hold the",
1197     "button before releasing and an outline of the image will",
1198     "appear to help you identify your location.",
1199     "",
1200     "The actual colors of the pasted image is saved.  However,",
1201     "the color that appears in image window may be different.",
1202     "For example, on a monochrome screen image window will appear",
1203     "black or white even though your pasted image may have",
1204     "many colors.  If the image is saved to a file it is written",
1205     "with the correct colors.  To assure the correct colors are",
1206     "saved in the final image, any PseudoClass image is promoted",
1207     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1208     "to remain PseudoClass, use -colors.",
1209     (char *) NULL,
1210   },
1211   *ImageROIHelp[] =
1212   {
1213     "In region of interest mode, the Command widget has these",
1214     "options:",
1215     "",
1216     "    Help",
1217     "    Dismiss",
1218     "",
1219     "To define a region of interest, press button 1 and drag.",
1220     "The region of interest is defined by a highlighted rectangle",
1221     "that expands or contracts as it follows the pointer.  Once",
1222     "you are satisfied with the region of interest, release the",
1223     "button.  You are now in apply mode.  In apply mode the",
1224     "Command widget has these options:",
1225     "",
1226     "      File",
1227     "        Save...",
1228     "        Print...",
1229     "      Edit",
1230     "        Undo",
1231     "        Redo",
1232     "      Transform",
1233     "        Flop",
1234     "        Flip",
1235     "        Rotate Right",
1236     "        Rotate Left",
1237     "      Enhance",
1238     "        Hue...",
1239     "        Saturation...",
1240     "        Brightness...",
1241     "        Gamma...",
1242     "        Spiff",
1243     "        Dull",
1244     "        Contrast Stretch",
1245     "        Sigmoidal Contrast...",
1246     "        Normalize",
1247     "        Equalize",
1248     "        Negate",
1249     "        Grayscale",
1250     "        Map...",
1251     "        Quantize...",
1252     "      Effects",
1253     "        Despeckle",
1254     "        Emboss",
1255     "        Reduce Noise",
1256     "        Sharpen...",
1257     "        Blur...",
1258     "        Threshold...",
1259     "        Edge Detect...",
1260     "        Spread...",
1261     "        Shade...",
1262     "        Raise...",
1263     "        Segment...",
1264     "      F/X",
1265     "        Solarize...",
1266     "        Sepia Tone...",
1267     "        Swirl...",
1268     "        Implode...",
1269     "        Vignette...",
1270     "        Wave...",
1271     "        Oil Painting...",
1272     "        Charcoal Drawing...",
1273     "      Miscellany",
1274     "        Image Info",
1275     "        Zoom Image",
1276     "        Show Preview...",
1277     "        Show Histogram",
1278     "        Show Matte",
1279     "      Help",
1280     "      Dismiss",
1281     "",
1282     "You can make adjustments to the region of interest by moving",
1283     "the pointer to one of the rectangle corners, pressing a",
1284     "button, and dragging.  Finally, choose an image processing",
1285     "technique from the Command widget.  You can choose more than",
1286     "one image processing technique to apply to an area.",
1287     "Alternatively, you can move the region of interest before",
1288     "applying another image processing technique.  To exit, press",
1289     "Dismiss.",
1290     (char *) NULL,
1291   },
1292   *ImageRotateHelp[] =
1293   {
1294     "In rotate mode, the Command widget has these options:",
1295     "",
1296     "    Pixel Color",
1297     "      black",
1298     "      blue",
1299     "      cyan",
1300     "      green",
1301     "      gray",
1302     "      red",
1303     "      magenta",
1304     "      yellow",
1305     "      white",
1306     "      Browser...",
1307     "    Direction",
1308     "      horizontal",
1309     "      vertical",
1310     "    Help",
1311     "    Dismiss",
1312     "",
1313     "Choose a background color from the Pixel Color sub-menu.",
1314     "Additional background colors can be specified with the color",
1315     "browser.  You can change the menu colors by setting the X",
1316     "resources pen1 through pen9.",
1317     "",
1318     "If you choose the color browser and press Grab, you can",
1319     "select the background color by moving the pointer to the",
1320     "desired color on the screen and press any button.",
1321     "",
1322     "Choose a point in the image window and press this button and",
1323     "hold.  Next, move the pointer to another location in the",
1324     "image.  As you move a line connects the initial location and",
1325     "the pointer.  When you release the button, the degree of",
1326     "image rotation is determined by the slope of the line you",
1327     "just drew.  The slope is relative to the direction you",
1328     "choose from the Direction sub-menu of the Command widget.",
1329     "",
1330     "To cancel the image rotation, move the pointer back to the",
1331     "starting point of the line and release the button.",
1332     (char *) NULL,
1333   };
1334 \f
1335 /*
1336   Enumeration declarations.
1337 */
1338 typedef enum
1339 {
1340   CopyMode,
1341   CropMode,
1342   CutMode
1343 } ClipboardMode;
1344
1345 typedef enum
1346 {
1347   OpenCommand,
1348   NextCommand,
1349   FormerCommand,
1350   SelectCommand,
1351   SaveCommand,
1352   PrintCommand,
1353   DeleteCommand,
1354   NewCommand,
1355   VisualDirectoryCommand,
1356   QuitCommand,
1357   UndoCommand,
1358   RedoCommand,
1359   CutCommand,
1360   CopyCommand,
1361   PasteCommand,
1362   HalfSizeCommand,
1363   OriginalSizeCommand,
1364   DoubleSizeCommand,
1365   ResizeCommand,
1366   ApplyCommand,
1367   RefreshCommand,
1368   RestoreCommand,
1369   CropCommand,
1370   ChopCommand,
1371   FlopCommand,
1372   FlipCommand,
1373   RotateRightCommand,
1374   RotateLeftCommand,
1375   RotateCommand,
1376   ShearCommand,
1377   RollCommand,
1378   TrimCommand,
1379   HueCommand,
1380   SaturationCommand,
1381   BrightnessCommand,
1382   GammaCommand,
1383   SpiffCommand,
1384   DullCommand,
1385   ContrastStretchCommand,
1386   SigmoidalContrastCommand,
1387   NormalizeCommand,
1388   EqualizeCommand,
1389   NegateCommand,
1390   GrayscaleCommand,
1391   MapCommand,
1392   QuantizeCommand,
1393   DespeckleCommand,
1394   EmbossCommand,
1395   ReduceNoiseCommand,
1396   AddNoiseCommand,
1397   SharpenCommand,
1398   BlurCommand,
1399   ThresholdCommand,
1400   EdgeDetectCommand,
1401   SpreadCommand,
1402   ShadeCommand,
1403   RaiseCommand,
1404   SegmentCommand,
1405   SolarizeCommand,
1406   SepiaToneCommand,
1407   SwirlCommand,
1408   ImplodeCommand,
1409   VignetteCommand,
1410   WaveCommand,
1411   OilPaintCommand,
1412   CharcoalDrawCommand,
1413   AnnotateCommand,
1414   DrawCommand,
1415   ColorCommand,
1416   MatteCommand,
1417   CompositeCommand,
1418   AddBorderCommand,
1419   AddFrameCommand,
1420   CommentCommand,
1421   LaunchCommand,
1422   RegionofInterestCommand,
1423   ROIHelpCommand,
1424   ROIDismissCommand,
1425   InfoCommand,
1426   ZoomCommand,
1427   ShowPreviewCommand,
1428   ShowHistogramCommand,
1429   ShowMatteCommand,
1430   BackgroundCommand,
1431   SlideShowCommand,
1432   PreferencesCommand,
1433   HelpCommand,
1434   BrowseDocumentationCommand,
1435   VersionCommand,
1436   SaveToUndoBufferCommand,
1437   FreeBuffersCommand,
1438   NullCommand
1439 } CommandType;
1440
1441 typedef enum
1442 {
1443   AnnotateNameCommand,
1444   AnnotateFontColorCommand,
1445   AnnotateBackgroundColorCommand,
1446   AnnotateRotateCommand,
1447   AnnotateHelpCommand,
1448   AnnotateDismissCommand,
1449   TextHelpCommand,
1450   TextApplyCommand,
1451   ChopDirectionCommand,
1452   ChopHelpCommand,
1453   ChopDismissCommand,
1454   HorizontalChopCommand,
1455   VerticalChopCommand,
1456   ColorEditMethodCommand,
1457   ColorEditColorCommand,
1458   ColorEditBorderCommand,
1459   ColorEditFuzzCommand,
1460   ColorEditUndoCommand,
1461   ColorEditHelpCommand,
1462   ColorEditDismissCommand,
1463   CompositeOperatorsCommand,
1464   CompositeDissolveCommand,
1465   CompositeDisplaceCommand,
1466   CompositeHelpCommand,
1467   CompositeDismissCommand,
1468   CropHelpCommand,
1469   CropDismissCommand,
1470   RectifyCopyCommand,
1471   RectifyHelpCommand,
1472   RectifyDismissCommand,
1473   DrawElementCommand,
1474   DrawColorCommand,
1475   DrawStippleCommand,
1476   DrawWidthCommand,
1477   DrawUndoCommand,
1478   DrawHelpCommand,
1479   DrawDismissCommand,
1480   MatteEditMethod,
1481   MatteEditBorderCommand,
1482   MatteEditFuzzCommand,
1483   MatteEditValueCommand,
1484   MatteEditUndoCommand,
1485   MatteEditHelpCommand,
1486   MatteEditDismissCommand,
1487   PasteOperatorsCommand,
1488   PasteHelpCommand,
1489   PasteDismissCommand,
1490   RotateColorCommand,
1491   RotateDirectionCommand,
1492   RotateCropCommand,
1493   RotateSharpenCommand,
1494   RotateHelpCommand,
1495   RotateDismissCommand,
1496   HorizontalRotateCommand,
1497   VerticalRotateCommand,
1498   TileLoadCommand,
1499   TileNextCommand,
1500   TileFormerCommand,
1501   TileDeleteCommand,
1502   TileUpdateCommand
1503 } ModeType;
1504 \f
1505 /*
1506   Stipples.
1507 */
1508 #define BricksWidth  20
1509 #define BricksHeight  20
1510 #define DiagonalWidth  16
1511 #define DiagonalHeight  16
1512 #define HighlightWidth  8
1513 #define HighlightHeight  8
1514 #define OpaqueWidth  8
1515 #define OpaqueHeight  8
1516 #define ScalesWidth  16
1517 #define ScalesHeight  16
1518 #define ShadowWidth  8
1519 #define ShadowHeight  8
1520 #define VerticalWidth  16
1521 #define VerticalHeight  16
1522 #define WavyWidth  16
1523 #define WavyHeight  16
1524 \f
1525 /*
1526   Constant declaration.
1527 */
1528 static const int
1529   RoiDelta = 8;
1530
1531 static const unsigned char
1532   BricksBitmap[] =
1533   {
1534     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1535     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1536     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1537     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1538     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1539   },
1540   DiagonalBitmap[] =
1541   {
1542     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1543     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1544     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1545   },
1546   ScalesBitmap[] =
1547   {
1548     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1549     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1550     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1551   },
1552   VerticalBitmap[] =
1553   {
1554     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1555     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1556     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1557   },
1558   WavyBitmap[] =
1559   {
1560     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1561     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1562     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1563   };
1564 \f
1565 /*
1566   Function prototypes.
1567 */
1568 static CommandType
1569   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1570     const MagickStatusType,KeySym,Image **,ExceptionInfo *);
1571
1572 static Image
1573   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1574     Image **,ExceptionInfo *),
1575   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1576   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *,
1577     ExceptionInfo *),
1578   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *,
1579     ExceptionInfo *);
1580
1581 static MagickBooleanType
1582   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *,
1583     ExceptionInfo *),
1584   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **,
1585     ExceptionInfo *),
1586   XChopImage(Display *,XResourceInfo *,XWindows *,Image **,
1587     ExceptionInfo *),
1588   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode,
1589     ExceptionInfo *),
1590   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1591     ExceptionInfo *),
1592   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *,
1593     ExceptionInfo *),
1594   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1595   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1596     ExceptionInfo *),
1597   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **,
1598     ExceptionInfo *),
1599   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1600   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1601   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **,
1602     ExceptionInfo *),
1603   XROIImage(Display *,XResourceInfo *,XWindows *,Image **,ExceptionInfo *),
1604   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1605   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *);
1606
1607 static void
1608   XDrawPanRectangle(Display *,XWindows *),
1609   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **,
1610     ExceptionInfo *),
1611   XMagnifyImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1612   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *,ExceptionInfo *),
1613   XPanImage(Display *,XWindows *,XEvent *,ExceptionInfo *),
1614   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1615     const KeySym,ExceptionInfo *),
1616   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1617   XScreenEvent(Display *,XWindows *,XEvent *,ExceptionInfo *),
1618   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1619 \f
1620 /*
1621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1622 %                                                                             %
1623 %                                                                             %
1624 %                                                                             %
1625 %   D i s p l a y I m a g e s                                                 %
1626 %                                                                             %
1627 %                                                                             %
1628 %                                                                             %
1629 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1630 %
1631 %  DisplayImages() displays an image sequence to any X window screen.  It
1632 %  returns a value other than 0 if successful.  Check the exception member
1633 %  of image to determine the reason for any failure.
1634 %
1635 %  The format of the DisplayImages method is:
1636 %
1637 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1638 %        Image *images,ExceptionInfo *exception)
1639 %
1640 %  A description of each parameter follows:
1641 %
1642 %    o image_info: the image info.
1643 %
1644 %    o image: the image.
1645 %
1646 %    o exception: return any errors or warnings in this structure.
1647 %
1648 */
1649 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1650   Image *images,ExceptionInfo *exception)
1651 {
1652   char
1653     *argv[1];
1654
1655   Display
1656     *display;
1657
1658   Image
1659     *image;
1660
1661   register ssize_t
1662     i;
1663
1664   size_t
1665     state;
1666
1667   XrmDatabase
1668     resource_database;
1669
1670   XResourceInfo
1671     resource_info;
1672
1673   assert(image_info != (const ImageInfo *) NULL);
1674   assert(image_info->signature == MagickSignature);
1675   assert(images != (Image *) NULL);
1676   assert(images->signature == MagickSignature);
1677   if (images->debug != MagickFalse)
1678     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1679   display=XOpenDisplay(image_info->server_name);
1680   if (display == (Display *) NULL)
1681     {
1682       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1683         "UnableToOpenXServer","'%s'",XDisplayName(image_info->server_name));
1684       return(MagickFalse);
1685     }
1686   if (exception->severity != UndefinedException)
1687     CatchException(exception);
1688   (void) XSetErrorHandler(XError);
1689   resource_database=XGetResourceDatabase(display,GetClientName());
1690   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1691   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1692   if (image_info->page != (char *) NULL)
1693     resource_info.image_geometry=AcquireString(image_info->page);
1694   resource_info.immutable=MagickTrue;
1695   argv[0]=AcquireString(GetClientName());
1696   state=DefaultState;
1697   for (i=0; (state & ExitState) == 0; i++)
1698   {
1699     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1700       break;
1701     image=GetImageFromList(images,i % GetImageListLength(images));
1702     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state,exception);
1703   }
1704   (void) SetErrorHandler((ErrorHandler) NULL);
1705   (void) SetWarningHandler((WarningHandler) NULL);
1706   argv[0]=DestroyString(argv[0]);
1707   (void) XCloseDisplay(display);
1708   XDestroyResourceInfo(&resource_info);
1709   if (exception->severity != UndefinedException)
1710     return(MagickFalse);
1711   return(MagickTrue);
1712 }
1713 \f
1714 /*
1715 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1716 %                                                                             %
1717 %                                                                             %
1718 %                                                                             %
1719 %   R e m o t e D i s p l a y C o m m a n d                                   %
1720 %                                                                             %
1721 %                                                                             %
1722 %                                                                             %
1723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1724 %
1725 %  RemoteDisplayCommand() encourages a remote display program to display the
1726 %  specified image filename.
1727 %
1728 %  The format of the RemoteDisplayCommand method is:
1729 %
1730 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1731 %        const char *window,const char *filename,ExceptionInfo *exception)
1732 %
1733 %  A description of each parameter follows:
1734 %
1735 %    o image_info: the image info.
1736 %
1737 %    o window: Specifies the name or id of an X window.
1738 %
1739 %    o filename: the name of the image filename to display.
1740 %
1741 %    o exception: return any errors or warnings in this structure.
1742 %
1743 */
1744 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1745   const char *window,const char *filename,ExceptionInfo *exception)
1746 {
1747   Display
1748     *display;
1749
1750   MagickStatusType
1751     status;
1752
1753   assert(image_info != (const ImageInfo *) NULL);
1754   assert(image_info->signature == MagickSignature);
1755   assert(filename != (char *) NULL);
1756   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1757   display=XOpenDisplay(image_info->server_name);
1758   if (display == (Display *) NULL)
1759     {
1760       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1761         "UnableToOpenXServer","'%s'",XDisplayName(image_info->server_name));
1762       return(MagickFalse);
1763     }
1764   (void) XSetErrorHandler(XError);
1765   status=XRemoteCommand(display,window,filename);
1766   (void) XCloseDisplay(display);
1767   return(status != 0 ? MagickTrue : MagickFalse);
1768 }
1769 \f
1770 /*
1771 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1772 %                                                                             %
1773 %                                                                             %
1774 %                                                                             %
1775 +   X A n n o t a t e E d i t I m a g e                                       %
1776 %                                                                             %
1777 %                                                                             %
1778 %                                                                             %
1779 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1780 %
1781 %  XAnnotateEditImage() annotates the image with text.
1782 %
1783 %  The format of the XAnnotateEditImage method is:
1784 %
1785 %      MagickBooleanType XAnnotateEditImage(Display *display,
1786 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
1787 %        ExceptionInfo *exception)
1788 %
1789 %  A description of each parameter follows:
1790 %
1791 %    o display: Specifies a connection to an X server;  returned from
1792 %      XOpenDisplay.
1793 %
1794 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1795 %
1796 %    o windows: Specifies a pointer to a XWindows structure.
1797 %
1798 %    o image: the image; returned from ReadImage.
1799 %
1800 */
1801
1802 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1803 {
1804   if (x > y)
1805     return(x);
1806   return(y);
1807 }
1808
1809 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1810 {
1811   if (x < y)
1812     return(x);
1813   return(y);
1814 }
1815
1816 static MagickBooleanType XAnnotateEditImage(Display *display,
1817   XResourceInfo *resource_info,XWindows *windows,Image *image,
1818   ExceptionInfo *exception)
1819 {
1820   static const char
1821     *AnnotateMenu[] =
1822     {
1823       "Font Name",
1824       "Font Color",
1825       "Box Color",
1826       "Rotate Text",
1827       "Help",
1828       "Dismiss",
1829       (char *) NULL
1830     },
1831     *TextMenu[] =
1832     {
1833       "Help",
1834       "Apply",
1835       (char *) NULL
1836     };
1837
1838   static const ModeType
1839     AnnotateCommands[] =
1840     {
1841       AnnotateNameCommand,
1842       AnnotateFontColorCommand,
1843       AnnotateBackgroundColorCommand,
1844       AnnotateRotateCommand,
1845       AnnotateHelpCommand,
1846       AnnotateDismissCommand
1847     },
1848     TextCommands[] =
1849     {
1850       TextHelpCommand,
1851       TextApplyCommand
1852     };
1853
1854   static MagickBooleanType
1855     transparent_box = MagickTrue,
1856     transparent_pen = MagickFalse;
1857
1858   static MagickRealType
1859     degrees = 0.0;
1860
1861   static unsigned int
1862     box_id = MaxNumberPens-2,
1863     font_id = 0,
1864     pen_id = 0;
1865
1866   char
1867     command[MaxTextExtent],
1868     text[MaxTextExtent];
1869
1870   const char
1871     *ColorMenu[MaxNumberPens+1];
1872
1873   Cursor
1874     cursor;
1875
1876   GC
1877     annotate_context;
1878
1879   int
1880     id,
1881     pen_number,
1882     status,
1883     x,
1884     y;
1885
1886   KeySym
1887     key_symbol;
1888
1889   register char
1890     *p;
1891
1892   register ssize_t
1893     i;
1894
1895   unsigned int
1896     height,
1897     width;
1898
1899   size_t
1900     state;
1901
1902   XAnnotateInfo
1903     *annotate_info,
1904     *previous_info;
1905
1906   XColor
1907     color;
1908
1909   XFontStruct
1910     *font_info;
1911
1912   XEvent
1913     event,
1914     text_event;
1915
1916   /*
1917     Map Command widget.
1918   */
1919   (void) CloneString(&windows->command.name,"Annotate");
1920   windows->command.data=4;
1921   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1922   (void) XMapRaised(display,windows->command.id);
1923   XClientMessage(display,windows->image.id,windows->im_protocols,
1924     windows->im_update_widget,CurrentTime);
1925   /*
1926     Track pointer until button 1 is pressed.
1927   */
1928   XQueryPosition(display,windows->image.id,&x,&y);
1929   (void) XSelectInput(display,windows->image.id,
1930     windows->image.attributes.event_mask | PointerMotionMask);
1931   cursor=XCreateFontCursor(display,XC_left_side);
1932   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1933   state=DefaultState;
1934   do
1935   {
1936     if (windows->info.mapped != MagickFalse)
1937       {
1938         /*
1939           Display pointer position.
1940         */
1941         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
1942           x+windows->image.x,y+windows->image.y);
1943         XInfoWidget(display,windows,text);
1944       }
1945     /*
1946       Wait for next event.
1947     */
1948     XScreenEvent(display,windows,&event,exception);
1949     if (event.xany.window == windows->command.id)
1950       {
1951         /*
1952           Select a command from the Command widget.
1953         */
1954         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1955         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1956         if (id < 0)
1957           continue;
1958         switch (AnnotateCommands[id])
1959         {
1960           case AnnotateNameCommand:
1961           {
1962             const char
1963               *FontMenu[MaxNumberFonts];
1964
1965             int
1966               font_number;
1967
1968             /*
1969               Initialize menu selections.
1970             */
1971             for (i=0; i < MaxNumberFonts; i++)
1972               FontMenu[i]=resource_info->font_name[i];
1973             FontMenu[MaxNumberFonts-2]="Browser...";
1974             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1975             /*
1976               Select a font name from the pop-up menu.
1977             */
1978             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1979               (const char **) FontMenu,command);
1980             if (font_number < 0)
1981               break;
1982             if (font_number == (MaxNumberFonts-2))
1983               {
1984                 static char
1985                   font_name[MaxTextExtent] = "fixed";
1986
1987                 /*
1988                   Select a font name from a browser.
1989                 */
1990                 resource_info->font_name[font_number]=font_name;
1991                 XFontBrowserWidget(display,windows,"Select",font_name);
1992                 if (*font_name == '\0')
1993                   break;
1994               }
1995             /*
1996               Initialize font info.
1997             */
1998             font_info=XLoadQueryFont(display,resource_info->font_name[
1999               font_number]);
2000             if (font_info == (XFontStruct *) NULL)
2001               {
2002                 XNoticeWidget(display,windows,"Unable to load font:",
2003                   resource_info->font_name[font_number]);
2004                 break;
2005               }
2006             font_id=(unsigned int) font_number;
2007             (void) XFreeFont(display,font_info);
2008             break;
2009           }
2010           case AnnotateFontColorCommand:
2011           {
2012             /*
2013               Initialize menu selections.
2014             */
2015             for (i=0; i < (int) (MaxNumberPens-2); i++)
2016               ColorMenu[i]=resource_info->pen_colors[i];
2017             ColorMenu[MaxNumberPens-2]="transparent";
2018             ColorMenu[MaxNumberPens-1]="Browser...";
2019             ColorMenu[MaxNumberPens]=(const char *) NULL;
2020             /*
2021               Select a pen color from the pop-up menu.
2022             */
2023             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2024               (const char **) ColorMenu,command);
2025             if (pen_number < 0)
2026               break;
2027             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2028               MagickFalse;
2029             if (transparent_pen != MagickFalse)
2030               break;
2031             if (pen_number == (MaxNumberPens-1))
2032               {
2033                 static char
2034                   color_name[MaxTextExtent] = "gray";
2035
2036                 /*
2037                   Select a pen color from a dialog.
2038                 */
2039                 resource_info->pen_colors[pen_number]=color_name;
2040                 XColorBrowserWidget(display,windows,"Select",color_name);
2041                 if (*color_name == '\0')
2042                   break;
2043               }
2044             /*
2045               Set pen color.
2046             */
2047             (void) XParseColor(display,windows->map_info->colormap,
2048               resource_info->pen_colors[pen_number],&color);
2049             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2050               (unsigned int) MaxColors,&color);
2051             windows->pixel_info->pen_colors[pen_number]=color;
2052             pen_id=(unsigned int) pen_number;
2053             break;
2054           }
2055           case AnnotateBackgroundColorCommand:
2056           {
2057             /*
2058               Initialize menu selections.
2059             */
2060             for (i=0; i < (int) (MaxNumberPens-2); i++)
2061               ColorMenu[i]=resource_info->pen_colors[i];
2062             ColorMenu[MaxNumberPens-2]="transparent";
2063             ColorMenu[MaxNumberPens-1]="Browser...";
2064             ColorMenu[MaxNumberPens]=(const char *) NULL;
2065             /*
2066               Select a pen color from the pop-up menu.
2067             */
2068             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2069               (const char **) ColorMenu,command);
2070             if (pen_number < 0)
2071               break;
2072             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2073               MagickFalse;
2074             if (transparent_box != MagickFalse)
2075               break;
2076             if (pen_number == (MaxNumberPens-1))
2077               {
2078                 static char
2079                   color_name[MaxTextExtent] = "gray";
2080
2081                 /*
2082                   Select a pen color from a dialog.
2083                 */
2084                 resource_info->pen_colors[pen_number]=color_name;
2085                 XColorBrowserWidget(display,windows,"Select",color_name);
2086                 if (*color_name == '\0')
2087                   break;
2088               }
2089             /*
2090               Set pen color.
2091             */
2092             (void) XParseColor(display,windows->map_info->colormap,
2093               resource_info->pen_colors[pen_number],&color);
2094             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2095               (unsigned int) MaxColors,&color);
2096             windows->pixel_info->pen_colors[pen_number]=color;
2097             box_id=(unsigned int) pen_number;
2098             break;
2099           }
2100           case AnnotateRotateCommand:
2101           {
2102             int
2103               entry;
2104
2105             static char
2106               angle[MaxTextExtent] = "30.0";
2107
2108             static const char
2109               *RotateMenu[] =
2110               {
2111                 "-90",
2112                 "-45",
2113                 "-30",
2114                 "0",
2115                 "30",
2116                 "45",
2117                 "90",
2118                 "180",
2119                 "Dialog...",
2120                 (char *) NULL,
2121               };
2122
2123             /*
2124               Select a command from the pop-up menu.
2125             */
2126             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2127               command);
2128             if (entry < 0)
2129               break;
2130             if (entry != 8)
2131               {
2132                 degrees=StringToDouble(RotateMenu[entry],(char **) NULL);
2133                 break;
2134               }
2135             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2136               angle);
2137             if (*angle == '\0')
2138               break;
2139             degrees=StringToDouble(angle,(char **) NULL);
2140             break;
2141           }
2142           case AnnotateHelpCommand:
2143           {
2144             XTextViewWidget(display,resource_info,windows,MagickFalse,
2145               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2146             break;
2147           }
2148           case AnnotateDismissCommand:
2149           {
2150             /*
2151               Prematurely exit.
2152             */
2153             state|=EscapeState;
2154             state|=ExitState;
2155             break;
2156           }
2157           default:
2158             break;
2159         }
2160         continue;
2161       }
2162     switch (event.type)
2163     {
2164       case ButtonPress:
2165       {
2166         if (event.xbutton.button != Button1)
2167           break;
2168         if (event.xbutton.window != windows->image.id)
2169           break;
2170         /*
2171           Change to text entering mode.
2172         */
2173         x=event.xbutton.x;
2174         y=event.xbutton.y;
2175         state|=ExitState;
2176         break;
2177       }
2178       case ButtonRelease:
2179         break;
2180       case Expose:
2181         break;
2182       case KeyPress:
2183       {
2184         if (event.xkey.window != windows->image.id)
2185           break;
2186         /*
2187           Respond to a user key press.
2188         */
2189         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2190           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2191         switch ((int) key_symbol)
2192         {
2193           case XK_Escape:
2194           case XK_F20:
2195           {
2196             /*
2197               Prematurely exit.
2198             */
2199             state|=EscapeState;
2200             state|=ExitState;
2201             break;
2202           }
2203           case XK_F1:
2204           case XK_Help:
2205           {
2206             XTextViewWidget(display,resource_info,windows,MagickFalse,
2207               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2208             break;
2209           }
2210           default:
2211           {
2212             (void) XBell(display,0);
2213             break;
2214           }
2215         }
2216         break;
2217       }
2218       case MotionNotify:
2219       {
2220         /*
2221           Map and unmap Info widget as cursor crosses its boundaries.
2222         */
2223         x=event.xmotion.x;
2224         y=event.xmotion.y;
2225         if (windows->info.mapped != MagickFalse)
2226           {
2227             if ((x < (int) (windows->info.x+windows->info.width)) &&
2228                 (y < (int) (windows->info.y+windows->info.height)))
2229               (void) XWithdrawWindow(display,windows->info.id,
2230                 windows->info.screen);
2231           }
2232         else
2233           if ((x > (int) (windows->info.x+windows->info.width)) ||
2234               (y > (int) (windows->info.y+windows->info.height)))
2235             (void) XMapWindow(display,windows->info.id);
2236         break;
2237       }
2238       default:
2239         break;
2240     }
2241   } while ((state & ExitState) == 0);
2242   (void) XSelectInput(display,windows->image.id,
2243     windows->image.attributes.event_mask);
2244   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2245   if ((state & EscapeState) != 0)
2246     return(MagickTrue);
2247   /*
2248     Set font info and check boundary conditions.
2249   */
2250   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2251   if (font_info == (XFontStruct *) NULL)
2252     {
2253       XNoticeWidget(display,windows,"Unable to load font:",
2254         resource_info->font_name[font_id]);
2255       font_info=windows->font_info;
2256     }
2257   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2258     x=(int) windows->image.width-font_info->max_bounds.width;
2259   if (y < (int) (font_info->ascent+font_info->descent))
2260     y=(int) font_info->ascent+font_info->descent;
2261   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2262       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2263     return(MagickFalse);
2264   /*
2265     Initialize annotate structure.
2266   */
2267   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2268   if (annotate_info == (XAnnotateInfo *) NULL)
2269     return(MagickFalse);
2270   XGetAnnotateInfo(annotate_info);
2271   annotate_info->x=x;
2272   annotate_info->y=y;
2273   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2274     annotate_info->stencil=OpaqueStencil;
2275   else
2276     if (transparent_box == MagickFalse)
2277       annotate_info->stencil=BackgroundStencil;
2278     else
2279       annotate_info->stencil=ForegroundStencil;
2280   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2281   annotate_info->degrees=degrees;
2282   annotate_info->font_info=font_info;
2283   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2284     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2285     sizeof(*annotate_info->text));
2286   if (annotate_info->text == (char *) NULL)
2287     return(MagickFalse);
2288   /*
2289     Create cursor and set graphic context.
2290   */
2291   cursor=XCreateFontCursor(display,XC_pencil);
2292   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2293   annotate_context=windows->image.annotate_context;
2294   (void) XSetFont(display,annotate_context,font_info->fid);
2295   (void) XSetBackground(display,annotate_context,
2296     windows->pixel_info->pen_colors[box_id].pixel);
2297   (void) XSetForeground(display,annotate_context,
2298     windows->pixel_info->pen_colors[pen_id].pixel);
2299   /*
2300     Begin annotating the image with text.
2301   */
2302   (void) CloneString(&windows->command.name,"Text");
2303   windows->command.data=0;
2304   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2305   state=DefaultState;
2306   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2307   text_event.xexpose.width=(int) font_info->max_bounds.width;
2308   text_event.xexpose.height=font_info->max_bounds.ascent+
2309     font_info->max_bounds.descent;
2310   p=annotate_info->text;
2311   do
2312   {
2313     /*
2314       Display text cursor.
2315     */
2316     *p='\0';
2317     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2318     /*
2319       Wait for next event.
2320     */
2321     XScreenEvent(display,windows,&event,exception);
2322     if (event.xany.window == windows->command.id)
2323       {
2324         /*
2325           Select a command from the Command widget.
2326         */
2327         (void) XSetBackground(display,annotate_context,
2328           windows->pixel_info->background_color.pixel);
2329         (void) XSetForeground(display,annotate_context,
2330           windows->pixel_info->foreground_color.pixel);
2331         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2332         (void) XSetBackground(display,annotate_context,
2333           windows->pixel_info->pen_colors[box_id].pixel);
2334         (void) XSetForeground(display,annotate_context,
2335           windows->pixel_info->pen_colors[pen_id].pixel);
2336         if (id < 0)
2337           continue;
2338         switch (TextCommands[id])
2339         {
2340           case TextHelpCommand:
2341           {
2342             XTextViewWidget(display,resource_info,windows,MagickFalse,
2343               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2344             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2345             break;
2346           }
2347           case TextApplyCommand:
2348           {
2349             /*
2350               Finished annotating.
2351             */
2352             annotate_info->width=(unsigned int) XTextWidth(font_info,
2353               annotate_info->text,(int) strlen(annotate_info->text));
2354             XRefreshWindow(display,&windows->image,&text_event);
2355             state|=ExitState;
2356             break;
2357           }
2358           default:
2359             break;
2360         }
2361         continue;
2362       }
2363     /*
2364       Erase text cursor.
2365     */
2366     text_event.xexpose.x=x;
2367     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2368     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2369       (unsigned int) text_event.xexpose.width,(unsigned int)
2370       text_event.xexpose.height,MagickFalse);
2371     XRefreshWindow(display,&windows->image,&text_event);
2372     switch (event.type)
2373     {
2374       case ButtonPress:
2375       {
2376         if (event.xbutton.window != windows->image.id)
2377           break;
2378         if (event.xbutton.button == Button2)
2379           {
2380             /*
2381               Request primary selection.
2382             */
2383             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2384               windows->image.id,CurrentTime);
2385             break;
2386           }
2387         break;
2388       }
2389       case Expose:
2390       {
2391         if (event.xexpose.count == 0)
2392           {
2393             XAnnotateInfo
2394               *text_info;
2395
2396             /*
2397               Refresh Image window.
2398             */
2399             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2400             text_info=annotate_info;
2401             while (text_info != (XAnnotateInfo *) NULL)
2402             {
2403               if (annotate_info->stencil == ForegroundStencil)
2404                 (void) XDrawString(display,windows->image.id,annotate_context,
2405                   text_info->x,text_info->y,text_info->text,
2406                   (int) strlen(text_info->text));
2407               else
2408                 (void) XDrawImageString(display,windows->image.id,
2409                   annotate_context,text_info->x,text_info->y,text_info->text,
2410                   (int) strlen(text_info->text));
2411               text_info=text_info->previous;
2412             }
2413             (void) XDrawString(display,windows->image.id,annotate_context,
2414               x,y,"_",1);
2415           }
2416         break;
2417       }
2418       case KeyPress:
2419       {
2420         int
2421           length;
2422
2423         if (event.xkey.window != windows->image.id)
2424           break;
2425         /*
2426           Respond to a user key press.
2427         */
2428         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2429           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2430         *(command+length)='\0';
2431         if (((event.xkey.state & ControlMask) != 0) ||
2432             ((event.xkey.state & Mod1Mask) != 0))
2433           state|=ModifierState;
2434         if ((state & ModifierState) != 0)
2435           switch ((int) key_symbol)
2436           {
2437             case XK_u:
2438             case XK_U:
2439             {
2440               key_symbol=DeleteCommand;
2441               break;
2442             }
2443             default:
2444               break;
2445           }
2446         switch ((int) key_symbol)
2447         {
2448           case XK_BackSpace:
2449           {
2450             /*
2451               Erase one character.
2452             */
2453             if (p == annotate_info->text)
2454               {
2455                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2456                   break;
2457                 else
2458                   {
2459                     /*
2460                       Go to end of the previous line of text.
2461                     */
2462                     annotate_info=annotate_info->previous;
2463                     p=annotate_info->text;
2464                     x=annotate_info->x+annotate_info->width;
2465                     y=annotate_info->y;
2466                     if (annotate_info->width != 0)
2467                       p+=strlen(annotate_info->text);
2468                     break;
2469                   }
2470               }
2471             p--;
2472             x-=XTextWidth(font_info,p,1);
2473             text_event.xexpose.x=x;
2474             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2475             XRefreshWindow(display,&windows->image,&text_event);
2476             break;
2477           }
2478           case XK_bracketleft:
2479           {
2480             key_symbol=XK_Escape;
2481             break;
2482           }
2483           case DeleteCommand:
2484           {
2485             /*
2486               Erase the entire line of text.
2487             */
2488             while (p != annotate_info->text)
2489             {
2490               p--;
2491               x-=XTextWidth(font_info,p,1);
2492               text_event.xexpose.x=x;
2493               XRefreshWindow(display,&windows->image,&text_event);
2494             }
2495             break;
2496           }
2497           case XK_Escape:
2498           case XK_F20:
2499           {
2500             /*
2501               Finished annotating.
2502             */
2503             annotate_info->width=(unsigned int) XTextWidth(font_info,
2504               annotate_info->text,(int) strlen(annotate_info->text));
2505             XRefreshWindow(display,&windows->image,&text_event);
2506             state|=ExitState;
2507             break;
2508           }
2509           default:
2510           {
2511             /*
2512               Draw a single character on the Image window.
2513             */
2514             if ((state & ModifierState) != 0)
2515               break;
2516             if (*command == '\0')
2517               break;
2518             *p=(*command);
2519             if (annotate_info->stencil == ForegroundStencil)
2520               (void) XDrawString(display,windows->image.id,annotate_context,
2521                 x,y,p,1);
2522             else
2523               (void) XDrawImageString(display,windows->image.id,
2524                 annotate_context,x,y,p,1);
2525             x+=XTextWidth(font_info,p,1);
2526             p++;
2527             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2528               break;
2529           }
2530           case XK_Return:
2531           case XK_KP_Enter:
2532           {
2533             /*
2534               Advance to the next line of text.
2535             */
2536             *p='\0';
2537             annotate_info->width=(unsigned int) XTextWidth(font_info,
2538               annotate_info->text,(int) strlen(annotate_info->text));
2539             if (annotate_info->next != (XAnnotateInfo *) NULL)
2540               {
2541                 /*
2542                   Line of text already exists.
2543                 */
2544                 annotate_info=annotate_info->next;
2545                 x=annotate_info->x;
2546                 y=annotate_info->y;
2547                 p=annotate_info->text;
2548                 break;
2549               }
2550             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2551               sizeof(*annotate_info->next));
2552             if (annotate_info->next == (XAnnotateInfo *) NULL)
2553               return(MagickFalse);
2554             *annotate_info->next=(*annotate_info);
2555             annotate_info->next->previous=annotate_info;
2556             annotate_info=annotate_info->next;
2557             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2558               windows->image.width/MagickMax((ssize_t)
2559               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2560             if (annotate_info->text == (char *) NULL)
2561               return(MagickFalse);
2562             annotate_info->y+=annotate_info->height;
2563             if (annotate_info->y > (int) windows->image.height)
2564               annotate_info->y=(int) annotate_info->height;
2565             annotate_info->next=(XAnnotateInfo *) NULL;
2566             x=annotate_info->x;
2567             y=annotate_info->y;
2568             p=annotate_info->text;
2569             break;
2570           }
2571         }
2572         break;
2573       }
2574       case KeyRelease:
2575       {
2576         /*
2577           Respond to a user key release.
2578         */
2579         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2580           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2581         state&=(~ModifierState);
2582         break;
2583       }
2584       case SelectionNotify:
2585       {
2586         Atom
2587           type;
2588
2589         int
2590           format;
2591
2592         unsigned char
2593           *data;
2594
2595         unsigned long
2596           after,
2597           length;
2598
2599         /*
2600           Obtain response from primary selection.
2601         */
2602         if (event.xselection.property == (Atom) None)
2603           break;
2604         status=XGetWindowProperty(display,event.xselection.requestor,
2605           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2606           &type,&format,&length,&after,&data);
2607         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2608             (length == 0))
2609           break;
2610         /*
2611           Annotate Image window with primary selection.
2612         */
2613         for (i=0; i < (ssize_t) length; i++)
2614         {
2615           if ((char) data[i] != '\n')
2616             {
2617               /*
2618                 Draw a single character on the Image window.
2619               */
2620               *p=(char) data[i];
2621               (void) XDrawString(display,windows->image.id,annotate_context,
2622                 x,y,p,1);
2623               x+=XTextWidth(font_info,p,1);
2624               p++;
2625               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2626                 continue;
2627             }
2628           /*
2629             Advance to the next line of text.
2630           */
2631           *p='\0';
2632           annotate_info->width=(unsigned int) XTextWidth(font_info,
2633             annotate_info->text,(int) strlen(annotate_info->text));
2634           if (annotate_info->next != (XAnnotateInfo *) NULL)
2635             {
2636               /*
2637                 Line of text already exists.
2638               */
2639               annotate_info=annotate_info->next;
2640               x=annotate_info->x;
2641               y=annotate_info->y;
2642               p=annotate_info->text;
2643               continue;
2644             }
2645           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2646             sizeof(*annotate_info->next));
2647           if (annotate_info->next == (XAnnotateInfo *) NULL)
2648             return(MagickFalse);
2649           *annotate_info->next=(*annotate_info);
2650           annotate_info->next->previous=annotate_info;
2651           annotate_info=annotate_info->next;
2652           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2653             windows->image.width/MagickMax((ssize_t)
2654             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2655           if (annotate_info->text == (char *) NULL)
2656             return(MagickFalse);
2657           annotate_info->y+=annotate_info->height;
2658           if (annotate_info->y > (int) windows->image.height)
2659             annotate_info->y=(int) annotate_info->height;
2660           annotate_info->next=(XAnnotateInfo *) NULL;
2661           x=annotate_info->x;
2662           y=annotate_info->y;
2663           p=annotate_info->text;
2664         }
2665         (void) XFree((void *) data);
2666         break;
2667       }
2668       default:
2669         break;
2670     }
2671   } while ((state & ExitState) == 0);
2672   (void) XFreeCursor(display,cursor);
2673   /*
2674     Annotation is relative to image configuration.
2675   */
2676   width=(unsigned int) image->columns;
2677   height=(unsigned int) image->rows;
2678   x=0;
2679   y=0;
2680   if (windows->image.crop_geometry != (char *) NULL)
2681     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2682   /*
2683     Initialize annotated image.
2684   */
2685   XSetCursorState(display,windows,MagickTrue);
2686   XCheckRefreshWindows(display,windows);
2687   while (annotate_info != (XAnnotateInfo *) NULL)
2688   {
2689     if (annotate_info->width == 0)
2690       {
2691         /*
2692           No text on this line--  go to the next line of text.
2693         */
2694         previous_info=annotate_info->previous;
2695         annotate_info->text=(char *)
2696           RelinquishMagickMemory(annotate_info->text);
2697         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2698         annotate_info=previous_info;
2699         continue;
2700       }
2701     /*
2702       Determine pixel index for box and pen color.
2703     */
2704     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2705     if (windows->pixel_info->colors != 0)
2706       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2707         if (windows->pixel_info->pixels[i] ==
2708             windows->pixel_info->pen_colors[box_id].pixel)
2709           {
2710             windows->pixel_info->box_index=(unsigned short) i;
2711             break;
2712           }
2713     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2714     if (windows->pixel_info->colors != 0)
2715       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2716         if (windows->pixel_info->pixels[i] ==
2717             windows->pixel_info->pen_colors[pen_id].pixel)
2718           {
2719             windows->pixel_info->pen_index=(unsigned short) i;
2720             break;
2721           }
2722     /*
2723       Define the annotate geometry string.
2724     */
2725     annotate_info->x=(int)
2726       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2727     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2728       windows->image.y)/windows->image.ximage->height;
2729     (void) FormatLocaleString(annotate_info->geometry,MaxTextExtent,
2730       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2731       height*annotate_info->height/windows->image.ximage->height,
2732       annotate_info->x+x,annotate_info->y+y);
2733     /*
2734       Annotate image with text.
2735     */
2736     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image,
2737       exception);
2738     if (status == 0)
2739       return(MagickFalse);
2740     /*
2741       Free up memory.
2742     */
2743     previous_info=annotate_info->previous;
2744     annotate_info->text=DestroyString(annotate_info->text);
2745     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2746     annotate_info=previous_info;
2747   }
2748   (void) XSetForeground(display,annotate_context,
2749     windows->pixel_info->foreground_color.pixel);
2750   (void) XSetBackground(display,annotate_context,
2751     windows->pixel_info->background_color.pixel);
2752   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2753   XSetCursorState(display,windows,MagickFalse);
2754   (void) XFreeFont(display,font_info);
2755   /*
2756     Update image configuration.
2757   */
2758   XConfigureImageColormap(display,resource_info,windows,image,exception);
2759   (void) XConfigureImage(display,resource_info,windows,image,exception);
2760   return(MagickTrue);
2761 }
2762 \f
2763 /*
2764 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2765 %                                                                             %
2766 %                                                                             %
2767 %                                                                             %
2768 +   X B a c k g r o u n d I m a g e                                           %
2769 %                                                                             %
2770 %                                                                             %
2771 %                                                                             %
2772 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2773 %
2774 %  XBackgroundImage() displays the image in the background of a window.
2775 %
2776 %  The format of the XBackgroundImage method is:
2777 %
2778 %      MagickBooleanType XBackgroundImage(Display *display,
2779 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
2780 %        ExceptionInfo *exception)
2781 %
2782 %  A description of each parameter follows:
2783 %
2784 %    o display: Specifies a connection to an X server; returned from
2785 %      XOpenDisplay.
2786 %
2787 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2788 %
2789 %    o windows: Specifies a pointer to a XWindows structure.
2790 %
2791 %    o image: the image.
2792 %
2793 %    o exception: return any errors or warnings in this structure.
2794 %
2795 */
2796 static MagickBooleanType XBackgroundImage(Display *display,
2797   XResourceInfo *resource_info,XWindows *windows,Image **image,
2798   ExceptionInfo *exception)
2799 {
2800 #define BackgroundImageTag  "Background/Image"
2801
2802   int
2803     status;
2804
2805   static char
2806     window_id[MaxTextExtent] = "root";
2807
2808   XResourceInfo
2809     background_resources;
2810
2811   /*
2812     Put image in background.
2813   */
2814   status=XDialogWidget(display,windows,"Background",
2815     "Enter window id (id 0x00 selects window with pointer):",window_id);
2816   if (*window_id == '\0')
2817     return(MagickFalse);
2818   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
2819     exception);
2820   XInfoWidget(display,windows,BackgroundImageTag);
2821   XSetCursorState(display,windows,MagickTrue);
2822   XCheckRefreshWindows(display,windows);
2823   background_resources=(*resource_info);
2824   background_resources.window_id=window_id;
2825   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2826   status=XDisplayBackgroundImage(display,&background_resources,*image,
2827     exception);
2828   if (status != MagickFalse)
2829     XClientMessage(display,windows->image.id,windows->im_protocols,
2830       windows->im_retain_colors,CurrentTime);
2831   XSetCursorState(display,windows,MagickFalse);
2832   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image,
2833     exception);
2834   return(MagickTrue);
2835 }
2836 \f
2837 /*
2838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2839 %                                                                             %
2840 %                                                                             %
2841 %                                                                             %
2842 +   X C h o p I m a g e                                                       %
2843 %                                                                             %
2844 %                                                                             %
2845 %                                                                             %
2846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2847 %
2848 %  XChopImage() chops the X image.
2849 %
2850 %  The format of the XChopImage method is:
2851 %
2852 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2853 %      XWindows *windows,Image **image,ExceptionInfo *exception)
2854 %
2855 %  A description of each parameter follows:
2856 %
2857 %    o display: Specifies a connection to an X server; returned from
2858 %      XOpenDisplay.
2859 %
2860 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2861 %
2862 %    o windows: Specifies a pointer to a XWindows structure.
2863 %
2864 %    o image: the image.
2865 %
2866 %    o exception: return any errors or warnings in this structure.
2867 %
2868 */
2869 static MagickBooleanType XChopImage(Display *display,
2870   XResourceInfo *resource_info,XWindows *windows,Image **image,
2871   ExceptionInfo *exception)
2872 {
2873   static const char
2874     *ChopMenu[] =
2875     {
2876       "Direction",
2877       "Help",
2878       "Dismiss",
2879       (char *) NULL
2880     };
2881
2882   static ModeType
2883     direction = HorizontalChopCommand;
2884
2885   static const ModeType
2886     ChopCommands[] =
2887     {
2888       ChopDirectionCommand,
2889       ChopHelpCommand,
2890       ChopDismissCommand
2891     },
2892     DirectionCommands[] =
2893     {
2894       HorizontalChopCommand,
2895       VerticalChopCommand
2896     };
2897
2898   char
2899     text[MaxTextExtent];
2900
2901   Image
2902     *chop_image;
2903
2904   int
2905     id,
2906     x,
2907     y;
2908
2909   MagickRealType
2910     scale_factor;
2911
2912   RectangleInfo
2913     chop_info;
2914
2915   unsigned int
2916     distance,
2917     height,
2918     width;
2919
2920   size_t
2921     state;
2922
2923   XEvent
2924     event;
2925
2926   XSegment
2927     segment_info;
2928
2929   /*
2930     Map Command widget.
2931   */
2932   (void) CloneString(&windows->command.name,"Chop");
2933   windows->command.data=1;
2934   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2935   (void) XMapRaised(display,windows->command.id);
2936   XClientMessage(display,windows->image.id,windows->im_protocols,
2937     windows->im_update_widget,CurrentTime);
2938   /*
2939     Track pointer until button 1 is pressed.
2940   */
2941   XQueryPosition(display,windows->image.id,&x,&y);
2942   (void) XSelectInput(display,windows->image.id,
2943     windows->image.attributes.event_mask | PointerMotionMask);
2944   state=DefaultState;
2945   do
2946   {
2947     if (windows->info.mapped != MagickFalse)
2948       {
2949         /*
2950           Display pointer position.
2951         */
2952         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
2953           x+windows->image.x,y+windows->image.y);
2954         XInfoWidget(display,windows,text);
2955       }
2956     /*
2957       Wait for next event.
2958     */
2959     XScreenEvent(display,windows,&event,exception);
2960     if (event.xany.window == windows->command.id)
2961       {
2962         /*
2963           Select a command from the Command widget.
2964         */
2965         id=XCommandWidget(display,windows,ChopMenu,&event);
2966         if (id < 0)
2967           continue;
2968         switch (ChopCommands[id])
2969         {
2970           case ChopDirectionCommand:
2971           {
2972             char
2973               command[MaxTextExtent];
2974
2975             static const char
2976               *Directions[] =
2977               {
2978                 "horizontal",
2979                 "vertical",
2980                 (char *) NULL,
2981               };
2982
2983             /*
2984               Select a command from the pop-up menu.
2985             */
2986             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2987             if (id >= 0)
2988               direction=DirectionCommands[id];
2989             break;
2990           }
2991           case ChopHelpCommand:
2992           {
2993             XTextViewWidget(display,resource_info,windows,MagickFalse,
2994               "Help Viewer - Image Chop",ImageChopHelp);
2995             break;
2996           }
2997           case ChopDismissCommand:
2998           {
2999             /*
3000               Prematurely exit.
3001             */
3002             state|=EscapeState;
3003             state|=ExitState;
3004             break;
3005           }
3006           default:
3007             break;
3008         }
3009         continue;
3010       }
3011     switch (event.type)
3012     {
3013       case ButtonPress:
3014       {
3015         if (event.xbutton.button != Button1)
3016           break;
3017         if (event.xbutton.window != windows->image.id)
3018           break;
3019         /*
3020           User has committed to start point of chopping line.
3021         */
3022         segment_info.x1=(short int) event.xbutton.x;
3023         segment_info.x2=(short int) event.xbutton.x;
3024         segment_info.y1=(short int) event.xbutton.y;
3025         segment_info.y2=(short int) event.xbutton.y;
3026         state|=ExitState;
3027         break;
3028       }
3029       case ButtonRelease:
3030         break;
3031       case Expose:
3032         break;
3033       case KeyPress:
3034       {
3035         char
3036           command[MaxTextExtent];
3037
3038         KeySym
3039           key_symbol;
3040
3041         if (event.xkey.window != windows->image.id)
3042           break;
3043         /*
3044           Respond to a user key press.
3045         */
3046         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3047           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3048         switch ((int) key_symbol)
3049         {
3050           case XK_Escape:
3051           case XK_F20:
3052           {
3053             /*
3054               Prematurely exit.
3055             */
3056             state|=EscapeState;
3057             state|=ExitState;
3058             break;
3059           }
3060           case XK_F1:
3061           case XK_Help:
3062           {
3063             (void) XSetFunction(display,windows->image.highlight_context,
3064               GXcopy);
3065             XTextViewWidget(display,resource_info,windows,MagickFalse,
3066               "Help Viewer - Image Chop",ImageChopHelp);
3067             (void) XSetFunction(display,windows->image.highlight_context,
3068               GXinvert);
3069             break;
3070           }
3071           default:
3072           {
3073             (void) XBell(display,0);
3074             break;
3075           }
3076         }
3077         break;
3078       }
3079       case MotionNotify:
3080       {
3081         /*
3082           Map and unmap Info widget as text cursor crosses its boundaries.
3083         */
3084         x=event.xmotion.x;
3085         y=event.xmotion.y;
3086         if (windows->info.mapped != MagickFalse)
3087           {
3088             if ((x < (int) (windows->info.x+windows->info.width)) &&
3089                 (y < (int) (windows->info.y+windows->info.height)))
3090               (void) XWithdrawWindow(display,windows->info.id,
3091                 windows->info.screen);
3092           }
3093         else
3094           if ((x > (int) (windows->info.x+windows->info.width)) ||
3095               (y > (int) (windows->info.y+windows->info.height)))
3096             (void) XMapWindow(display,windows->info.id);
3097       }
3098     }
3099   } while ((state & ExitState) == 0);
3100   (void) XSelectInput(display,windows->image.id,
3101     windows->image.attributes.event_mask);
3102   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3103   if ((state & EscapeState) != 0)
3104     return(MagickTrue);
3105   /*
3106     Draw line as pointer moves until the mouse button is released.
3107   */
3108   chop_info.width=0;
3109   chop_info.height=0;
3110   chop_info.x=0;
3111   chop_info.y=0;
3112   distance=0;
3113   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3114   state=DefaultState;
3115   do
3116   {
3117     if (distance > 9)
3118       {
3119         /*
3120           Display info and draw chopping line.
3121         */
3122         if (windows->info.mapped == MagickFalse)
3123           (void) XMapWindow(display,windows->info.id);
3124         (void) FormatLocaleString(text,MaxTextExtent,
3125           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3126           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3127         XInfoWidget(display,windows,text);
3128         XHighlightLine(display,windows->image.id,
3129           windows->image.highlight_context,&segment_info);
3130       }
3131     else
3132       if (windows->info.mapped != MagickFalse)
3133         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3134     /*
3135       Wait for next event.
3136     */
3137     XScreenEvent(display,windows,&event,exception);
3138     if (distance > 9)
3139       XHighlightLine(display,windows->image.id,
3140         windows->image.highlight_context,&segment_info);
3141     switch (event.type)
3142     {
3143       case ButtonPress:
3144       {
3145         segment_info.x2=(short int) event.xmotion.x;
3146         segment_info.y2=(short int) event.xmotion.y;
3147         break;
3148       }
3149       case ButtonRelease:
3150       {
3151         /*
3152           User has committed to chopping line.
3153         */
3154         segment_info.x2=(short int) event.xbutton.x;
3155         segment_info.y2=(short int) event.xbutton.y;
3156         state|=ExitState;
3157         break;
3158       }
3159       case Expose:
3160         break;
3161       case MotionNotify:
3162       {
3163         segment_info.x2=(short int) event.xmotion.x;
3164         segment_info.y2=(short int) event.xmotion.y;
3165       }
3166       default:
3167         break;
3168     }
3169     /*
3170       Check boundary conditions.
3171     */
3172     if (segment_info.x2 < 0)
3173       segment_info.x2=0;
3174     else
3175       if (segment_info.x2 > windows->image.ximage->width)
3176         segment_info.x2=windows->image.ximage->width;
3177     if (segment_info.y2 < 0)
3178       segment_info.y2=0;
3179     else
3180       if (segment_info.y2 > windows->image.ximage->height)
3181         segment_info.y2=windows->image.ximage->height;
3182     distance=(unsigned int)
3183       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3184        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3185     /*
3186       Compute chopping geometry.
3187     */
3188     if (direction == HorizontalChopCommand)
3189       {
3190         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3191         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3192         chop_info.height=0;
3193         chop_info.y=0;
3194         if (segment_info.x1 > (int) segment_info.x2)
3195           {
3196             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3197             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3198           }
3199       }
3200     else
3201       {
3202         chop_info.width=0;
3203         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3204         chop_info.x=0;
3205         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3206         if (segment_info.y1 > segment_info.y2)
3207           {
3208             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3209             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3210           }
3211       }
3212   } while ((state & ExitState) == 0);
3213   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3214   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3215   if (distance <= 9)
3216     return(MagickTrue);
3217   /*
3218     Image chopping is relative to image configuration.
3219   */
3220   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
3221     exception);
3222   XSetCursorState(display,windows,MagickTrue);
3223   XCheckRefreshWindows(display,windows);
3224   windows->image.window_changes.width=windows->image.ximage->width-
3225     (unsigned int) chop_info.width;
3226   windows->image.window_changes.height=windows->image.ximage->height-
3227     (unsigned int) chop_info.height;
3228   width=(unsigned int) (*image)->columns;
3229   height=(unsigned int) (*image)->rows;
3230   x=0;
3231   y=0;
3232   if (windows->image.crop_geometry != (char *) NULL)
3233     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3234   scale_factor=(MagickRealType) width/windows->image.ximage->width;
3235   chop_info.x+=x;
3236   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3237   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3238   scale_factor=(MagickRealType) height/windows->image.ximage->height;
3239   chop_info.y+=y;
3240   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3241   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3242   /*
3243     Chop image.
3244   */
3245   chop_image=ChopImage(*image,&chop_info,exception);
3246   XSetCursorState(display,windows,MagickFalse);
3247   if (chop_image == (Image *) NULL)
3248     return(MagickFalse);
3249   *image=DestroyImage(*image);
3250   *image=chop_image;
3251   /*
3252     Update image configuration.
3253   */
3254   XConfigureImageColormap(display,resource_info,windows,*image,exception);
3255   (void) XConfigureImage(display,resource_info,windows,*image,exception);
3256   return(MagickTrue);
3257 }
3258 \f
3259 /*
3260 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3261 %                                                                             %
3262 %                                                                             %
3263 %                                                                             %
3264 +   X C o l o r E d i t I m a g e                                             %
3265 %                                                                             %
3266 %                                                                             %
3267 %                                                                             %
3268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3269 %
3270 %  XColorEditImage() allows the user to interactively change the color of one
3271 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3272 %
3273 %  The format of the XColorEditImage method is:
3274 %
3275 %      MagickBooleanType XColorEditImage(Display *display,
3276 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
3277 %          ExceptionInfo *exception)
3278 %
3279 %  A description of each parameter follows:
3280 %
3281 %    o display: Specifies a connection to an X server;  returned from
3282 %      XOpenDisplay.
3283 %
3284 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3285 %
3286 %    o windows: Specifies a pointer to a XWindows structure.
3287 %
3288 %    o image: the image; returned from ReadImage.
3289 %
3290 %    o exception: return any errors or warnings in this structure.
3291 %
3292 */
3293 static MagickBooleanType XColorEditImage(Display *display,
3294   XResourceInfo *resource_info,XWindows *windows,Image **image,
3295   ExceptionInfo *exception)
3296 {
3297   static const char
3298     *ColorEditMenu[] =
3299     {
3300       "Method",
3301       "Pixel Color",
3302       "Border Color",
3303       "Fuzz",
3304       "Undo",
3305       "Help",
3306       "Dismiss",
3307       (char *) NULL
3308     };
3309
3310   static const ModeType
3311     ColorEditCommands[] =
3312     {
3313       ColorEditMethodCommand,
3314       ColorEditColorCommand,
3315       ColorEditBorderCommand,
3316       ColorEditFuzzCommand,
3317       ColorEditUndoCommand,
3318       ColorEditHelpCommand,
3319       ColorEditDismissCommand
3320     };
3321
3322   static PaintMethod
3323     method = PointMethod;
3324
3325   static unsigned int
3326     pen_id = 0;
3327
3328   static XColor
3329     border_color = { 0, 0, 0, 0, 0, 0 };
3330
3331   char
3332     command[MaxTextExtent],
3333     text[MaxTextExtent];
3334
3335   Cursor
3336     cursor;
3337
3338   int
3339     entry,
3340     id,
3341     x,
3342     x_offset,
3343     y,
3344     y_offset;
3345
3346   register Quantum
3347     *q;
3348
3349   register ssize_t
3350     i;
3351
3352   unsigned int
3353     height,
3354     width;
3355
3356   size_t
3357     state;
3358
3359   XColor
3360     color;
3361
3362   XEvent
3363     event;
3364
3365   /*
3366     Map Command widget.
3367   */
3368   (void) CloneString(&windows->command.name,"Color Edit");
3369   windows->command.data=4;
3370   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3371   (void) XMapRaised(display,windows->command.id);
3372   XClientMessage(display,windows->image.id,windows->im_protocols,
3373     windows->im_update_widget,CurrentTime);
3374   /*
3375     Make cursor.
3376   */
3377   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3378     resource_info->background_color,resource_info->foreground_color);
3379   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3380   /*
3381     Track pointer until button 1 is pressed.
3382   */
3383   XQueryPosition(display,windows->image.id,&x,&y);
3384   (void) XSelectInput(display,windows->image.id,
3385     windows->image.attributes.event_mask | PointerMotionMask);
3386   state=DefaultState;
3387   do
3388   {
3389     if (windows->info.mapped != MagickFalse)
3390       {
3391         /*
3392           Display pointer position.
3393         */
3394         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
3395           x+windows->image.x,y+windows->image.y);
3396         XInfoWidget(display,windows,text);
3397       }
3398     /*
3399       Wait for next event.
3400     */
3401     XScreenEvent(display,windows,&event,exception);
3402     if (event.xany.window == windows->command.id)
3403       {
3404         /*
3405           Select a command from the Command widget.
3406         */
3407         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3408         if (id < 0)
3409           {
3410             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3411             continue;
3412           }
3413         switch (ColorEditCommands[id])
3414         {
3415           case ColorEditMethodCommand:
3416           {
3417             char
3418               **methods;
3419
3420             /*
3421               Select a method from the pop-up menu.
3422             */
3423             methods=(char **) GetCommandOptions(MagickMethodOptions);
3424             if (methods == (char **) NULL)
3425               break;
3426             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3427               (const char **) methods,command);
3428             if (entry >= 0)
3429               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
3430                 MagickFalse,methods[entry]);
3431             methods=DestroyStringList(methods);
3432             break;
3433           }
3434           case ColorEditColorCommand:
3435           {
3436             const char
3437               *ColorMenu[MaxNumberPens];
3438
3439             int
3440               pen_number;
3441
3442             /*
3443               Initialize menu selections.
3444             */
3445             for (i=0; i < (int) (MaxNumberPens-2); i++)
3446               ColorMenu[i]=resource_info->pen_colors[i];
3447             ColorMenu[MaxNumberPens-2]="Browser...";
3448             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3449             /*
3450               Select a pen color from the pop-up menu.
3451             */
3452             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3453               (const char **) ColorMenu,command);
3454             if (pen_number < 0)
3455               break;
3456             if (pen_number == (MaxNumberPens-2))
3457               {
3458                 static char
3459                   color_name[MaxTextExtent] = "gray";
3460
3461                 /*
3462                   Select a pen color from a dialog.
3463                 */
3464                 resource_info->pen_colors[pen_number]=color_name;
3465                 XColorBrowserWidget(display,windows,"Select",color_name);
3466                 if (*color_name == '\0')
3467                   break;
3468               }
3469             /*
3470               Set pen color.
3471             */
3472             (void) XParseColor(display,windows->map_info->colormap,
3473               resource_info->pen_colors[pen_number],&color);
3474             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3475               (unsigned int) MaxColors,&color);
3476             windows->pixel_info->pen_colors[pen_number]=color;
3477             pen_id=(unsigned int) pen_number;
3478             break;
3479           }
3480           case ColorEditBorderCommand:
3481           {
3482             const char
3483               *ColorMenu[MaxNumberPens];
3484
3485             int
3486               pen_number;
3487
3488             /*
3489               Initialize menu selections.
3490             */
3491             for (i=0; i < (int) (MaxNumberPens-2); i++)
3492               ColorMenu[i]=resource_info->pen_colors[i];
3493             ColorMenu[MaxNumberPens-2]="Browser...";
3494             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3495             /*
3496               Select a pen color from the pop-up menu.
3497             */
3498             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3499               (const char **) ColorMenu,command);
3500             if (pen_number < 0)
3501               break;
3502             if (pen_number == (MaxNumberPens-2))
3503               {
3504                 static char
3505                   color_name[MaxTextExtent] = "gray";
3506
3507                 /*
3508                   Select a pen color from a dialog.
3509                 */
3510                 resource_info->pen_colors[pen_number]=color_name;
3511                 XColorBrowserWidget(display,windows,"Select",color_name);
3512                 if (*color_name == '\0')
3513                   break;
3514               }
3515             /*
3516               Set border color.
3517             */
3518             (void) XParseColor(display,windows->map_info->colormap,
3519               resource_info->pen_colors[pen_number],&border_color);
3520             break;
3521           }
3522           case ColorEditFuzzCommand:
3523           {
3524             static char
3525               fuzz[MaxTextExtent];
3526
3527             static const char
3528               *FuzzMenu[] =
3529               {
3530                 "0%",
3531                 "2%",
3532                 "5%",
3533                 "10%",
3534                 "15%",
3535                 "Dialog...",
3536                 (char *) NULL,
3537               };
3538
3539             /*
3540               Select a command from the pop-up menu.
3541             */
3542             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3543               command);
3544             if (entry < 0)
3545               break;
3546             if (entry != 5)
3547               {
3548                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
3549                   QuantumRange+1.0);
3550                 break;
3551               }
3552             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3553             (void) XDialogWidget(display,windows,"Ok",
3554               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3555             if (*fuzz == '\0')
3556               break;
3557             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3558             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
3559               1.0);
3560             break;
3561           }
3562           case ColorEditUndoCommand:
3563           {
3564             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3565               image,exception);
3566             break;
3567           }
3568           case ColorEditHelpCommand:
3569           default:
3570           {
3571             XTextViewWidget(display,resource_info,windows,MagickFalse,
3572               "Help Viewer - Image Annotation",ImageColorEditHelp);
3573             break;
3574           }
3575           case ColorEditDismissCommand:
3576           {
3577             /*
3578               Prematurely exit.
3579             */
3580             state|=EscapeState;
3581             state|=ExitState;
3582             break;
3583           }
3584         }
3585         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3586         continue;
3587       }
3588     switch (event.type)
3589     {
3590       case ButtonPress:
3591       {
3592         if (event.xbutton.button != Button1)
3593           break;
3594         if ((event.xbutton.window != windows->image.id) &&
3595             (event.xbutton.window != windows->magnify.id))
3596           break;
3597         /*
3598           exit loop.
3599         */
3600         x=event.xbutton.x;
3601         y=event.xbutton.y;
3602         (void) XMagickCommand(display,resource_info,windows,
3603           SaveToUndoBufferCommand,image,exception);
3604         state|=UpdateConfigurationState;
3605         break;
3606       }
3607       case ButtonRelease:
3608       {
3609         if (event.xbutton.button != Button1)
3610           break;
3611         if ((event.xbutton.window != windows->image.id) &&
3612             (event.xbutton.window != windows->magnify.id))
3613           break;
3614         /*
3615           Update colormap information.
3616         */
3617         x=event.xbutton.x;
3618         y=event.xbutton.y;
3619         XConfigureImageColormap(display,resource_info,windows,*image,exception);
3620         (void) XConfigureImage(display,resource_info,windows,*image,exception);
3621         XInfoWidget(display,windows,text);
3622         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3623         state&=(~UpdateConfigurationState);
3624         break;
3625       }
3626       case Expose:
3627         break;
3628       case KeyPress:
3629       {
3630         KeySym
3631           key_symbol;
3632
3633         if (event.xkey.window == windows->magnify.id)
3634           {
3635             Window
3636               window;
3637
3638             window=windows->magnify.id;
3639             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3640           }
3641         if (event.xkey.window != windows->image.id)
3642           break;
3643         /*
3644           Respond to a user key press.
3645         */
3646         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3647           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3648         switch ((int) key_symbol)
3649         {
3650           case XK_Escape:
3651           case XK_F20:
3652           {
3653             /*
3654               Prematurely exit.
3655             */
3656             state|=ExitState;
3657             break;
3658           }
3659           case XK_F1:
3660           case XK_Help:
3661           {
3662             XTextViewWidget(display,resource_info,windows,MagickFalse,
3663               "Help Viewer - Image Annotation",ImageColorEditHelp);
3664             break;
3665           }
3666           default:
3667           {
3668             (void) XBell(display,0);
3669             break;
3670           }
3671         }
3672         break;
3673       }
3674       case MotionNotify:
3675       {
3676         /*
3677           Map and unmap Info widget as cursor crosses its boundaries.
3678         */
3679         x=event.xmotion.x;
3680         y=event.xmotion.y;
3681         if (windows->info.mapped != MagickFalse)
3682           {
3683             if ((x < (int) (windows->info.x+windows->info.width)) &&
3684                 (y < (int) (windows->info.y+windows->info.height)))
3685               (void) XWithdrawWindow(display,windows->info.id,
3686                 windows->info.screen);
3687           }
3688         else
3689           if ((x > (int) (windows->info.x+windows->info.width)) ||
3690               (y > (int) (windows->info.y+windows->info.height)))
3691             (void) XMapWindow(display,windows->info.id);
3692         break;
3693       }
3694       default:
3695         break;
3696     }
3697     if (event.xany.window == windows->magnify.id)
3698       {
3699         x=windows->magnify.x-windows->image.x;
3700         y=windows->magnify.y-windows->image.y;
3701       }
3702     x_offset=x;
3703     y_offset=y;
3704     if ((state & UpdateConfigurationState) != 0)
3705       {
3706         CacheView
3707           *image_view;
3708
3709         int
3710           x,
3711           y;
3712
3713         /*
3714           Pixel edit is relative to image configuration.
3715         */
3716         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3717           MagickTrue);
3718         color=windows->pixel_info->pen_colors[pen_id];
3719         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3720         width=(unsigned int) (*image)->columns;
3721         height=(unsigned int) (*image)->rows;
3722         x=0;
3723         y=0;
3724         if (windows->image.crop_geometry != (char *) NULL)
3725           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3726             &width,&height);
3727         x_offset=(int)
3728           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3729         y_offset=(int)
3730           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3731         if ((x_offset < 0) || (y_offset < 0))
3732           continue;
3733         if ((x_offset >= (int) (*image)->columns) ||
3734             (y_offset >= (int) (*image)->rows))
3735           continue;
3736         image_view=AcquireAuthenticCacheView(*image,exception);
3737         switch (method)
3738         {
3739           case PointMethod:
3740           default:
3741           {
3742             /*
3743               Update color information using point algorithm.
3744             */
3745             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3746               return(MagickFalse);
3747             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3748               (ssize_t) y_offset,1,1,exception);
3749             if (q == (Quantum *) NULL)
3750               break;
3751             SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3752             SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3753             SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3754             (void) SyncCacheViewAuthenticPixels(image_view,exception);
3755             break;
3756           }
3757           case ReplaceMethod:
3758           {
3759             PixelInfo
3760               pixel,
3761               target;
3762
3763             /*
3764               Update color information using replace algorithm.
3765             */
3766             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
3767               x_offset,(ssize_t) y_offset,&target,exception);
3768             if ((*image)->storage_class == DirectClass)
3769               {
3770                 for (y=0; y < (int) (*image)->rows; y++)
3771                 {
3772                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3773                     (*image)->columns,1,exception);
3774                   if (q == (Quantum *) NULL)
3775                     break;
3776                   for (x=0; x < (int) (*image)->columns; x++)
3777                   {
3778                     GetPixelInfoPixel(*image,q,&pixel);
3779                     if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
3780                       {
3781                         SetPixelRed(*image,ScaleShortToQuantum(
3782                           color.red),q);
3783                         SetPixelGreen(*image,ScaleShortToQuantum(
3784                           color.green),q);
3785                         SetPixelBlue(*image,ScaleShortToQuantum(
3786                           color.blue),q);
3787                       }
3788                     q+=GetPixelChannels(*image);
3789                   }
3790                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3791                     break;
3792                 }
3793               }
3794             else
3795               {
3796                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3797                   if (IsFuzzyEquivalencePixelInfo((*image)->colormap+i,&target))
3798                     {
3799                       (*image)->colormap[i].red=(double) ScaleShortToQuantum(
3800                         color.red);
3801                       (*image)->colormap[i].green=(double) ScaleShortToQuantum(
3802                         color.green);
3803                       (*image)->colormap[i].blue=(double) ScaleShortToQuantum(
3804                         color.blue);
3805                     }
3806                 (void) SyncImage(*image,exception);
3807               }
3808             break;
3809           }
3810           case FloodfillMethod:
3811           case FillToBorderMethod:
3812           {
3813             DrawInfo
3814               *draw_info;
3815
3816             PixelInfo
3817               target;
3818
3819             /*
3820               Update color information using floodfill algorithm.
3821             */
3822             (void) GetOneVirtualPixelInfo(*image,
3823               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
3824               y_offset,&target,exception);
3825             if (method == FillToBorderMethod)
3826               {
3827                 target.red=(MagickRealType)
3828                   ScaleShortToQuantum(border_color.red);
3829                 target.green=(MagickRealType)
3830                   ScaleShortToQuantum(border_color.green);
3831                 target.blue=(MagickRealType)
3832                   ScaleShortToQuantum(border_color.blue);
3833               }
3834             draw_info=CloneDrawInfo(resource_info->image_info,
3835               (DrawInfo *) NULL);
3836             (void) QueryColorCompliance(resource_info->pen_colors[pen_id],
3837               AllCompliance,&draw_info->fill,exception);
3838             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
3839               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
3840               MagickFalse : MagickTrue,exception);
3841             draw_info=DestroyDrawInfo(draw_info);
3842             break;
3843           }
3844           case ResetMethod:
3845           {
3846             /*
3847               Update color information using reset algorithm.
3848             */
3849             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
3850               return(MagickFalse);
3851             for (y=0; y < (int) (*image)->rows; y++)
3852             {
3853               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3854                 (*image)->columns,1,exception);
3855               if (q == (Quantum *) NULL)
3856                 break;
3857               for (x=0; x < (int) (*image)->columns; x++)
3858               {
3859                 SetPixelRed(*image,ScaleShortToQuantum(color.red),q);
3860                 SetPixelGreen(*image,ScaleShortToQuantum(color.green),q);
3861                 SetPixelBlue(*image,ScaleShortToQuantum(color.blue),q);
3862                 q+=GetPixelChannels(*image);
3863               }
3864               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3865                 break;
3866             }
3867             break;
3868           }
3869         }
3870         image_view=DestroyCacheView(image_view);
3871         state&=(~UpdateConfigurationState);
3872       }
3873   } while ((state & ExitState) == 0);
3874   (void) XSelectInput(display,windows->image.id,
3875     windows->image.attributes.event_mask);
3876   XSetCursorState(display,windows,MagickFalse);
3877   (void) XFreeCursor(display,cursor);
3878   return(MagickTrue);
3879 }
3880 \f
3881 /*
3882 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3883 %                                                                             %
3884 %                                                                             %
3885 %                                                                             %
3886 +   X C o m p o s i t e I m a g e                                             %
3887 %                                                                             %
3888 %                                                                             %
3889 %                                                                             %
3890 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3891 %
3892 %  XCompositeImage() requests an image name from the user, reads the image and
3893 %  composites it with the X window image at a location the user chooses with
3894 %  the pointer.
3895 %
3896 %  The format of the XCompositeImage method is:
3897 %
3898 %      MagickBooleanType XCompositeImage(Display *display,
3899 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
3900 %        ExceptionInfo *exception)
3901 %
3902 %  A description of each parameter follows:
3903 %
3904 %    o display: Specifies a connection to an X server;  returned from
3905 %      XOpenDisplay.
3906 %
3907 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3908 %
3909 %    o windows: Specifies a pointer to a XWindows structure.
3910 %
3911 %    o image: the image; returned from ReadImage.
3912 %
3913 %    o exception: return any errors or warnings in this structure.
3914 %
3915 */
3916 static MagickBooleanType XCompositeImage(Display *display,
3917   XResourceInfo *resource_info,XWindows *windows,Image *image,
3918   ExceptionInfo *exception)
3919 {
3920   static char
3921     displacement_geometry[MaxTextExtent] = "30x30",
3922     filename[MaxTextExtent] = "\0";
3923
3924   static const char
3925     *CompositeMenu[] =
3926     {
3927       "Operators",
3928       "Dissolve",
3929       "Displace",
3930       "Help",
3931       "Dismiss",
3932       (char *) NULL
3933     };
3934
3935   static CompositeOperator
3936     compose = CopyCompositeOp;
3937
3938   static const ModeType
3939     CompositeCommands[] =
3940     {
3941       CompositeOperatorsCommand,
3942       CompositeDissolveCommand,
3943       CompositeDisplaceCommand,
3944       CompositeHelpCommand,
3945       CompositeDismissCommand
3946     };
3947
3948   char
3949     text[MaxTextExtent];
3950
3951   Cursor
3952     cursor;
3953
3954   Image
3955     *composite_image;
3956
3957   int
3958     entry,
3959     id,
3960     x,
3961     y;
3962
3963   MagickRealType
3964     blend,
3965     scale_factor;
3966
3967   RectangleInfo
3968     highlight_info,
3969     composite_info;
3970
3971   unsigned int
3972     height,
3973     width;
3974
3975   size_t
3976     state;
3977
3978   XEvent
3979     event;
3980
3981   /*
3982     Request image file name from user.
3983   */
3984   XFileBrowserWidget(display,windows,"Composite",filename);
3985   if (*filename == '\0')
3986     return(MagickTrue);
3987   /*
3988     Read image.
3989   */
3990   XSetCursorState(display,windows,MagickTrue);
3991   XCheckRefreshWindows(display,windows);
3992   (void) CopyMagickString(resource_info->image_info->filename,filename,
3993     MaxTextExtent);
3994   composite_image=ReadImage(resource_info->image_info,exception);
3995   CatchException(exception);
3996   XSetCursorState(display,windows,MagickFalse);
3997   if (composite_image == (Image *) NULL)
3998     return(MagickFalse);
3999   /*
4000     Map Command widget.
4001   */
4002   (void) CloneString(&windows->command.name,"Composite");
4003   windows->command.data=1;
4004   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
4005   (void) XMapRaised(display,windows->command.id);
4006   XClientMessage(display,windows->image.id,windows->im_protocols,
4007     windows->im_update_widget,CurrentTime);
4008   /*
4009     Track pointer until button 1 is pressed.
4010   */
4011   XQueryPosition(display,windows->image.id,&x,&y);
4012   (void) XSelectInput(display,windows->image.id,
4013     windows->image.attributes.event_mask | PointerMotionMask);
4014   composite_info.x=(ssize_t) windows->image.x+x;
4015   composite_info.y=(ssize_t) windows->image.y+y;
4016   composite_info.width=0;
4017   composite_info.height=0;
4018   cursor=XCreateFontCursor(display,XC_ul_angle);
4019   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4020   blend=0.0;
4021   state=DefaultState;
4022   do
4023   {
4024     if (windows->info.mapped != MagickFalse)
4025       {
4026         /*
4027           Display pointer position.
4028         */
4029         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4030           (long) composite_info.x,(long) composite_info.y);
4031         XInfoWidget(display,windows,text);
4032       }
4033     highlight_info=composite_info;
4034     highlight_info.x=composite_info.x-windows->image.x;
4035     highlight_info.y=composite_info.y-windows->image.y;
4036     XHighlightRectangle(display,windows->image.id,
4037       windows->image.highlight_context,&highlight_info);
4038     /*
4039       Wait for next event.
4040     */
4041     XScreenEvent(display,windows,&event,exception);
4042     XHighlightRectangle(display,windows->image.id,
4043       windows->image.highlight_context,&highlight_info);
4044     if (event.xany.window == windows->command.id)
4045       {
4046         /*
4047           Select a command from the Command widget.
4048         */
4049         id=XCommandWidget(display,windows,CompositeMenu,&event);
4050         if (id < 0)
4051           continue;
4052         switch (CompositeCommands[id])
4053         {
4054           case CompositeOperatorsCommand:
4055           {
4056             char
4057               command[MaxTextExtent],
4058               **operators;
4059
4060             /*
4061               Select a command from the pop-up menu.
4062             */
4063             operators=GetCommandOptions(MagickComposeOptions);
4064             if (operators == (char **) NULL)
4065               break;
4066             entry=XMenuWidget(display,windows,CompositeMenu[id],
4067               (const char **) operators,command);
4068             if (entry >= 0)
4069               compose=(CompositeOperator) ParseCommandOption(
4070                 MagickComposeOptions,MagickFalse,operators[entry]);
4071             operators=DestroyStringList(operators);
4072             break;
4073           }
4074           case CompositeDissolveCommand:
4075           {
4076             static char
4077               factor[MaxTextExtent] = "20.0";
4078
4079             /*
4080               Dissolve the two images a given percent.
4081             */
4082             (void) XSetFunction(display,windows->image.highlight_context,
4083               GXcopy);
4084             (void) XDialogWidget(display,windows,"Dissolve",
4085               "Enter the blend factor (0.0 - 99.9%):",factor);
4086             (void) XSetFunction(display,windows->image.highlight_context,
4087               GXinvert);
4088             if (*factor == '\0')
4089               break;
4090             blend=StringToDouble(factor,(char **) NULL);
4091             compose=DissolveCompositeOp;
4092             break;
4093           }
4094           case CompositeDisplaceCommand:
4095           {
4096             /*
4097               Get horizontal and vertical scale displacement geometry.
4098             */
4099             (void) XSetFunction(display,windows->image.highlight_context,
4100               GXcopy);
4101             (void) XDialogWidget(display,windows,"Displace",
4102               "Enter the horizontal and vertical scale:",displacement_geometry);
4103             (void) XSetFunction(display,windows->image.highlight_context,
4104               GXinvert);
4105             if (*displacement_geometry == '\0')
4106               break;
4107             compose=DisplaceCompositeOp;
4108             break;
4109           }
4110           case CompositeHelpCommand:
4111           {
4112             (void) XSetFunction(display,windows->image.highlight_context,
4113               GXcopy);
4114             XTextViewWidget(display,resource_info,windows,MagickFalse,
4115               "Help Viewer - Image Composite",ImageCompositeHelp);
4116             (void) XSetFunction(display,windows->image.highlight_context,
4117               GXinvert);
4118             break;
4119           }
4120           case CompositeDismissCommand:
4121           {
4122             /*
4123               Prematurely exit.
4124             */
4125             state|=EscapeState;
4126             state|=ExitState;
4127             break;
4128           }
4129           default:
4130             break;
4131         }
4132         continue;
4133       }
4134     switch (event.type)
4135     {
4136       case ButtonPress:
4137       {
4138         if (image->debug != MagickFalse)
4139           (void) LogMagickEvent(X11Event,GetMagickModule(),
4140             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4141             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4142         if (event.xbutton.button != Button1)
4143           break;
4144         if (event.xbutton.window != windows->image.id)
4145           break;
4146         /*
4147           Change cursor.
4148         */
4149         composite_info.width=composite_image->columns;
4150         composite_info.height=composite_image->rows;
4151         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4152         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4153         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4154         break;
4155       }
4156       case ButtonRelease:
4157       {
4158         if (image->debug != MagickFalse)
4159           (void) LogMagickEvent(X11Event,GetMagickModule(),
4160             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4161             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4162         if (event.xbutton.button != Button1)
4163           break;
4164         if (event.xbutton.window != windows->image.id)
4165           break;
4166         if ((composite_info.width != 0) && (composite_info.height != 0))
4167           {
4168             /*
4169               User has selected the location of the composite image.
4170             */
4171             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4172             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4173             state|=ExitState;
4174           }
4175         break;
4176       }
4177       case Expose:
4178         break;
4179       case KeyPress:
4180       {
4181         char
4182           command[MaxTextExtent];
4183
4184         KeySym
4185           key_symbol;
4186
4187         int
4188           length;
4189
4190         if (event.xkey.window != windows->image.id)
4191           break;
4192         /*
4193           Respond to a user key press.
4194         */
4195         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4196           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4197         *(command+length)='\0';
4198         if (image->debug != MagickFalse)
4199           (void) LogMagickEvent(X11Event,GetMagickModule(),
4200             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4201         switch ((int) key_symbol)
4202         {
4203           case XK_Escape:
4204           case XK_F20:
4205           {
4206             /*
4207               Prematurely exit.
4208             */
4209             composite_image=DestroyImage(composite_image);
4210             state|=EscapeState;
4211             state|=ExitState;
4212             break;
4213           }
4214           case XK_F1:
4215           case XK_Help:
4216           {
4217             (void) XSetFunction(display,windows->image.highlight_context,
4218               GXcopy);
4219             XTextViewWidget(display,resource_info,windows,MagickFalse,
4220               "Help Viewer - Image Composite",ImageCompositeHelp);
4221             (void) XSetFunction(display,windows->image.highlight_context,
4222               GXinvert);
4223             break;
4224           }
4225           default:
4226           {
4227             (void) XBell(display,0);
4228             break;
4229           }
4230         }
4231         break;
4232       }
4233       case MotionNotify:
4234       {
4235         /*
4236           Map and unmap Info widget as text cursor crosses its boundaries.
4237         */
4238         x=event.xmotion.x;
4239         y=event.xmotion.y;
4240         if (windows->info.mapped != MagickFalse)
4241           {
4242             if ((x < (int) (windows->info.x+windows->info.width)) &&
4243                 (y < (int) (windows->info.y+windows->info.height)))
4244               (void) XWithdrawWindow(display,windows->info.id,
4245                 windows->info.screen);
4246           }
4247         else
4248           if ((x > (int) (windows->info.x+windows->info.width)) ||
4249               (y > (int) (windows->info.y+windows->info.height)))
4250             (void) XMapWindow(display,windows->info.id);
4251         composite_info.x=(ssize_t) windows->image.x+x;
4252         composite_info.y=(ssize_t) windows->image.y+y;
4253         break;
4254       }
4255       default:
4256       {
4257         if (image->debug != MagickFalse)
4258           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4259             event.type);
4260         break;
4261       }
4262     }
4263   } while ((state & ExitState) == 0);
4264   (void) XSelectInput(display,windows->image.id,
4265     windows->image.attributes.event_mask);
4266   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4267   XSetCursorState(display,windows,MagickFalse);
4268   (void) XFreeCursor(display,cursor);
4269   if ((state & EscapeState) != 0)
4270     return(MagickTrue);
4271   /*
4272     Image compositing is relative to image configuration.
4273   */
4274   XSetCursorState(display,windows,MagickTrue);
4275   XCheckRefreshWindows(display,windows);
4276   width=(unsigned int) image->columns;
4277   height=(unsigned int) image->rows;
4278   x=0;
4279   y=0;
4280   if (windows->image.crop_geometry != (char *) NULL)
4281     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4282   scale_factor=(MagickRealType) width/windows->image.ximage->width;
4283   composite_info.x+=x;
4284   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4285   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4286   scale_factor=(MagickRealType) height/windows->image.ximage->height;
4287   composite_info.y+=y;
4288   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4289   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4290   if ((composite_info.width != composite_image->columns) ||
4291       (composite_info.height != composite_image->rows))
4292     {
4293       Image
4294         *resize_image;
4295
4296       /*
4297         Scale composite image.
4298       */
4299       resize_image=ResizeImage(composite_image,composite_info.width,
4300         composite_info.height,composite_image->filter,exception);
4301       composite_image=DestroyImage(composite_image);
4302       if (resize_image == (Image *) NULL)
4303         {
4304           XSetCursorState(display,windows,MagickFalse);
4305           return(MagickFalse);
4306         }
4307       composite_image=resize_image;
4308     }
4309   if (compose == DisplaceCompositeOp)
4310     (void) SetImageArtifact(composite_image,"compose:args",
4311       displacement_geometry);
4312   if (blend != 0.0)
4313     {
4314       CacheView
4315         *image_view;
4316
4317       int
4318         y;
4319
4320       Quantum
4321         opacity;
4322
4323       register int
4324         x;
4325
4326       register Quantum
4327         *q;
4328
4329       /*
4330         Create mattes for blending.
4331       */
4332       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel,exception);
4333       opacity=(Quantum) (ScaleQuantumToChar(QuantumRange)-
4334         ((ssize_t) ScaleQuantumToChar(QuantumRange)*blend)/100);
4335       if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
4336         return(MagickFalse);
4337       image->matte=MagickTrue;
4338       image_view=AcquireAuthenticCacheView(image,exception);
4339       for (y=0; y < (int) image->rows; y++)
4340       {
4341         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4342           exception);
4343         if (q == (Quantum *) NULL)
4344           break;
4345         for (x=0; x < (int) image->columns; x++)
4346         {
4347           SetPixelAlpha(image,opacity,q);
4348           q+=GetPixelChannels(image);
4349         }
4350         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4351           break;
4352       }
4353       image_view=DestroyCacheView(image_view);
4354     }
4355   /*
4356     Composite image with X Image window.
4357   */
4358   (void) CompositeImage(image,composite_image,compose,MagickTrue,
4359     composite_info.x,composite_info.y,exception);
4360   composite_image=DestroyImage(composite_image);
4361   XSetCursorState(display,windows,MagickFalse);
4362   /*
4363     Update image configuration.
4364   */
4365   XConfigureImageColormap(display,resource_info,windows,image,exception);
4366   (void) XConfigureImage(display,resource_info,windows,image,exception);
4367   return(MagickTrue);
4368 }
4369 \f
4370 /*
4371 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4372 %                                                                             %
4373 %                                                                             %
4374 %                                                                             %
4375 +   X C o n f i g u r e I m a g e                                             %
4376 %                                                                             %
4377 %                                                                             %
4378 %                                                                             %
4379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4380 %
4381 %  XConfigureImage() creates a new X image.  It also notifies the window
4382 %  manager of the new image size and configures the transient widows.
4383 %
4384 %  The format of the XConfigureImage method is:
4385 %
4386 %      MagickBooleanType XConfigureImage(Display *display,
4387 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4388 %        ExceptionInfo *exception)
4389 %
4390 %  A description of each parameter follows:
4391 %
4392 %    o display: Specifies a connection to an X server; returned from
4393 %      XOpenDisplay.
4394 %
4395 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4396 %
4397 %    o windows: Specifies a pointer to a XWindows structure.
4398 %
4399 %    o image: the image.
4400 %
4401 %    o exception: return any errors or warnings in this structure.
4402 %
4403 %    o exception: return any errors or warnings in this structure.
4404 %
4405 */
4406 static MagickBooleanType XConfigureImage(Display *display,
4407   XResourceInfo *resource_info,XWindows *windows,Image *image,
4408   ExceptionInfo *exception)
4409 {
4410   char
4411     geometry[MaxTextExtent];
4412
4413   MagickStatusType
4414     status;
4415
4416   size_t
4417     mask,
4418     height,
4419     width;
4420
4421   ssize_t
4422     x,
4423     y;
4424
4425   XSizeHints
4426     *size_hints;
4427
4428   XWindowChanges
4429     window_changes;
4430
4431   /*
4432     Dismiss if window dimensions are zero.
4433   */
4434   width=(unsigned int) windows->image.window_changes.width;
4435   height=(unsigned int) windows->image.window_changes.height;
4436   if (image->debug != MagickFalse)
4437     (void) LogMagickEvent(X11Event,GetMagickModule(),
4438       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4439       windows->image.ximage->height,(double) width,(double) height);
4440   if ((width*height) == 0)
4441     return(MagickTrue);
4442   x=0;
4443   y=0;
4444   /*
4445     Resize image to fit Image window dimensions.
4446   */
4447   XSetCursorState(display,windows,MagickTrue);
4448   (void) XFlush(display);
4449   if (((int) width != windows->image.ximage->width) ||
4450       ((int) height != windows->image.ximage->height))
4451     image->taint=MagickTrue;
4452   windows->magnify.x=(int)
4453     width*windows->magnify.x/windows->image.ximage->width;
4454   windows->magnify.y=(int)
4455     height*windows->magnify.y/windows->image.ximage->height;
4456   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4457   windows->image.y=(int)
4458     (height*windows->image.y/windows->image.ximage->height);
4459   status=XMakeImage(display,resource_info,&windows->image,image,
4460     (unsigned int) width,(unsigned int) height,exception);
4461   if (status == MagickFalse)
4462     XNoticeWidget(display,windows,"Unable to configure X image:",
4463       windows->image.name);
4464   /*
4465     Notify window manager of the new configuration.
4466   */
4467   if (resource_info->image_geometry != (char *) NULL)
4468     (void) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4469       resource_info->image_geometry);
4470   else
4471     (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4472       XDisplayWidth(display,windows->image.screen),
4473       XDisplayHeight(display,windows->image.screen));
4474   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4475   window_changes.width=(int) width;
4476   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4477     window_changes.width=XDisplayWidth(display,windows->image.screen);
4478   window_changes.height=(int) height;
4479   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4480     window_changes.height=XDisplayHeight(display,windows->image.screen);
4481   mask=(size_t) (CWWidth | CWHeight);
4482   if (resource_info->backdrop)
4483     {
4484       mask|=CWX | CWY;
4485       window_changes.x=(int)
4486         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4487       window_changes.y=(int)
4488         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4489     }
4490   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4491     (unsigned int) mask,&window_changes);
4492   (void) XClearWindow(display,windows->image.id);
4493   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4494   /*
4495     Update Magnify window configuration.
4496   */
4497   if (windows->magnify.mapped != MagickFalse)
4498     XMakeMagnifyImage(display,windows,exception);
4499   windows->pan.crop_geometry=windows->image.crop_geometry;
4500   XBestIconSize(display,&windows->pan,image);
4501   while (((windows->pan.width << 1) < MaxIconSize) &&
4502          ((windows->pan.height << 1) < MaxIconSize))
4503   {
4504     windows->pan.width<<=1;
4505     windows->pan.height<<=1;
4506   }
4507   if (windows->pan.geometry != (char *) NULL)
4508     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4509       &windows->pan.width,&windows->pan.height);
4510   window_changes.width=(int) windows->pan.width;
4511   window_changes.height=(int) windows->pan.height;
4512   size_hints=XAllocSizeHints();
4513   if (size_hints != (XSizeHints *) NULL)
4514     {
4515       /*
4516         Set new size hints.
4517       */
4518       size_hints->flags=PSize | PMinSize | PMaxSize;
4519       size_hints->width=window_changes.width;
4520       size_hints->height=window_changes.height;
4521       size_hints->min_width=size_hints->width;
4522       size_hints->min_height=size_hints->height;
4523       size_hints->max_width=size_hints->width;
4524       size_hints->max_height=size_hints->height;
4525       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4526       (void) XFree((void *) size_hints);
4527     }
4528   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4529     (unsigned int) (CWWidth | CWHeight),&window_changes);
4530   /*
4531     Update icon window configuration.
4532   */
4533   windows->icon.crop_geometry=windows->image.crop_geometry;
4534   XBestIconSize(display,&windows->icon,image);
4535   window_changes.width=(int) windows->icon.width;
4536   window_changes.height=(int) windows->icon.height;
4537   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4538     (unsigned int) (CWWidth | CWHeight),&window_changes);
4539   XSetCursorState(display,windows,MagickFalse);
4540   return(status != 0 ? MagickTrue : MagickFalse);
4541 }
4542 \f
4543 /*
4544 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4545 %                                                                             %
4546 %                                                                             %
4547 %                                                                             %
4548 +   X C r o p I m a g e                                                       %
4549 %                                                                             %
4550 %                                                                             %
4551 %                                                                             %
4552 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4553 %
4554 %  XCropImage() allows the user to select a region of the image and crop, copy,
4555 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4556 %  the image with XPasteImage.
4557 %
4558 %  The format of the XCropImage method is:
4559 %
4560 %      MagickBooleanType XCropImage(Display *display,
4561 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4562 %        const ClipboardMode mode,ExceptionInfo *exception)
4563 %
4564 %  A description of each parameter follows:
4565 %
4566 %    o display: Specifies a connection to an X server; returned from
4567 %      XOpenDisplay.
4568 %
4569 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4570 %
4571 %    o windows: Specifies a pointer to a XWindows structure.
4572 %
4573 %    o image: the image; returned from ReadImage.
4574 %
4575 %    o mode: This unsigned value specified whether the image should be
4576 %      cropped, copied, or cut.
4577 %
4578 %    o exception: return any errors or warnings in this structure.
4579 %
4580 */
4581 static MagickBooleanType XCropImage(Display *display,
4582   XResourceInfo *resource_info,XWindows *windows,Image *image,
4583   const ClipboardMode mode,ExceptionInfo *exception)
4584 {
4585   static const char
4586     *CropModeMenu[] =
4587     {
4588       "Help",
4589       "Dismiss",
4590       (char *) NULL
4591     },
4592     *RectifyModeMenu[] =
4593     {
4594       "Crop",
4595       "Help",
4596       "Dismiss",
4597       (char *) NULL
4598     };
4599
4600   static const ModeType
4601     CropCommands[] =
4602     {
4603       CropHelpCommand,
4604       CropDismissCommand
4605     },
4606     RectifyCommands[] =
4607     {
4608       RectifyCopyCommand,
4609       RectifyHelpCommand,
4610       RectifyDismissCommand
4611     };
4612
4613   CacheView
4614     *image_view;
4615
4616   char
4617     command[MaxTextExtent],
4618     text[MaxTextExtent];
4619
4620   Cursor
4621     cursor;
4622
4623   int
4624     id,
4625     x,
4626     y;
4627
4628   KeySym
4629     key_symbol;
4630
4631   Image
4632     *crop_image;
4633
4634   MagickRealType
4635     scale_factor;
4636
4637   RectangleInfo
4638     crop_info,
4639     highlight_info;
4640
4641   register Quantum
4642     *q;
4643
4644   unsigned int
4645     height,
4646     width;
4647
4648   size_t
4649     state;
4650
4651   XEvent
4652     event;
4653
4654   /*
4655     Map Command widget.
4656   */
4657   switch (mode)
4658   {
4659     case CopyMode:
4660     {
4661       (void) CloneString(&windows->command.name,"Copy");
4662       break;
4663     }
4664     case CropMode:
4665     {
4666       (void) CloneString(&windows->command.name,"Crop");
4667       break;
4668     }
4669     case CutMode:
4670     {
4671       (void) CloneString(&windows->command.name,"Cut");
4672       break;
4673     }
4674   }
4675   RectifyModeMenu[0]=windows->command.name;
4676   windows->command.data=0;
4677   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4678   (void) XMapRaised(display,windows->command.id);
4679   XClientMessage(display,windows->image.id,windows->im_protocols,
4680     windows->im_update_widget,CurrentTime);
4681   /*
4682     Track pointer until button 1 is pressed.
4683   */
4684   XQueryPosition(display,windows->image.id,&x,&y);
4685   (void) XSelectInput(display,windows->image.id,
4686     windows->image.attributes.event_mask | PointerMotionMask);
4687   crop_info.x=(ssize_t) windows->image.x+x;
4688   crop_info.y=(ssize_t) windows->image.y+y;
4689   crop_info.width=0;
4690   crop_info.height=0;
4691   cursor=XCreateFontCursor(display,XC_fleur);
4692   state=DefaultState;
4693   do
4694   {
4695     if (windows->info.mapped != MagickFalse)
4696       {
4697         /*
4698           Display pointer position.
4699         */
4700         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
4701           (long) crop_info.x,(long) crop_info.y);
4702         XInfoWidget(display,windows,text);
4703       }
4704     /*
4705       Wait for next event.
4706     */
4707     XScreenEvent(display,windows,&event,exception);
4708     if (event.xany.window == windows->command.id)
4709       {
4710         /*
4711           Select a command from the Command widget.
4712         */
4713         id=XCommandWidget(display,windows,CropModeMenu,&event);
4714         if (id < 0)
4715           continue;
4716         switch (CropCommands[id])
4717         {
4718           case CropHelpCommand:
4719           {
4720             switch (mode)
4721             {
4722               case CopyMode:
4723               {
4724                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4725                   "Help Viewer - Image Copy",ImageCopyHelp);
4726                 break;
4727               }
4728               case CropMode:
4729               {
4730                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4731                   "Help Viewer - Image Crop",ImageCropHelp);
4732                 break;
4733               }
4734               case CutMode:
4735               {
4736                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4737                   "Help Viewer - Image Cut",ImageCutHelp);
4738                 break;
4739               }
4740             }
4741             break;
4742           }
4743           case CropDismissCommand:
4744           {
4745             /*
4746               Prematurely exit.
4747             */
4748             state|=EscapeState;
4749             state|=ExitState;
4750             break;
4751           }
4752           default:
4753             break;
4754         }
4755         continue;
4756       }
4757     switch (event.type)
4758     {
4759       case ButtonPress:
4760       {
4761         if (event.xbutton.button != Button1)
4762           break;
4763         if (event.xbutton.window != windows->image.id)
4764           break;
4765         /*
4766           Note first corner of cropping rectangle-- exit loop.
4767         */
4768         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4769         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4770         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4771         state|=ExitState;
4772         break;
4773       }
4774       case ButtonRelease:
4775         break;
4776       case Expose:
4777         break;
4778       case KeyPress:
4779       {
4780         if (event.xkey.window != windows->image.id)
4781           break;
4782         /*
4783           Respond to a user key press.
4784         */
4785         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4786           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4787         switch ((int) key_symbol)
4788         {
4789           case XK_Escape:
4790           case XK_F20:
4791           {
4792             /*
4793               Prematurely exit.
4794             */
4795             state|=EscapeState;
4796             state|=ExitState;
4797             break;
4798           }
4799           case XK_F1:
4800           case XK_Help:
4801           {
4802             switch (mode)
4803             {
4804               case CopyMode:
4805               {
4806                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4807                   "Help Viewer - Image Copy",ImageCopyHelp);
4808                 break;
4809               }
4810               case CropMode:
4811               {
4812                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4813                   "Help Viewer - Image Crop",ImageCropHelp);
4814                 break;
4815               }
4816               case CutMode:
4817               {
4818                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4819                   "Help Viewer - Image Cut",ImageCutHelp);
4820                 break;
4821               }
4822             }
4823             break;
4824           }
4825           default:
4826           {
4827             (void) XBell(display,0);
4828             break;
4829           }
4830         }
4831         break;
4832       }
4833       case MotionNotify:
4834       {
4835         if (event.xmotion.window != windows->image.id)
4836           break;
4837         /*
4838           Map and unmap Info widget as text cursor crosses its boundaries.
4839         */
4840         x=event.xmotion.x;
4841         y=event.xmotion.y;
4842         if (windows->info.mapped != MagickFalse)
4843           {
4844             if ((x < (int) (windows->info.x+windows->info.width)) &&
4845                 (y < (int) (windows->info.y+windows->info.height)))
4846               (void) XWithdrawWindow(display,windows->info.id,
4847                 windows->info.screen);
4848           }
4849         else
4850           if ((x > (int) (windows->info.x+windows->info.width)) ||
4851               (y > (int) (windows->info.y+windows->info.height)))
4852             (void) XMapWindow(display,windows->info.id);
4853         crop_info.x=(ssize_t) windows->image.x+x;
4854         crop_info.y=(ssize_t) windows->image.y+y;
4855         break;
4856       }
4857       default:
4858         break;
4859     }
4860   } while ((state & ExitState) == 0);
4861   (void) XSelectInput(display,windows->image.id,
4862     windows->image.attributes.event_mask);
4863   if ((state & EscapeState) != 0)
4864     {
4865       /*
4866         User want to exit without cropping.
4867       */
4868       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4869       (void) XFreeCursor(display,cursor);
4870       return(MagickTrue);
4871     }
4872   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4873   do
4874   {
4875     /*
4876       Size rectangle as pointer moves until the mouse button is released.
4877     */
4878     x=(int) crop_info.x;
4879     y=(int) crop_info.y;
4880     crop_info.width=0;
4881     crop_info.height=0;
4882     state=DefaultState;
4883     do
4884     {
4885       highlight_info=crop_info;
4886       highlight_info.x=crop_info.x-windows->image.x;
4887       highlight_info.y=crop_info.y-windows->image.y;
4888       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4889         {
4890           /*
4891             Display info and draw cropping rectangle.
4892           */
4893           if (windows->info.mapped == MagickFalse)
4894             (void) XMapWindow(display,windows->info.id);
4895           (void) FormatLocaleString(text,MaxTextExtent,
4896             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4897             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4898           XInfoWidget(display,windows,text);
4899           XHighlightRectangle(display,windows->image.id,
4900             windows->image.highlight_context,&highlight_info);
4901         }
4902       else
4903         if (windows->info.mapped != MagickFalse)
4904           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4905       /*
4906         Wait for next event.
4907       */
4908       XScreenEvent(display,windows,&event,exception);
4909       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4910         XHighlightRectangle(display,windows->image.id,
4911           windows->image.highlight_context,&highlight_info);
4912       switch (event.type)
4913       {
4914         case ButtonPress:
4915         {
4916           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4917           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4918           break;
4919         }
4920         case ButtonRelease:
4921         {
4922           /*
4923             User has committed to cropping rectangle.
4924           */
4925           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4926           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4927           XSetCursorState(display,windows,MagickFalse);
4928           state|=ExitState;
4929           windows->command.data=0;
4930           (void) XCommandWidget(display,windows,RectifyModeMenu,
4931             (XEvent *) NULL);
4932           break;
4933         }
4934         case Expose:
4935           break;
4936         case MotionNotify:
4937         {
4938           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4939           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4940         }
4941         default:
4942           break;
4943       }
4944       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4945           ((state & ExitState) != 0))
4946         {
4947           /*
4948             Check boundary conditions.
4949           */
4950           if (crop_info.x < 0)
4951             crop_info.x=0;
4952           else
4953             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4954               crop_info.x=(ssize_t) windows->image.ximage->width;
4955           if ((int) crop_info.x < x)
4956             crop_info.width=(unsigned int) (x-crop_info.x);
4957           else
4958             {
4959               crop_info.width=(unsigned int) (crop_info.x-x);
4960               crop_info.x=(ssize_t) x;
4961             }
4962           if (crop_info.y < 0)
4963             crop_info.y=0;
4964           else
4965             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4966               crop_info.y=(ssize_t) windows->image.ximage->height;
4967           if ((int) crop_info.y < y)
4968             crop_info.height=(unsigned int) (y-crop_info.y);
4969           else
4970             {
4971               crop_info.height=(unsigned int) (crop_info.y-y);
4972               crop_info.y=(ssize_t) y;
4973             }
4974         }
4975     } while ((state & ExitState) == 0);
4976     /*
4977       Wait for user to grab a corner of the rectangle or press return.
4978     */
4979     state=DefaultState;
4980     (void) XMapWindow(display,windows->info.id);
4981     do
4982     {
4983       if (windows->info.mapped != MagickFalse)
4984         {
4985           /*
4986             Display pointer position.
4987           */
4988           (void) FormatLocaleString(text,MaxTextExtent,
4989             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4990             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4991           XInfoWidget(display,windows,text);
4992         }
4993       highlight_info=crop_info;
4994       highlight_info.x=crop_info.x-windows->image.x;
4995       highlight_info.y=crop_info.y-windows->image.y;
4996       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4997         {
4998           state|=EscapeState;
4999           state|=ExitState;
5000           break;
5001         }
5002       XHighlightRectangle(display,windows->image.id,
5003         windows->image.highlight_context,&highlight_info);
5004       XScreenEvent(display,windows,&event,exception);
5005       if (event.xany.window == windows->command.id)
5006         {
5007           /*
5008             Select a command from the Command widget.
5009           */
5010           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5011           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
5012           (void) XSetFunction(display,windows->image.highlight_context,
5013             GXinvert);
5014           XHighlightRectangle(display,windows->image.id,
5015             windows->image.highlight_context,&highlight_info);
5016           if (id >= 0)
5017             switch (RectifyCommands[id])
5018             {
5019               case RectifyCopyCommand:
5020               {
5021                 state|=ExitState;
5022                 break;
5023               }
5024               case RectifyHelpCommand:
5025               {
5026                 (void) XSetFunction(display,windows->image.highlight_context,
5027                   GXcopy);
5028                 switch (mode)
5029                 {
5030                   case CopyMode:
5031                   {
5032                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5033                       "Help Viewer - Image Copy",ImageCopyHelp);
5034                     break;
5035                   }
5036                   case CropMode:
5037                   {
5038                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5039                       "Help Viewer - Image Crop",ImageCropHelp);
5040                     break;
5041                   }
5042                   case CutMode:
5043                   {
5044                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5045                       "Help Viewer - Image Cut",ImageCutHelp);
5046                     break;
5047                   }
5048                 }
5049                 (void) XSetFunction(display,windows->image.highlight_context,
5050                   GXinvert);
5051                 break;
5052               }
5053               case RectifyDismissCommand:
5054               {
5055                 /*
5056                   Prematurely exit.
5057                 */
5058                 state|=EscapeState;
5059                 state|=ExitState;
5060                 break;
5061               }
5062               default:
5063                 break;
5064             }
5065           continue;
5066         }
5067       XHighlightRectangle(display,windows->image.id,
5068         windows->image.highlight_context,&highlight_info);
5069       switch (event.type)
5070       {
5071         case ButtonPress:
5072         {
5073           if (event.xbutton.button != Button1)
5074             break;
5075           if (event.xbutton.window != windows->image.id)
5076             break;
5077           x=windows->image.x+event.xbutton.x;
5078           y=windows->image.y+event.xbutton.y;
5079           if ((x < (int) (crop_info.x+RoiDelta)) &&
5080               (x > (int) (crop_info.x-RoiDelta)) &&
5081               (y < (int) (crop_info.y+RoiDelta)) &&
5082               (y > (int) (crop_info.y-RoiDelta)))
5083             {
5084               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5085               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5086               state|=UpdateConfigurationState;
5087               break;
5088             }
5089           if ((x < (int) (crop_info.x+RoiDelta)) &&
5090               (x > (int) (crop_info.x-RoiDelta)) &&
5091               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5092               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5093             {
5094               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5095               state|=UpdateConfigurationState;
5096               break;
5097             }
5098           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5099               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5100               (y < (int) (crop_info.y+RoiDelta)) &&
5101               (y > (int) (crop_info.y-RoiDelta)))
5102             {
5103               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5104               state|=UpdateConfigurationState;
5105               break;
5106             }
5107           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5108               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5109               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5110               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5111             {
5112               state|=UpdateConfigurationState;
5113               break;
5114             }
5115         }
5116         case ButtonRelease:
5117         {
5118           if (event.xbutton.window == windows->pan.id)
5119             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5120                 (highlight_info.y != crop_info.y-windows->image.y))
5121               XHighlightRectangle(display,windows->image.id,
5122                 windows->image.highlight_context,&highlight_info);
5123           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5124             event.xbutton.time);
5125           break;
5126         }
5127         case Expose:
5128         {
5129           if (event.xexpose.window == windows->image.id)
5130             if (event.xexpose.count == 0)
5131               {
5132                 event.xexpose.x=(int) highlight_info.x;
5133                 event.xexpose.y=(int) highlight_info.y;
5134                 event.xexpose.width=(int) highlight_info.width;
5135                 event.xexpose.height=(int) highlight_info.height;
5136                 XRefreshWindow(display,&windows->image,&event);
5137               }
5138           if (event.xexpose.window == windows->info.id)
5139             if (event.xexpose.count == 0)
5140               XInfoWidget(display,windows,text);
5141           break;
5142         }
5143         case KeyPress:
5144         {
5145           if (event.xkey.window != windows->image.id)
5146             break;
5147           /*
5148             Respond to a user key press.
5149           */
5150           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5151             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5152           switch ((int) key_symbol)
5153           {
5154             case XK_Escape:
5155             case XK_F20:
5156               state|=EscapeState;
5157             case XK_Return:
5158             {
5159               state|=ExitState;
5160               break;
5161             }
5162             case XK_Home:
5163             case XK_KP_Home:
5164             {
5165               crop_info.x=(ssize_t) (windows->image.width/2L-
5166                 crop_info.width/2L);
5167               crop_info.y=(ssize_t) (windows->image.height/2L-
5168                 crop_info.height/2L);
5169               break;
5170             }
5171             case XK_Left:
5172             case XK_KP_Left:
5173             {
5174               crop_info.x--;
5175               break;
5176             }
5177             case XK_Up:
5178             case XK_KP_Up:
5179             case XK_Next:
5180             {
5181               crop_info.y--;
5182               break;
5183             }
5184             case XK_Right:
5185             case XK_KP_Right:
5186             {
5187               crop_info.x++;
5188               break;
5189             }
5190             case XK_Prior:
5191             case XK_Down:
5192             case XK_KP_Down:
5193             {
5194               crop_info.y++;
5195               break;
5196             }
5197             case XK_F1:
5198             case XK_Help:
5199             {
5200               (void) XSetFunction(display,windows->image.highlight_context,
5201                 GXcopy);
5202               switch (mode)
5203               {
5204                 case CopyMode:
5205                 {
5206                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5207                     "Help Viewer - Image Copy",ImageCopyHelp);
5208                   break;
5209                 }
5210                 case CropMode:
5211                 {
5212                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5213                     "Help Viewer - Image Cropg",ImageCropHelp);
5214                   break;
5215                 }
5216                 case CutMode:
5217                 {
5218                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5219                     "Help Viewer - Image Cutg",ImageCutHelp);
5220                   break;
5221                 }
5222               }
5223               (void) XSetFunction(display,windows->image.highlight_context,
5224                 GXinvert);
5225               break;
5226             }
5227             default:
5228             {
5229               (void) XBell(display,0);
5230               break;
5231             }
5232           }
5233           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5234             event.xkey.time);
5235           break;
5236         }
5237         case KeyRelease:
5238           break;
5239         case MotionNotify:
5240         {
5241           if (event.xmotion.window != windows->image.id)
5242             break;
5243           /*
5244             Map and unmap Info widget as text cursor crosses its boundaries.
5245           */
5246           x=event.xmotion.x;
5247           y=event.xmotion.y;
5248           if (windows->info.mapped != MagickFalse)
5249             {
5250               if ((x < (int) (windows->info.x+windows->info.width)) &&
5251                   (y < (int) (windows->info.y+windows->info.height)))
5252                 (void) XWithdrawWindow(display,windows->info.id,
5253                   windows->info.screen);
5254             }
5255           else
5256             if ((x > (int) (windows->info.x+windows->info.width)) ||
5257                 (y > (int) (windows->info.y+windows->info.height)))
5258               (void) XMapWindow(display,windows->info.id);
5259           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5260           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5261           break;
5262         }
5263         case SelectionRequest:
5264         {
5265           XSelectionEvent
5266             notify;
5267
5268           XSelectionRequestEvent
5269             *request;
5270
5271           /*
5272             Set primary selection.
5273           */
5274           (void) FormatLocaleString(text,MaxTextExtent,
5275             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5276             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5277           request=(&(event.xselectionrequest));
5278           (void) XChangeProperty(request->display,request->requestor,
5279             request->property,request->target,8,PropModeReplace,
5280             (unsigned char *) text,(int) strlen(text));
5281           notify.type=SelectionNotify;
5282           notify.display=request->display;
5283           notify.requestor=request->requestor;
5284           notify.selection=request->selection;
5285           notify.target=request->target;
5286           notify.time=request->time;
5287           if (request->property == None)
5288             notify.property=request->target;
5289           else
5290             notify.property=request->property;
5291           (void) XSendEvent(request->display,request->requestor,False,0,
5292             (XEvent *) &notify);
5293         }
5294         default:
5295           break;
5296       }
5297       if ((state & UpdateConfigurationState) != 0)
5298         {
5299           (void) XPutBackEvent(display,&event);
5300           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5301           break;
5302         }
5303     } while ((state & ExitState) == 0);
5304   } while ((state & ExitState) == 0);
5305   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5306   XSetCursorState(display,windows,MagickFalse);
5307   if ((state & EscapeState) != 0)
5308     return(MagickTrue);
5309   if (mode == CropMode)
5310     if (((int) crop_info.width != windows->image.ximage->width) ||
5311         ((int) crop_info.height != windows->image.ximage->height))
5312       {
5313         /*
5314           Reconfigure Image window as defined by cropping rectangle.
5315         */
5316         XSetCropGeometry(display,windows,&crop_info,image);
5317         windows->image.window_changes.width=(int) crop_info.width;
5318         windows->image.window_changes.height=(int) crop_info.height;
5319         (void) XConfigureImage(display,resource_info,windows,image,exception);
5320         return(MagickTrue);
5321       }
5322   /*
5323     Copy image before applying image transforms.
5324   */
5325   XSetCursorState(display,windows,MagickTrue);
5326   XCheckRefreshWindows(display,windows);
5327   width=(unsigned int) image->columns;
5328   height=(unsigned int) image->rows;
5329   x=0;
5330   y=0;
5331   if (windows->image.crop_geometry != (char *) NULL)
5332     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5333   scale_factor=(MagickRealType) width/windows->image.ximage->width;
5334   crop_info.x+=x;
5335   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5336   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5337   scale_factor=(MagickRealType) height/windows->image.ximage->height;
5338   crop_info.y+=y;
5339   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5340   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5341   crop_image=CropImage(image,&crop_info,exception);
5342   XSetCursorState(display,windows,MagickFalse);
5343   if (crop_image == (Image *) NULL)
5344     return(MagickFalse);
5345   if (resource_info->copy_image != (Image *) NULL)
5346     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5347   resource_info->copy_image=crop_image;
5348   if (mode == CopyMode)
5349     {
5350       (void) XConfigureImage(display,resource_info,windows,image,exception);
5351       return(MagickTrue);
5352     }
5353   /*
5354     Cut image.
5355   */
5356   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
5357     return(MagickFalse);
5358   image->matte=MagickTrue;
5359   image_view=AcquireAuthenticCacheView(image,exception);
5360   for (y=0; y < (int) crop_info.height; y++)
5361   {
5362     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5363       crop_info.width,1,exception);
5364     if (q == (Quantum *) NULL)
5365       break;
5366     for (x=0; x < (int) crop_info.width; x++)
5367     {
5368       SetPixelAlpha(image,TransparentAlpha,q);
5369       q+=GetPixelChannels(image);
5370     }
5371     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5372       break;
5373   }
5374   image_view=DestroyCacheView(image_view);
5375   /*
5376     Update image configuration.
5377   */
5378   XConfigureImageColormap(display,resource_info,windows,image,exception);
5379   (void) XConfigureImage(display,resource_info,windows,image,exception);
5380   return(MagickTrue);
5381 }
5382 \f
5383 /*
5384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5385 %                                                                             %
5386 %                                                                             %
5387 %                                                                             %
5388 +   X D r a w I m a g e                                                       %
5389 %                                                                             %
5390 %                                                                             %
5391 %                                                                             %
5392 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5393 %
5394 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5395 %  the image.
5396 %
5397 %  The format of the XDrawEditImage method is:
5398 %
5399 %      MagickBooleanType XDrawEditImage(Display *display,
5400 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
5401 %        ExceptionInfo *exception)
5402 %
5403 %  A description of each parameter follows:
5404 %
5405 %    o display: Specifies a connection to an X server; returned from
5406 %      XOpenDisplay.
5407 %
5408 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5409 %
5410 %    o windows: Specifies a pointer to a XWindows structure.
5411 %
5412 %    o image: the image.
5413 %
5414 %    o exception: return any errors or warnings in this structure.
5415 %
5416 */
5417 static MagickBooleanType XDrawEditImage(Display *display,
5418   XResourceInfo *resource_info,XWindows *windows,Image **image,
5419   ExceptionInfo *exception)
5420 {
5421   static const char
5422     *DrawMenu[] =
5423     {
5424       "Element",
5425       "Color",
5426       "Stipple",
5427       "Width",
5428       "Undo",
5429       "Help",
5430       "Dismiss",
5431       (char *) NULL
5432     };
5433
5434   static ElementType
5435     element = PointElement;
5436
5437   static const ModeType
5438     DrawCommands[] =
5439     {
5440       DrawElementCommand,
5441       DrawColorCommand,
5442       DrawStippleCommand,
5443       DrawWidthCommand,
5444       DrawUndoCommand,
5445       DrawHelpCommand,
5446       DrawDismissCommand
5447     };
5448
5449   static Pixmap
5450     stipple = (Pixmap) NULL;
5451
5452   static unsigned int
5453     pen_id = 0,
5454     line_width = 1;
5455
5456   char
5457     command[MaxTextExtent],
5458     text[MaxTextExtent];
5459
5460   Cursor
5461     cursor;
5462
5463   int
5464     entry,
5465     id,
5466     number_coordinates,
5467     x,
5468     y;
5469
5470   MagickRealType
5471     degrees;
5472
5473   MagickStatusType
5474     status;
5475
5476   RectangleInfo
5477     rectangle_info;
5478
5479   register int
5480     i;
5481
5482   unsigned int
5483     distance,
5484     height,
5485     max_coordinates,
5486     width;
5487
5488   size_t
5489     state;
5490
5491   Window
5492     root_window;
5493
5494   XDrawInfo
5495     draw_info;
5496
5497   XEvent
5498     event;
5499
5500   XPoint
5501     *coordinate_info;
5502
5503   XSegment
5504     line_info;
5505
5506   /*
5507     Allocate polygon info.
5508   */
5509   max_coordinates=2048;
5510   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5511     sizeof(*coordinate_info));
5512   if (coordinate_info == (XPoint *) NULL)
5513     {
5514       (void) ThrowMagickException(exception,GetMagickModule(),
5515         ResourceLimitError,"MemoryAllocationFailed","'%s'","...");
5516       return(MagickFalse);
5517     }
5518   /*
5519     Map Command widget.
5520   */
5521   (void) CloneString(&windows->command.name,"Draw");
5522   windows->command.data=4;
5523   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5524   (void) XMapRaised(display,windows->command.id);
5525   XClientMessage(display,windows->image.id,windows->im_protocols,
5526     windows->im_update_widget,CurrentTime);
5527   /*
5528     Wait for first button press.
5529   */
5530   root_window=XRootWindow(display,XDefaultScreen(display));
5531   draw_info.stencil=OpaqueStencil;
5532   status=MagickTrue;
5533   cursor=XCreateFontCursor(display,XC_tcross);
5534   for ( ; ; )
5535   {
5536     XQueryPosition(display,windows->image.id,&x,&y);
5537     (void) XSelectInput(display,windows->image.id,
5538       windows->image.attributes.event_mask | PointerMotionMask);
5539     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5540     state=DefaultState;
5541     do
5542     {
5543       if (windows->info.mapped != MagickFalse)
5544         {
5545           /*
5546             Display pointer position.
5547           */
5548           (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
5549             x+windows->image.x,y+windows->image.y);
5550           XInfoWidget(display,windows,text);
5551         }
5552       /*
5553         Wait for next event.
5554       */
5555       XScreenEvent(display,windows,&event,exception);
5556       if (event.xany.window == windows->command.id)
5557         {
5558           /*
5559             Select a command from the Command widget.
5560           */
5561           id=XCommandWidget(display,windows,DrawMenu,&event);
5562           if (id < 0)
5563             continue;
5564           switch (DrawCommands[id])
5565           {
5566             case DrawElementCommand:
5567             {
5568               static const char
5569                 *Elements[] =
5570                 {
5571                   "point",
5572                   "line",
5573                   "rectangle",
5574                   "fill rectangle",
5575                   "circle",
5576                   "fill circle",
5577                   "ellipse",
5578                   "fill ellipse",
5579                   "polygon",
5580                   "fill polygon",
5581                   (char *) NULL,
5582                 };
5583
5584               /*
5585                 Select a command from the pop-up menu.
5586               */
5587               element=(ElementType) (XMenuWidget(display,windows,
5588                 DrawMenu[id],Elements,command)+1);
5589               break;
5590             }
5591             case DrawColorCommand:
5592             {
5593               const char
5594                 *ColorMenu[MaxNumberPens+1];
5595
5596               int
5597                 pen_number;
5598
5599               MagickBooleanType
5600                 transparent;
5601
5602               XColor
5603                 color;
5604
5605               /*
5606                 Initialize menu selections.
5607               */
5608               for (i=0; i < (int) (MaxNumberPens-2); i++)
5609                 ColorMenu[i]=resource_info->pen_colors[i];
5610               ColorMenu[MaxNumberPens-2]="transparent";
5611               ColorMenu[MaxNumberPens-1]="Browser...";
5612               ColorMenu[MaxNumberPens]=(char *) NULL;
5613               /*
5614                 Select a pen color from the pop-up menu.
5615               */
5616               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5617                 (const char **) ColorMenu,command);
5618               if (pen_number < 0)
5619                 break;
5620               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5621                 MagickFalse;
5622               if (transparent != MagickFalse)
5623                 {
5624                   draw_info.stencil=TransparentStencil;
5625                   break;
5626                 }
5627               if (pen_number == (MaxNumberPens-1))
5628                 {
5629                   static char
5630                     color_name[MaxTextExtent] = "gray";
5631
5632                   /*
5633                     Select a pen color from a dialog.
5634                   */
5635                   resource_info->pen_colors[pen_number]=color_name;
5636                   XColorBrowserWidget(display,windows,"Select",color_name);
5637                   if (*color_name == '\0')
5638                     break;
5639                 }
5640               /*
5641                 Set pen color.
5642               */
5643               (void) XParseColor(display,windows->map_info->colormap,
5644                 resource_info->pen_colors[pen_number],&color);
5645               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5646                 (unsigned int) MaxColors,&color);
5647               windows->pixel_info->pen_colors[pen_number]=color;
5648               pen_id=(unsigned int) pen_number;
5649               draw_info.stencil=OpaqueStencil;
5650               break;
5651             }
5652             case DrawStippleCommand:
5653             {
5654               Image
5655                 *stipple_image;
5656
5657               ImageInfo
5658                 *image_info;
5659
5660               int
5661                 status;
5662
5663               static char
5664                 filename[MaxTextExtent] = "\0";
5665
5666               static const char
5667                 *StipplesMenu[] =
5668                 {
5669                   "Brick",
5670                   "Diagonal",
5671                   "Scales",
5672                   "Vertical",
5673                   "Wavy",
5674                   "Translucent",
5675                   "Opaque",
5676                   (char *) NULL,
5677                   (char *) NULL,
5678                 };
5679
5680               /*
5681                 Select a command from the pop-up menu.
5682               */
5683               StipplesMenu[7]="Open...";
5684               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5685                 command);
5686               if (entry < 0)
5687                 break;
5688               if (stipple != (Pixmap) NULL)
5689                 (void) XFreePixmap(display,stipple);
5690               stipple=(Pixmap) NULL;
5691               if (entry != 7)
5692                 {
5693                   switch (entry)
5694                   {
5695                     case 0:
5696                     {
5697                       stipple=XCreateBitmapFromData(display,root_window,
5698                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5699                       break;
5700                     }
5701                     case 1:
5702                     {
5703                       stipple=XCreateBitmapFromData(display,root_window,
5704                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5705                       break;
5706                     }
5707                     case 2:
5708                     {
5709                       stipple=XCreateBitmapFromData(display,root_window,
5710                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5711                       break;
5712                     }
5713                     case 3:
5714                     {
5715                       stipple=XCreateBitmapFromData(display,root_window,
5716                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5717                       break;
5718                     }
5719                     case 4:
5720                     {
5721                       stipple=XCreateBitmapFromData(display,root_window,
5722                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5723                       break;
5724                     }
5725                     case 5:
5726                     {
5727                       stipple=XCreateBitmapFromData(display,root_window,
5728                         (char *) HighlightBitmap,HighlightWidth,
5729                         HighlightHeight);
5730                       break;
5731                     }
5732                     case 6:
5733                     default:
5734                     {
5735                       stipple=XCreateBitmapFromData(display,root_window,
5736                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5737                       break;
5738                     }
5739                   }
5740                   break;
5741                 }
5742               XFileBrowserWidget(display,windows,"Stipple",filename);
5743               if (*filename == '\0')
5744                 break;
5745               /*
5746                 Read image.
5747               */
5748               XSetCursorState(display,windows,MagickTrue);
5749               XCheckRefreshWindows(display,windows);
5750               image_info=AcquireImageInfo();
5751               (void) CopyMagickString(image_info->filename,filename,
5752                 MaxTextExtent);
5753               stipple_image=ReadImage(image_info,exception);
5754               CatchException(exception);
5755               XSetCursorState(display,windows,MagickFalse);
5756               if (stipple_image == (Image *) NULL)
5757                 break;
5758               (void) AcquireUniqueFileResource(filename);
5759               (void) FormatLocaleString(stipple_image->filename,MaxTextExtent,
5760                 "xbm:%s",filename);
5761               (void) WriteImage(image_info,stipple_image,exception);
5762               stipple_image=DestroyImage(stipple_image);
5763               image_info=DestroyImageInfo(image_info);
5764               status=XReadBitmapFile(display,root_window,filename,&width,
5765                 &height,&stipple,&x,&y);
5766               (void) RelinquishUniqueFileResource(filename);
5767               if ((status != BitmapSuccess) != 0)
5768                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5769                   filename);
5770               break;
5771             }
5772             case DrawWidthCommand:
5773             {
5774               static char
5775                 width[MaxTextExtent] = "0";
5776
5777               static const char
5778                 *WidthsMenu[] =
5779                 {
5780                   "1",
5781                   "2",
5782                   "4",
5783                   "8",
5784                   "16",
5785                   "Dialog...",
5786                   (char *) NULL,
5787                 };
5788
5789               /*
5790                 Select a command from the pop-up menu.
5791               */
5792               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5793                 command);
5794               if (entry < 0)
5795                 break;
5796               if (entry != 5)
5797                 {
5798                   line_width=(unsigned int) StringToUnsignedLong(
5799                     WidthsMenu[entry]);
5800                   break;
5801                 }
5802               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5803                 width);
5804               if (*width == '\0')
5805                 break;
5806               line_width=(unsigned int) StringToUnsignedLong(width);
5807               break;
5808             }
5809             case DrawUndoCommand:
5810             {
5811               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5812                 image,exception);
5813               break;
5814             }
5815             case DrawHelpCommand:
5816             {
5817               XTextViewWidget(display,resource_info,windows,MagickFalse,
5818                 "Help Viewer - Image Rotation",ImageDrawHelp);
5819               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5820               break;
5821             }
5822             case DrawDismissCommand:
5823             {
5824               /*
5825                 Prematurely exit.
5826               */
5827               state|=EscapeState;
5828               state|=ExitState;
5829               break;
5830             }
5831             default:
5832               break;
5833           }
5834           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5835           continue;
5836         }
5837       switch (event.type)
5838       {
5839         case ButtonPress:
5840         {
5841           if (event.xbutton.button != Button1)
5842             break;
5843           if (event.xbutton.window != windows->image.id)
5844             break;
5845           /*
5846             exit loop.
5847           */
5848           x=event.xbutton.x;
5849           y=event.xbutton.y;
5850           state|=ExitState;
5851           break;
5852         }
5853         case ButtonRelease:
5854           break;
5855         case Expose:
5856           break;
5857         case KeyPress:
5858         {
5859           KeySym
5860             key_symbol;
5861
5862           if (event.xkey.window != windows->image.id)
5863             break;
5864           /*
5865             Respond to a user key press.
5866           */
5867           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5868             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5869           switch ((int) key_symbol)
5870           {
5871             case XK_Escape:
5872             case XK_F20:
5873             {
5874               /*
5875                 Prematurely exit.
5876               */
5877               state|=EscapeState;
5878               state|=ExitState;
5879               break;
5880             }
5881             case XK_F1:
5882             case XK_Help:
5883             {
5884               XTextViewWidget(display,resource_info,windows,MagickFalse,
5885                 "Help Viewer - Image Rotation",ImageDrawHelp);
5886               break;
5887             }
5888             default:
5889             {
5890               (void) XBell(display,0);
5891               break;
5892             }
5893           }
5894           break;
5895         }
5896         case MotionNotify:
5897         {
5898           /*
5899             Map and unmap Info widget as text cursor crosses its boundaries.
5900           */
5901           x=event.xmotion.x;
5902           y=event.xmotion.y;
5903           if (windows->info.mapped != MagickFalse)
5904             {
5905               if ((x < (int) (windows->info.x+windows->info.width)) &&
5906                   (y < (int) (windows->info.y+windows->info.height)))
5907                 (void) XWithdrawWindow(display,windows->info.id,
5908                   windows->info.screen);
5909             }
5910           else
5911             if ((x > (int) (windows->info.x+windows->info.width)) ||
5912                 (y > (int) (windows->info.y+windows->info.height)))
5913               (void) XMapWindow(display,windows->info.id);
5914           break;
5915         }
5916       }
5917     } while ((state & ExitState) == 0);
5918     (void) XSelectInput(display,windows->image.id,
5919       windows->image.attributes.event_mask);
5920     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5921     if ((state & EscapeState) != 0)
5922       break;
5923     /*
5924       Draw element as pointer moves until the button is released.
5925     */
5926     distance=0;
5927     degrees=0.0;
5928     line_info.x1=x;
5929     line_info.y1=y;
5930     line_info.x2=x;
5931     line_info.y2=y;
5932     rectangle_info.x=(ssize_t) x;
5933     rectangle_info.y=(ssize_t) y;
5934     rectangle_info.width=0;
5935     rectangle_info.height=0;
5936     number_coordinates=1;
5937     coordinate_info->x=x;
5938     coordinate_info->y=y;
5939     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5940     state=DefaultState;
5941     do
5942     {
5943       switch (element)
5944       {
5945         case PointElement:
5946         default:
5947         {
5948           if (number_coordinates > 1)
5949             {
5950               (void) XDrawLines(display,windows->image.id,
5951                 windows->image.highlight_context,coordinate_info,
5952                 number_coordinates,CoordModeOrigin);
5953               (void) FormatLocaleString(text,MaxTextExtent," %+d%+d",
5954                 coordinate_info[number_coordinates-1].x,
5955                 coordinate_info[number_coordinates-1].y);
5956               XInfoWidget(display,windows,text);
5957             }
5958           break;
5959         }
5960         case LineElement:
5961         {
5962           if (distance > 9)
5963             {
5964               /*
5965                 Display angle of the line.
5966               */
5967               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5968                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5969               (void) FormatLocaleString(text,MaxTextExtent," %g",
5970                 (double) degrees);
5971               XInfoWidget(display,windows,text);
5972               XHighlightLine(display,windows->image.id,
5973                 windows->image.highlight_context,&line_info);
5974             }
5975           else
5976             if (windows->info.mapped != MagickFalse)
5977               (void) XWithdrawWindow(display,windows->info.id,
5978                 windows->info.screen);
5979           break;
5980         }
5981         case RectangleElement:
5982         case FillRectangleElement:
5983         {
5984           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5985             {
5986               /*
5987                 Display info and draw drawing rectangle.
5988               */
5989               (void) FormatLocaleString(text,MaxTextExtent,
5990                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5991                 (double) rectangle_info.height,(double) rectangle_info.x,
5992                 (double) rectangle_info.y);
5993               XInfoWidget(display,windows,text);
5994               XHighlightRectangle(display,windows->image.id,
5995                 windows->image.highlight_context,&rectangle_info);
5996             }
5997           else
5998             if (windows->info.mapped != MagickFalse)
5999               (void) XWithdrawWindow(display,windows->info.id,
6000                 windows->info.screen);
6001           break;
6002         }
6003         case CircleElement:
6004         case FillCircleElement:
6005         case EllipseElement:
6006         case FillEllipseElement:
6007         {
6008           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6009             {
6010               /*
6011                 Display info and draw drawing rectangle.
6012               */
6013               (void) FormatLocaleString(text,MaxTextExtent,
6014                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
6015                 (double) rectangle_info.height,(double) rectangle_info.x,
6016                 (double) rectangle_info.y);
6017               XInfoWidget(display,windows,text);
6018               XHighlightEllipse(display,windows->image.id,
6019                 windows->image.highlight_context,&rectangle_info);
6020             }
6021           else
6022             if (windows->info.mapped != MagickFalse)
6023               (void) XWithdrawWindow(display,windows->info.id,
6024                 windows->info.screen);
6025           break;
6026         }
6027         case PolygonElement:
6028         case FillPolygonElement:
6029         {
6030           if (number_coordinates > 1)
6031             (void) XDrawLines(display,windows->image.id,
6032               windows->image.highlight_context,coordinate_info,
6033               number_coordinates,CoordModeOrigin);
6034           if (distance > 9)
6035             {
6036               /*
6037                 Display angle of the line.
6038               */
6039               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
6040                 line_info.y1),(double) (line_info.x2-line_info.x1)));
6041               (void) FormatLocaleString(text,MaxTextExtent," %g",
6042                 (double) degrees);
6043               XInfoWidget(display,windows,text);
6044               XHighlightLine(display,windows->image.id,
6045                 windows->image.highlight_context,&line_info);
6046             }
6047           else
6048             if (windows->info.mapped != MagickFalse)
6049               (void) XWithdrawWindow(display,windows->info.id,
6050                 windows->info.screen);
6051           break;
6052         }
6053       }
6054       /*
6055         Wait for next event.
6056       */
6057       XScreenEvent(display,windows,&event,exception);
6058       switch (element)
6059       {
6060         case PointElement:
6061         default:
6062         {
6063           if (number_coordinates > 1)
6064             (void) XDrawLines(display,windows->image.id,
6065               windows->image.highlight_context,coordinate_info,
6066               number_coordinates,CoordModeOrigin);
6067           break;
6068         }
6069         case LineElement:
6070         {
6071           if (distance > 9)
6072             XHighlightLine(display,windows->image.id,
6073               windows->image.highlight_context,&line_info);
6074           break;
6075         }
6076         case RectangleElement:
6077         case FillRectangleElement:
6078         {
6079           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6080             XHighlightRectangle(display,windows->image.id,
6081               windows->image.highlight_context,&rectangle_info);
6082           break;
6083         }
6084         case CircleElement:
6085         case FillCircleElement:
6086         case EllipseElement:
6087         case FillEllipseElement:
6088         {
6089           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6090             XHighlightEllipse(display,windows->image.id,
6091               windows->image.highlight_context,&rectangle_info);
6092           break;
6093         }
6094         case PolygonElement:
6095         case FillPolygonElement:
6096         {
6097           if (number_coordinates > 1)
6098             (void) XDrawLines(display,windows->image.id,
6099               windows->image.highlight_context,coordinate_info,
6100               number_coordinates,CoordModeOrigin);
6101           if (distance > 9)
6102             XHighlightLine(display,windows->image.id,
6103               windows->image.highlight_context,&line_info);
6104           break;
6105         }
6106       }
6107       switch (event.type)
6108       {
6109         case ButtonPress:
6110           break;
6111         case ButtonRelease:
6112         {
6113           /*
6114             User has committed to element.
6115           */
6116           line_info.x2=event.xbutton.x;
6117           line_info.y2=event.xbutton.y;
6118           rectangle_info.x=(ssize_t) event.xbutton.x;
6119           rectangle_info.y=(ssize_t) event.xbutton.y;
6120           coordinate_info[number_coordinates].x=event.xbutton.x;
6121           coordinate_info[number_coordinates].y=event.xbutton.y;
6122           if (((element != PolygonElement) &&
6123                (element != FillPolygonElement)) || (distance <= 9))
6124             {
6125               state|=ExitState;
6126               break;
6127             }
6128           number_coordinates++;
6129           if (number_coordinates < (int) max_coordinates)
6130             {
6131               line_info.x1=event.xbutton.x;
6132               line_info.y1=event.xbutton.y;
6133               break;
6134             }
6135           max_coordinates<<=1;
6136           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6137             max_coordinates,sizeof(*coordinate_info));
6138           if (coordinate_info == (XPoint *) NULL)
6139             (void) ThrowMagickException(exception,GetMagickModule(),
6140               ResourceLimitError,"MemoryAllocationFailed","'%s'","...");
6141           break;
6142         }
6143         case Expose:
6144           break;
6145         case MotionNotify:
6146         {
6147           if (event.xmotion.window != windows->image.id)
6148             break;
6149           if (element != PointElement)
6150             {
6151               line_info.x2=event.xmotion.x;
6152               line_info.y2=event.xmotion.y;
6153               rectangle_info.x=(ssize_t) event.xmotion.x;
6154               rectangle_info.y=(ssize_t) event.xmotion.y;
6155               break;
6156             }
6157           coordinate_info[number_coordinates].x=event.xbutton.x;
6158           coordinate_info[number_coordinates].y=event.xbutton.y;
6159           number_coordinates++;
6160           if (number_coordinates < (int) max_coordinates)
6161             break;
6162           max_coordinates<<=1;
6163           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6164             max_coordinates,sizeof(*coordinate_info));
6165           if (coordinate_info == (XPoint *) NULL)
6166             (void) ThrowMagickException(exception,GetMagickModule(),
6167               ResourceLimitError,"MemoryAllocationFailed","'%s'","...");
6168           break;
6169         }
6170         default:
6171           break;
6172       }
6173       /*
6174         Check boundary conditions.
6175       */
6176       if (line_info.x2 < 0)
6177         line_info.x2=0;
6178       else
6179         if (line_info.x2 > (int) windows->image.width)
6180           line_info.x2=(short) windows->image.width;
6181       if (line_info.y2 < 0)
6182         line_info.y2=0;
6183       else
6184         if (line_info.y2 > (int) windows->image.height)
6185           line_info.y2=(short) windows->image.height;
6186       distance=(unsigned int)
6187         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6188          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6189       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6190           ((state & ExitState) != 0))
6191         {
6192           if (rectangle_info.x < 0)
6193             rectangle_info.x=0;
6194           else
6195             if (rectangle_info.x > (ssize_t) windows->image.width)
6196               rectangle_info.x=(ssize_t) windows->image.width;
6197           if ((int) rectangle_info.x < x)
6198             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6199           else
6200             {
6201               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6202               rectangle_info.x=(ssize_t) x;
6203             }
6204           if (rectangle_info.y < 0)
6205             rectangle_info.y=0;
6206           else
6207             if (rectangle_info.y > (ssize_t) windows->image.height)
6208               rectangle_info.y=(ssize_t) windows->image.height;
6209           if ((int) rectangle_info.y < y)
6210             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6211           else
6212             {
6213               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6214               rectangle_info.y=(ssize_t) y;
6215             }
6216         }
6217     } while ((state & ExitState) == 0);
6218     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6219     if ((element == PointElement) || (element == PolygonElement) ||
6220         (element == FillPolygonElement))
6221       {
6222         /*
6223           Determine polygon bounding box.
6224         */
6225         rectangle_info.x=(ssize_t) coordinate_info->x;
6226         rectangle_info.y=(ssize_t) coordinate_info->y;
6227         x=coordinate_info->x;
6228         y=coordinate_info->y;
6229         for (i=1; i < number_coordinates; i++)
6230         {
6231           if (coordinate_info[i].x > x)
6232             x=coordinate_info[i].x;
6233           if (coordinate_info[i].y > y)
6234             y=coordinate_info[i].y;
6235           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6236             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6237           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6238             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6239         }
6240         rectangle_info.width=(size_t) (x-rectangle_info.x);
6241         rectangle_info.height=(size_t) (y-rectangle_info.y);
6242         for (i=0; i < number_coordinates; i++)
6243         {
6244           coordinate_info[i].x-=rectangle_info.x;
6245           coordinate_info[i].y-=rectangle_info.y;
6246         }
6247       }
6248     else
6249       if (distance <= 9)
6250         continue;
6251       else
6252         if ((element == RectangleElement) ||
6253             (element == CircleElement) || (element == EllipseElement))
6254           {
6255             rectangle_info.width--;
6256             rectangle_info.height--;
6257           }
6258     /*
6259       Drawing is relative to image configuration.
6260     */
6261     draw_info.x=(int) rectangle_info.x;
6262     draw_info.y=(int) rectangle_info.y;
6263     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6264       image,exception);
6265     width=(unsigned int) (*image)->columns;
6266     height=(unsigned int) (*image)->rows;
6267     x=0;
6268     y=0;
6269     if (windows->image.crop_geometry != (char *) NULL)
6270       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6271     draw_info.x+=windows->image.x-(line_width/2);
6272     if (draw_info.x < 0)
6273       draw_info.x=0;
6274     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6275     draw_info.y+=windows->image.y-(line_width/2);
6276     if (draw_info.y < 0)
6277       draw_info.y=0;
6278     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6279     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6280     if (draw_info.width > (unsigned int) (*image)->columns)
6281       draw_info.width=(unsigned int) (*image)->columns;
6282     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6283     if (draw_info.height > (unsigned int) (*image)->rows)
6284       draw_info.height=(unsigned int) (*image)->rows;
6285     (void) FormatLocaleString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6286       width*draw_info.width/windows->image.ximage->width,
6287       height*draw_info.height/windows->image.ximage->height,
6288       draw_info.x+x,draw_info.y+y);
6289     /*
6290       Initialize drawing attributes.
6291     */
6292     draw_info.degrees=0.0;
6293     draw_info.element=element;
6294     draw_info.stipple=stipple;
6295     draw_info.line_width=line_width;
6296     draw_info.line_info=line_info;
6297     if (line_info.x1 > (int) (line_width/2))
6298       draw_info.line_info.x1=(short) line_width/2;
6299     if (line_info.y1 > (int) (line_width/2))
6300       draw_info.line_info.y1=(short) line_width/2;
6301     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6302     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6303     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6304       {
6305         draw_info.line_info.x2=(-draw_info.line_info.x2);
6306         draw_info.line_info.y2=(-draw_info.line_info.y2);
6307       }
6308     if (draw_info.line_info.x2 < 0)
6309       {
6310         draw_info.line_info.x2=(-draw_info.line_info.x2);
6311         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6312       }
6313     if (draw_info.line_info.y2 < 0)
6314       {
6315         draw_info.line_info.y2=(-draw_info.line_info.y2);
6316         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6317       }
6318     draw_info.rectangle_info=rectangle_info;
6319     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6320       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6321     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6322       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6323     draw_info.number_coordinates=(unsigned int) number_coordinates;
6324     draw_info.coordinate_info=coordinate_info;
6325     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6326     /*
6327       Draw element on image.
6328     */
6329     XSetCursorState(display,windows,MagickTrue);
6330     XCheckRefreshWindows(display,windows);
6331     status=XDrawImage(display,windows->pixel_info,&draw_info,*image,exception);
6332     XSetCursorState(display,windows,MagickFalse);
6333     /*
6334       Update image colormap and return to image drawing.
6335     */
6336     XConfigureImageColormap(display,resource_info,windows,*image,exception);
6337     (void) XConfigureImage(display,resource_info,windows,*image,exception);
6338   }
6339   XSetCursorState(display,windows,MagickFalse);
6340   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6341   return(status != 0 ? MagickTrue : MagickFalse);
6342 }
6343 \f
6344 /*
6345 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6346 %                                                                             %
6347 %                                                                             %
6348 %                                                                             %
6349 +   X D r a w P a n R e c t a n g l e                                         %
6350 %                                                                             %
6351 %                                                                             %
6352 %                                                                             %
6353 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6354 %
6355 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6356 %  displays a zoom image and the rectangle shows which portion of the image is
6357 %  displayed in the Image window.
6358 %
6359 %  The format of the XDrawPanRectangle method is:
6360 %
6361 %      XDrawPanRectangle(Display *display,XWindows *windows)
6362 %
6363 %  A description of each parameter follows:
6364 %
6365 %    o display: Specifies a connection to an X server;  returned from
6366 %      XOpenDisplay.
6367 %
6368 %    o windows: Specifies a pointer to a XWindows structure.
6369 %
6370 */
6371 static void XDrawPanRectangle(Display *display,XWindows *windows)
6372 {
6373   MagickRealType
6374     scale_factor;
6375
6376   RectangleInfo
6377     highlight_info;
6378
6379   /*
6380     Determine dimensions of the panning rectangle.
6381   */
6382   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6383   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6384   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6385   scale_factor=(MagickRealType)
6386     windows->pan.height/windows->image.ximage->height;
6387   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6388   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6389   /*
6390     Display the panning rectangle.
6391   */
6392   (void) XClearWindow(display,windows->pan.id);
6393   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6394     &highlight_info);
6395 }
6396 \f
6397 /*
6398 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6399 %                                                                             %
6400 %                                                                             %
6401 %                                                                             %
6402 +   X I m a g e C a c h e                                                     %
6403 %                                                                             %
6404 %                                                                             %
6405 %                                                                             %
6406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6407 %
6408 %  XImageCache() handles the creation, manipulation, and destruction of the
6409 %  image cache (undo and redo buffers).
6410 %
6411 %  The format of the XImageCache method is:
6412 %
6413 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6414 %        XWindows *windows,const CommandType command,Image **image,
6415 %        ExceptionInfo *exception)
6416 %
6417 %  A description of each parameter follows:
6418 %
6419 %    o display: Specifies a connection to an X server; returned from
6420 %      XOpenDisplay.
6421 %
6422 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6423 %
6424 %    o windows: Specifies a pointer to a XWindows structure.
6425 %
6426 %    o command: Specifies a command to perform.
6427 %
6428 %    o image: the image;  XImageCache may transform the image and return a new
6429 %      image pointer.
6430 %
6431 %    o exception: return any errors or warnings in this structure.
6432 %
6433 */
6434 static void XImageCache(Display *display,XResourceInfo *resource_info,
6435   XWindows *windows,const CommandType command,Image **image,
6436   ExceptionInfo *exception)
6437 {
6438   Image
6439     *cache_image;
6440
6441   static Image
6442     *redo_image = (Image *) NULL,
6443     *undo_image = (Image *) NULL;
6444
6445   switch (command)
6446   {
6447     case FreeBuffersCommand:
6448     {
6449       /*
6450         Free memory from the undo and redo cache.
6451       */
6452       while (undo_image != (Image *) NULL)
6453       {
6454         cache_image=undo_image;
6455         undo_image=GetPreviousImageInList(undo_image);
6456         cache_image->list=DestroyImage(cache_image->list);
6457         cache_image=DestroyImage(cache_image);
6458       }
6459       undo_image=NewImageList();
6460       if (redo_image != (Image *) NULL)
6461         redo_image=DestroyImage(redo_image);
6462       redo_image=NewImageList();
6463       return;
6464     }
6465     case UndoCommand:
6466     {
6467       char
6468         image_geometry[MaxTextExtent];
6469
6470       /*
6471         Undo the last image transformation.
6472       */
6473       if (undo_image == (Image *) NULL)
6474         {
6475           (void) XBell(display,0);
6476           return;
6477         }
6478       cache_image=undo_image;
6479       undo_image=GetPreviousImageInList(undo_image);
6480       windows->image.window_changes.width=(int) cache_image->columns;
6481       windows->image.window_changes.height=(int) cache_image->rows;
6482       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6483         windows->image.ximage->width,windows->image.ximage->height);
6484       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
6485         exception);
6486       if (windows->image.crop_geometry != (char *) NULL)
6487         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
6488           windows->image.crop_geometry);
6489       windows->image.crop_geometry=cache_image->geometry;
6490       if (redo_image != (Image *) NULL)
6491         redo_image=DestroyImage(redo_image);
6492       redo_image=(*image);
6493       *image=cache_image->list;
6494       cache_image=DestroyImage(cache_image);
6495       if (windows->image.orphan != MagickFalse)
6496         return;
6497       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6498       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6499       return;
6500     }
6501     case CutCommand:
6502     case PasteCommand:
6503     case ApplyCommand:
6504     case HalfSizeCommand:
6505     case OriginalSizeCommand:
6506     case DoubleSizeCommand:
6507     case ResizeCommand:
6508     case TrimCommand:
6509     case CropCommand:
6510     case ChopCommand:
6511     case FlipCommand:
6512     case FlopCommand:
6513     case RotateRightCommand:
6514     case RotateLeftCommand:
6515     case RotateCommand:
6516     case ShearCommand:
6517     case RollCommand:
6518     case NegateCommand:
6519     case ContrastStretchCommand:
6520     case SigmoidalContrastCommand:
6521     case NormalizeCommand:
6522     case EqualizeCommand:
6523     case HueCommand:
6524     case SaturationCommand:
6525     case BrightnessCommand:
6526     case GammaCommand:
6527     case SpiffCommand:
6528     case DullCommand:
6529     case GrayscaleCommand:
6530     case MapCommand:
6531     case QuantizeCommand:
6532     case DespeckleCommand:
6533     case EmbossCommand:
6534     case ReduceNoiseCommand:
6535     case AddNoiseCommand:
6536     case SharpenCommand:
6537     case BlurCommand:
6538     case ThresholdCommand:
6539     case EdgeDetectCommand:
6540     case SpreadCommand:
6541     case ShadeCommand:
6542     case RaiseCommand:
6543     case SegmentCommand:
6544     case SolarizeCommand:
6545     case SepiaToneCommand:
6546     case SwirlCommand:
6547     case ImplodeCommand:
6548     case VignetteCommand:
6549     case WaveCommand:
6550     case OilPaintCommand:
6551     case CharcoalDrawCommand:
6552     case AnnotateCommand:
6553     case AddBorderCommand:
6554     case AddFrameCommand:
6555     case CompositeCommand:
6556     case CommentCommand:
6557     case LaunchCommand:
6558     case RegionofInterestCommand:
6559     case SaveToUndoBufferCommand:
6560     case RedoCommand:
6561     {
6562       Image
6563         *previous_image;
6564
6565       ssize_t
6566         bytes;
6567
6568       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelInfo));
6569       if (undo_image != (Image *) NULL)
6570         {
6571           /*
6572             Ensure the undo cache has enough memory available.
6573           */
6574           previous_image=undo_image;
6575           while (previous_image != (Image *) NULL)
6576           {
6577             bytes+=previous_image->list->columns*previous_image->list->rows*
6578               sizeof(PixelInfo);
6579             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6580               {
6581                 previous_image=GetPreviousImageInList(previous_image);
6582                 continue;
6583               }
6584             bytes-=previous_image->list->columns*previous_image->list->rows*
6585               sizeof(PixelInfo);
6586             if (previous_image == undo_image)
6587               undo_image=NewImageList();
6588             else
6589               previous_image->next->previous=NewImageList();
6590             break;
6591           }
6592           while (previous_image != (Image *) NULL)
6593           {
6594             /*
6595               Delete any excess memory from undo cache.
6596             */
6597             cache_image=previous_image;
6598             previous_image=GetPreviousImageInList(previous_image);
6599             cache_image->list=DestroyImage(cache_image->list);
6600             cache_image=DestroyImage(cache_image);
6601           }
6602         }
6603       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6604         break;
6605       /*
6606         Save image before transformations are applied.
6607       */
6608       cache_image=AcquireImage((ImageInfo *) NULL,exception);
6609       if (cache_image == (Image *) NULL)
6610         break;
6611       XSetCursorState(display,windows,MagickTrue);
6612       XCheckRefreshWindows(display,windows);
6613       cache_image->list=CloneImage(*image,0,0,MagickTrue,exception);
6614       XSetCursorState(display,windows,MagickFalse);
6615       if (cache_image->list == (Image *) NULL)
6616         {
6617           cache_image=DestroyImage(cache_image);
6618           break;
6619         }
6620       cache_image->columns=(size_t) windows->image.ximage->width;
6621       cache_image->rows=(size_t) windows->image.ximage->height;
6622       cache_image->geometry=windows->image.crop_geometry;
6623       if (windows->image.crop_geometry != (char *) NULL)
6624         {
6625           cache_image->geometry=AcquireString((char *) NULL);
6626           (void) CopyMagickString(cache_image->geometry,
6627             windows->image.crop_geometry,MaxTextExtent);
6628         }
6629       if (undo_image == (Image *) NULL)
6630         {
6631           undo_image=cache_image;
6632           break;
6633         }
6634       undo_image->next=cache_image;
6635       undo_image->next->previous=undo_image;
6636       undo_image=undo_image->next;
6637       break;
6638     }
6639     default:
6640       break;
6641   }
6642   if (command == RedoCommand)
6643     {
6644       /*
6645         Redo the last image transformation.
6646       */
6647       if (redo_image == (Image *) NULL)
6648         {
6649           (void) XBell(display,0);
6650           return;
6651         }
6652       windows->image.window_changes.width=(int) redo_image->columns;
6653       windows->image.window_changes.height=(int) redo_image->rows;
6654       if (windows->image.crop_geometry != (char *) NULL)
6655         windows->image.crop_geometry=(char *)
6656           RelinquishMagickMemory(windows->image.crop_geometry);
6657       windows->image.crop_geometry=redo_image->geometry;
6658       *image=DestroyImage(*image);
6659       *image=redo_image;
6660       redo_image=NewImageList();
6661       if (windows->image.orphan != MagickFalse)
6662         return;
6663       XConfigureImageColormap(display,resource_info,windows,*image,exception);
6664       (void) XConfigureImage(display,resource_info,windows,*image,exception);
6665       return;
6666     }
6667   if (command != InfoCommand)
6668     return;
6669   /*
6670     Display image info.
6671   */
6672   XSetCursorState(display,windows,MagickTrue);
6673   XCheckRefreshWindows(display,windows);
6674   XDisplayImageInfo(display,resource_info,windows,undo_image,*image,exception);
6675   XSetCursorState(display,windows,MagickFalse);
6676 }
6677 \f
6678 /*
6679 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6680 %                                                                             %
6681 %                                                                             %
6682 %                                                                             %
6683 +   X I m a g e W i n d o w C o m m a n d                                     %
6684 %                                                                             %
6685 %                                                                             %
6686 %                                                                             %
6687 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6688 %
6689 %  XImageWindowCommand() makes a transform to the image or Image window as
6690 %  specified by a user menu button or keyboard command.
6691 %
6692 %  The format of the XImageWindowCommand method is:
6693 %
6694 %      CommandType XImageWindowCommand(Display *display,
6695 %        XResourceInfo *resource_info,XWindows *windows,
6696 %        const MagickStatusType state,KeySym key_symbol,Image **image,
6697 %        ExceptionInfo *exception)
6698 %
6699 %  A description of each parameter follows:
6700 %
6701 %    o nexus:  Method XImageWindowCommand returns an image when the
6702 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6703 %      image is returned.
6704 %
6705 %    o display: Specifies a connection to an X server; returned from
6706 %      XOpenDisplay.
6707 %
6708 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6709 %
6710 %    o windows: Specifies a pointer to a XWindows structure.
6711 %
6712 %    o state: key mask.
6713 %
6714 %    o key_symbol: Specifies a command to perform.
6715 %
6716 %    o image: the image;  XImageWIndowCommand may transform the image and
6717 %      return a new image pointer.
6718 %
6719 %    o exception: return any errors or warnings in this structure.
6720 %
6721 */
6722 static CommandType XImageWindowCommand(Display *display,
6723   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6724   KeySym key_symbol,Image **image,ExceptionInfo *exception)
6725 {
6726   static char
6727     delta[MaxTextExtent] = "";
6728
6729   static const char
6730     Digits[] = "01234567890";
6731
6732   static KeySym
6733     last_symbol = XK_0;
6734
6735   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6736     {
6737       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6738         {
6739           *delta='\0';
6740           resource_info->quantum=1;
6741         }
6742       last_symbol=key_symbol;
6743       delta[strlen(delta)+1]='\0';
6744       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6745       resource_info->quantum=StringToLong(delta);
6746       return(NullCommand);
6747     }
6748   last_symbol=key_symbol;
6749   if (resource_info->immutable)
6750     {
6751       /*
6752         Virtual image window has a restricted command set.
6753       */
6754       switch (key_symbol)
6755       {
6756         case XK_question:
6757           return(InfoCommand);
6758         case XK_p:
6759         case XK_Print:
6760           return(PrintCommand);
6761         case XK_space:
6762           return(NextCommand);
6763         case XK_q:
6764         case XK_Escape:
6765           return(QuitCommand);
6766         default:
6767           break;
6768       }
6769       return(NullCommand);
6770     }
6771   switch ((int) key_symbol)
6772   {
6773     case XK_o:
6774     {
6775       if ((state & ControlMask) == 0)
6776         break;
6777       return(OpenCommand);
6778     }
6779     case XK_space:
6780       return(NextCommand);
6781     case XK_BackSpace:
6782       return(FormerCommand);
6783     case XK_s:
6784     {
6785       if ((state & Mod1Mask) != 0)
6786         return(SwirlCommand);
6787       if ((state & ControlMask) == 0)
6788         return(ShearCommand);
6789       return(SaveCommand);
6790     }
6791     case XK_p:
6792     case XK_Print:
6793     {
6794       if ((state & Mod1Mask) != 0)
6795         return(OilPaintCommand);
6796       if ((state & Mod4Mask) != 0)
6797         return(ColorCommand);
6798       if ((state & ControlMask) == 0)
6799         return(NullCommand);
6800       return(PrintCommand);
6801     }
6802     case XK_d:
6803     {
6804       if ((state & Mod4Mask) != 0)
6805         return(DrawCommand);
6806       if ((state & ControlMask) == 0)
6807         return(NullCommand);
6808       return(DeleteCommand);
6809     }
6810     case XK_Select:
6811     {
6812       if ((state & ControlMask) == 0)
6813         return(NullCommand);
6814       return(SelectCommand);
6815     }
6816     case XK_n:
6817     {
6818       if ((state & ControlMask) == 0)
6819         return(NullCommand);
6820       return(NewCommand);
6821     }
6822     case XK_q:
6823     case XK_Escape:
6824       return(QuitCommand);
6825     case XK_z:
6826     case XK_Undo:
6827     {
6828       if ((state & ControlMask) == 0)
6829         return(NullCommand);
6830       return(UndoCommand);
6831     }
6832     case XK_r:
6833     case XK_Redo:
6834     {
6835       if ((state & ControlMask) == 0)
6836         return(RollCommand);
6837       return(RedoCommand);
6838     }
6839     case XK_x:
6840     {
6841       if ((state & ControlMask) == 0)
6842         return(NullCommand);
6843       return(CutCommand);
6844     }
6845     case XK_c:
6846     {
6847       if ((state & Mod1Mask) != 0)
6848         return(CharcoalDrawCommand);
6849       if ((state & ControlMask) == 0)
6850         return(CropCommand);
6851       return(CopyCommand);
6852     }
6853     case XK_v:
6854     case XK_Insert:
6855     {
6856       if ((state & Mod4Mask) != 0)
6857         return(CompositeCommand);
6858       if ((state & ControlMask) == 0)
6859         return(FlipCommand);
6860       return(PasteCommand);
6861     }
6862     case XK_less:
6863       return(HalfSizeCommand);
6864     case XK_minus:
6865       return(OriginalSizeCommand);
6866     case XK_greater:
6867       return(DoubleSizeCommand);
6868     case XK_percent:
6869       return(ResizeCommand);
6870     case XK_at:
6871       return(RefreshCommand);
6872     case XK_bracketleft:
6873       return(ChopCommand);
6874     case XK_h:
6875       return(FlopCommand);
6876     case XK_slash:
6877       return(RotateRightCommand);
6878     case XK_backslash:
6879       return(RotateLeftCommand);
6880     case XK_asterisk:
6881       return(RotateCommand);
6882     case XK_t:
6883       return(TrimCommand);
6884     case XK_H:
6885       return(HueCommand);
6886     case XK_S:
6887       return(SaturationCommand);
6888     case XK_L:
6889       return(BrightnessCommand);
6890     case XK_G:
6891       return(GammaCommand);
6892     case XK_C:
6893       return(SpiffCommand);
6894     case XK_Z:
6895       return(DullCommand);
6896     case XK_N:
6897       return(NormalizeCommand);
6898     case XK_equal:
6899       return(EqualizeCommand);
6900     case XK_asciitilde:
6901       return(NegateCommand);
6902     case XK_period:
6903       return(GrayscaleCommand);
6904     case XK_numbersign:
6905       return(QuantizeCommand);
6906     case XK_F2:
6907       return(DespeckleCommand);
6908     case XK_F3:
6909       return(EmbossCommand);
6910     case XK_F4:
6911       return(ReduceNoiseCommand);
6912     case XK_F5:
6913       return(AddNoiseCommand);
6914     case XK_F6:
6915       return(SharpenCommand);
6916     case XK_F7:
6917       return(BlurCommand);
6918     case XK_F8:
6919       return(ThresholdCommand);
6920     case XK_F9:
6921       return(EdgeDetectCommand);
6922     case XK_F10:
6923       return(SpreadCommand);
6924     case XK_F11:
6925       return(ShadeCommand);
6926     case XK_F12:
6927       return(RaiseCommand);
6928     case XK_F13:
6929       return(SegmentCommand);
6930     case XK_i:
6931     {
6932       if ((state & Mod1Mask) == 0)
6933         return(NullCommand);
6934       return(ImplodeCommand);
6935     }
6936     case XK_w:
6937     {
6938       if ((state & Mod1Mask) == 0)
6939         return(NullCommand);
6940       return(WaveCommand);
6941     }
6942     case XK_m:
6943     {
6944       if ((state & Mod4Mask) == 0)
6945         return(NullCommand);
6946       return(MatteCommand);
6947     }
6948     case XK_b:
6949     {
6950       if ((state & Mod4Mask) == 0)
6951         return(NullCommand);
6952       return(AddBorderCommand);
6953     }
6954     case XK_f:
6955     {
6956       if ((state & Mod4Mask) == 0)
6957         return(NullCommand);
6958       return(AddFrameCommand);
6959     }
6960     case XK_exclam:
6961     {
6962       if ((state & Mod4Mask) == 0)
6963         return(NullCommand);
6964       return(CommentCommand);
6965     }
6966     case XK_a:
6967     {
6968       if ((state & Mod1Mask) != 0)
6969         return(ApplyCommand);
6970       if ((state & Mod4Mask) != 0)
6971         return(AnnotateCommand);
6972       if ((state & ControlMask) == 0)
6973         return(NullCommand);
6974       return(RegionofInterestCommand);
6975     }
6976     case XK_question:
6977       return(InfoCommand);
6978     case XK_plus:
6979       return(ZoomCommand);
6980     case XK_P:
6981     {
6982       if ((state & ShiftMask) == 0)
6983         return(NullCommand);
6984       return(ShowPreviewCommand);
6985     }
6986     case XK_Execute:
6987       return(LaunchCommand);
6988     case XK_F1:
6989       return(HelpCommand);
6990     case XK_Find:
6991       return(BrowseDocumentationCommand);
6992     case XK_Menu:
6993     {
6994       (void) XMapRaised(display,windows->command.id);
6995       return(NullCommand);
6996     }
6997     case XK_Next:
6998     case XK_Prior:
6999     case XK_Home:
7000     case XK_KP_Home:
7001     {
7002       XTranslateImage(display,windows,*image,key_symbol);
7003       return(NullCommand);
7004     }
7005     case XK_Up:
7006     case XK_KP_Up:
7007     case XK_Down:
7008     case XK_KP_Down:
7009     case XK_Left:
7010     case XK_KP_Left:
7011     case XK_Right:
7012     case XK_KP_Right:
7013     {
7014       if ((state & Mod1Mask) != 0)
7015         {
7016           RectangleInfo
7017             crop_info;
7018
7019           /*
7020             Trim one pixel from edge of image.
7021           */
7022           crop_info.x=0;
7023           crop_info.y=0;
7024           crop_info.width=(size_t) windows->image.ximage->width;
7025           crop_info.height=(size_t) windows->image.ximage->height;
7026           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
7027             {
7028               if (resource_info->quantum >= (int) crop_info.height)
7029                 resource_info->quantum=(int) crop_info.height-1;
7030               crop_info.height-=resource_info->quantum;
7031             }
7032           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
7033             {
7034               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
7035                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
7036               crop_info.y+=resource_info->quantum;
7037               crop_info.height-=resource_info->quantum;
7038             }
7039           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
7040             {
7041               if (resource_info->quantum >= (int) crop_info.width)
7042                 resource_info->quantum=(int) crop_info.width-1;
7043               crop_info.width-=resource_info->quantum;
7044             }
7045           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
7046             {
7047               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
7048                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
7049               crop_info.x+=resource_info->quantum;
7050               crop_info.width-=resource_info->quantum;
7051             }
7052           if ((int) (windows->image.x+windows->image.width) >
7053               (int) crop_info.width)
7054             windows->image.x=(int) (crop_info.width-windows->image.width);
7055           if ((int) (windows->image.y+windows->image.height) >
7056               (int) crop_info.height)
7057             windows->image.y=(int) (crop_info.height-windows->image.height);
7058           XSetCropGeometry(display,windows,&crop_info,*image);
7059           windows->image.window_changes.width=(int) crop_info.width;
7060           windows->image.window_changes.height=(int) crop_info.height;
7061           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7062           (void) XConfigureImage(display,resource_info,windows,*image,
7063             exception);
7064           return(NullCommand);
7065         }
7066       XTranslateImage(display,windows,*image,key_symbol);
7067       return(NullCommand);
7068     }
7069     default:
7070       return(NullCommand);
7071   }
7072   return(NullCommand);
7073 }
7074 \f
7075 /*
7076 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7077 %                                                                             %
7078 %                                                                             %
7079 %                                                                             %
7080 +   X M a g i c k C o m m a n d                                               %
7081 %                                                                             %
7082 %                                                                             %
7083 %                                                                             %
7084 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7085 %
7086 %  XMagickCommand() makes a transform to the image or Image window as
7087 %  specified by a user menu button or keyboard command.
7088 %
7089 %  The format of the XMagickCommand method is:
7090 %
7091 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7092 %        XWindows *windows,const CommandType command,Image **image,
7093 %        ExceptionInfo *exception)
7094 %
7095 %  A description of each parameter follows:
7096 %
7097 %    o display: Specifies a connection to an X server; returned from
7098 %      XOpenDisplay.
7099 %
7100 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7101 %
7102 %    o windows: Specifies a pointer to a XWindows structure.
7103 %
7104 %    o command: Specifies a command to perform.
7105 %
7106 %    o image: the image;  XMagickCommand may transform the image and return a
7107 %      new image pointer.
7108 %
7109 %    o exception: return any errors or warnings in this structure.
7110 %
7111 */
7112 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7113   XWindows *windows,const CommandType command,Image **image,
7114   ExceptionInfo *exception)
7115 {
7116   char
7117     filename[MaxTextExtent],
7118     geometry[MaxTextExtent],
7119     modulate_factors[MaxTextExtent];
7120
7121   GeometryInfo
7122     geometry_info;
7123
7124   Image
7125     *nexus;
7126
7127   ImageInfo
7128     *image_info;
7129
7130   int
7131     x,
7132     y;
7133
7134   MagickStatusType
7135     flags,
7136     status;
7137
7138   QuantizeInfo
7139     quantize_info;
7140
7141   RectangleInfo
7142     page_geometry;
7143
7144   register int
7145     i;
7146
7147   static char
7148     color[MaxTextExtent] = "gray";
7149
7150   unsigned int
7151     height,
7152     width;
7153
7154   /*
7155     Process user command.
7156   */
7157   XCheckRefreshWindows(display,windows);
7158   XImageCache(display,resource_info,windows,command,image,exception);
7159   nexus=NewImageList();
7160   windows->image.window_changes.width=windows->image.ximage->width;
7161   windows->image.window_changes.height=windows->image.ximage->height;
7162   image_info=CloneImageInfo(resource_info->image_info);
7163   SetGeometryInfo(&geometry_info);
7164   GetQuantizeInfo(&quantize_info);
7165   switch (command)
7166   {
7167     case OpenCommand:
7168     {
7169       /*
7170         Load image.
7171       */
7172       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7173       break;
7174     }
7175     case NextCommand:
7176     {
7177       /*
7178         Display next image.
7179       */
7180       for (i=0; i < resource_info->quantum; i++)
7181         XClientMessage(display,windows->image.id,windows->im_protocols,
7182           windows->im_next_image,CurrentTime);
7183       break;
7184     }
7185     case FormerCommand:
7186     {
7187       /*
7188         Display former image.
7189       */
7190       for (i=0; i < resource_info->quantum; i++)
7191         XClientMessage(display,windows->image.id,windows->im_protocols,
7192           windows->im_former_image,CurrentTime);
7193       break;
7194     }
7195     case SelectCommand:
7196     {
7197       int
7198         status;
7199
7200       /*
7201         Select image.
7202       */
7203       status=chdir(resource_info->home_directory);
7204       if (status == -1)
7205         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
7206           "UnableToOpenFile","%s",resource_info->home_directory);
7207       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7208       break;
7209     }
7210     case SaveCommand:
7211     {
7212       /*
7213         Save image.
7214       */
7215       status=XSaveImage(display,resource_info,windows,*image,exception);
7216       if (status == MagickFalse)
7217         {
7218           char
7219             message[MaxTextExtent];
7220
7221           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7222             exception->reason != (char *) NULL ? exception->reason : "",
7223             exception->description != (char *) NULL ? exception->description :
7224             "");
7225           XNoticeWidget(display,windows,"Unable to save file:",message);
7226           break;
7227         }
7228       break;
7229     }
7230     case PrintCommand:
7231     {
7232       /*
7233         Print image.
7234       */
7235       status=XPrintImage(display,resource_info,windows,*image,exception);
7236       if (status == MagickFalse)
7237         {
7238           char
7239             message[MaxTextExtent];
7240
7241           (void) FormatLocaleString(message,MaxTextExtent,"%s:%s",
7242             exception->reason != (char *) NULL ? exception->reason : "",
7243             exception->description != (char *) NULL ? exception->description :
7244             "");
7245           XNoticeWidget(display,windows,"Unable to print file:",message);
7246           break;
7247         }
7248       break;
7249     }
7250     case DeleteCommand:
7251     {
7252       static char
7253         filename[MaxTextExtent] = "\0";
7254
7255       /*
7256         Delete image file.
7257       */
7258       XFileBrowserWidget(display,windows,"Delete",filename);
7259       if (*filename == '\0')
7260         break;
7261       status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
7262       if (status != MagickFalse)
7263         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7264       break;
7265     }
7266     case NewCommand:
7267     {
7268       int
7269         status;
7270
7271       static char
7272         color[MaxTextExtent] = "gray",
7273         geometry[MaxTextExtent] = "640x480";
7274
7275       static const char
7276         *format = "gradient";
7277
7278       /*
7279         Query user for canvas geometry.
7280       */
7281       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7282         geometry);
7283       if (*geometry == '\0')
7284         break;
7285       if (status == 0)
7286         format="xc";
7287       XColorBrowserWidget(display,windows,"Select",color);
7288       if (*color == '\0')
7289         break;
7290       /*
7291         Create canvas.
7292       */
7293       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7294         "%s:%s",format,color);
7295       (void) CloneString(&image_info->size,geometry);
7296       nexus=ReadImage(image_info,exception);
7297       CatchException(exception);
7298       XClientMessage(display,windows->image.id,windows->im_protocols,
7299         windows->im_next_image,CurrentTime);
7300       break;
7301     }
7302     case VisualDirectoryCommand:
7303     {
7304       /*
7305         Visual Image directory.
7306       */
7307       nexus=XVisualDirectoryImage(display,resource_info,windows,exception);
7308       break;
7309     }
7310     case QuitCommand:
7311     {
7312       /*
7313         exit program.
7314       */
7315       if (resource_info->confirm_exit == MagickFalse)
7316         XClientMessage(display,windows->image.id,windows->im_protocols,
7317           windows->im_exit,CurrentTime);
7318       else
7319         {
7320           int
7321             status;
7322
7323           /*
7324             Confirm program exit.
7325           */
7326           status=XConfirmWidget(display,windows,"Do you really want to exit",
7327             resource_info->client_name);
7328           if (status > 0)
7329             XClientMessage(display,windows->image.id,windows->im_protocols,
7330               windows->im_exit,CurrentTime);
7331         }
7332       break;
7333     }
7334     case CutCommand:
7335     {
7336       /*
7337         Cut image.
7338       */
7339       (void) XCropImage(display,resource_info,windows,*image,CutMode,exception);
7340       break;
7341     }
7342     case CopyCommand:
7343     {
7344       /*
7345         Copy image.
7346       */
7347       (void) XCropImage(display,resource_info,windows,*image,CopyMode,
7348         exception);
7349       break;
7350     }
7351     case PasteCommand:
7352     {
7353       /*
7354         Paste image.
7355       */
7356       status=XPasteImage(display,resource_info,windows,*image,exception);
7357       if (status == MagickFalse)
7358         {
7359           XNoticeWidget(display,windows,"Unable to paste X image",
7360             (*image)->filename);
7361           break;
7362         }
7363       break;
7364     }
7365     case HalfSizeCommand:
7366     {
7367       /*
7368         Half image size.
7369       */
7370       windows->image.window_changes.width=windows->image.ximage->width/2;
7371       windows->image.window_changes.height=windows->image.ximage->height/2;
7372       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7373       break;
7374     }
7375     case OriginalSizeCommand:
7376     {
7377       /*
7378         Original image size.
7379       */
7380       windows->image.window_changes.width=(int) (*image)->columns;
7381       windows->image.window_changes.height=(int) (*image)->rows;
7382       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7383       break;
7384     }
7385     case DoubleSizeCommand:
7386     {
7387       /*
7388         Double the image size.
7389       */
7390       windows->image.window_changes.width=windows->image.ximage->width << 1;
7391       windows->image.window_changes.height=windows->image.ximage->height << 1;
7392       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7393       break;
7394     }
7395     case ResizeCommand:
7396     {
7397       int
7398         status;
7399
7400       size_t
7401         height,
7402         width;
7403
7404       ssize_t
7405         x,
7406         y;
7407
7408       /*
7409         Resize image.
7410       */
7411       width=(size_t) windows->image.ximage->width;
7412       height=(size_t) windows->image.ximage->height;
7413       x=0;
7414       y=0;
7415       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7416         (double) width,(double) height);
7417       status=XDialogWidget(display,windows,"Resize",
7418         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7419       if (*geometry == '\0')
7420         break;
7421       if (status == 0)
7422         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7423       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7424       windows->image.window_changes.width=(int) width;
7425       windows->image.window_changes.height=(int) height;
7426       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7427       break;
7428     }
7429     case ApplyCommand:
7430     {
7431       char
7432         image_geometry[MaxTextExtent];
7433
7434       if ((windows->image.crop_geometry == (char *) NULL) &&
7435           ((int) (*image)->columns == windows->image.ximage->width) &&
7436           ((int) (*image)->rows == windows->image.ximage->height))
7437         break;
7438       /*
7439         Apply size transforms to image.
7440       */
7441       XSetCursorState(display,windows,MagickTrue);
7442       XCheckRefreshWindows(display,windows);
7443       /*
7444         Crop and/or scale displayed image.
7445       */
7446       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7447         windows->image.ximage->width,windows->image.ximage->height);
7448       (void) TransformImage(image,windows->image.crop_geometry,image_geometry,
7449         exception);
7450       if (windows->image.crop_geometry != (char *) NULL)
7451         windows->image.crop_geometry=(char *) RelinquishMagickMemory(
7452           windows->image.crop_geometry);
7453       windows->image.x=0;
7454       windows->image.y=0;
7455       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7456       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7457       break;
7458     }
7459     case RefreshCommand:
7460     {
7461       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7462       break;
7463     }
7464     case RestoreCommand:
7465     {
7466       /*
7467         Restore Image window to its original size.
7468       */
7469       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7470           (windows->image.height == (unsigned int) (*image)->rows) &&
7471           (windows->image.crop_geometry == (char *) NULL))
7472         {
7473           (void) XBell(display,0);
7474           break;
7475         }
7476       windows->image.window_changes.width=(int) (*image)->columns;
7477       windows->image.window_changes.height=(int) (*image)->rows;
7478       if (windows->image.crop_geometry != (char *) NULL)
7479         {
7480           windows->image.crop_geometry=(char *)
7481             RelinquishMagickMemory(windows->image.crop_geometry);
7482           windows->image.crop_geometry=(char *) NULL;
7483           windows->image.x=0;
7484           windows->image.y=0;
7485         }
7486       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7487       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7488       break;
7489     }
7490     case CropCommand:
7491     {
7492       /*
7493         Crop image.
7494       */
7495       (void) XCropImage(display,resource_info,windows,*image,CropMode,
7496         exception);
7497       break;
7498     }
7499     case ChopCommand:
7500     {
7501       /*
7502         Chop image.
7503       */
7504       status=XChopImage(display,resource_info,windows,image,exception);
7505       if (status == MagickFalse)
7506         {
7507           XNoticeWidget(display,windows,"Unable to cut X image",
7508             (*image)->filename);
7509           break;
7510         }
7511       break;
7512     }
7513     case FlopCommand:
7514     {
7515       Image
7516         *flop_image;
7517
7518       /*
7519         Flop image scanlines.
7520       */
7521       XSetCursorState(display,windows,MagickTrue);
7522       XCheckRefreshWindows(display,windows);
7523       flop_image=FlopImage(*image,exception);
7524       if (flop_image != (Image *) NULL)
7525         {
7526           *image=DestroyImage(*image);
7527           *image=flop_image;
7528         }
7529       CatchException(exception);
7530       XSetCursorState(display,windows,MagickFalse);
7531       if (windows->image.crop_geometry != (char *) NULL)
7532         {
7533           /*
7534             Flop crop geometry.
7535           */
7536           width=(unsigned int) (*image)->columns;
7537           height=(unsigned int) (*image)->rows;
7538           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7539             &width,&height);
7540           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7541             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7542         }
7543       if (windows->image.orphan != MagickFalse)
7544         break;
7545       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7546       break;
7547     }
7548     case FlipCommand:
7549     {
7550       Image
7551         *flip_image;
7552
7553       /*
7554         Flip image scanlines.
7555       */
7556       XSetCursorState(display,windows,MagickTrue);
7557       XCheckRefreshWindows(display,windows);
7558       flip_image=FlipImage(*image,exception);
7559       if (flip_image != (Image *) NULL)
7560         {
7561           *image=DestroyImage(*image);
7562           *image=flip_image;
7563         }
7564       CatchException(exception);
7565       XSetCursorState(display,windows,MagickFalse);
7566       if (windows->image.crop_geometry != (char *) NULL)
7567         {
7568           /*
7569             Flip crop geometry.
7570           */
7571           width=(unsigned int) (*image)->columns;
7572           height=(unsigned int) (*image)->rows;
7573           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7574             &width,&height);
7575           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7576             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7577         }
7578       if (windows->image.orphan != MagickFalse)
7579         break;
7580       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7581       break;
7582     }
7583     case RotateRightCommand:
7584     {
7585       /*
7586         Rotate image 90 degrees clockwise.
7587       */
7588       status=XRotateImage(display,resource_info,windows,90.0,image,exception);
7589       if (status == MagickFalse)
7590         {
7591           XNoticeWidget(display,windows,"Unable to rotate X image",
7592             (*image)->filename);
7593           break;
7594         }
7595       break;
7596     }
7597     case RotateLeftCommand:
7598     {
7599       /*
7600         Rotate image 90 degrees counter-clockwise.
7601       */
7602       status=XRotateImage(display,resource_info,windows,-90.0,image,exception);
7603       if (status == MagickFalse)
7604         {
7605           XNoticeWidget(display,windows,"Unable to rotate X image",
7606             (*image)->filename);
7607           break;
7608         }
7609       break;
7610     }
7611     case RotateCommand:
7612     {
7613       /*
7614         Rotate image.
7615       */
7616       status=XRotateImage(display,resource_info,windows,0.0,image,exception);
7617       if (status == MagickFalse)
7618         {
7619           XNoticeWidget(display,windows,"Unable to rotate X image",
7620             (*image)->filename);
7621           break;
7622         }
7623       break;
7624     }
7625     case ShearCommand:
7626     {
7627       Image
7628         *shear_image;
7629
7630       static char
7631         geometry[MaxTextExtent] = "45.0x45.0";
7632
7633       /*
7634         Query user for shear color and geometry.
7635       */
7636       XColorBrowserWidget(display,windows,"Select",color);
7637       if (*color == '\0')
7638         break;
7639       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7640         geometry);
7641       if (*geometry == '\0')
7642         break;
7643       /*
7644         Shear image.
7645       */
7646       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7647         exception);
7648       XSetCursorState(display,windows,MagickTrue);
7649       XCheckRefreshWindows(display,windows);
7650       (void) QueryColorCompliance(color,AllCompliance,
7651         &(*image)->background_color,exception);
7652       flags=ParseGeometry(geometry,&geometry_info);
7653       if ((flags & SigmaValue) == 0)
7654         geometry_info.sigma=geometry_info.rho;
7655       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7656         exception);
7657       if (shear_image != (Image *) NULL)
7658         {
7659           *image=DestroyImage(*image);
7660           *image=shear_image;
7661         }
7662       CatchException(exception);
7663       XSetCursorState(display,windows,MagickFalse);
7664       if (windows->image.orphan != MagickFalse)
7665         break;
7666       windows->image.window_changes.width=(int) (*image)->columns;
7667       windows->image.window_changes.height=(int) (*image)->rows;
7668       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7669       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7670       break;
7671     }
7672     case RollCommand:
7673     {
7674       Image
7675         *roll_image;
7676
7677       static char
7678         geometry[MaxTextExtent] = "+2+2";
7679
7680       /*
7681         Query user for the roll geometry.
7682       */
7683       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7684         geometry);
7685       if (*geometry == '\0')
7686         break;
7687       /*
7688         Roll image.
7689       */
7690       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
7691         exception);
7692       XSetCursorState(display,windows,MagickTrue);
7693       XCheckRefreshWindows(display,windows);
7694       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7695         exception);
7696       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7697         exception);
7698       if (roll_image != (Image *) NULL)
7699         {
7700           *image=DestroyImage(*image);
7701           *image=roll_image;
7702         }
7703       CatchException(exception);
7704       XSetCursorState(display,windows,MagickFalse);
7705       if (windows->image.orphan != MagickFalse)
7706         break;
7707       windows->image.window_changes.width=(int) (*image)->columns;
7708       windows->image.window_changes.height=(int) (*image)->rows;
7709       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7710       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7711       break;
7712     }
7713     case TrimCommand:
7714     {
7715       static char
7716         fuzz[MaxTextExtent];
7717
7718       /*
7719         Query user for the fuzz factor.
7720       */
7721       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7722         (*image)->fuzz/(QuantumRange+1.0));
7723       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7724       if (*fuzz == '\0')
7725         break;
7726       (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+1.0);
7727       /*
7728         Trim image.
7729       */
7730       status=XTrimImage(display,resource_info,windows,*image,exception);
7731       if (status == MagickFalse)
7732         {
7733           XNoticeWidget(display,windows,"Unable to trim X image",
7734             (*image)->filename);
7735           break;
7736         }
7737       break;
7738     }
7739     case HueCommand:
7740     {
7741       static char
7742         hue_percent[MaxTextExtent] = "110";
7743
7744       /*
7745         Query user for percent hue change.
7746       */
7747       (void) XDialogWidget(display,windows,"Apply",
7748         "Enter percent change in image hue (0-200):",hue_percent);
7749       if (*hue_percent == '\0')
7750         break;
7751       /*
7752         Vary the image hue.
7753       */
7754       XSetCursorState(display,windows,MagickTrue);
7755       XCheckRefreshWindows(display,windows);
7756       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7757       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7758         MaxTextExtent);
7759       (void) ModulateImage(*image,modulate_factors,exception);
7760       XSetCursorState(display,windows,MagickFalse);
7761       if (windows->image.orphan != MagickFalse)
7762         break;
7763       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7764       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7765       break;
7766     }
7767     case SaturationCommand:
7768     {
7769       static char
7770         saturation_percent[MaxTextExtent] = "110";
7771
7772       /*
7773         Query user for percent saturation change.
7774       */
7775       (void) XDialogWidget(display,windows,"Apply",
7776         "Enter percent change in color saturation (0-200):",saturation_percent);
7777       if (*saturation_percent == '\0')
7778         break;
7779       /*
7780         Vary color saturation.
7781       */
7782       XSetCursorState(display,windows,MagickTrue);
7783       XCheckRefreshWindows(display,windows);
7784       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7785       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7786         MaxTextExtent);
7787       (void) ModulateImage(*image,modulate_factors,exception);
7788       XSetCursorState(display,windows,MagickFalse);
7789       if (windows->image.orphan != MagickFalse)
7790         break;
7791       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7792       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7793       break;
7794     }
7795     case BrightnessCommand:
7796     {
7797       static char
7798         brightness_percent[MaxTextExtent] = "110";
7799
7800       /*
7801         Query user for percent brightness change.
7802       */
7803       (void) XDialogWidget(display,windows,"Apply",
7804         "Enter percent change in color brightness (0-200):",brightness_percent);
7805       if (*brightness_percent == '\0')
7806         break;
7807       /*
7808         Vary the color brightness.
7809       */
7810       XSetCursorState(display,windows,MagickTrue);
7811       XCheckRefreshWindows(display,windows);
7812       (void) CopyMagickString(modulate_factors,brightness_percent,
7813         MaxTextExtent);
7814       (void) ModulateImage(*image,modulate_factors,exception);
7815       XSetCursorState(display,windows,MagickFalse);
7816       if (windows->image.orphan != MagickFalse)
7817         break;
7818       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7819       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7820       break;
7821     }
7822     case GammaCommand:
7823     {
7824       static char
7825         factor[MaxTextExtent] = "1.6";
7826
7827       /*
7828         Query user for gamma value.
7829       */
7830       (void) XDialogWidget(display,windows,"Gamma",
7831         "Enter gamma value (e.g. 1.2):",factor);
7832       if (*factor == '\0')
7833         break;
7834       /*
7835         Gamma correct image.
7836       */
7837       XSetCursorState(display,windows,MagickTrue);
7838       XCheckRefreshWindows(display,windows);
7839       (void) GammaImage(*image,atof(factor),exception);
7840       XSetCursorState(display,windows,MagickFalse);
7841       if (windows->image.orphan != MagickFalse)
7842         break;
7843       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7844       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7845       break;
7846     }
7847     case SpiffCommand:
7848     {
7849       /*
7850         Sharpen the image contrast.
7851       */
7852       XSetCursorState(display,windows,MagickTrue);
7853       XCheckRefreshWindows(display,windows);
7854       (void) ContrastImage(*image,MagickTrue,exception);
7855       XSetCursorState(display,windows,MagickFalse);
7856       if (windows->image.orphan != MagickFalse)
7857         break;
7858       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7859       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7860       break;
7861     }
7862     case DullCommand:
7863     {
7864       /*
7865         Dull the image contrast.
7866       */
7867       XSetCursorState(display,windows,MagickTrue);
7868       XCheckRefreshWindows(display,windows);
7869       (void) ContrastImage(*image,MagickFalse,exception);
7870       XSetCursorState(display,windows,MagickFalse);
7871       if (windows->image.orphan != MagickFalse)
7872         break;
7873       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7874       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7875       break;
7876     }
7877     case ContrastStretchCommand:
7878     {
7879       double
7880         black_point,
7881         white_point;
7882
7883       static char
7884         levels[MaxTextExtent] = "1%";
7885
7886       /*
7887         Query user for gamma value.
7888       */
7889       (void) XDialogWidget(display,windows,"Contrast Stretch",
7890         "Enter black and white points:",levels);
7891       if (*levels == '\0')
7892         break;
7893       /*
7894         Contrast stretch image.
7895       */
7896       XSetCursorState(display,windows,MagickTrue);
7897       XCheckRefreshWindows(display,windows);
7898       flags=ParseGeometry(levels,&geometry_info);
7899       black_point=geometry_info.rho;
7900       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7901       if ((flags & PercentValue) != 0)
7902         {
7903           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7904           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7905         }
7906       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7907       (void) ContrastStretchImage(*image,black_point,white_point,
7908         exception);
7909       XSetCursorState(display,windows,MagickFalse);
7910       if (windows->image.orphan != MagickFalse)
7911         break;
7912       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7913       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7914       break;
7915     }
7916     case SigmoidalContrastCommand:
7917     {
7918       GeometryInfo
7919         geometry_info;
7920
7921       MagickStatusType
7922         flags;
7923
7924       static char
7925         levels[MaxTextExtent] = "3x50%";
7926
7927       /*
7928         Query user for gamma value.
7929       */
7930       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7931         "Enter contrast and midpoint:",levels);
7932       if (*levels == '\0')
7933         break;
7934       /*
7935         Contrast stretch image.
7936       */
7937       XSetCursorState(display,windows,MagickTrue);
7938       XCheckRefreshWindows(display,windows);
7939       flags=ParseGeometry(levels,&geometry_info);
7940       if ((flags & SigmaValue) == 0)
7941         geometry_info.sigma=1.0*QuantumRange/2.0;
7942       if ((flags & PercentValue) != 0)
7943         geometry_info.sigma=1.0*QuantumRange*geometry_info.sigma/100.0;
7944       (void) SigmoidalContrastImage(*image,MagickTrue,geometry_info.rho,
7945         geometry_info.sigma,exception);
7946       XSetCursorState(display,windows,MagickFalse);
7947       if (windows->image.orphan != MagickFalse)
7948         break;
7949       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7950       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7951       break;
7952     }
7953     case NormalizeCommand:
7954     {
7955       /*
7956         Perform histogram normalization on the image.
7957       */
7958       XSetCursorState(display,windows,MagickTrue);
7959       XCheckRefreshWindows(display,windows);
7960       (void) NormalizeImage(*image,exception);
7961       XSetCursorState(display,windows,MagickFalse);
7962       if (windows->image.orphan != MagickFalse)
7963         break;
7964       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7965       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7966       break;
7967     }
7968     case EqualizeCommand:
7969     {
7970       /*
7971         Perform histogram equalization on the image.
7972       */
7973       XSetCursorState(display,windows,MagickTrue);
7974       XCheckRefreshWindows(display,windows);
7975       (void) EqualizeImage(*image,exception);
7976       XSetCursorState(display,windows,MagickFalse);
7977       if (windows->image.orphan != MagickFalse)
7978         break;
7979       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7980       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7981       break;
7982     }
7983     case NegateCommand:
7984     {
7985       /*
7986         Negate colors in image.
7987       */
7988       XSetCursorState(display,windows,MagickTrue);
7989       XCheckRefreshWindows(display,windows);
7990       (void) NegateImage(*image,MagickFalse,exception);
7991       XSetCursorState(display,windows,MagickFalse);
7992       if (windows->image.orphan != MagickFalse)
7993         break;
7994       XConfigureImageColormap(display,resource_info,windows,*image,exception);
7995       (void) XConfigureImage(display,resource_info,windows,*image,exception);
7996       break;
7997     }
7998     case GrayscaleCommand:
7999     {
8000       /*
8001         Convert image to grayscale.
8002       */
8003       XSetCursorState(display,windows,MagickTrue);
8004       XCheckRefreshWindows(display,windows);
8005       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
8006         GrayscaleType : GrayscaleMatteType,exception);
8007       XSetCursorState(display,windows,MagickFalse);
8008       if (windows->image.orphan != MagickFalse)
8009         break;
8010       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8011       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8012       break;
8013     }
8014     case MapCommand:
8015     {
8016       Image
8017         *affinity_image;
8018
8019       static char
8020         filename[MaxTextExtent] = "\0";
8021
8022       /*
8023         Request image file name from user.
8024       */
8025       XFileBrowserWidget(display,windows,"Map",filename);
8026       if (*filename == '\0')
8027         break;
8028       /*
8029         Map image.
8030       */
8031       XSetCursorState(display,windows,MagickTrue);
8032       XCheckRefreshWindows(display,windows);
8033       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8034       affinity_image=ReadImage(image_info,exception);
8035       if (affinity_image != (Image *) NULL)
8036         {
8037           (void) RemapImage(&quantize_info,*image,affinity_image,exception);
8038           affinity_image=DestroyImage(affinity_image);
8039         }
8040       CatchException(exception);
8041       XSetCursorState(display,windows,MagickFalse);
8042       if (windows->image.orphan != MagickFalse)
8043         break;
8044       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8045       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8046       break;
8047     }
8048     case QuantizeCommand:
8049     {
8050       int
8051         status;
8052
8053       static char
8054         colors[MaxTextExtent] = "256";
8055
8056       /*
8057         Query user for maximum number of colors.
8058       */
8059       status=XDialogWidget(display,windows,"Quantize",
8060         "Maximum number of colors:",colors);
8061       if (*colors == '\0')
8062         break;
8063       /*
8064         Color reduce the image.
8065       */
8066       XSetCursorState(display,windows,MagickTrue);
8067       XCheckRefreshWindows(display,windows);
8068       quantize_info.number_colors=StringToUnsignedLong(colors);
8069       quantize_info.dither_method=status != 0 ? RiemersmaDitherMethod :
8070         NoDitherMethod;
8071       (void) QuantizeImage(&quantize_info,*image,exception);
8072       XSetCursorState(display,windows,MagickFalse);
8073       if (windows->image.orphan != MagickFalse)
8074         break;
8075       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8076       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8077       break;
8078     }
8079     case DespeckleCommand:
8080     {
8081       Image
8082         *despeckle_image;
8083
8084       /*
8085         Despeckle image.
8086       */
8087       XSetCursorState(display,windows,MagickTrue);
8088       XCheckRefreshWindows(display,windows);
8089       despeckle_image=DespeckleImage(*image,exception);
8090       if (despeckle_image != (Image *) NULL)
8091         {
8092           *image=DestroyImage(*image);
8093           *image=despeckle_image;
8094         }
8095       CatchException(exception);
8096       XSetCursorState(display,windows,MagickFalse);
8097       if (windows->image.orphan != MagickFalse)
8098         break;
8099       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8100       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8101       break;
8102     }
8103     case EmbossCommand:
8104     {
8105       Image
8106         *emboss_image;
8107
8108       static char
8109         radius[MaxTextExtent] = "0.0x1.0";
8110
8111       /*
8112         Query user for emboss radius.
8113       */
8114       (void) XDialogWidget(display,windows,"Emboss",
8115         "Enter the emboss radius and standard deviation:",radius);
8116       if (*radius == '\0')
8117         break;
8118       /*
8119         Reduce noise in the image.
8120       */
8121       XSetCursorState(display,windows,MagickTrue);
8122       XCheckRefreshWindows(display,windows);
8123       flags=ParseGeometry(radius,&geometry_info);
8124       if ((flags & SigmaValue) == 0)
8125         geometry_info.sigma=1.0;
8126       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8127         exception);
8128       if (emboss_image != (Image *) NULL)
8129         {
8130           *image=DestroyImage(*image);
8131           *image=emboss_image;
8132         }
8133       CatchException(exception);
8134       XSetCursorState(display,windows,MagickFalse);
8135       if (windows->image.orphan != MagickFalse)
8136         break;
8137       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8138       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8139       break;
8140     }
8141     case ReduceNoiseCommand:
8142     {
8143       Image
8144         *noise_image;
8145
8146       static char
8147         radius[MaxTextExtent] = "0";
8148
8149       /*
8150         Query user for noise radius.
8151       */
8152       (void) XDialogWidget(display,windows,"Reduce Noise",
8153         "Enter the noise radius:",radius);
8154       if (*radius == '\0')
8155         break;
8156       /*
8157         Reduce noise in the image.
8158       */
8159       XSetCursorState(display,windows,MagickTrue);
8160       XCheckRefreshWindows(display,windows);
8161       flags=ParseGeometry(radius,&geometry_info);
8162       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8163         geometry_info.rho,(size_t) geometry_info.rho,exception);
8164       if (noise_image != (Image *) NULL)
8165         {
8166           *image=DestroyImage(*image);
8167           *image=noise_image;
8168         }
8169       CatchException(exception);
8170       XSetCursorState(display,windows,MagickFalse);
8171       if (windows->image.orphan != MagickFalse)
8172         break;
8173       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8174       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8175       break;
8176     }
8177     case AddNoiseCommand:
8178     {
8179       char
8180         **noises;
8181
8182       Image
8183         *noise_image;
8184
8185       static char
8186         noise_type[MaxTextExtent] = "Gaussian";
8187
8188       /*
8189         Add noise to the image.
8190       */
8191       noises=GetCommandOptions(MagickNoiseOptions);
8192       if (noises == (char **) NULL)
8193         break;
8194       XListBrowserWidget(display,windows,&windows->widget,
8195         (const char **) noises,"Add Noise",
8196         "Select a type of noise to add to your image:",noise_type);
8197       noises=DestroyStringList(noises);
8198       if (*noise_type == '\0')
8199         break;
8200       XSetCursorState(display,windows,MagickTrue);
8201       XCheckRefreshWindows(display,windows);
8202       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8203         MagickNoiseOptions,MagickFalse,noise_type),1.0,exception);
8204       if (noise_image != (Image *) NULL)
8205         {
8206           *image=DestroyImage(*image);
8207           *image=noise_image;
8208         }
8209       CatchException(exception);
8210       XSetCursorState(display,windows,MagickFalse);
8211       if (windows->image.orphan != MagickFalse)
8212         break;
8213       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8214       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8215       break;
8216     }
8217     case SharpenCommand:
8218     {
8219       Image
8220         *sharp_image;
8221
8222       static char
8223         radius[MaxTextExtent] = "0.0x1.0";
8224
8225       /*
8226         Query user for sharpen radius.
8227       */
8228       (void) XDialogWidget(display,windows,"Sharpen",
8229         "Enter the sharpen radius and standard deviation:",radius);
8230       if (*radius == '\0')
8231         break;
8232       /*
8233         Sharpen image scanlines.
8234       */
8235       XSetCursorState(display,windows,MagickTrue);
8236       XCheckRefreshWindows(display,windows);
8237       flags=ParseGeometry(radius,&geometry_info);
8238       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8239         exception);
8240       if (sharp_image != (Image *) NULL)
8241         {
8242           *image=DestroyImage(*image);
8243           *image=sharp_image;
8244         }
8245       CatchException(exception);
8246       XSetCursorState(display,windows,MagickFalse);
8247       if (windows->image.orphan != MagickFalse)
8248         break;
8249       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8250       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8251       break;
8252     }
8253     case BlurCommand:
8254     {
8255       Image
8256         *blur_image;
8257
8258       static char
8259         radius[MaxTextExtent] = "0.0x1.0";
8260
8261       /*
8262         Query user for blur radius.
8263       */
8264       (void) XDialogWidget(display,windows,"Blur",
8265         "Enter the blur radius and standard deviation:",radius);
8266       if (*radius == '\0')
8267         break;
8268       /*
8269         Blur an image.
8270       */
8271       XSetCursorState(display,windows,MagickTrue);
8272       XCheckRefreshWindows(display,windows);
8273       flags=ParseGeometry(radius,&geometry_info);
8274       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8275         exception);
8276       if (blur_image != (Image *) NULL)
8277         {
8278           *image=DestroyImage(*image);
8279           *image=blur_image;
8280         }
8281       CatchException(exception);
8282       XSetCursorState(display,windows,MagickFalse);
8283       if (windows->image.orphan != MagickFalse)
8284         break;
8285       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8286       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8287       break;
8288     }
8289     case ThresholdCommand:
8290     {
8291       double
8292         threshold;
8293
8294       static char
8295         factor[MaxTextExtent] = "128";
8296
8297       /*
8298         Query user for threshold value.
8299       */
8300       (void) XDialogWidget(display,windows,"Threshold",
8301         "Enter threshold value:",factor);
8302       if (*factor == '\0')
8303         break;
8304       /*
8305         Gamma correct image.
8306       */
8307       XSetCursorState(display,windows,MagickTrue);
8308       XCheckRefreshWindows(display,windows);
8309       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8310       (void) BilevelImage(*image,threshold,exception);
8311       XSetCursorState(display,windows,MagickFalse);
8312       if (windows->image.orphan != MagickFalse)
8313         break;
8314       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8315       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8316       break;
8317     }
8318     case EdgeDetectCommand:
8319     {
8320       Image
8321         *edge_image;
8322
8323       static char
8324         radius[MaxTextExtent] = "0";
8325
8326       /*
8327         Query user for edge factor.
8328       */
8329       (void) XDialogWidget(display,windows,"Detect Edges",
8330         "Enter the edge detect radius:",radius);
8331       if (*radius == '\0')
8332         break;
8333       /*
8334         Detect edge in image.
8335       */
8336       XSetCursorState(display,windows,MagickTrue);
8337       XCheckRefreshWindows(display,windows);
8338       flags=ParseGeometry(radius,&geometry_info);
8339       edge_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8340         exception);
8341       if (edge_image != (Image *) NULL)
8342         {
8343           *image=DestroyImage(*image);
8344           *image=edge_image;
8345         }
8346       CatchException(exception);
8347       XSetCursorState(display,windows,MagickFalse);
8348       if (windows->image.orphan != MagickFalse)
8349         break;
8350       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8351       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8352       break;
8353     }
8354     case SpreadCommand:
8355     {
8356       Image
8357         *spread_image;
8358
8359       static char
8360         amount[MaxTextExtent] = "2";
8361
8362       /*
8363         Query user for spread amount.
8364       */
8365       (void) XDialogWidget(display,windows,"Spread",
8366         "Enter the displacement amount:",amount);
8367       if (*amount == '\0')
8368         break;
8369       /*
8370         Displace image pixels by a random amount.
8371       */
8372       XSetCursorState(display,windows,MagickTrue);
8373       XCheckRefreshWindows(display,windows);
8374       flags=ParseGeometry(amount,&geometry_info);
8375       spread_image=EdgeImage(*image,geometry_info.rho,geometry_info.sigma,
8376         exception);
8377       if (spread_image != (Image *) NULL)
8378         {
8379           *image=DestroyImage(*image);
8380           *image=spread_image;
8381         }
8382       CatchException(exception);
8383       XSetCursorState(display,windows,MagickFalse);
8384       if (windows->image.orphan != MagickFalse)
8385         break;
8386       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8387       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8388       break;
8389     }
8390     case ShadeCommand:
8391     {
8392       Image
8393         *shade_image;
8394
8395       int
8396         status;
8397
8398       static char
8399         geometry[MaxTextExtent] = "30x30";
8400
8401       /*
8402         Query user for the shade geometry.
8403       */
8404       status=XDialogWidget(display,windows,"Shade",
8405         "Enter the azimuth and elevation of the light source:",geometry);
8406       if (*geometry == '\0')
8407         break;
8408       /*
8409         Shade image pixels.
8410       */
8411       XSetCursorState(display,windows,MagickTrue);
8412       XCheckRefreshWindows(display,windows);
8413       flags=ParseGeometry(geometry,&geometry_info);
8414       if ((flags & SigmaValue) == 0)
8415         geometry_info.sigma=1.0;
8416       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8417         geometry_info.rho,geometry_info.sigma,exception);
8418       if (shade_image != (Image *) NULL)
8419         {
8420           *image=DestroyImage(*image);
8421           *image=shade_image;
8422         }
8423       CatchException(exception);
8424       XSetCursorState(display,windows,MagickFalse);
8425       if (windows->image.orphan != MagickFalse)
8426         break;
8427       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8428       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8429       break;
8430     }
8431     case RaiseCommand:
8432     {
8433       static char
8434         bevel_width[MaxTextExtent] = "10";
8435
8436       /*
8437         Query user for bevel width.
8438       */
8439       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8440       if (*bevel_width == '\0')
8441         break;
8442       /*
8443         Raise an image.
8444       */
8445       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8446         exception);
8447       XSetCursorState(display,windows,MagickTrue);
8448       XCheckRefreshWindows(display,windows);
8449       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8450         exception);
8451       (void) RaiseImage(*image,&page_geometry,MagickTrue,exception);
8452       XSetCursorState(display,windows,MagickFalse);
8453       if (windows->image.orphan != MagickFalse)
8454         break;
8455       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8456       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8457       break;
8458     }
8459     case SegmentCommand:
8460     {
8461       static char
8462         threshold[MaxTextExtent] = "1.0x1.5";
8463
8464       /*
8465         Query user for smoothing threshold.
8466       */
8467       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8468         threshold);
8469       if (*threshold == '\0')
8470         break;
8471       /*
8472         Segment an image.
8473       */
8474       XSetCursorState(display,windows,MagickTrue);
8475       XCheckRefreshWindows(display,windows);
8476       flags=ParseGeometry(threshold,&geometry_info);
8477       if ((flags & SigmaValue) == 0)
8478         geometry_info.sigma=1.0;
8479       (void) SegmentImage(*image,sRGBColorspace,MagickFalse,geometry_info.rho,
8480         geometry_info.sigma,exception);
8481       XSetCursorState(display,windows,MagickFalse);
8482       if (windows->image.orphan != MagickFalse)
8483         break;
8484       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8485       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8486       break;
8487     }
8488     case SepiaToneCommand:
8489     {
8490       double
8491         threshold;
8492
8493       Image
8494         *sepia_image;
8495
8496       static char
8497         factor[MaxTextExtent] = "80%";
8498
8499       /*
8500         Query user for sepia-tone factor.
8501       */
8502       (void) XDialogWidget(display,windows,"Sepia Tone",
8503         "Enter the sepia tone factor (0 - 99.9%):",factor);
8504       if (*factor == '\0')
8505         break;
8506       /*
8507         Sepia tone image pixels.
8508       */
8509       XSetCursorState(display,windows,MagickTrue);
8510       XCheckRefreshWindows(display,windows);
8511       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8512       sepia_image=SepiaToneImage(*image,threshold,exception);
8513       if (sepia_image != (Image *) NULL)
8514         {
8515           *image=DestroyImage(*image);
8516           *image=sepia_image;
8517         }
8518       CatchException(exception);
8519       XSetCursorState(display,windows,MagickFalse);
8520       if (windows->image.orphan != MagickFalse)
8521         break;
8522       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8523       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8524       break;
8525     }
8526     case SolarizeCommand:
8527     {
8528       double
8529         threshold;
8530
8531       static char
8532         factor[MaxTextExtent] = "60%";
8533
8534       /*
8535         Query user for solarize factor.
8536       */
8537       (void) XDialogWidget(display,windows,"Solarize",
8538         "Enter the solarize factor (0 - 99.9%):",factor);
8539       if (*factor == '\0')
8540         break;
8541       /*
8542         Solarize image pixels.
8543       */
8544       XSetCursorState(display,windows,MagickTrue);
8545       XCheckRefreshWindows(display,windows);
8546       threshold=StringToDoubleInterval(factor,(double) QuantumRange+1.0);
8547       (void) SolarizeImage(*image,threshold,exception);
8548       XSetCursorState(display,windows,MagickFalse);
8549       if (windows->image.orphan != MagickFalse)
8550         break;
8551       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8552       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8553       break;
8554     }
8555     case SwirlCommand:
8556     {
8557       Image
8558         *swirl_image;
8559
8560       static char
8561         degrees[MaxTextExtent] = "60";
8562
8563       /*
8564         Query user for swirl angle.
8565       */
8566       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8567         degrees);
8568       if (*degrees == '\0')
8569         break;
8570       /*
8571         Swirl image pixels about the center.
8572       */
8573       XSetCursorState(display,windows,MagickTrue);
8574       XCheckRefreshWindows(display,windows);
8575       flags=ParseGeometry(degrees,&geometry_info);
8576       swirl_image=SwirlImage(*image,geometry_info.rho,(*image)->interpolate,
8577         exception);
8578       if (swirl_image != (Image *) NULL)
8579         {
8580           *image=DestroyImage(*image);
8581           *image=swirl_image;
8582         }
8583       CatchException(exception);
8584       XSetCursorState(display,windows,MagickFalse);
8585       if (windows->image.orphan != MagickFalse)
8586         break;
8587       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8588       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8589       break;
8590     }
8591     case ImplodeCommand:
8592     {
8593       Image
8594         *implode_image;
8595
8596       static char
8597         factor[MaxTextExtent] = "0.3";
8598
8599       /*
8600         Query user for implode factor.
8601       */
8602       (void) XDialogWidget(display,windows,"Implode",
8603         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8604       if (*factor == '\0')
8605         break;
8606       /*
8607         Implode image pixels about the center.
8608       */
8609       XSetCursorState(display,windows,MagickTrue);
8610       XCheckRefreshWindows(display,windows);
8611       flags=ParseGeometry(factor,&geometry_info);
8612       implode_image=ImplodeImage(*image,geometry_info.rho,(*image)->interpolate,
8613         exception);
8614       if (implode_image != (Image *) NULL)
8615         {
8616           *image=DestroyImage(*image);
8617           *image=implode_image;
8618         }
8619       CatchException(exception);
8620       XSetCursorState(display,windows,MagickFalse);
8621       if (windows->image.orphan != MagickFalse)
8622         break;
8623       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8624       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8625       break;
8626     }
8627     case VignetteCommand:
8628     {
8629       Image
8630         *vignette_image;
8631
8632       static char
8633         geometry[MaxTextExtent] = "0x20";
8634
8635       /*
8636         Query user for the vignette geometry.
8637       */
8638       (void) XDialogWidget(display,windows,"Vignette",
8639         "Enter the radius, sigma, and x and y offsets:",geometry);
8640       if (*geometry == '\0')
8641         break;
8642       /*
8643         Soften the edges of the image in vignette style
8644       */
8645       XSetCursorState(display,windows,MagickTrue);
8646       XCheckRefreshWindows(display,windows);
8647       flags=ParseGeometry(geometry,&geometry_info);
8648       if ((flags & SigmaValue) == 0)
8649         geometry_info.sigma=1.0;
8650       if ((flags & XiValue) == 0)
8651         geometry_info.xi=0.1*(*image)->columns;
8652       if ((flags & PsiValue) == 0)
8653         geometry_info.psi=0.1*(*image)->rows;
8654       vignette_image=VignetteImage(*image,geometry_info.rho,0.0,(ssize_t)
8655         ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-0.5),
8656         exception);
8657       if (vignette_image != (Image *) NULL)
8658         {
8659           *image=DestroyImage(*image);
8660           *image=vignette_image;
8661         }
8662       CatchException(exception);
8663       XSetCursorState(display,windows,MagickFalse);
8664       if (windows->image.orphan != MagickFalse)
8665         break;
8666       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8667       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8668       break;
8669     }
8670     case WaveCommand:
8671     {
8672       Image
8673         *wave_image;
8674
8675       static char
8676         geometry[MaxTextExtent] = "25x150";
8677
8678       /*
8679         Query user for the wave geometry.
8680       */
8681       (void) XDialogWidget(display,windows,"Wave",
8682         "Enter the amplitude and length of the wave:",geometry);
8683       if (*geometry == '\0')
8684         break;
8685       /*
8686         Alter an image along a sine wave.
8687       */
8688       XSetCursorState(display,windows,MagickTrue);
8689       XCheckRefreshWindows(display,windows);
8690       flags=ParseGeometry(geometry,&geometry_info);
8691       if ((flags & SigmaValue) == 0)
8692         geometry_info.sigma=1.0;
8693       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8694         (*image)->interpolate,exception);
8695       if (wave_image != (Image *) NULL)
8696         {
8697           *image=DestroyImage(*image);
8698           *image=wave_image;
8699         }
8700       CatchException(exception);
8701       XSetCursorState(display,windows,MagickFalse);
8702       if (windows->image.orphan != MagickFalse)
8703         break;
8704       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8705       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8706       break;
8707     }
8708     case OilPaintCommand:
8709     {
8710       Image
8711         *paint_image;
8712
8713       static char
8714         radius[MaxTextExtent] = "0";
8715
8716       /*
8717         Query user for circular neighborhood radius.
8718       */
8719       (void) XDialogWidget(display,windows,"Oil Paint",
8720         "Enter the mask radius:",radius);
8721       if (*radius == '\0')
8722         break;
8723       /*
8724         OilPaint image scanlines.
8725       */
8726       XSetCursorState(display,windows,MagickTrue);
8727       XCheckRefreshWindows(display,windows);
8728       flags=ParseGeometry(radius,&geometry_info);
8729       paint_image=OilPaintImage(*image,geometry_info.rho,geometry_info.sigma,
8730         exception);
8731       if (paint_image != (Image *) NULL)
8732         {
8733           *image=DestroyImage(*image);
8734           *image=paint_image;
8735         }
8736       CatchException(exception);
8737       XSetCursorState(display,windows,MagickFalse);
8738       if (windows->image.orphan != MagickFalse)
8739         break;
8740       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8741       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8742       break;
8743     }
8744     case CharcoalDrawCommand:
8745     {
8746       Image
8747         *charcoal_image;
8748
8749       static char
8750         radius[MaxTextExtent] = "0x1";
8751
8752       /*
8753         Query user for charcoal radius.
8754       */
8755       (void) XDialogWidget(display,windows,"Charcoal Draw",
8756         "Enter the charcoal radius and sigma:",radius);
8757       if (*radius == '\0')
8758         break;
8759       /*
8760         Charcoal the image.
8761       */
8762       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8763         exception);
8764       XSetCursorState(display,windows,MagickTrue);
8765       XCheckRefreshWindows(display,windows);
8766       flags=ParseGeometry(radius,&geometry_info);
8767       if ((flags & SigmaValue) == 0)
8768         geometry_info.sigma=geometry_info.rho;
8769       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8770         exception);
8771       if (charcoal_image != (Image *) NULL)
8772         {
8773           *image=DestroyImage(*image);
8774           *image=charcoal_image;
8775         }
8776       CatchException(exception);
8777       XSetCursorState(display,windows,MagickFalse);
8778       if (windows->image.orphan != MagickFalse)
8779         break;
8780       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8781       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8782       break;
8783     }
8784     case AnnotateCommand:
8785     {
8786       /*
8787         Annotate the image with text.
8788       */
8789       status=XAnnotateEditImage(display,resource_info,windows,*image,exception);
8790       if (status == MagickFalse)
8791         {
8792           XNoticeWidget(display,windows,"Unable to annotate X image",
8793             (*image)->filename);
8794           break;
8795         }
8796       break;
8797     }
8798     case DrawCommand:
8799     {
8800       /*
8801         Draw image.
8802       */
8803       status=XDrawEditImage(display,resource_info,windows,image,exception);
8804       if (status == MagickFalse)
8805         {
8806           XNoticeWidget(display,windows,"Unable to draw on the X image",
8807             (*image)->filename);
8808           break;
8809         }
8810       break;
8811     }
8812     case ColorCommand:
8813     {
8814       /*
8815         Color edit.
8816       */
8817       status=XColorEditImage(display,resource_info,windows,image,exception);
8818       if (status == MagickFalse)
8819         {
8820           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8821             (*image)->filename);
8822           break;
8823         }
8824       break;
8825     }
8826     case MatteCommand:
8827     {
8828       /*
8829         Matte edit.
8830       */
8831       status=XMatteEditImage(display,resource_info,windows,image,exception);
8832       if (status == MagickFalse)
8833         {
8834           XNoticeWidget(display,windows,"Unable to matte edit X image",
8835             (*image)->filename);
8836           break;
8837         }
8838       break;
8839     }
8840     case CompositeCommand:
8841     {
8842       /*
8843         Composite image.
8844       */
8845       status=XCompositeImage(display,resource_info,windows,*image,
8846         exception);
8847       if (status == MagickFalse)
8848         {
8849           XNoticeWidget(display,windows,"Unable to composite X image",
8850             (*image)->filename);
8851           break;
8852         }
8853       break;
8854     }
8855     case AddBorderCommand:
8856     {
8857       Image
8858         *border_image;
8859
8860       static char
8861         geometry[MaxTextExtent] = "6x6";
8862
8863       /*
8864         Query user for border color and geometry.
8865       */
8866       XColorBrowserWidget(display,windows,"Select",color);
8867       if (*color == '\0')
8868         break;
8869       (void) XDialogWidget(display,windows,"Add Border",
8870         "Enter border geometry:",geometry);
8871       if (*geometry == '\0')
8872         break;
8873       /*
8874         Add a border to the image.
8875       */
8876       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8877         exception);
8878       XSetCursorState(display,windows,MagickTrue);
8879       XCheckRefreshWindows(display,windows);
8880       (void) QueryColorCompliance(color,AllCompliance,&(*image)->border_color,
8881         exception);
8882       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8883         exception);
8884       border_image=BorderImage(*image,&page_geometry,(*image)->compose,
8885         exception);
8886       if (border_image != (Image *) NULL)
8887         {
8888           *image=DestroyImage(*image);
8889           *image=border_image;
8890         }
8891       CatchException(exception);
8892       XSetCursorState(display,windows,MagickFalse);
8893       if (windows->image.orphan != MagickFalse)
8894         break;
8895       windows->image.window_changes.width=(int) (*image)->columns;
8896       windows->image.window_changes.height=(int) (*image)->rows;
8897       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8898       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8899       break;
8900     }
8901     case AddFrameCommand:
8902     {
8903       FrameInfo
8904         frame_info;
8905
8906       Image
8907         *frame_image;
8908
8909       static char
8910         geometry[MaxTextExtent] = "6x6";
8911
8912       /*
8913         Query user for frame color and geometry.
8914       */
8915       XColorBrowserWidget(display,windows,"Select",color);
8916       if (*color == '\0')
8917         break;
8918       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8919         geometry);
8920       if (*geometry == '\0')
8921         break;
8922       /*
8923         Surround image with an ornamental border.
8924       */
8925       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
8926         exception);
8927       XSetCursorState(display,windows,MagickTrue);
8928       XCheckRefreshWindows(display,windows);
8929       (void) QueryColorCompliance(color,AllCompliance,&(*image)->matte_color,
8930         exception);
8931       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8932         exception);
8933       frame_info.width=page_geometry.width;
8934       frame_info.height=page_geometry.height;
8935       frame_info.outer_bevel=page_geometry.x;
8936       frame_info.inner_bevel=page_geometry.y;
8937       frame_info.x=(ssize_t) frame_info.width;
8938       frame_info.y=(ssize_t) frame_info.height;
8939       frame_info.width=(*image)->columns+2*frame_info.width;
8940       frame_info.height=(*image)->rows+2*frame_info.height;
8941       frame_image=FrameImage(*image,&frame_info,(*image)->compose,exception);
8942       if (frame_image != (Image *) NULL)
8943         {
8944           *image=DestroyImage(*image);
8945           *image=frame_image;
8946         }
8947       CatchException(exception);
8948       XSetCursorState(display,windows,MagickFalse);
8949       if (windows->image.orphan != MagickFalse)
8950         break;
8951       windows->image.window_changes.width=(int) (*image)->columns;
8952       windows->image.window_changes.height=(int) (*image)->rows;
8953       XConfigureImageColormap(display,resource_info,windows,*image,exception);
8954       (void) XConfigureImage(display,resource_info,windows,*image,exception);
8955       break;
8956     }
8957     case CommentCommand:
8958     {
8959       const char
8960         *value;
8961
8962       FILE
8963         *file;
8964
8965       int
8966         unique_file;
8967
8968       /*
8969         Edit image comment.
8970       */
8971       unique_file=AcquireUniqueFileResource(image_info->filename);
8972       if (unique_file == -1)
8973         XNoticeWidget(display,windows,"Unable to edit image comment",
8974           image_info->filename);
8975       value=GetImageProperty(*image,"comment",exception);
8976       if (value == (char *) NULL)
8977         unique_file=close(unique_file)-1;
8978       else
8979         {
8980           register const char
8981             *p;
8982
8983           file=fdopen(unique_file,"w");
8984           if (file == (FILE *) NULL)
8985             {
8986               XNoticeWidget(display,windows,"Unable to edit image comment",
8987                 image_info->filename);
8988               break;
8989             }
8990           for (p=value; *p != '\0'; p++)
8991             (void) fputc((int) *p,file);
8992           (void) fputc('\n',file);
8993           (void) fclose(file);
8994         }
8995       XSetCursorState(display,windows,MagickTrue);
8996       XCheckRefreshWindows(display,windows);
8997       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8998         exception);
8999       if (status == MagickFalse)
9000         XNoticeWidget(display,windows,"Unable to edit image comment",
9001           (char *) NULL);
9002       else
9003         {
9004           char
9005             *comment;
9006
9007           comment=FileToString(image_info->filename,~0UL,exception);
9008           if (comment != (char *) NULL)
9009             {
9010               (void) SetImageProperty(*image,"comment",comment,exception);
9011               (*image)->taint=MagickTrue;
9012             }
9013         }
9014       (void) RelinquishUniqueFileResource(image_info->filename);
9015       XSetCursorState(display,windows,MagickFalse);
9016       break;
9017     }
9018     case LaunchCommand:
9019     {
9020       /*
9021         Launch program.
9022       */
9023       XSetCursorState(display,windows,MagickTrue);
9024       XCheckRefreshWindows(display,windows);
9025       (void) AcquireUniqueFilename(filename);
9026       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
9027         filename);
9028       status=WriteImage(image_info,*image,exception);
9029       if (status == MagickFalse)
9030         XNoticeWidget(display,windows,"Unable to launch image editor",
9031           (char *) NULL);
9032       else
9033         {
9034           nexus=ReadImage(resource_info->image_info,exception);
9035           CatchException(exception);
9036           XClientMessage(display,windows->image.id,windows->im_protocols,
9037             windows->im_next_image,CurrentTime);
9038         }
9039       (void) RelinquishUniqueFileResource(filename);
9040       XSetCursorState(display,windows,MagickFalse);
9041       break;
9042     }
9043     case RegionofInterestCommand:
9044     {
9045       /*
9046         Apply an image processing technique to a region of interest.
9047       */
9048       (void) XROIImage(display,resource_info,windows,image,exception);
9049       break;
9050     }
9051     case InfoCommand:
9052       break;
9053     case ZoomCommand:
9054     {
9055       /*
9056         Zoom image.
9057       */
9058       if (windows->magnify.mapped != MagickFalse)
9059         (void) XRaiseWindow(display,windows->magnify.id);
9060       else
9061         {
9062           /*
9063             Make magnify image.
9064           */
9065           XSetCursorState(display,windows,MagickTrue);
9066           (void) XMapRaised(display,windows->magnify.id);
9067           XSetCursorState(display,windows,MagickFalse);
9068         }
9069       break;
9070     }
9071     case ShowPreviewCommand:
9072     {
9073       char
9074         **previews;
9075
9076       Image
9077         *preview_image;
9078
9079       static char
9080         preview_type[MaxTextExtent] = "Gamma";
9081
9082       /*
9083         Select preview type from menu.
9084       */
9085       previews=GetCommandOptions(MagickPreviewOptions);
9086       if (previews == (char **) NULL)
9087         break;
9088       XListBrowserWidget(display,windows,&windows->widget,
9089         (const char **) previews,"Preview",
9090         "Select an enhancement, effect, or F/X:",preview_type);
9091       previews=DestroyStringList(previews);
9092       if (*preview_type == '\0')
9093         break;
9094       /*
9095         Show image preview.
9096       */
9097       XSetCursorState(display,windows,MagickTrue);
9098       XCheckRefreshWindows(display,windows);
9099       image_info->preview_type=(PreviewType)
9100         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9101       image_info->group=(ssize_t) windows->image.id;
9102       (void) DeleteImageProperty(*image,"label");
9103       (void) SetImageProperty(*image,"label","Preview",exception);
9104       (void) AcquireUniqueFilename(filename);
9105       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9106         filename);
9107       status=WriteImage(image_info,*image,exception);
9108       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9109       preview_image=ReadImage(image_info,exception);
9110       (void) RelinquishUniqueFileResource(filename);
9111       if (preview_image == (Image *) NULL)
9112         break;
9113       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9114         filename);
9115       status=WriteImage(image_info,preview_image,exception);
9116       preview_image=DestroyImage(preview_image);
9117       if (status == MagickFalse)
9118         XNoticeWidget(display,windows,"Unable to show image preview",
9119           (*image)->filename);
9120       XDelay(display,1500);
9121       XSetCursorState(display,windows,MagickFalse);
9122       break;
9123     }
9124     case ShowHistogramCommand:
9125     {
9126       Image
9127         *histogram_image;
9128
9129       /*
9130         Show image histogram.
9131       */
9132       XSetCursorState(display,windows,MagickTrue);
9133       XCheckRefreshWindows(display,windows);
9134       image_info->group=(ssize_t) windows->image.id;
9135       (void) DeleteImageProperty(*image,"label");
9136       (void) SetImageProperty(*image,"label","Histogram",exception);
9137       (void) AcquireUniqueFilename(filename);
9138       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9139         filename);
9140       status=WriteImage(image_info,*image,exception);
9141       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9142       histogram_image=ReadImage(image_info,exception);
9143       (void) RelinquishUniqueFileResource(filename);
9144       if (histogram_image == (Image *) NULL)
9145         break;
9146       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9147         "show:%s",filename);
9148       status=WriteImage(image_info,histogram_image,exception);
9149       histogram_image=DestroyImage(histogram_image);
9150       if (status == MagickFalse)
9151         XNoticeWidget(display,windows,"Unable to show histogram",
9152           (*image)->filename);
9153       XDelay(display,1500);
9154       XSetCursorState(display,windows,MagickFalse);
9155       break;
9156     }
9157     case ShowMatteCommand:
9158     {
9159       Image
9160         *matte_image;
9161
9162       if ((*image)->matte == MagickFalse)
9163         {
9164           XNoticeWidget(display,windows,
9165             "Image does not have any matte information",(*image)->filename);
9166           break;
9167         }
9168       /*
9169         Show image matte.
9170       */
9171       XSetCursorState(display,windows,MagickTrue);
9172       XCheckRefreshWindows(display,windows);
9173       image_info->group=(ssize_t) windows->image.id;
9174       (void) DeleteImageProperty(*image,"label");
9175       (void) SetImageProperty(*image,"label","Matte",exception);
9176       (void) AcquireUniqueFilename(filename);
9177       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9178         filename);
9179       status=WriteImage(image_info,*image,exception);
9180       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9181       matte_image=ReadImage(image_info,exception);
9182       (void) RelinquishUniqueFileResource(filename);
9183       if (matte_image == (Image *) NULL)
9184         break;
9185       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9186         filename);
9187       status=WriteImage(image_info,matte_image,exception);
9188       matte_image=DestroyImage(matte_image);
9189       if (status == MagickFalse)
9190         XNoticeWidget(display,windows,"Unable to show matte",
9191           (*image)->filename);
9192       XDelay(display,1500);
9193       XSetCursorState(display,windows,MagickFalse);
9194       break;
9195     }
9196     case BackgroundCommand:
9197     {
9198       /*
9199         Background image.
9200       */
9201       status=XBackgroundImage(display,resource_info,windows,image,exception);
9202       if (status == MagickFalse)
9203         break;
9204       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9205       if (nexus != (Image *) NULL)
9206         XClientMessage(display,windows->image.id,windows->im_protocols,
9207           windows->im_next_image,CurrentTime);
9208       break;
9209     }
9210     case SlideShowCommand:
9211     {
9212       static char
9213         delay[MaxTextExtent] = "5";
9214
9215       /*
9216         Display next image after pausing.
9217       */
9218       (void) XDialogWidget(display,windows,"Slide Show",
9219         "Pause how many 1/100ths of a second between images:",delay);
9220       if (*delay == '\0')
9221         break;
9222       resource_info->delay=StringToUnsignedLong(delay);
9223       XClientMessage(display,windows->image.id,windows->im_protocols,
9224         windows->im_next_image,CurrentTime);
9225       break;
9226     }
9227     case PreferencesCommand:
9228     {
9229       /*
9230         Set user preferences.
9231       */
9232       status=XPreferencesWidget(display,resource_info,windows);
9233       if (status == MagickFalse)
9234         break;
9235       nexus=CloneImage(*image,0,0,MagickTrue,exception);
9236       if (nexus != (Image *) NULL)
9237         XClientMessage(display,windows->image.id,windows->im_protocols,
9238           windows->im_next_image,CurrentTime);
9239       break;
9240     }
9241     case HelpCommand:
9242     {
9243       /*
9244         User requested help.
9245       */
9246       XTextViewWidget(display,resource_info,windows,MagickFalse,
9247         "Help Viewer - Display",DisplayHelp);
9248       break;
9249     }
9250     case BrowseDocumentationCommand:
9251     {
9252       Atom
9253         mozilla_atom;
9254
9255       Window
9256         mozilla_window,
9257         root_window;
9258
9259       /*
9260         Browse the ImageMagick documentation.
9261       */
9262       root_window=XRootWindow(display,XDefaultScreen(display));
9263       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9264       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9265       if (mozilla_window != (Window) NULL)
9266         {
9267           char
9268             command[MaxTextExtent],
9269             *url;
9270
9271           /*
9272             Display documentation using Netscape remote control.
9273           */
9274           url=GetMagickHomeURL();
9275           (void) FormatLocaleString(command,MaxTextExtent,
9276             "openurl(%s,new-tab)",url);
9277           url=DestroyString(url);
9278           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9279           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9280             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9281           XSetCursorState(display,windows,MagickFalse);
9282           break;
9283         }
9284       XSetCursorState(display,windows,MagickTrue);
9285       XCheckRefreshWindows(display,windows);
9286       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9287         exception);
9288       if (status == MagickFalse)
9289         XNoticeWidget(display,windows,"Unable to browse documentation",
9290           (char *) NULL);
9291       XDelay(display,1500);
9292       XSetCursorState(display,windows,MagickFalse);
9293       break;
9294     }
9295     case VersionCommand:
9296     {
9297       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9298         GetMagickCopyright());
9299       break;
9300     }
9301     case SaveToUndoBufferCommand:
9302       break;
9303     default:
9304     {
9305       (void) XBell(display,0);
9306       break;
9307     }
9308   }
9309   image_info=DestroyImageInfo(image_info);
9310   return(nexus);
9311 }
9312 \f
9313 /*
9314 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9315 %                                                                             %
9316 %                                                                             %
9317 %                                                                             %
9318 +   X M a g n i f y I m a g e                                                 %
9319 %                                                                             %
9320 %                                                                             %
9321 %                                                                             %
9322 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9323 %
9324 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9325 %  The magnified portion is displayed in a separate window.
9326 %
9327 %  The format of the XMagnifyImage method is:
9328 %
9329 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9330 %        ExceptionInfo *exception)
9331 %
9332 %  A description of each parameter follows:
9333 %
9334 %    o display: Specifies a connection to an X server;  returned from
9335 %      XOpenDisplay.
9336 %
9337 %    o windows: Specifies a pointer to a XWindows structure.
9338 %
9339 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9340 %      the entire image is refreshed.
9341 %
9342 %    o exception: return any errors or warnings in this structure.
9343 %
9344 */
9345 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event,
9346   ExceptionInfo *exception)
9347 {
9348   char
9349     text[MaxTextExtent];
9350
9351   register int
9352     x,
9353     y;
9354
9355   size_t
9356     state;
9357
9358   /*
9359     Update magnified image until the mouse button is released.
9360   */
9361   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9362   state=DefaultState;
9363   x=event->xbutton.x;
9364   y=event->xbutton.y;
9365   windows->magnify.x=(int) windows->image.x+x;
9366   windows->magnify.y=(int) windows->image.y+y;
9367   do
9368   {
9369     /*
9370       Map and unmap Info widget as text cursor crosses its boundaries.
9371     */
9372     if (windows->info.mapped != MagickFalse)
9373       {
9374         if ((x < (int) (windows->info.x+windows->info.width)) &&
9375             (y < (int) (windows->info.y+windows->info.height)))
9376           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9377       }
9378     else
9379       if ((x > (int) (windows->info.x+windows->info.width)) ||
9380           (y > (int) (windows->info.y+windows->info.height)))
9381         (void) XMapWindow(display,windows->info.id);
9382     if (windows->info.mapped != MagickFalse)
9383       {
9384         /*
9385           Display pointer position.
9386         */
9387         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9388           windows->magnify.x,windows->magnify.y);
9389         XInfoWidget(display,windows,text);
9390       }
9391     /*
9392       Wait for next event.
9393     */
9394     XScreenEvent(display,windows,event,exception);
9395     switch (event->type)
9396     {
9397       case ButtonPress:
9398         break;
9399       case ButtonRelease:
9400       {
9401         /*
9402           User has finished magnifying image.
9403         */
9404         x=event->xbutton.x;
9405         y=event->xbutton.y;
9406         state|=ExitState;
9407         break;
9408       }
9409       case Expose:
9410         break;
9411       case MotionNotify:
9412       {
9413         x=event->xmotion.x;
9414         y=event->xmotion.y;
9415         break;
9416       }
9417       default:
9418         break;
9419     }
9420     /*
9421       Check boundary conditions.
9422     */
9423     if (x < 0)
9424       x=0;
9425     else
9426       if (x >= (int) windows->image.width)
9427         x=(int) windows->image.width-1;
9428     if (y < 0)
9429       y=0;
9430     else
9431      if (y >= (int) windows->image.height)
9432        y=(int) windows->image.height-1;
9433   } while ((state & ExitState) == 0);
9434   /*
9435     Display magnified image.
9436   */
9437   XSetCursorState(display,windows,MagickFalse);
9438 }
9439 \f
9440 /*
9441 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9442 %                                                                             %
9443 %                                                                             %
9444 %                                                                             %
9445 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9446 %                                                                             %
9447 %                                                                             %
9448 %                                                                             %
9449 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9450 %
9451 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9452 %  pixel as specified by the key symbol.
9453 %
9454 %  The format of the XMagnifyWindowCommand method is:
9455 %
9456 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9457 %        const MagickStatusType state,const KeySym key_symbol,
9458 %        ExceptionInfo *exception)
9459 %
9460 %  A description of each parameter follows:
9461 %
9462 %    o display: Specifies a connection to an X server; returned from
9463 %      XOpenDisplay.
9464 %
9465 %    o windows: Specifies a pointer to a XWindows structure.
9466 %
9467 %    o state: key mask.
9468 %
9469 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9470 %      to trim.
9471 %
9472 %    o exception: return any errors or warnings in this structure.
9473 %
9474 */
9475 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9476   const MagickStatusType state,const KeySym key_symbol,ExceptionInfo *exception)
9477 {
9478   unsigned int
9479     quantum;
9480
9481   /*
9482     User specified a magnify factor or position.
9483   */
9484   quantum=1;
9485   if ((state & Mod1Mask) != 0)
9486     quantum=10;
9487   switch ((int) key_symbol)
9488   {
9489     case QuitCommand:
9490     {
9491       (void) XWithdrawWindow(display,windows->magnify.id,
9492         windows->magnify.screen);
9493       break;
9494     }
9495     case XK_Home:
9496     case XK_KP_Home:
9497     {
9498       windows->magnify.x=(int) windows->image.width/2;
9499       windows->magnify.y=(int) windows->image.height/2;
9500       break;
9501     }
9502     case XK_Left:
9503     case XK_KP_Left:
9504     {
9505       if (windows->magnify.x > 0)
9506         windows->magnify.x-=quantum;
9507       break;
9508     }
9509     case XK_Up:
9510     case XK_KP_Up:
9511     {
9512       if (windows->magnify.y > 0)
9513         windows->magnify.y-=quantum;
9514       break;
9515     }
9516     case XK_Right:
9517     case XK_KP_Right:
9518     {
9519       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9520         windows->magnify.x+=quantum;
9521       break;
9522     }
9523     case XK_Down:
9524     case XK_KP_Down:
9525     {
9526       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9527         windows->magnify.y+=quantum;
9528       break;
9529     }
9530     case XK_0:
9531     case XK_1:
9532     case XK_2:
9533     case XK_3:
9534     case XK_4:
9535     case XK_5:
9536     case XK_6:
9537     case XK_7:
9538     case XK_8:
9539     case XK_9:
9540     {
9541       windows->magnify.data=(key_symbol-XK_0);
9542       break;
9543     }
9544     case XK_KP_0:
9545     case XK_KP_1:
9546     case XK_KP_2:
9547     case XK_KP_3:
9548     case XK_KP_4:
9549     case XK_KP_5:
9550     case XK_KP_6:
9551     case XK_KP_7:
9552     case XK_KP_8:
9553     case XK_KP_9:
9554     {
9555       windows->magnify.data=(key_symbol-XK_KP_0);
9556       break;
9557     }
9558     default:
9559       break;
9560   }
9561   XMakeMagnifyImage(display,windows,exception);
9562 }
9563 \f
9564 /*
9565 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9566 %                                                                             %
9567 %                                                                             %
9568 %                                                                             %
9569 +   X M a k e P a n I m a g e                                                 %
9570 %                                                                             %
9571 %                                                                             %
9572 %                                                                             %
9573 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9574 %
9575 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9576 %  icon window.
9577 %
9578 %  The format of the XMakePanImage method is:
9579 %
9580 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9581 %          XWindows *windows,Image *image,ExceptionInfo *exception)
9582 %
9583 %  A description of each parameter follows:
9584 %
9585 %    o display: Specifies a connection to an X server;  returned from
9586 %      XOpenDisplay.
9587 %
9588 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9589 %
9590 %    o windows: Specifies a pointer to a XWindows structure.
9591 %
9592 %    o image: the image.
9593 %
9594 %    o exception: return any errors or warnings in this structure.
9595 %
9596 */
9597 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9598   XWindows *windows,Image *image,ExceptionInfo *exception)
9599 {
9600   MagickStatusType
9601     status;
9602
9603   /*
9604     Create and display image for panning icon.
9605   */
9606   XSetCursorState(display,windows,MagickTrue);
9607   XCheckRefreshWindows(display,windows);
9608   windows->pan.x=(int) windows->image.x;
9609   windows->pan.y=(int) windows->image.y;
9610   status=XMakeImage(display,resource_info,&windows->pan,image,
9611     windows->pan.width,windows->pan.height,exception);
9612   if (status == MagickFalse)
9613     ThrowXWindowFatalException(ResourceLimitError,
9614      "MemoryAllocationFailed",image->filename);
9615   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9616     windows->pan.pixmap);
9617   (void) XClearWindow(display,windows->pan.id);
9618   XDrawPanRectangle(display,windows);
9619   XSetCursorState(display,windows,MagickFalse);
9620 }
9621 \f
9622 /*
9623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9624 %                                                                             %
9625 %                                                                             %
9626 %                                                                             %
9627 +   X M a t t a E d i t I m a g e                                             %
9628 %                                                                             %
9629 %                                                                             %
9630 %                                                                             %
9631 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9632 %
9633 %  XMatteEditImage() allows the user to interactively change the Matte channel
9634 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9635 %  before the matte information is stored.
9636 %
9637 %  The format of the XMatteEditImage method is:
9638 %
9639 %      MagickBooleanType XMatteEditImage(Display *display,
9640 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
9641 %        ExceptionInfo *exception)
9642 %
9643 %  A description of each parameter follows:
9644 %
9645 %    o display: Specifies a connection to an X server;  returned from
9646 %      XOpenDisplay.
9647 %
9648 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9649 %
9650 %    o windows: Specifies a pointer to a XWindows structure.
9651 %
9652 %    o image: the image; returned from ReadImage.
9653 %
9654 %    o exception: return any errors or warnings in this structure.
9655 %
9656 */
9657 static MagickBooleanType XMatteEditImage(Display *display,
9658   XResourceInfo *resource_info,XWindows *windows,Image **image,
9659   ExceptionInfo *exception)
9660 {
9661   static char
9662     matte[MaxTextExtent] = "0";
9663
9664   static const char
9665     *MatteEditMenu[] =
9666     {
9667       "Method",
9668       "Border Color",
9669       "Fuzz",
9670       "Matte Value",
9671       "Undo",
9672       "Help",
9673       "Dismiss",
9674       (char *) NULL
9675     };
9676
9677   static const ModeType
9678     MatteEditCommands[] =
9679     {
9680       MatteEditMethod,
9681       MatteEditBorderCommand,
9682       MatteEditFuzzCommand,
9683       MatteEditValueCommand,
9684       MatteEditUndoCommand,
9685       MatteEditHelpCommand,
9686       MatteEditDismissCommand
9687     };
9688
9689   static PaintMethod
9690     method = PointMethod;
9691
9692   static XColor
9693     border_color = { 0, 0, 0, 0, 0, 0 };
9694
9695   char
9696     command[MaxTextExtent],
9697     text[MaxTextExtent];
9698
9699   Cursor
9700     cursor;
9701
9702   int
9703     entry,
9704     id,
9705     x,
9706     x_offset,
9707     y,
9708     y_offset;
9709
9710   register int
9711     i;
9712
9713   register Quantum
9714     *q;
9715
9716   unsigned int
9717     height,
9718     width;
9719
9720   size_t
9721     state;
9722
9723   XEvent
9724     event;
9725
9726   /*
9727     Map Command widget.
9728   */
9729   (void) CloneString(&windows->command.name,"Matte Edit");
9730   windows->command.data=4;
9731   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9732   (void) XMapRaised(display,windows->command.id);
9733   XClientMessage(display,windows->image.id,windows->im_protocols,
9734     windows->im_update_widget,CurrentTime);
9735   /*
9736     Make cursor.
9737   */
9738   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9739     resource_info->background_color,resource_info->foreground_color);
9740   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9741   /*
9742     Track pointer until button 1 is pressed.
9743   */
9744   XQueryPosition(display,windows->image.id,&x,&y);
9745   (void) XSelectInput(display,windows->image.id,
9746     windows->image.attributes.event_mask | PointerMotionMask);
9747   state=DefaultState;
9748   do
9749   {
9750     if (windows->info.mapped != MagickFalse)
9751       {
9752         /*
9753           Display pointer position.
9754         */
9755         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9756           x+windows->image.x,y+windows->image.y);
9757         XInfoWidget(display,windows,text);
9758       }
9759     /*
9760       Wait for next event.
9761     */
9762     XScreenEvent(display,windows,&event,exception);
9763     if (event.xany.window == windows->command.id)
9764       {
9765         /*
9766           Select a command from the Command widget.
9767         */
9768         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9769         if (id < 0)
9770           {
9771             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9772             continue;
9773           }
9774         switch (MatteEditCommands[id])
9775         {
9776           case MatteEditMethod:
9777           {
9778             char
9779               **methods;
9780
9781             /*
9782               Select a method from the pop-up menu.
9783             */
9784             methods=GetCommandOptions(MagickMethodOptions);
9785             if (methods == (char **) NULL)
9786               break;
9787             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9788               (const char **) methods,command);
9789             if (entry >= 0)
9790               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9791                 MagickFalse,methods[entry]);
9792             methods=DestroyStringList(methods);
9793             break;
9794           }
9795           case MatteEditBorderCommand:
9796           {
9797             const char
9798               *ColorMenu[MaxNumberPens];
9799
9800             int
9801               pen_number;
9802
9803             /*
9804               Initialize menu selections.
9805             */
9806             for (i=0; i < (int) (MaxNumberPens-2); i++)
9807               ColorMenu[i]=resource_info->pen_colors[i];
9808             ColorMenu[MaxNumberPens-2]="Browser...";
9809             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9810             /*
9811               Select a pen color from the pop-up menu.
9812             */
9813             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9814               (const char **) ColorMenu,command);
9815             if (pen_number < 0)
9816               break;
9817             if (pen_number == (MaxNumberPens-2))
9818               {
9819                 static char
9820                   color_name[MaxTextExtent] = "gray";
9821
9822                 /*
9823                   Select a pen color from a dialog.
9824                 */
9825                 resource_info->pen_colors[pen_number]=color_name;
9826                 XColorBrowserWidget(display,windows,"Select",color_name);
9827                 if (*color_name == '\0')
9828                   break;
9829               }
9830             /*
9831               Set border color.
9832             */
9833             (void) XParseColor(display,windows->map_info->colormap,
9834               resource_info->pen_colors[pen_number],&border_color);
9835             break;
9836           }
9837           case MatteEditFuzzCommand:
9838           {
9839             static char
9840               fuzz[MaxTextExtent];
9841
9842             static const char
9843               *FuzzMenu[] =
9844               {
9845                 "0%",
9846                 "2%",
9847                 "5%",
9848                 "10%",
9849                 "15%",
9850                 "Dialog...",
9851                 (char *) NULL,
9852               };
9853
9854             /*
9855               Select a command from the pop-up menu.
9856             */
9857             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9858               command);
9859             if (entry < 0)
9860               break;
9861             if (entry != 5)
9862               {
9863                 (*image)->fuzz=StringToDoubleInterval(FuzzMenu[entry],(double)
9864                   QuantumRange+1.0);
9865                 break;
9866               }
9867             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9868             (void) XDialogWidget(display,windows,"Ok",
9869               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9870             if (*fuzz == '\0')
9871               break;
9872             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9873             (*image)->fuzz=StringToDoubleInterval(fuzz,(double) QuantumRange+
9874               1.0);
9875             break;
9876           }
9877           case MatteEditValueCommand:
9878           {
9879             static char
9880               message[MaxTextExtent];
9881
9882             static const char
9883               *MatteMenu[] =
9884               {
9885                 "Opaque",
9886                 "Transparent",
9887                 "Dialog...",
9888                 (char *) NULL,
9889               };
9890
9891             /*
9892               Select a command from the pop-up menu.
9893             */
9894             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9895               command);
9896             if (entry < 0)
9897               break;
9898             if (entry != 2)
9899               {
9900                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9901                   OpaqueAlpha);
9902                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9903                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9904                     (Quantum) TransparentAlpha);
9905                 break;
9906               }
9907             (void) FormatLocaleString(message,MaxTextExtent,
9908               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9909               QuantumRange);
9910             (void) XDialogWidget(display,windows,"Matte",message,matte);
9911             if (*matte == '\0')
9912               break;
9913             break;
9914           }
9915           case MatteEditUndoCommand:
9916           {
9917             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9918               image,exception);
9919             break;
9920           }
9921           case MatteEditHelpCommand:
9922           {
9923             XTextViewWidget(display,resource_info,windows,MagickFalse,
9924               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9925             break;
9926           }
9927           case MatteEditDismissCommand:
9928           {
9929             /*
9930               Prematurely exit.
9931             */
9932             state|=EscapeState;
9933             state|=ExitState;
9934             break;
9935           }
9936           default:
9937             break;
9938         }
9939         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9940         continue;
9941       }
9942     switch (event.type)
9943     {
9944       case ButtonPress:
9945       {
9946         if (event.xbutton.button != Button1)
9947           break;
9948         if ((event.xbutton.window != windows->image.id) &&
9949             (event.xbutton.window != windows->magnify.id))
9950           break;
9951         /*
9952           Update matte data.
9953         */
9954         x=event.xbutton.x;
9955         y=event.xbutton.y;
9956         (void) XMagickCommand(display,resource_info,windows,
9957           SaveToUndoBufferCommand,image,exception);
9958         state|=UpdateConfigurationState;
9959         break;
9960       }
9961       case ButtonRelease:
9962       {
9963         if (event.xbutton.button != Button1)
9964           break;
9965         if ((event.xbutton.window != windows->image.id) &&
9966             (event.xbutton.window != windows->magnify.id))
9967           break;
9968         /*
9969           Update colormap information.
9970         */
9971         x=event.xbutton.x;
9972         y=event.xbutton.y;
9973         XConfigureImageColormap(display,resource_info,windows,*image,exception);
9974         (void) XConfigureImage(display,resource_info,windows,*image,exception);
9975         XInfoWidget(display,windows,text);
9976         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9977         state&=(~UpdateConfigurationState);
9978         break;
9979       }
9980       case Expose:
9981         break;
9982       case KeyPress:
9983       {
9984         char
9985           command[MaxTextExtent];
9986
9987         KeySym
9988           key_symbol;
9989
9990         if (event.xkey.window == windows->magnify.id)
9991           {
9992             Window
9993               window;
9994
9995             window=windows->magnify.id;
9996             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9997           }
9998         if (event.xkey.window != windows->image.id)
9999           break;
10000         /*
10001           Respond to a user key press.
10002         */
10003         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
10004           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10005         switch ((int) key_symbol)
10006         {
10007           case XK_Escape:
10008           case XK_F20:
10009           {
10010             /*
10011               Prematurely exit.
10012             */
10013             state|=ExitState;
10014             break;
10015           }
10016           case XK_F1:
10017           case XK_Help:
10018           {
10019             XTextViewWidget(display,resource_info,windows,MagickFalse,
10020               "Help Viewer - Matte Edit",ImageMatteEditHelp);
10021             break;
10022           }
10023           default:
10024           {
10025             (void) XBell(display,0);
10026             break;
10027           }
10028         }
10029         break;
10030       }
10031       case MotionNotify:
10032       {
10033         /*
10034           Map and unmap Info widget as cursor crosses its boundaries.
10035         */
10036         x=event.xmotion.x;
10037         y=event.xmotion.y;
10038         if (windows->info.mapped != MagickFalse)
10039           {
10040             if ((x < (int) (windows->info.x+windows->info.width)) &&
10041                 (y < (int) (windows->info.y+windows->info.height)))
10042               (void) XWithdrawWindow(display,windows->info.id,
10043                 windows->info.screen);
10044           }
10045         else
10046           if ((x > (int) (windows->info.x+windows->info.width)) ||
10047               (y > (int) (windows->info.y+windows->info.height)))
10048             (void) XMapWindow(display,windows->info.id);
10049         break;
10050       }
10051       default:
10052         break;
10053     }
10054     if (event.xany.window == windows->magnify.id)
10055       {
10056         x=windows->magnify.x-windows->image.x;
10057         y=windows->magnify.y-windows->image.y;
10058       }
10059     x_offset=x;
10060     y_offset=y;
10061     if ((state & UpdateConfigurationState) != 0)
10062       {
10063         CacheView
10064           *image_view;
10065
10066         int
10067           x,
10068           y;
10069
10070         /*
10071           Matte edit is relative to image configuration.
10072         */
10073         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
10074           MagickTrue);
10075         XPutPixel(windows->image.ximage,x_offset,y_offset,
10076           windows->pixel_info->background_color.pixel);
10077         width=(unsigned int) (*image)->columns;
10078         height=(unsigned int) (*image)->rows;
10079         x=0;
10080         y=0;
10081         if (windows->image.crop_geometry != (char *) NULL)
10082           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,
10083             &height);
10084         x_offset=(int) (width*(windows->image.x+x_offset)/
10085           windows->image.ximage->width+x);
10086         y_offset=(int) (height*(windows->image.y+y_offset)/
10087           windows->image.ximage->height+y);
10088         if ((x_offset < 0) || (y_offset < 0))
10089           continue;
10090         if ((x_offset >= (int) (*image)->columns) ||
10091             (y_offset >= (int) (*image)->rows))
10092           continue;
10093         if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10094           return(MagickFalse);
10095         if ((*image)->matte == MagickFalse)
10096           (void) SetImageAlphaChannel(*image,OpaqueAlphaChannel,exception);
10097         image_view=AcquireAuthenticCacheView(*image,exception);
10098         switch (method)
10099         {
10100           case PointMethod:
10101           default:
10102           {
10103             /*
10104               Update matte information using point algorithm.
10105             */
10106             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
10107               (ssize_t) y_offset,1,1,exception);
10108             if (q == (Quantum *) NULL)
10109               break;
10110             SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10111             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10112             break;
10113           }
10114           case ReplaceMethod:
10115           {
10116             PixelInfo
10117               pixel,
10118               target;
10119
10120             /*
10121               Update matte information using replace algorithm.
10122             */
10123             (void) GetOneCacheViewVirtualPixelInfo(image_view,(ssize_t)
10124               x_offset,(ssize_t) y_offset,&target,exception);
10125             for (y=0; y < (int) (*image)->rows; y++)
10126             {
10127               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10128                 (*image)->columns,1,exception);
10129               if (q == (Quantum *) NULL)
10130                 break;
10131               for (x=0; x < (int) (*image)->columns; x++)
10132               {
10133                 GetPixelInfoPixel(*image,q,&pixel);
10134                 if (IsFuzzyEquivalencePixelInfo(&pixel,&target))
10135                   SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10136                 q+=GetPixelChannels(*image);
10137               }
10138               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10139                 break;
10140             }
10141             break;
10142           }
10143           case FloodfillMethod:
10144           case FillToBorderMethod:
10145           {
10146             ChannelType
10147               channel_mask;
10148
10149             DrawInfo
10150               *draw_info;
10151
10152             PixelInfo
10153               target;
10154
10155             /*
10156               Update matte information using floodfill algorithm.
10157             */
10158             (void) GetOneVirtualPixelInfo(*image,
10159               GetPixelCacheVirtualMethod(*image),(ssize_t) x_offset,(ssize_t)
10160               y_offset,&target,exception);
10161             if (method == FillToBorderMethod)
10162               {
10163                 target.red=(MagickRealType) ScaleShortToQuantum(
10164                   border_color.red);
10165                 target.green=(MagickRealType) ScaleShortToQuantum(
10166                   border_color.green);
10167                 target.blue=(MagickRealType) ScaleShortToQuantum(
10168                   border_color.blue);
10169               }
10170             draw_info=CloneDrawInfo(resource_info->image_info,
10171               (DrawInfo *) NULL);
10172             draw_info->fill.alpha=(MagickRealType) ClampToQuantum(
10173               StringToDouble(matte,(char **) NULL));
10174             channel_mask=SetPixelChannelMask(*image,AlphaChannel); 
10175             (void) FloodfillPaintImage(*image,draw_info,&target,(ssize_t)
10176               x_offset,(ssize_t) y_offset,method == FloodfillMethod ?
10177               MagickFalse : MagickTrue,exception);
10178             (void) SetPixelChannelMapMask(*image,channel_mask);
10179             draw_info=DestroyDrawInfo(draw_info);
10180             break;
10181           }
10182           case ResetMethod:
10183           {
10184             /*
10185               Update matte information using reset algorithm.
10186             */
10187             if (SetImageStorageClass(*image,DirectClass,exception) == MagickFalse)
10188               return(MagickFalse);
10189             for (y=0; y < (int) (*image)->rows; y++)
10190             {
10191               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10192                 (*image)->columns,1,exception);
10193               if (q == (Quantum *) NULL)
10194                 break;
10195               for (x=0; x < (int) (*image)->columns; x++)
10196               {
10197                 SetPixelAlpha(*image,(Quantum) StringToLong(matte),q);
10198                 q+=GetPixelChannels(*image);
10199               }
10200               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10201                 break;
10202             }
10203             if (StringToLong(matte) == (long) OpaqueAlpha)
10204               (*image)->matte=MagickFalse;
10205             break;
10206           }
10207         }
10208         image_view=DestroyCacheView(image_view);
10209         state&=(~UpdateConfigurationState);
10210       }
10211   } while ((state & ExitState) == 0);
10212   (void) XSelectInput(display,windows->image.id,
10213     windows->image.attributes.event_mask);
10214   XSetCursorState(display,windows,MagickFalse);
10215   (void) XFreeCursor(display,cursor);
10216   return(MagickTrue);
10217 }
10218 \f
10219 /*
10220 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10221 %                                                                             %
10222 %                                                                             %
10223 %                                                                             %
10224 +   X O p e n I m a g e                                                       %
10225 %                                                                             %
10226 %                                                                             %
10227 %                                                                             %
10228 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10229 %
10230 %  XOpenImage() loads an image from a file.
10231 %
10232 %  The format of the XOpenImage method is:
10233 %
10234 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10235 %       XWindows *windows,const unsigned int command)
10236 %
10237 %  A description of each parameter follows:
10238 %
10239 %    o display: Specifies a connection to an X server; returned from
10240 %      XOpenDisplay.
10241 %
10242 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10243 %
10244 %    o windows: Specifies a pointer to a XWindows structure.
10245 %
10246 %    o command: A value other than zero indicates that the file is selected
10247 %      from the command line argument list.
10248 %
10249 */
10250 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10251   XWindows *windows,const MagickBooleanType command)
10252 {
10253   const MagickInfo
10254     *magick_info;
10255
10256   ExceptionInfo
10257     *exception;
10258
10259   Image
10260     *nexus;
10261
10262   ImageInfo
10263     *image_info;
10264
10265   static char
10266     filename[MaxTextExtent] = "\0";
10267
10268   /*
10269     Request file name from user.
10270   */
10271   if (command == MagickFalse)
10272     XFileBrowserWidget(display,windows,"Open",filename);
10273   else
10274     {
10275       char
10276         **filelist,
10277         **files;
10278
10279       int
10280         count,
10281         status;
10282
10283       register int
10284         i,
10285         j;
10286
10287       /*
10288         Select next image from the command line.
10289       */
10290       status=XGetCommand(display,windows->image.id,&files,&count);
10291       if (status == 0)
10292         {
10293           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10294           return((Image *) NULL);
10295         }
10296       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10297       if (filelist == (char **) NULL)
10298         {
10299           ThrowXWindowFatalException(ResourceLimitError,
10300             "MemoryAllocationFailed","...");
10301           (void) XFreeStringList(files);
10302           return((Image *) NULL);
10303         }
10304       j=0;
10305       for (i=1; i < count; i++)
10306         if (*files[i] != '-')
10307           filelist[j++]=files[i];
10308       filelist[j]=(char *) NULL;
10309       XListBrowserWidget(display,windows,&windows->widget,
10310         (const char **) filelist,"Load","Select Image to Load:",filename);
10311       filelist=(char **) RelinquishMagickMemory(filelist);
10312       (void) XFreeStringList(files);
10313     }
10314   if (*filename == '\0')
10315     return((Image *) NULL);
10316   image_info=CloneImageInfo(resource_info->image_info);
10317   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10318     (void *) NULL);
10319   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10320   exception=AcquireExceptionInfo();
10321   (void) SetImageInfo(image_info,0,exception);
10322   if (LocaleCompare(image_info->magick,"X") == 0)
10323     {
10324       char
10325         seconds[MaxTextExtent];
10326
10327       /*
10328         User may want to delay the X server screen grab.
10329       */
10330       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10331       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10332         seconds);
10333       if (*seconds == '\0')
10334         return((Image *) NULL);
10335       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10336     }
10337   magick_info=GetMagickInfo(image_info->magick,exception);
10338   if ((magick_info != (const MagickInfo *) NULL) &&
10339       (magick_info->raw != MagickFalse))
10340     {
10341       char
10342         geometry[MaxTextExtent];
10343
10344       /*
10345         Request image size from the user.
10346       */
10347       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10348       if (image_info->size != (char *) NULL)
10349         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10350       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10351         geometry);
10352       (void) CloneString(&image_info->size,geometry);
10353     }
10354   /*
10355     Load the image.
10356   */
10357   XSetCursorState(display,windows,MagickTrue);
10358   XCheckRefreshWindows(display,windows);
10359   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10360   nexus=ReadImage(image_info,exception);
10361   CatchException(exception);
10362   XSetCursorState(display,windows,MagickFalse);
10363   if (nexus != (Image *) NULL)
10364     XClientMessage(display,windows->image.id,windows->im_protocols,
10365       windows->im_next_image,CurrentTime);
10366   else
10367     {
10368       char
10369         *text,
10370         **textlist;
10371
10372       /*
10373         Unknown image format.
10374       */
10375       text=FileToString(filename,~0,exception);
10376       if (text == (char *) NULL)
10377         return((Image *) NULL);
10378       textlist=StringToList(text);
10379       if (textlist != (char **) NULL)
10380         {
10381           char
10382             title[MaxTextExtent];
10383
10384           register int
10385             i;
10386
10387           (void) FormatLocaleString(title,MaxTextExtent,
10388             "Unknown format: %s",filename);
10389           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10390             (const char **) textlist);
10391           for (i=0; textlist[i] != (char *) NULL; i++)
10392             textlist[i]=DestroyString(textlist[i]);
10393           textlist=(char **) RelinquishMagickMemory(textlist);
10394         }
10395       text=DestroyString(text);
10396     }
10397   exception=DestroyExceptionInfo(exception);
10398   image_info=DestroyImageInfo(image_info);
10399   return(nexus);
10400 }
10401 \f
10402 /*
10403 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10404 %                                                                             %
10405 %                                                                             %
10406 %                                                                             %
10407 +   X P a n I m a g e                                                         %
10408 %                                                                             %
10409 %                                                                             %
10410 %                                                                             %
10411 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10412 %
10413 %  XPanImage() pans the image until the mouse button is released.
10414 %
10415 %  The format of the XPanImage method is:
10416 %
10417 %      void XPanImage(Display *display,XWindows *windows,XEvent *event,
10418 %        ExceptionInfo *exception)
10419 %
10420 %  A description of each parameter follows:
10421 %
10422 %    o display: Specifies a connection to an X server;  returned from
10423 %      XOpenDisplay.
10424 %
10425 %    o windows: Specifies a pointer to a XWindows structure.
10426 %
10427 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10428 %      the entire image is refreshed.
10429 %
10430 %    o exception: return any errors or warnings in this structure.
10431 %
10432 */
10433 static void XPanImage(Display *display,XWindows *windows,XEvent *event,
10434   ExceptionInfo *exception)
10435 {
10436   char
10437     text[MaxTextExtent];
10438
10439   Cursor
10440     cursor;
10441
10442   MagickRealType
10443     x_factor,
10444     y_factor;
10445
10446   RectangleInfo
10447     pan_info;
10448
10449   size_t
10450     state;
10451
10452   /*
10453     Define cursor.
10454   */
10455   if ((windows->image.ximage->width > (int) windows->image.width) &&
10456       (windows->image.ximage->height > (int) windows->image.height))
10457     cursor=XCreateFontCursor(display,XC_fleur);
10458   else
10459     if (windows->image.ximage->width > (int) windows->image.width)
10460       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10461     else
10462       if (windows->image.ximage->height > (int) windows->image.height)
10463         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10464       else
10465         cursor=XCreateFontCursor(display,XC_arrow);
10466   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10467   /*
10468     Pan image as pointer moves until the mouse button is released.
10469   */
10470   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10471   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10472   pan_info.width=windows->pan.width*windows->image.width/
10473     windows->image.ximage->width;
10474   pan_info.height=windows->pan.height*windows->image.height/
10475     windows->image.ximage->height;
10476   pan_info.x=0;
10477   pan_info.y=0;
10478   state=UpdateConfigurationState;
10479   do
10480   {
10481     switch (event->type)
10482     {
10483       case ButtonPress:
10484       {
10485         /*
10486           User choose an initial pan location.
10487         */
10488         pan_info.x=(ssize_t) event->xbutton.x;
10489         pan_info.y=(ssize_t) event->xbutton.y;
10490         state|=UpdateConfigurationState;
10491         break;
10492       }
10493       case ButtonRelease:
10494       {
10495         /*
10496           User has finished panning the image.
10497         */
10498         pan_info.x=(ssize_t) event->xbutton.x;
10499         pan_info.y=(ssize_t) event->xbutton.y;
10500         state|=UpdateConfigurationState | ExitState;
10501         break;
10502       }
10503       case MotionNotify:
10504       {
10505         pan_info.x=(ssize_t) event->xmotion.x;
10506         pan_info.y=(ssize_t) event->xmotion.y;
10507         state|=UpdateConfigurationState;
10508       }
10509       default:
10510         break;
10511     }
10512     if ((state & UpdateConfigurationState) != 0)
10513       {
10514         /*
10515           Check boundary conditions.
10516         */
10517         if (pan_info.x < (ssize_t) (pan_info.width/2))
10518           pan_info.x=0;
10519         else
10520           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10521         if (pan_info.x < 0)
10522           pan_info.x=0;
10523         else
10524           if ((int) (pan_info.x+windows->image.width) >
10525               windows->image.ximage->width)
10526             pan_info.x=(ssize_t)
10527               (windows->image.ximage->width-windows->image.width);
10528         if (pan_info.y < (ssize_t) (pan_info.height/2))
10529           pan_info.y=0;
10530         else
10531           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10532         if (pan_info.y < 0)
10533           pan_info.y=0;
10534         else
10535           if ((int) (pan_info.y+windows->image.height) >
10536               windows->image.ximage->height)
10537             pan_info.y=(ssize_t)
10538               (windows->image.ximage->height-windows->image.height);
10539         if ((windows->image.x != (int) pan_info.x) ||
10540             (windows->image.y != (int) pan_info.y))
10541           {
10542             /*
10543               Display image pan offset.
10544             */
10545             windows->image.x=(int) pan_info.x;
10546             windows->image.y=(int) pan_info.y;
10547             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10548               windows->image.width,windows->image.height,windows->image.x,
10549               windows->image.y);
10550             XInfoWidget(display,windows,text);
10551             /*
10552               Refresh Image window.
10553             */
10554             XDrawPanRectangle(display,windows);
10555             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10556           }
10557         state&=(~UpdateConfigurationState);
10558       }
10559     /*
10560       Wait for next event.
10561     */
10562     if ((state & ExitState) == 0)
10563       XScreenEvent(display,windows,event,exception);
10564   } while ((state & ExitState) == 0);
10565   /*
10566     Restore cursor.
10567   */
10568   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10569   (void) XFreeCursor(display,cursor);
10570   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10571 }
10572 \f
10573 /*
10574 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10575 %                                                                             %
10576 %                                                                             %
10577 %                                                                             %
10578 +   X P a s t e I m a g e                                                     %
10579 %                                                                             %
10580 %                                                                             %
10581 %                                                                             %
10582 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10583 %
10584 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10585 %  window image at a location the user chooses with the pointer.
10586 %
10587 %  The format of the XPasteImage method is:
10588 %
10589 %      MagickBooleanType XPasteImage(Display *display,
10590 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10591 %        ExceptionInfo *exception)
10592 %
10593 %  A description of each parameter follows:
10594 %
10595 %    o display: Specifies a connection to an X server;  returned from
10596 %      XOpenDisplay.
10597 %
10598 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10599 %
10600 %    o windows: Specifies a pointer to a XWindows structure.
10601 %
10602 %    o image: the image; returned from ReadImage.
10603 %
10604 %    o exception: return any errors or warnings in this structure.
10605 %
10606 */
10607 static MagickBooleanType XPasteImage(Display *display,
10608   XResourceInfo *resource_info,XWindows *windows,Image *image,
10609   ExceptionInfo *exception)
10610 {
10611   static const char
10612     *PasteMenu[] =
10613     {
10614       "Operator",
10615       "Help",
10616       "Dismiss",
10617       (char *) NULL
10618     };
10619
10620   static const ModeType
10621     PasteCommands[] =
10622     {
10623       PasteOperatorsCommand,
10624       PasteHelpCommand,
10625       PasteDismissCommand
10626     };
10627
10628   static CompositeOperator
10629     compose = CopyCompositeOp;
10630
10631   char
10632     text[MaxTextExtent];
10633
10634   Cursor
10635     cursor;
10636
10637   Image
10638     *paste_image;
10639
10640   int
10641     entry,
10642     id,
10643     x,
10644     y;
10645
10646   MagickRealType
10647     scale_factor;
10648
10649   RectangleInfo
10650     highlight_info,
10651     paste_info;
10652
10653   unsigned int
10654     height,
10655     width;
10656
10657   size_t
10658     state;
10659
10660   XEvent
10661     event;
10662
10663   /*
10664     Copy image.
10665   */
10666   if (resource_info->copy_image == (Image *) NULL)
10667     return(MagickFalse);
10668   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,exception);
10669   /*
10670     Map Command widget.
10671   */
10672   (void) CloneString(&windows->command.name,"Paste");
10673   windows->command.data=1;
10674   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10675   (void) XMapRaised(display,windows->command.id);
10676   XClientMessage(display,windows->image.id,windows->im_protocols,
10677     windows->im_update_widget,CurrentTime);
10678   /*
10679     Track pointer until button 1 is pressed.
10680   */
10681   XSetCursorState(display,windows,MagickFalse);
10682   XQueryPosition(display,windows->image.id,&x,&y);
10683   (void) XSelectInput(display,windows->image.id,
10684     windows->image.attributes.event_mask | PointerMotionMask);
10685   paste_info.x=(ssize_t) windows->image.x+x;
10686   paste_info.y=(ssize_t) windows->image.y+y;
10687   paste_info.width=0;
10688   paste_info.height=0;
10689   cursor=XCreateFontCursor(display,XC_ul_angle);
10690   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10691   state=DefaultState;
10692   do
10693   {
10694     if (windows->info.mapped != MagickFalse)
10695       {
10696         /*
10697           Display pointer position.
10698         */
10699         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10700           (long) paste_info.x,(long) paste_info.y);
10701         XInfoWidget(display,windows,text);
10702       }
10703     highlight_info=paste_info;
10704     highlight_info.x=paste_info.x-windows->image.x;
10705     highlight_info.y=paste_info.y-windows->image.y;
10706     XHighlightRectangle(display,windows->image.id,
10707       windows->image.highlight_context,&highlight_info);
10708     /*
10709       Wait for next event.
10710     */
10711     XScreenEvent(display,windows,&event,exception);
10712     XHighlightRectangle(display,windows->image.id,
10713       windows->image.highlight_context,&highlight_info);
10714     if (event.xany.window == windows->command.id)
10715       {
10716         /*
10717           Select a command from the Command widget.
10718         */
10719         id=XCommandWidget(display,windows,PasteMenu,&event);
10720         if (id < 0)
10721           continue;
10722         switch (PasteCommands[id])
10723         {
10724           case PasteOperatorsCommand:
10725           {
10726             char
10727               command[MaxTextExtent],
10728               **operators;
10729
10730             /*
10731               Select a command from the pop-up menu.
10732             */
10733             operators=GetCommandOptions(MagickComposeOptions);
10734             if (operators == (char **) NULL)
10735               break;
10736             entry=XMenuWidget(display,windows,PasteMenu[id],
10737               (const char **) operators,command);
10738             if (entry >= 0)
10739               compose=(CompositeOperator) ParseCommandOption(
10740                 MagickComposeOptions,MagickFalse,operators[entry]);
10741             operators=DestroyStringList(operators);
10742             break;
10743           }
10744           case PasteHelpCommand:
10745           {
10746             XTextViewWidget(display,resource_info,windows,MagickFalse,
10747               "Help Viewer - Image Composite",ImagePasteHelp);
10748             break;
10749           }
10750           case PasteDismissCommand:
10751           {
10752             /*
10753               Prematurely exit.
10754             */
10755             state|=EscapeState;
10756             state|=ExitState;
10757             break;
10758           }
10759           default:
10760             break;
10761         }
10762         continue;
10763       }
10764     switch (event.type)
10765     {
10766       case ButtonPress:
10767       {
10768         if (image->debug != MagickFalse)
10769           (void) LogMagickEvent(X11Event,GetMagickModule(),
10770             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10771             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10772         if (event.xbutton.button != Button1)
10773           break;
10774         if (event.xbutton.window != windows->image.id)
10775           break;
10776         /*
10777           Paste rectangle is relative to image configuration.
10778         */
10779         width=(unsigned int) image->columns;
10780         height=(unsigned int) image->rows;
10781         x=0;
10782         y=0;
10783         if (windows->image.crop_geometry != (char *) NULL)
10784           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10785             &width,&height);
10786         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10787         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10788         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10789         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10790         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10791         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10792         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10793         break;
10794       }
10795       case ButtonRelease:
10796       {
10797         if (image->debug != MagickFalse)
10798           (void) LogMagickEvent(X11Event,GetMagickModule(),
10799             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10800             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10801         if (event.xbutton.button != Button1)
10802           break;
10803         if (event.xbutton.window != windows->image.id)
10804           break;
10805         if ((paste_info.width != 0) && (paste_info.height != 0))
10806           {
10807             /*
10808               User has selected the location of the paste image.
10809             */
10810             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10811             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10812             state|=ExitState;
10813           }
10814         break;
10815       }
10816       case Expose:
10817         break;
10818       case KeyPress:
10819       {
10820         char
10821           command[MaxTextExtent];
10822
10823         KeySym
10824           key_symbol;
10825
10826         int
10827           length;
10828
10829         if (event.xkey.window != windows->image.id)
10830           break;
10831         /*
10832           Respond to a user key press.
10833         */
10834         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10835           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10836         *(command+length)='\0';
10837         if (image->debug != MagickFalse)
10838           (void) LogMagickEvent(X11Event,GetMagickModule(),
10839             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10840         switch ((int) key_symbol)
10841         {
10842           case XK_Escape:
10843           case XK_F20:
10844           {
10845             /*
10846               Prematurely exit.
10847             */
10848             paste_image=DestroyImage(paste_image);
10849             state|=EscapeState;
10850             state|=ExitState;
10851             break;
10852           }
10853           case XK_F1:
10854           case XK_Help:
10855           {
10856             (void) XSetFunction(display,windows->image.highlight_context,
10857               GXcopy);
10858             XTextViewWidget(display,resource_info,windows,MagickFalse,
10859               "Help Viewer - Image Composite",ImagePasteHelp);
10860             (void) XSetFunction(display,windows->image.highlight_context,
10861               GXinvert);
10862             break;
10863           }
10864           default:
10865           {
10866             (void) XBell(display,0);
10867             break;
10868           }
10869         }
10870         break;
10871       }
10872       case MotionNotify:
10873       {
10874         /*
10875           Map and unmap Info widget as text cursor crosses its boundaries.
10876         */
10877         x=event.xmotion.x;
10878         y=event.xmotion.y;
10879         if (windows->info.mapped != MagickFalse)
10880           {
10881             if ((x < (int) (windows->info.x+windows->info.width)) &&
10882                 (y < (int) (windows->info.y+windows->info.height)))
10883               (void) XWithdrawWindow(display,windows->info.id,
10884                 windows->info.screen);
10885           }
10886         else
10887           if ((x > (int) (windows->info.x+windows->info.width)) ||
10888               (y > (int) (windows->info.y+windows->info.height)))
10889             (void) XMapWindow(display,windows->info.id);
10890         paste_info.x=(ssize_t) windows->image.x+x;
10891         paste_info.y=(ssize_t) windows->image.y+y;
10892         break;
10893       }
10894       default:
10895       {
10896         if (image->debug != MagickFalse)
10897           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10898             event.type);
10899         break;
10900       }
10901     }
10902   } while ((state & ExitState) == 0);
10903   (void) XSelectInput(display,windows->image.id,
10904     windows->image.attributes.event_mask);
10905   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10906   XSetCursorState(display,windows,MagickFalse);
10907   (void) XFreeCursor(display,cursor);
10908   if ((state & EscapeState) != 0)
10909     return(MagickTrue);
10910   /*
10911     Image pasting is relative to image configuration.
10912   */
10913   XSetCursorState(display,windows,MagickTrue);
10914   XCheckRefreshWindows(display,windows);
10915   width=(unsigned int) image->columns;
10916   height=(unsigned int) image->rows;
10917   x=0;
10918   y=0;
10919   if (windows->image.crop_geometry != (char *) NULL)
10920     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10921   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10922   paste_info.x+=x;
10923   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10924   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10925   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10926   paste_info.y+=y;
10927   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10928   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10929   /*
10930     Paste image with X Image window.
10931   */
10932   (void) CompositeImage(image,paste_image,compose,MagickTrue,paste_info.x,
10933     paste_info.y,exception);
10934   paste_image=DestroyImage(paste_image);
10935   XSetCursorState(display,windows,MagickFalse);
10936   /*
10937     Update image colormap.
10938   */
10939   XConfigureImageColormap(display,resource_info,windows,image,exception);
10940   (void) XConfigureImage(display,resource_info,windows,image,exception);
10941   return(MagickTrue);
10942 }
10943 \f
10944 /*
10945 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10946 %                                                                             %
10947 %                                                                             %
10948 %                                                                             %
10949 +   X P r i n t I m a g e                                                     %
10950 %                                                                             %
10951 %                                                                             %
10952 %                                                                             %
10953 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10954 %
10955 %  XPrintImage() prints an image to a Postscript printer.
10956 %
10957 %  The format of the XPrintImage method is:
10958 %
10959 %      MagickBooleanType XPrintImage(Display *display,
10960 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
10961 %        ExceptionInfo *exception)
10962 %
10963 %  A description of each parameter follows:
10964 %
10965 %    o display: Specifies a connection to an X server; returned from
10966 %      XOpenDisplay.
10967 %
10968 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10969 %
10970 %    o windows: Specifies a pointer to a XWindows structure.
10971 %
10972 %    o image: the image.
10973 %
10974 %    o exception: return any errors or warnings in this structure.
10975 %
10976 */
10977 static MagickBooleanType XPrintImage(Display *display,
10978   XResourceInfo *resource_info,XWindows *windows,Image *image,
10979   ExceptionInfo *exception)
10980 {
10981   char
10982     filename[MaxTextExtent],
10983     geometry[MaxTextExtent];
10984
10985   Image
10986     *print_image;
10987
10988   ImageInfo
10989     *image_info;
10990
10991   MagickStatusType
10992     status;
10993
10994   /*
10995     Request Postscript page geometry from user.
10996   */
10997   image_info=CloneImageInfo(resource_info->image_info);
10998   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10999   if (image_info->page != (char *) NULL)
11000     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
11001   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
11002     "Select Postscript Page Geometry:",geometry);
11003   if (*geometry == '\0')
11004     return(MagickTrue);
11005   image_info->page=GetPageGeometry(geometry);
11006   /*
11007     Apply image transforms.
11008   */
11009   XSetCursorState(display,windows,MagickTrue);
11010   XCheckRefreshWindows(display,windows);
11011   print_image=CloneImage(image,0,0,MagickTrue,exception);
11012   if (print_image == (Image *) NULL)
11013     return(MagickFalse);
11014   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
11015     windows->image.ximage->width,windows->image.ximage->height);
11016   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry,
11017     exception);
11018   /*
11019     Print image.
11020   */
11021   (void) AcquireUniqueFilename(filename);
11022   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
11023     filename);
11024   status=WriteImage(image_info,print_image,exception);
11025   (void) RelinquishUniqueFileResource(filename);
11026   print_image=DestroyImage(print_image);
11027   image_info=DestroyImageInfo(image_info);
11028   XSetCursorState(display,windows,MagickFalse);
11029   return(status != 0 ? MagickTrue : MagickFalse);
11030 }
11031 \f
11032 /*
11033 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11034 %                                                                             %
11035 %                                                                             %
11036 %                                                                             %
11037 +   X R O I I m a g e                                                         %
11038 %                                                                             %
11039 %                                                                             %
11040 %                                                                             %
11041 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11042 %
11043 %  XROIImage() applies an image processing technique to a region of interest.
11044 %
11045 %  The format of the XROIImage method is:
11046 %
11047 %      MagickBooleanType XROIImage(Display *display,
11048 %        XResourceInfo *resource_info,XWindows *windows,Image **image,
11049 %        ExceptionInfo *exception)
11050 %
11051 %  A description of each parameter follows:
11052 %
11053 %    o display: Specifies a connection to an X server; returned from
11054 %      XOpenDisplay.
11055 %
11056 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11057 %
11058 %    o windows: Specifies a pointer to a XWindows structure.
11059 %
11060 %    o image: the image; returned from ReadImage.
11061 %
11062 %    o exception: return any errors or warnings in this structure.
11063 %
11064 */
11065 static MagickBooleanType XROIImage(Display *display,
11066   XResourceInfo *resource_info,XWindows *windows,Image **image,
11067   ExceptionInfo *exception)
11068 {
11069 #define ApplyMenus  7
11070
11071   static const char
11072     *ROIMenu[] =
11073     {
11074       "Help",
11075       "Dismiss",
11076       (char *) NULL
11077     },
11078     *ApplyMenu[] =
11079     {
11080       "File",
11081       "Edit",
11082       "Transform",
11083       "Enhance",
11084       "Effects",
11085       "F/X",
11086       "Miscellany",
11087       "Help",
11088       "Dismiss",
11089       (char *) NULL
11090     },
11091     *FileMenu[] =
11092     {
11093       "Save...",
11094       "Print...",
11095       (char *) NULL
11096     },
11097     *EditMenu[] =
11098     {
11099       "Undo",
11100       "Redo",
11101       (char *) NULL
11102     },
11103     *TransformMenu[] =
11104     {
11105       "Flop",
11106       "Flip",
11107       "Rotate Right",
11108       "Rotate Left",
11109       (char *) NULL
11110     },
11111     *EnhanceMenu[] =
11112     {
11113       "Hue...",
11114       "Saturation...",
11115       "Brightness...",
11116       "Gamma...",
11117       "Spiff",
11118       "Dull",
11119       "Contrast Stretch...",
11120       "Sigmoidal Contrast...",
11121       "Normalize",
11122       "Equalize",
11123       "Negate",
11124       "Grayscale",
11125       "Map...",
11126       "Quantize...",
11127       (char *) NULL
11128     },
11129     *EffectsMenu[] =
11130     {
11131       "Despeckle",
11132       "Emboss",
11133       "Reduce Noise",
11134       "Add Noise",
11135       "Sharpen...",
11136       "Blur...",
11137       "Threshold...",
11138       "Edge Detect...",
11139       "Spread...",
11140       "Shade...",
11141       "Raise...",
11142       "Segment...",
11143       (char *) NULL
11144     },
11145     *FXMenu[] =
11146     {
11147       "Solarize...",
11148       "Sepia Tone...",
11149       "Swirl...",
11150       "Implode...",
11151       "Vignette...",
11152       "Wave...",
11153       "Oil Paint...",
11154       "Charcoal Draw...",
11155       (char *) NULL
11156     },
11157     *MiscellanyMenu[] =
11158     {
11159       "Image Info",
11160       "Zoom Image",
11161       "Show Preview...",
11162       "Show Histogram",
11163       "Show Matte",
11164       (char *) NULL
11165     };
11166
11167   static const char
11168     **Menus[ApplyMenus] =
11169     {
11170       FileMenu,
11171       EditMenu,
11172       TransformMenu,
11173       EnhanceMenu,
11174       EffectsMenu,
11175       FXMenu,
11176       MiscellanyMenu
11177     };
11178
11179   static const CommandType
11180     ApplyCommands[] =
11181     {
11182       NullCommand,
11183       NullCommand,
11184       NullCommand,
11185       NullCommand,
11186       NullCommand,
11187       NullCommand,
11188       NullCommand,
11189       HelpCommand,
11190       QuitCommand
11191     },
11192     FileCommands[] =
11193     {
11194       SaveCommand,
11195       PrintCommand
11196     },
11197     EditCommands[] =
11198     {
11199       UndoCommand,
11200       RedoCommand
11201     },
11202     TransformCommands[] =
11203     {
11204       FlopCommand,
11205       FlipCommand,
11206       RotateRightCommand,
11207       RotateLeftCommand
11208     },
11209     EnhanceCommands[] =
11210     {
11211       HueCommand,
11212       SaturationCommand,
11213       BrightnessCommand,
11214       GammaCommand,
11215       SpiffCommand,
11216       DullCommand,
11217       ContrastStretchCommand,
11218       SigmoidalContrastCommand,
11219       NormalizeCommand,
11220       EqualizeCommand,
11221       NegateCommand,
11222       GrayscaleCommand,
11223       MapCommand,
11224       QuantizeCommand
11225     },
11226     EffectsCommands[] =
11227     {
11228       DespeckleCommand,
11229       EmbossCommand,
11230       ReduceNoiseCommand,
11231       AddNoiseCommand,
11232       SharpenCommand,
11233       BlurCommand,
11234       EdgeDetectCommand,
11235       SpreadCommand,
11236       ShadeCommand,
11237       RaiseCommand,
11238       SegmentCommand
11239     },
11240     FXCommands[] =
11241     {
11242       SolarizeCommand,
11243       SepiaToneCommand,
11244       SwirlCommand,
11245       ImplodeCommand,
11246       VignetteCommand,
11247       WaveCommand,
11248       OilPaintCommand,
11249       CharcoalDrawCommand
11250     },
11251     MiscellanyCommands[] =
11252     {
11253       InfoCommand,
11254       ZoomCommand,
11255       ShowPreviewCommand,
11256       ShowHistogramCommand,
11257       ShowMatteCommand
11258     },
11259     ROICommands[] =
11260     {
11261       ROIHelpCommand,
11262       ROIDismissCommand
11263     };
11264
11265   static const CommandType
11266     *Commands[ApplyMenus] =
11267     {
11268       FileCommands,
11269       EditCommands,
11270       TransformCommands,
11271       EnhanceCommands,
11272       EffectsCommands,
11273       FXCommands,
11274       MiscellanyCommands
11275     };
11276
11277   char
11278     command[MaxTextExtent],
11279     text[MaxTextExtent];
11280
11281   CommandType
11282     command_type;
11283
11284   Cursor
11285     cursor;
11286
11287   Image
11288     *roi_image;
11289
11290   int
11291     entry,
11292     id,
11293     x,
11294     y;
11295
11296   MagickRealType
11297     scale_factor;
11298
11299   MagickProgressMonitor
11300     progress_monitor;
11301
11302   RectangleInfo
11303     crop_info,
11304     highlight_info,
11305     roi_info;
11306
11307   unsigned int
11308     height,
11309     width;
11310
11311   size_t
11312     state;
11313
11314   XEvent
11315     event;
11316
11317   /*
11318     Map Command widget.
11319   */
11320   (void) CloneString(&windows->command.name,"ROI");
11321   windows->command.data=0;
11322   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11323   (void) XMapRaised(display,windows->command.id);
11324   XClientMessage(display,windows->image.id,windows->im_protocols,
11325     windows->im_update_widget,CurrentTime);
11326   /*
11327     Track pointer until button 1 is pressed.
11328   */
11329   XQueryPosition(display,windows->image.id,&x,&y);
11330   (void) XSelectInput(display,windows->image.id,
11331     windows->image.attributes.event_mask | PointerMotionMask);
11332   roi_info.x=(ssize_t) windows->image.x+x;
11333   roi_info.y=(ssize_t) windows->image.y+y;
11334   roi_info.width=0;
11335   roi_info.height=0;
11336   cursor=XCreateFontCursor(display,XC_fleur);
11337   state=DefaultState;
11338   do
11339   {
11340     if (windows->info.mapped != MagickFalse)
11341       {
11342         /*
11343           Display pointer position.
11344         */
11345         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11346           (long) roi_info.x,(long) roi_info.y);
11347         XInfoWidget(display,windows,text);
11348       }
11349     /*
11350       Wait for next event.
11351     */
11352     XScreenEvent(display,windows,&event,exception);
11353     if (event.xany.window == windows->command.id)
11354       {
11355         /*
11356           Select a command from the Command widget.
11357         */
11358         id=XCommandWidget(display,windows,ROIMenu,&event);
11359         if (id < 0)
11360           continue;
11361         switch (ROICommands[id])
11362         {
11363           case ROIHelpCommand:
11364           {
11365             XTextViewWidget(display,resource_info,windows,MagickFalse,
11366               "Help Viewer - Region of Interest",ImageROIHelp);
11367             break;
11368           }
11369           case ROIDismissCommand:
11370           {
11371             /*
11372               Prematurely exit.
11373             */
11374             state|=EscapeState;
11375             state|=ExitState;
11376             break;
11377           }
11378           default:
11379             break;
11380         }
11381         continue;
11382       }
11383     switch (event.type)
11384     {
11385       case ButtonPress:
11386       {
11387         if (event.xbutton.button != Button1)
11388           break;
11389         if (event.xbutton.window != windows->image.id)
11390           break;
11391         /*
11392           Note first corner of region of interest rectangle-- exit loop.
11393         */
11394         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11395         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11396         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11397         state|=ExitState;
11398         break;
11399       }
11400       case ButtonRelease:
11401         break;
11402       case Expose:
11403         break;
11404       case KeyPress:
11405       {
11406         KeySym
11407           key_symbol;
11408
11409         if (event.xkey.window != windows->image.id)
11410           break;
11411         /*
11412           Respond to a user key press.
11413         */
11414         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11415           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11416         switch ((int) key_symbol)
11417         {
11418           case XK_Escape:
11419           case XK_F20:
11420           {
11421             /*
11422               Prematurely exit.
11423             */
11424             state|=EscapeState;
11425             state|=ExitState;
11426             break;
11427           }
11428           case XK_F1:
11429           case XK_Help:
11430           {
11431             XTextViewWidget(display,resource_info,windows,MagickFalse,
11432               "Help Viewer - Region of Interest",ImageROIHelp);
11433             break;
11434           }
11435           default:
11436           {
11437             (void) XBell(display,0);
11438             break;
11439           }
11440         }
11441         break;
11442       }
11443       case MotionNotify:
11444       {
11445         /*
11446           Map and unmap Info widget as text cursor crosses its boundaries.
11447         */
11448         x=event.xmotion.x;
11449         y=event.xmotion.y;
11450         if (windows->info.mapped != MagickFalse)
11451           {
11452             if ((x < (int) (windows->info.x+windows->info.width)) &&
11453                 (y < (int) (windows->info.y+windows->info.height)))
11454               (void) XWithdrawWindow(display,windows->info.id,
11455                 windows->info.screen);
11456           }
11457         else
11458           if ((x > (int) (windows->info.x+windows->info.width)) ||
11459               (y > (int) (windows->info.y+windows->info.height)))
11460             (void) XMapWindow(display,windows->info.id);
11461         roi_info.x=(ssize_t) windows->image.x+x;
11462         roi_info.y=(ssize_t) windows->image.y+y;
11463         break;
11464       }
11465       default:
11466         break;
11467     }
11468   } while ((state & ExitState) == 0);
11469   (void) XSelectInput(display,windows->image.id,
11470     windows->image.attributes.event_mask);
11471   if ((state & EscapeState) != 0)
11472     {
11473       /*
11474         User want to exit without region of interest.
11475       */
11476       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11477       (void) XFreeCursor(display,cursor);
11478       return(MagickTrue);
11479     }
11480   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11481   do
11482   {
11483     /*
11484       Size rectangle as pointer moves until the mouse button is released.
11485     */
11486     x=(int) roi_info.x;
11487     y=(int) roi_info.y;
11488     roi_info.width=0;
11489     roi_info.height=0;
11490     state=DefaultState;
11491     do
11492     {
11493       highlight_info=roi_info;
11494       highlight_info.x=roi_info.x-windows->image.x;
11495       highlight_info.y=roi_info.y-windows->image.y;
11496       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11497         {
11498           /*
11499             Display info and draw region of interest rectangle.
11500           */
11501           if (windows->info.mapped == MagickFalse)
11502             (void) XMapWindow(display,windows->info.id);
11503           (void) FormatLocaleString(text,MaxTextExtent,
11504             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11505             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11506           XInfoWidget(display,windows,text);
11507           XHighlightRectangle(display,windows->image.id,
11508             windows->image.highlight_context,&highlight_info);
11509         }
11510       else
11511         if (windows->info.mapped != MagickFalse)
11512           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11513       /*
11514         Wait for next event.
11515       */
11516       XScreenEvent(display,windows,&event,exception);
11517       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11518         XHighlightRectangle(display,windows->image.id,
11519           windows->image.highlight_context,&highlight_info);
11520       switch (event.type)
11521       {
11522         case ButtonPress:
11523         {
11524           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11525           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11526           break;
11527         }
11528         case ButtonRelease:
11529         {
11530           /*
11531             User has committed to region of interest rectangle.
11532           */
11533           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11534           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11535           XSetCursorState(display,windows,MagickFalse);
11536           state|=ExitState;
11537           if (LocaleCompare(windows->command.name,"Apply") == 0)
11538             break;
11539           (void) CloneString(&windows->command.name,"Apply");
11540           windows->command.data=ApplyMenus;
11541           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11542           break;
11543         }
11544         case Expose:
11545           break;
11546         case MotionNotify:
11547         {
11548           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11549           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11550         }
11551         default:
11552           break;
11553       }
11554       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11555           ((state & ExitState) != 0))
11556         {
11557           /*
11558             Check boundary conditions.
11559           */
11560           if (roi_info.x < 0)
11561             roi_info.x=0;
11562           else
11563             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11564               roi_info.x=(ssize_t) windows->image.ximage->width;
11565           if ((int) roi_info.x < x)
11566             roi_info.width=(unsigned int) (x-roi_info.x);
11567           else
11568             {
11569               roi_info.width=(unsigned int) (roi_info.x-x);
11570               roi_info.x=(ssize_t) x;
11571             }
11572           if (roi_info.y < 0)
11573             roi_info.y=0;
11574           else
11575             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11576               roi_info.y=(ssize_t) windows->image.ximage->height;
11577           if ((int) roi_info.y < y)
11578             roi_info.height=(unsigned int) (y-roi_info.y);
11579           else
11580             {
11581               roi_info.height=(unsigned int) (roi_info.y-y);
11582               roi_info.y=(ssize_t) y;
11583             }
11584         }
11585     } while ((state & ExitState) == 0);
11586     /*
11587       Wait for user to grab a corner of the rectangle or press return.
11588     */
11589     state=DefaultState;
11590     command_type=NullCommand;
11591     (void) XMapWindow(display,windows->info.id);
11592     do
11593     {
11594       if (windows->info.mapped != MagickFalse)
11595         {
11596           /*
11597             Display pointer position.
11598           */
11599           (void) FormatLocaleString(text,MaxTextExtent,
11600             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11601             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11602           XInfoWidget(display,windows,text);
11603         }
11604       highlight_info=roi_info;
11605       highlight_info.x=roi_info.x-windows->image.x;
11606       highlight_info.y=roi_info.y-windows->image.y;
11607       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11608         {
11609           state|=EscapeState;
11610           state|=ExitState;
11611           break;
11612         }
11613       if ((state & UpdateRegionState) != 0)
11614         {
11615           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11616           switch (command_type)
11617           {
11618             case UndoCommand:
11619             case RedoCommand:
11620             {
11621               (void) XMagickCommand(display,resource_info,windows,command_type,
11622                 image,exception);
11623               break;
11624             }
11625             default:
11626             {
11627               /*
11628                 Region of interest is relative to image configuration.
11629               */
11630               progress_monitor=SetImageProgressMonitor(*image,
11631                 (MagickProgressMonitor) NULL,(*image)->client_data);
11632               crop_info=roi_info;
11633               width=(unsigned int) (*image)->columns;
11634               height=(unsigned int) (*image)->rows;
11635               x=0;
11636               y=0;
11637               if (windows->image.crop_geometry != (char *) NULL)
11638                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11639                   &width,&height);
11640               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11641               crop_info.x+=x;
11642               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11643               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11644               scale_factor=(MagickRealType)
11645                 height/windows->image.ximage->height;
11646               crop_info.y+=y;
11647               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11648               crop_info.height=(unsigned int)
11649                 (scale_factor*crop_info.height+0.5);
11650               roi_image=CropImage(*image,&crop_info,exception);
11651               (void) SetImageProgressMonitor(*image,progress_monitor,
11652                 (*image)->client_data);
11653               if (roi_image == (Image *) NULL)
11654                 continue;
11655               /*
11656                 Apply image processing technique to the region of interest.
11657               */
11658               windows->image.orphan=MagickTrue;
11659               (void) XMagickCommand(display,resource_info,windows,command_type,
11660                 &roi_image,exception);
11661               progress_monitor=SetImageProgressMonitor(*image,
11662                 (MagickProgressMonitor) NULL,(*image)->client_data);
11663               (void) XMagickCommand(display,resource_info,windows,
11664                 SaveToUndoBufferCommand,image,exception);
11665               windows->image.orphan=MagickFalse;
11666               (void) CompositeImage(*image,roi_image,CopyCompositeOp,
11667                 MagickTrue,crop_info.x,crop_info.y,exception);
11668               roi_image=DestroyImage(roi_image);
11669               (void) SetImageProgressMonitor(*image,progress_monitor,
11670                 (*image)->client_data);
11671               break;
11672             }
11673           }
11674           if (command_type != InfoCommand)
11675             {
11676               XConfigureImageColormap(display,resource_info,windows,*image,
11677                 exception);
11678               (void) XConfigureImage(display,resource_info,windows,*image,
11679                 exception);
11680             }
11681           XCheckRefreshWindows(display,windows);
11682           XInfoWidget(display,windows,text);
11683           (void) XSetFunction(display,windows->image.highlight_context,
11684             GXinvert);
11685           state&=(~UpdateRegionState);
11686         }
11687       XHighlightRectangle(display,windows->image.id,
11688         windows->image.highlight_context,&highlight_info);
11689       XScreenEvent(display,windows,&event,exception);
11690       if (event.xany.window == windows->command.id)
11691         {
11692           /*
11693             Select a command from the Command widget.
11694           */
11695           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11696           command_type=NullCommand;
11697           id=XCommandWidget(display,windows,ApplyMenu,&event);
11698           if (id >= 0)
11699             {
11700               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11701               command_type=ApplyCommands[id];
11702               if (id < ApplyMenus)
11703                 {
11704                   /*
11705                     Select a command from a pop-up menu.
11706                   */
11707                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11708                     (const char **) Menus[id],command);
11709                   if (entry >= 0)
11710                     {
11711                       (void) CopyMagickString(command,Menus[id][entry],
11712                         MaxTextExtent);
11713                       command_type=Commands[id][entry];
11714                     }
11715                 }
11716             }
11717           (void) XSetFunction(display,windows->image.highlight_context,
11718             GXinvert);
11719           XHighlightRectangle(display,windows->image.id,
11720             windows->image.highlight_context,&highlight_info);
11721           if (command_type == HelpCommand)
11722             {
11723               (void) XSetFunction(display,windows->image.highlight_context,
11724                 GXcopy);
11725               XTextViewWidget(display,resource_info,windows,MagickFalse,
11726                 "Help Viewer - Region of Interest",ImageROIHelp);
11727               (void) XSetFunction(display,windows->image.highlight_context,
11728                 GXinvert);
11729               continue;
11730             }
11731           if (command_type == QuitCommand)
11732             {
11733               /*
11734                 exit.
11735               */
11736               state|=EscapeState;
11737               state|=ExitState;
11738               continue;
11739             }
11740           if (command_type != NullCommand)
11741             state|=UpdateRegionState;
11742           continue;
11743         }
11744       XHighlightRectangle(display,windows->image.id,
11745         windows->image.highlight_context,&highlight_info);
11746       switch (event.type)
11747       {
11748         case ButtonPress:
11749         {
11750           x=windows->image.x;
11751           y=windows->image.y;
11752           if (event.xbutton.button != Button1)
11753             break;
11754           if (event.xbutton.window != windows->image.id)
11755             break;
11756           x=windows->image.x+event.xbutton.x;
11757           y=windows->image.y+event.xbutton.y;
11758           if ((x < (int) (roi_info.x+RoiDelta)) &&
11759               (x > (int) (roi_info.x-RoiDelta)) &&
11760               (y < (int) (roi_info.y+RoiDelta)) &&
11761               (y > (int) (roi_info.y-RoiDelta)))
11762             {
11763               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11764               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11765               state|=UpdateConfigurationState;
11766               break;
11767             }
11768           if ((x < (int) (roi_info.x+RoiDelta)) &&
11769               (x > (int) (roi_info.x-RoiDelta)) &&
11770               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11771               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11772             {
11773               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11774               state|=UpdateConfigurationState;
11775               break;
11776             }
11777           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11778               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11779               (y < (int) (roi_info.y+RoiDelta)) &&
11780               (y > (int) (roi_info.y-RoiDelta)))
11781             {
11782               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11783               state|=UpdateConfigurationState;
11784               break;
11785             }
11786           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11787               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11788               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11789               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11790             {
11791               state|=UpdateConfigurationState;
11792               break;
11793             }
11794         }
11795         case ButtonRelease:
11796         {
11797           if (event.xbutton.window == windows->pan.id)
11798             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11799                 (highlight_info.y != crop_info.y-windows->image.y))
11800               XHighlightRectangle(display,windows->image.id,
11801                 windows->image.highlight_context,&highlight_info);
11802           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11803             event.xbutton.time);
11804           break;
11805         }
11806         case Expose:
11807         {
11808           if (event.xexpose.window == windows->image.id)
11809             if (event.xexpose.count == 0)
11810               {
11811                 event.xexpose.x=(int) highlight_info.x;
11812                 event.xexpose.y=(int) highlight_info.y;
11813                 event.xexpose.width=(int) highlight_info.width;
11814                 event.xexpose.height=(int) highlight_info.height;
11815                 XRefreshWindow(display,&windows->image,&event);
11816               }
11817           if (event.xexpose.window == windows->info.id)
11818             if (event.xexpose.count == 0)
11819               XInfoWidget(display,windows,text);
11820           break;
11821         }
11822         case KeyPress:
11823         {
11824           KeySym
11825             key_symbol;
11826
11827           if (event.xkey.window != windows->image.id)
11828             break;
11829           /*
11830             Respond to a user key press.
11831           */
11832           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11833             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11834           switch ((int) key_symbol)
11835           {
11836             case XK_Shift_L:
11837             case XK_Shift_R:
11838               break;
11839             case XK_Escape:
11840             case XK_F20:
11841               state|=EscapeState;
11842             case XK_Return:
11843             {
11844               state|=ExitState;
11845               break;
11846             }
11847             case XK_Home:
11848             case XK_KP_Home:
11849             {
11850               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11851               roi_info.y=(ssize_t) (windows->image.height/2L-
11852                 roi_info.height/2L);
11853               break;
11854             }
11855             case XK_Left:
11856             case XK_KP_Left:
11857             {
11858               roi_info.x--;
11859               break;
11860             }
11861             case XK_Up:
11862             case XK_KP_Up:
11863             case XK_Next:
11864             {
11865               roi_info.y--;
11866               break;
11867             }
11868             case XK_Right:
11869             case XK_KP_Right:
11870             {
11871               roi_info.x++;
11872               break;
11873             }
11874             case XK_Prior:
11875             case XK_Down:
11876             case XK_KP_Down:
11877             {
11878               roi_info.y++;
11879               break;
11880             }
11881             case XK_F1:
11882             case XK_Help:
11883             {
11884               (void) XSetFunction(display,windows->image.highlight_context,
11885                 GXcopy);
11886               XTextViewWidget(display,resource_info,windows,MagickFalse,
11887                 "Help Viewer - Region of Interest",ImageROIHelp);
11888               (void) XSetFunction(display,windows->image.highlight_context,
11889                 GXinvert);
11890               break;
11891             }
11892             default:
11893             {
11894               command_type=XImageWindowCommand(display,resource_info,windows,
11895                 event.xkey.state,key_symbol,image,exception);
11896               if (command_type != NullCommand)
11897                 state|=UpdateRegionState;
11898               break;
11899             }
11900           }
11901           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11902             event.xkey.time);
11903           break;
11904         }
11905         case KeyRelease:
11906           break;
11907         case MotionNotify:
11908         {
11909           if (event.xbutton.window != windows->image.id)
11910             break;
11911           /*
11912             Map and unmap Info widget as text cursor crosses its boundaries.
11913           */
11914           x=event.xmotion.x;
11915           y=event.xmotion.y;
11916           if (windows->info.mapped != MagickFalse)
11917             {
11918               if ((x < (int) (windows->info.x+windows->info.width)) &&
11919                   (y < (int) (windows->info.y+windows->info.height)))
11920                 (void) XWithdrawWindow(display,windows->info.id,
11921                   windows->info.screen);
11922             }
11923           else
11924             if ((x > (int) (windows->info.x+windows->info.width)) ||
11925                 (y > (int) (windows->info.y+windows->info.height)))
11926               (void) XMapWindow(display,windows->info.id);
11927           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11928           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11929           break;
11930         }
11931         case SelectionRequest:
11932         {
11933           XSelectionEvent
11934             notify;
11935
11936           XSelectionRequestEvent
11937             *request;
11938
11939           /*
11940             Set primary selection.
11941           */
11942           (void) FormatLocaleString(text,MaxTextExtent,
11943             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11944             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11945           request=(&(event.xselectionrequest));
11946           (void) XChangeProperty(request->display,request->requestor,
11947             request->property,request->target,8,PropModeReplace,
11948             (unsigned char *) text,(int) strlen(text));
11949           notify.type=SelectionNotify;
11950           notify.display=request->display;
11951           notify.requestor=request->requestor;
11952           notify.selection=request->selection;
11953           notify.target=request->target;
11954           notify.time=request->time;
11955           if (request->property == None)
11956             notify.property=request->target;
11957           else
11958             notify.property=request->property;
11959           (void) XSendEvent(request->display,request->requestor,False,0,
11960             (XEvent *) &notify);
11961         }
11962         default:
11963           break;
11964       }
11965       if ((state & UpdateConfigurationState) != 0)
11966         {
11967           (void) XPutBackEvent(display,&event);
11968           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11969           break;
11970         }
11971     } while ((state & ExitState) == 0);
11972   } while ((state & ExitState) == 0);
11973   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11974   XSetCursorState(display,windows,MagickFalse);
11975   if ((state & EscapeState) != 0)
11976     return(MagickTrue);
11977   return(MagickTrue);
11978 }
11979 \f
11980 /*
11981 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11982 %                                                                             %
11983 %                                                                             %
11984 %                                                                             %
11985 +   X R o t a t e I m a g e                                                   %
11986 %                                                                             %
11987 %                                                                             %
11988 %                                                                             %
11989 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11990 %
11991 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11992 %  rotation angle is computed from the slope of a line drawn by the user.
11993 %
11994 %  The format of the XRotateImage method is:
11995 %
11996 %      MagickBooleanType XRotateImage(Display *display,
11997 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11998 %        Image **image,ExceptionInfo *exception)
11999 %
12000 %  A description of each parameter follows:
12001 %
12002 %    o display: Specifies a connection to an X server; returned from
12003 %      XOpenDisplay.
12004 %
12005 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12006 %
12007 %    o windows: Specifies a pointer to a XWindows structure.
12008 %
12009 %    o degrees: Specifies the number of degrees to rotate the image.
12010 %
12011 %    o image: the image.
12012 %
12013 %    o exception: return any errors or warnings in this structure.
12014 %
12015 */
12016 static MagickBooleanType XRotateImage(Display *display,
12017   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image,
12018   ExceptionInfo *exception)
12019 {
12020   static const char
12021     *RotateMenu[] =
12022     {
12023       "Pixel Color",
12024       "Direction",
12025       "Help",
12026       "Dismiss",
12027       (char *) NULL
12028     };
12029
12030   static ModeType
12031     direction = HorizontalRotateCommand;
12032
12033   static const ModeType
12034     DirectionCommands[] =
12035     {
12036       HorizontalRotateCommand,
12037       VerticalRotateCommand
12038     },
12039     RotateCommands[] =
12040     {
12041       RotateColorCommand,
12042       RotateDirectionCommand,
12043       RotateHelpCommand,
12044       RotateDismissCommand
12045     };
12046
12047   static unsigned int
12048     pen_id = 0;
12049
12050   char
12051     command[MaxTextExtent],
12052     text[MaxTextExtent];
12053
12054   Image
12055     *rotate_image;
12056
12057   int
12058     id,
12059     x,
12060     y;
12061
12062   MagickRealType
12063     normalized_degrees;
12064
12065   register int
12066     i;
12067
12068   unsigned int
12069     height,
12070     rotations,
12071     width;
12072
12073   if (degrees == 0.0)
12074     {
12075       unsigned int
12076         distance;
12077
12078       size_t
12079         state;
12080
12081       XEvent
12082         event;
12083
12084       XSegment
12085         rotate_info;
12086
12087       /*
12088         Map Command widget.
12089       */
12090       (void) CloneString(&windows->command.name,"Rotate");
12091       windows->command.data=2;
12092       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
12093       (void) XMapRaised(display,windows->command.id);
12094       XClientMessage(display,windows->image.id,windows->im_protocols,
12095         windows->im_update_widget,CurrentTime);
12096       /*
12097         Wait for first button press.
12098       */
12099       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12100       XQueryPosition(display,windows->image.id,&x,&y);
12101       rotate_info.x1=x;
12102       rotate_info.y1=y;
12103       rotate_info.x2=x;
12104       rotate_info.y2=y;
12105       state=DefaultState;
12106       do
12107       {
12108         XHighlightLine(display,windows->image.id,
12109           windows->image.highlight_context,&rotate_info);
12110         /*
12111           Wait for next event.
12112         */
12113         XScreenEvent(display,windows,&event,exception);
12114         XHighlightLine(display,windows->image.id,
12115           windows->image.highlight_context,&rotate_info);
12116         if (event.xany.window == windows->command.id)
12117           {
12118             /*
12119               Select a command from the Command widget.
12120             */
12121             id=XCommandWidget(display,windows,RotateMenu,&event);
12122             if (id < 0)
12123               continue;
12124             (void) XSetFunction(display,windows->image.highlight_context,
12125               GXcopy);
12126             switch (RotateCommands[id])
12127             {
12128               case RotateColorCommand:
12129               {
12130                 const char
12131                   *ColorMenu[MaxNumberPens];
12132
12133                 int
12134                   pen_number;
12135
12136                 XColor
12137                   color;
12138
12139                 /*
12140                   Initialize menu selections.
12141                 */
12142                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12143                   ColorMenu[i]=resource_info->pen_colors[i];
12144                 ColorMenu[MaxNumberPens-2]="Browser...";
12145                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12146                 /*
12147                   Select a pen color from the pop-up menu.
12148                 */
12149                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12150                   (const char **) ColorMenu,command);
12151                 if (pen_number < 0)
12152                   break;
12153                 if (pen_number == (MaxNumberPens-2))
12154                   {
12155                     static char
12156                       color_name[MaxTextExtent] = "gray";
12157
12158                     /*
12159                       Select a pen color from a dialog.
12160                     */
12161                     resource_info->pen_colors[pen_number]=color_name;
12162                     XColorBrowserWidget(display,windows,"Select",color_name);
12163                     if (*color_name == '\0')
12164                       break;
12165                   }
12166                 /*
12167                   Set pen color.
12168                 */
12169                 (void) XParseColor(display,windows->map_info->colormap,
12170                   resource_info->pen_colors[pen_number],&color);
12171                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12172                   (unsigned int) MaxColors,&color);
12173                 windows->pixel_info->pen_colors[pen_number]=color;
12174                 pen_id=(unsigned int) pen_number;
12175                 break;
12176               }
12177               case RotateDirectionCommand:
12178               {
12179                 static const char
12180                   *Directions[] =
12181                   {
12182                     "horizontal",
12183                     "vertical",
12184                     (char *) NULL,
12185                   };
12186
12187                 /*
12188                   Select a command from the pop-up menu.
12189                 */
12190                 id=XMenuWidget(display,windows,RotateMenu[id],
12191                   Directions,command);
12192                 if (id >= 0)
12193                   direction=DirectionCommands[id];
12194                 break;
12195               }
12196               case RotateHelpCommand:
12197               {
12198                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12199                   "Help Viewer - Image Rotation",ImageRotateHelp);
12200                 break;
12201               }
12202               case RotateDismissCommand:
12203               {
12204                 /*
12205                   Prematurely exit.
12206                 */
12207                 state|=EscapeState;
12208                 state|=ExitState;
12209                 break;
12210               }
12211               default:
12212                 break;
12213             }
12214             (void) XSetFunction(display,windows->image.highlight_context,
12215               GXinvert);
12216             continue;
12217           }
12218         switch (event.type)
12219         {
12220           case ButtonPress:
12221           {
12222             if (event.xbutton.button != Button1)
12223               break;
12224             if (event.xbutton.window != windows->image.id)
12225               break;
12226             /*
12227               exit loop.
12228             */
12229             (void) XSetFunction(display,windows->image.highlight_context,
12230               GXcopy);
12231             rotate_info.x1=event.xbutton.x;
12232             rotate_info.y1=event.xbutton.y;
12233             state|=ExitState;
12234             break;
12235           }
12236           case ButtonRelease:
12237             break;
12238           case Expose:
12239             break;
12240           case KeyPress:
12241           {
12242             char
12243               command[MaxTextExtent];
12244
12245             KeySym
12246               key_symbol;
12247
12248             if (event.xkey.window != windows->image.id)
12249               break;
12250             /*
12251               Respond to a user key press.
12252             */
12253             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12254               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12255             switch ((int) key_symbol)
12256             {
12257               case XK_Escape:
12258               case XK_F20:
12259               {
12260                 /*
12261                   Prematurely exit.
12262                 */
12263                 state|=EscapeState;
12264                 state|=ExitState;
12265                 break;
12266               }
12267               case XK_F1:
12268               case XK_Help:
12269               {
12270                 (void) XSetFunction(display,windows->image.highlight_context,
12271                   GXcopy);
12272                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12273                   "Help Viewer - Image Rotation",ImageRotateHelp);
12274                 (void) XSetFunction(display,windows->image.highlight_context,
12275                   GXinvert);
12276                 break;
12277               }
12278               default:
12279               {
12280                 (void) XBell(display,0);
12281                 break;
12282               }
12283             }
12284             break;
12285           }
12286           case MotionNotify:
12287           {
12288             rotate_info.x1=event.xmotion.x;
12289             rotate_info.y1=event.xmotion.y;
12290           }
12291         }
12292         rotate_info.x2=rotate_info.x1;
12293         rotate_info.y2=rotate_info.y1;
12294         if (direction == HorizontalRotateCommand)
12295           rotate_info.x2+=32;
12296         else
12297           rotate_info.y2-=32;
12298       } while ((state & ExitState) == 0);
12299       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12300       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12301       if ((state & EscapeState) != 0)
12302         return(MagickTrue);
12303       /*
12304         Draw line as pointer moves until the mouse button is released.
12305       */
12306       distance=0;
12307       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12308       state=DefaultState;
12309       do
12310       {
12311         if (distance > 9)
12312           {
12313             /*
12314               Display info and draw rotation line.
12315             */
12316             if (windows->info.mapped == MagickFalse)
12317               (void) XMapWindow(display,windows->info.id);
12318             (void) FormatLocaleString(text,MaxTextExtent," %g",
12319               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12320             XInfoWidget(display,windows,text);
12321             XHighlightLine(display,windows->image.id,
12322               windows->image.highlight_context,&rotate_info);
12323           }
12324         else
12325           if (windows->info.mapped != MagickFalse)
12326             (void) XWithdrawWindow(display,windows->info.id,
12327               windows->info.screen);
12328         /*
12329           Wait for next event.
12330         */
12331         XScreenEvent(display,windows,&event,exception);
12332         if (distance > 9)
12333           XHighlightLine(display,windows->image.id,
12334             windows->image.highlight_context,&rotate_info);
12335         switch (event.type)
12336         {
12337           case ButtonPress:
12338             break;
12339           case ButtonRelease:
12340           {
12341             /*
12342               User has committed to rotation line.
12343             */
12344             rotate_info.x2=event.xbutton.x;
12345             rotate_info.y2=event.xbutton.y;
12346             state|=ExitState;
12347             break;
12348           }
12349           case Expose:
12350             break;
12351           case MotionNotify:
12352           {
12353             rotate_info.x2=event.xmotion.x;
12354             rotate_info.y2=event.xmotion.y;
12355           }
12356           default:
12357             break;
12358         }
12359         /*
12360           Check boundary conditions.
12361         */
12362         if (rotate_info.x2 < 0)
12363           rotate_info.x2=0;
12364         else
12365           if (rotate_info.x2 > (int) windows->image.width)
12366             rotate_info.x2=(short) windows->image.width;
12367         if (rotate_info.y2 < 0)
12368           rotate_info.y2=0;
12369         else
12370           if (rotate_info.y2 > (int) windows->image.height)
12371             rotate_info.y2=(short) windows->image.height;
12372         /*
12373           Compute rotation angle from the slope of the line.
12374         */
12375         degrees=0.0;
12376         distance=(unsigned int)
12377           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12378           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12379         if (distance > 9)
12380           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12381             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12382       } while ((state & ExitState) == 0);
12383       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12384       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12385       if (distance <= 9)
12386         return(MagickTrue);
12387     }
12388   if (direction == VerticalRotateCommand)
12389     degrees-=90.0;
12390   if (degrees == 0.0)
12391     return(MagickTrue);
12392   /*
12393     Rotate image.
12394   */
12395   normalized_degrees=degrees;
12396   while (normalized_degrees < -45.0)
12397     normalized_degrees+=360.0;
12398   for (rotations=0; normalized_degrees > 45.0; rotations++)
12399     normalized_degrees-=90.0;
12400   if (normalized_degrees != 0.0)
12401     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image,
12402       exception);
12403   XSetCursorState(display,windows,MagickTrue);
12404   XCheckRefreshWindows(display,windows);
12405   (*image)->background_color.red=(double) ScaleShortToQuantum(
12406     windows->pixel_info->pen_colors[pen_id].red);
12407   (*image)->background_color.green=(double) ScaleShortToQuantum(
12408     windows->pixel_info->pen_colors[pen_id].green);
12409   (*image)->background_color.blue=(double) ScaleShortToQuantum(
12410     windows->pixel_info->pen_colors[pen_id].blue);
12411   rotate_image=RotateImage(*image,degrees,exception);
12412   XSetCursorState(display,windows,MagickFalse);
12413   if (rotate_image == (Image *) NULL)
12414     return(MagickFalse);
12415   *image=DestroyImage(*image);
12416   *image=rotate_image;
12417   if (windows->image.crop_geometry != (char *) NULL)
12418     {
12419       /*
12420         Rotate crop geometry.
12421       */
12422       width=(unsigned int) (*image)->columns;
12423       height=(unsigned int) (*image)->rows;
12424       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12425       switch (rotations % 4)
12426       {
12427         default:
12428         case 0:
12429           break;
12430         case 1:
12431         {
12432           /*
12433             Rotate 90 degrees.
12434           */
12435           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12436             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12437             (int) height-y,x);
12438           break;
12439         }
12440         case 2:
12441         {
12442           /*
12443             Rotate 180 degrees.
12444           */
12445           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12446             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12447           break;
12448         }
12449         case 3:
12450         {
12451           /*
12452             Rotate 270 degrees.
12453           */
12454           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12455             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12456           break;
12457         }
12458       }
12459     }
12460   if (windows->image.orphan != MagickFalse)
12461     return(MagickTrue);
12462   if (normalized_degrees != 0.0)
12463     {
12464       /*
12465         Update image colormap.
12466       */
12467       windows->image.window_changes.width=(int) (*image)->columns;
12468       windows->image.window_changes.height=(int) (*image)->rows;
12469       if (windows->image.crop_geometry != (char *) NULL)
12470         {
12471           /*
12472             Obtain dimensions of image from crop geometry.
12473           */
12474           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12475             &width,&height);
12476           windows->image.window_changes.width=(int) width;
12477           windows->image.window_changes.height=(int) height;
12478         }
12479       XConfigureImageColormap(display,resource_info,windows,*image,exception);
12480     }
12481   else
12482     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12483       {
12484         windows->image.window_changes.width=windows->image.ximage->height;
12485         windows->image.window_changes.height=windows->image.ximage->width;
12486       }
12487   /*
12488     Update image configuration.
12489   */
12490   (void) XConfigureImage(display,resource_info,windows,*image,exception);
12491   return(MagickTrue);
12492 }
12493 \f
12494 /*
12495 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12496 %                                                                             %
12497 %                                                                             %
12498 %                                                                             %
12499 +   X S a v e I m a g e                                                       %
12500 %                                                                             %
12501 %                                                                             %
12502 %                                                                             %
12503 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12504 %
12505 %  XSaveImage() saves an image to a file.
12506 %
12507 %  The format of the XSaveImage method is:
12508 %
12509 %      MagickBooleanType XSaveImage(Display *display,
12510 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
12511 %        ExceptionInfo *exception)
12512 %
12513 %  A description of each parameter follows:
12514 %
12515 %    o display: Specifies a connection to an X server; returned from
12516 %      XOpenDisplay.
12517 %
12518 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12519 %
12520 %    o windows: Specifies a pointer to a XWindows structure.
12521 %
12522 %    o image: the image.
12523 %
12524 %    o exception: return any errors or warnings in this structure.
12525 %
12526 */
12527 static MagickBooleanType XSaveImage(Display *display,
12528   XResourceInfo *resource_info,XWindows *windows,Image *image,
12529   ExceptionInfo *exception)
12530 {
12531   char
12532     filename[MaxTextExtent],
12533     geometry[MaxTextExtent];
12534
12535   Image
12536     *save_image;
12537
12538   ImageInfo
12539     *image_info;
12540
12541   MagickStatusType
12542     status;
12543
12544   /*
12545     Request file name from user.
12546   */
12547   if (resource_info->write_filename != (char *) NULL)
12548     (void) CopyMagickString(filename,resource_info->write_filename,
12549       MaxTextExtent);
12550   else
12551     {
12552       char
12553         path[MaxTextExtent];
12554
12555       int
12556         status;
12557
12558       GetPathComponent(image->filename,HeadPath,path);
12559       GetPathComponent(image->filename,TailPath,filename);
12560       if (*path != '\0')
12561         {
12562           status=chdir(path);
12563           if (status == -1)
12564             (void) ThrowMagickException(exception,GetMagickModule(),
12565               FileOpenError,"UnableToOpenFile","%s",path);
12566         }
12567     }
12568   XFileBrowserWidget(display,windows,"Save",filename);
12569   if (*filename == '\0')
12570     return(MagickTrue);
12571   if (IsPathAccessible(filename) != MagickFalse)
12572     {
12573       int
12574         status;
12575
12576       /*
12577         File exists-- seek user's permission before overwriting.
12578       */
12579       status=XConfirmWidget(display,windows,"Overwrite",filename);
12580       if (status <= 0)
12581         return(MagickTrue);
12582     }
12583   image_info=CloneImageInfo(resource_info->image_info);
12584   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12585   (void) SetImageInfo(image_info,1,exception);
12586   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12587       (LocaleCompare(image_info->magick,"JPG") == 0))
12588     {
12589       char
12590         quality[MaxTextExtent];
12591
12592       int
12593         status;
12594
12595       /*
12596         Request JPEG quality from user.
12597       */
12598       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12599         image->quality);
12600       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12601         quality);
12602       if (*quality == '\0')
12603         return(MagickTrue);
12604       image->quality=StringToUnsignedLong(quality);
12605       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12606     }
12607   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12608       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12609       (LocaleCompare(image_info->magick,"PS") == 0) ||
12610       (LocaleCompare(image_info->magick,"PS2") == 0))
12611     {
12612       char
12613         geometry[MaxTextExtent];
12614
12615       /*
12616         Request page geometry from user.
12617       */
12618       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12619       if (LocaleCompare(image_info->magick,"PDF") == 0)
12620         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12621       if (image_info->page != (char *) NULL)
12622         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12623       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12624         "Select page geometry:",geometry);
12625       if (*geometry != '\0')
12626         image_info->page=GetPageGeometry(geometry);
12627     }
12628   /*
12629     Apply image transforms.
12630   */
12631   XSetCursorState(display,windows,MagickTrue);
12632   XCheckRefreshWindows(display,windows);
12633   save_image=CloneImage(image,0,0,MagickTrue,exception);
12634   if (save_image == (Image *) NULL)
12635     return(MagickFalse);
12636   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12637     windows->image.ximage->width,windows->image.ximage->height);
12638   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry,
12639     exception);
12640   /*
12641     Write image.
12642   */
12643   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12644   status=WriteImage(image_info,save_image,exception);
12645   if (status != MagickFalse)
12646     image->taint=MagickFalse;
12647   save_image=DestroyImage(save_image);
12648   image_info=DestroyImageInfo(image_info);
12649   XSetCursorState(display,windows,MagickFalse);
12650   return(status != 0 ? MagickTrue : MagickFalse);
12651 }
12652 \f
12653 /*
12654 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12655 %                                                                             %
12656 %                                                                             %
12657 %                                                                             %
12658 +   X S c r e e n E v e n t                                                   %
12659 %                                                                             %
12660 %                                                                             %
12661 %                                                                             %
12662 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12663 %
12664 %  XScreenEvent() handles global events associated with the Pan and Magnify
12665 %  windows.
12666 %
12667 %  The format of the XScreenEvent function is:
12668 %
12669 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12670 %        ExceptionInfo *exception)
12671 %
12672 %  A description of each parameter follows:
12673 %
12674 %    o display: Specifies a pointer to the Display structure;  returned from
12675 %      XOpenDisplay.
12676 %
12677 %    o windows: Specifies a pointer to a XWindows structure.
12678 %
12679 %    o event: Specifies a pointer to a X11 XEvent structure.
12680 %
12681 %    o exception: return any errors or warnings in this structure.
12682 %
12683 */
12684
12685 #if defined(__cplusplus) || defined(c_plusplus)
12686 extern "C" {
12687 #endif
12688
12689 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12690 {
12691   register XWindows
12692     *windows;
12693
12694   windows=(XWindows *) data;
12695   if ((event->type == ClientMessage) &&
12696       (event->xclient.window == windows->image.id))
12697     return(MagickFalse);
12698   return(MagickTrue);
12699 }
12700
12701 #if defined(__cplusplus) || defined(c_plusplus)
12702 }
12703 #endif
12704
12705 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event,
12706   ExceptionInfo *exception)
12707 {
12708   register int
12709     x,
12710     y;
12711
12712   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12713   if (event->xany.window == windows->command.id)
12714     return;
12715   switch (event->type)
12716   {
12717     case ButtonPress:
12718     case ButtonRelease:
12719     {
12720       if ((event->xbutton.button == Button3) &&
12721           (event->xbutton.state & Mod1Mask))
12722         {
12723           /*
12724             Convert Alt-Button3 to Button2.
12725           */
12726           event->xbutton.button=Button2;
12727           event->xbutton.state&=(~Mod1Mask);
12728         }
12729       if (event->xbutton.window == windows->backdrop.id)
12730         {
12731           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12732             event->xbutton.time);
12733           break;
12734         }
12735       if (event->xbutton.window == windows->pan.id)
12736         {
12737           XPanImage(display,windows,event,exception);
12738           break;
12739         }
12740       if (event->xbutton.window == windows->image.id)
12741         if (event->xbutton.button == Button2)
12742           {
12743             /*
12744               Update magnified image.
12745             */
12746             x=event->xbutton.x;
12747             y=event->xbutton.y;
12748             if (x < 0)
12749               x=0;
12750             else
12751               if (x >= (int) windows->image.width)
12752                 x=(int) (windows->image.width-1);
12753             windows->magnify.x=(int) windows->image.x+x;
12754             if (y < 0)
12755               y=0;
12756             else
12757              if (y >= (int) windows->image.height)
12758                y=(int) (windows->image.height-1);
12759             windows->magnify.y=windows->image.y+y;
12760             if (windows->magnify.mapped == MagickFalse)
12761               (void) XMapRaised(display,windows->magnify.id);
12762             XMakeMagnifyImage(display,windows,exception);
12763             if (event->type == ButtonRelease)
12764               (void) XWithdrawWindow(display,windows->info.id,
12765                 windows->info.screen);
12766             break;
12767           }
12768       break;
12769     }
12770     case ClientMessage:
12771     {
12772       /*
12773         If client window delete message, exit.
12774       */
12775       if (event->xclient.message_type != windows->wm_protocols)
12776         break;
12777       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12778         break;
12779       if (event->xclient.window == windows->magnify.id)
12780         {
12781           (void) XWithdrawWindow(display,windows->magnify.id,
12782             windows->magnify.screen);
12783           break;
12784         }
12785       break;
12786     }
12787     case ConfigureNotify:
12788     {
12789       if (event->xconfigure.window == windows->magnify.id)
12790         {
12791           unsigned int
12792             magnify;
12793
12794           /*
12795             Magnify window has a new configuration.
12796           */
12797           windows->magnify.width=(unsigned int) event->xconfigure.width;
12798           windows->magnify.height=(unsigned int) event->xconfigure.height;
12799           if (windows->magnify.mapped == MagickFalse)
12800             break;
12801           magnify=1;
12802           while ((int) magnify <= event->xconfigure.width)
12803             magnify<<=1;
12804           while ((int) magnify <= event->xconfigure.height)
12805             magnify<<=1;
12806           magnify>>=1;
12807           if (((int) magnify != event->xconfigure.width) ||
12808               ((int) magnify != event->xconfigure.height))
12809             {
12810               XWindowChanges
12811                 window_changes;
12812
12813               window_changes.width=(int) magnify;
12814               window_changes.height=(int) magnify;
12815               (void) XReconfigureWMWindow(display,windows->magnify.id,
12816                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12817                 &window_changes);
12818               break;
12819             }
12820           XMakeMagnifyImage(display,windows,exception);
12821           break;
12822         }
12823       break;
12824     }
12825     case Expose:
12826     {
12827       if (event->xexpose.window == windows->image.id)
12828         {
12829           XRefreshWindow(display,&windows->image,event);
12830           break;
12831         }
12832       if (event->xexpose.window == windows->pan.id)
12833         if (event->xexpose.count == 0)
12834           {
12835             XDrawPanRectangle(display,windows);
12836             break;
12837           }
12838       if (event->xexpose.window == windows->magnify.id)
12839         if (event->xexpose.count == 0)
12840           {
12841             XMakeMagnifyImage(display,windows,exception);
12842             break;
12843           }
12844       break;
12845     }
12846     case KeyPress:
12847     {
12848       char
12849         command[MaxTextExtent];
12850
12851       KeySym
12852         key_symbol;
12853
12854       if (event->xkey.window != windows->magnify.id)
12855         break;
12856       /*
12857         Respond to a user key press.
12858       */
12859       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12860         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12861       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol,
12862         exception);
12863       break;
12864     }
12865     case MapNotify:
12866     {
12867       if (event->xmap.window == windows->magnify.id)
12868         {
12869           windows->magnify.mapped=MagickTrue;
12870           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12871           break;
12872         }
12873       if (event->xmap.window == windows->info.id)
12874         {
12875           windows->info.mapped=MagickTrue;
12876           break;
12877         }
12878       break;
12879     }
12880     case MotionNotify:
12881     {
12882       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12883       if (event->xmotion.window == windows->image.id)
12884         if (windows->magnify.mapped != MagickFalse)
12885           {
12886             /*
12887               Update magnified image.
12888             */
12889             x=event->xmotion.x;
12890             y=event->xmotion.y;
12891             if (x < 0)
12892               x=0;
12893             else
12894               if (x >= (int) windows->image.width)
12895                 x=(int) (windows->image.width-1);
12896             windows->magnify.x=(int) windows->image.x+x;
12897             if (y < 0)
12898               y=0;
12899             else
12900              if (y >= (int) windows->image.height)
12901                y=(int) (windows->image.height-1);
12902             windows->magnify.y=windows->image.y+y;
12903             XMakeMagnifyImage(display,windows,exception);
12904           }
12905       break;
12906     }
12907     case UnmapNotify:
12908     {
12909       if (event->xunmap.window == windows->magnify.id)
12910         {
12911           windows->magnify.mapped=MagickFalse;
12912           break;
12913         }
12914       if (event->xunmap.window == windows->info.id)
12915         {
12916           windows->info.mapped=MagickFalse;
12917           break;
12918         }
12919       break;
12920     }
12921     default:
12922       break;
12923   }
12924 }
12925 \f
12926 /*
12927 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12928 %                                                                             %
12929 %                                                                             %
12930 %                                                                             %
12931 +   X S e t C r o p G e o m e t r y                                           %
12932 %                                                                             %
12933 %                                                                             %
12934 %                                                                             %
12935 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12936 %
12937 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12938 %  and translates it to a cropping geometry relative to the image.
12939 %
12940 %  The format of the XSetCropGeometry method is:
12941 %
12942 %      void XSetCropGeometry(Display *display,XWindows *windows,
12943 %        RectangleInfo *crop_info,Image *image)
12944 %
12945 %  A description of each parameter follows:
12946 %
12947 %    o display: Specifies a connection to an X server; returned from
12948 %      XOpenDisplay.
12949 %
12950 %    o windows: Specifies a pointer to a XWindows structure.
12951 %
12952 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12953 %      Image window to crop.
12954 %
12955 %    o image: the image.
12956 %
12957 */
12958 static void XSetCropGeometry(Display *display,XWindows *windows,
12959   RectangleInfo *crop_info,Image *image)
12960 {
12961   char
12962     text[MaxTextExtent];
12963
12964   int
12965     x,
12966     y;
12967
12968   MagickRealType
12969     scale_factor;
12970
12971   unsigned int
12972     height,
12973     width;
12974
12975   if (windows->info.mapped != MagickFalse)
12976     {
12977       /*
12978         Display info on cropping rectangle.
12979       */
12980       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12981         (double) crop_info->width,(double) crop_info->height,(double)
12982         crop_info->x,(double) crop_info->y);
12983       XInfoWidget(display,windows,text);
12984     }
12985   /*
12986     Cropping geometry is relative to any previous crop geometry.
12987   */
12988   x=0;
12989   y=0;
12990   width=(unsigned int) image->columns;
12991   height=(unsigned int) image->rows;
12992   if (windows->image.crop_geometry != (char *) NULL)
12993     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12994   else
12995     windows->image.crop_geometry=AcquireString((char *) NULL);
12996   /*
12997     Define the crop geometry string from the cropping rectangle.
12998   */
12999   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13000   if (crop_info->x > 0)
13001     x+=(int) (scale_factor*crop_info->x+0.5);
13002   width=(unsigned int) (scale_factor*crop_info->width+0.5);
13003   if (width == 0)
13004     width=1;
13005   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13006   if (crop_info->y > 0)
13007     y+=(int) (scale_factor*crop_info->y+0.5);
13008   height=(unsigned int) (scale_factor*crop_info->height+0.5);
13009   if (height == 0)
13010     height=1;
13011   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
13012     "%ux%u%+d%+d",width,height,x,y);
13013 }
13014 \f
13015 /*
13016 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13017 %                                                                             %
13018 %                                                                             %
13019 %                                                                             %
13020 +   X T i l e I m a g e                                                       %
13021 %                                                                             %
13022 %                                                                             %
13023 %                                                                             %
13024 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13025 %
13026 %  XTileImage() loads or deletes a selected tile from a visual image directory.
13027 %  The load or delete command is chosen from a menu.
13028 %
13029 %  The format of the XTileImage method is:
13030 %
13031 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
13032 %        XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13033 %
13034 %  A description of each parameter follows:
13035 %
13036 %    o tile_image:  XTileImage reads or deletes the tile image
13037 %      and returns it.  A null image is returned if an error occurs.
13038 %
13039 %    o display: Specifies a connection to an X server;  returned from
13040 %      XOpenDisplay.
13041 %
13042 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13043 %
13044 %    o windows: Specifies a pointer to a XWindows structure.
13045 %
13046 %    o image: the image; returned from ReadImage.
13047 %
13048 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
13049 %      the entire image is refreshed.
13050 %
13051 %    o exception: return any errors or warnings in this structure.
13052 %
13053 */
13054 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
13055   XWindows *windows,Image *image,XEvent *event,ExceptionInfo *exception)
13056 {
13057   static const char
13058     *VerbMenu[] =
13059     {
13060       "Load",
13061       "Next",
13062       "Former",
13063       "Delete",
13064       "Update",
13065       (char *) NULL,
13066     };
13067
13068   static const ModeType
13069     TileCommands[] =
13070     {
13071       TileLoadCommand,
13072       TileNextCommand,
13073       TileFormerCommand,
13074       TileDeleteCommand,
13075       TileUpdateCommand
13076     };
13077
13078   char
13079     command[MaxTextExtent],
13080     filename[MaxTextExtent];
13081
13082   Image
13083     *tile_image;
13084
13085   int
13086     id,
13087     status,
13088     tile,
13089     x,
13090     y;
13091
13092   MagickRealType
13093     scale_factor;
13094
13095   register char
13096     *p,
13097     *q;
13098
13099   register int
13100     i;
13101
13102   unsigned int
13103     height,
13104     width;
13105
13106   /*
13107     Tile image is relative to montage image configuration.
13108   */
13109   x=0;
13110   y=0;
13111   width=(unsigned int) image->columns;
13112   height=(unsigned int) image->rows;
13113   if (windows->image.crop_geometry != (char *) NULL)
13114     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
13115   scale_factor=(MagickRealType) width/windows->image.ximage->width;
13116   event->xbutton.x+=windows->image.x;
13117   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
13118   scale_factor=(MagickRealType) height/windows->image.ximage->height;
13119   event->xbutton.y+=windows->image.y;
13120   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
13121   /*
13122     Determine size and location of each tile in the visual image directory.
13123   */
13124   width=(unsigned int) image->columns;
13125   height=(unsigned int) image->rows;
13126   x=0;
13127   y=0;
13128   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
13129   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
13130     (event->xbutton.x-x)/width;
13131   if (tile < 0)
13132     {
13133       /*
13134         Button press is outside any tile.
13135       */
13136       (void) XBell(display,0);
13137       return((Image *) NULL);
13138     }
13139   /*
13140     Determine file name from the tile directory.
13141   */
13142   p=image->directory;
13143   for (i=tile; (i != 0) && (*p != '\0'); )
13144   {
13145     if (*p == '\n')
13146       i--;
13147     p++;
13148   }
13149   if (*p == '\0')
13150     {
13151       /*
13152         Button press is outside any tile.
13153       */
13154       (void) XBell(display,0);
13155       return((Image *) NULL);
13156     }
13157   /*
13158     Select a command from the pop-up menu.
13159   */
13160   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13161   if (id < 0)
13162     return((Image *) NULL);
13163   q=p;
13164   while ((*q != '\n') && (*q != '\0'))
13165     q++;
13166   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13167   /*
13168     Perform command for the selected tile.
13169   */
13170   XSetCursorState(display,windows,MagickTrue);
13171   XCheckRefreshWindows(display,windows);
13172   tile_image=NewImageList();
13173   switch (TileCommands[id])
13174   {
13175     case TileLoadCommand:
13176     {
13177       /*
13178         Load tile image.
13179       */
13180       XCheckRefreshWindows(display,windows);
13181       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13182         MaxTextExtent);
13183       (void) CopyMagickString(resource_info->image_info->filename,filename,
13184         MaxTextExtent);
13185       tile_image=ReadImage(resource_info->image_info,exception);
13186       CatchException(exception);
13187       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13188       break;
13189     }
13190     case TileNextCommand:
13191     {
13192       /*
13193         Display next image.
13194       */
13195       XClientMessage(display,windows->image.id,windows->im_protocols,
13196         windows->im_next_image,CurrentTime);
13197       break;
13198     }
13199     case TileFormerCommand:
13200     {
13201       /*
13202         Display former image.
13203       */
13204       XClientMessage(display,windows->image.id,windows->im_protocols,
13205         windows->im_former_image,CurrentTime);
13206       break;
13207     }
13208     case TileDeleteCommand:
13209     {
13210       /*
13211         Delete tile image.
13212       */
13213       if (IsPathAccessible(filename) == MagickFalse)
13214         {
13215           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13216           break;
13217         }
13218       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13219       if (status <= 0)
13220         break;
13221       status=remove_utf8(filename) != 0 ? MagickTrue : MagickFalse;
13222       if (status != MagickFalse)
13223         {
13224           XNoticeWidget(display,windows,"Unable to delete image file:",
13225             filename);
13226           break;
13227         }
13228     }
13229     case TileUpdateCommand:
13230     {
13231       int
13232         x_offset,
13233         y_offset;
13234
13235       PixelInfo
13236         pixel;
13237
13238       register int
13239         j;
13240
13241       register Quantum
13242         *s;
13243
13244       /*
13245         Ensure all the images exist.
13246       */
13247       tile=0;
13248       GetPixelInfo(image,&pixel);
13249       for (p=image->directory; *p != '\0'; p++)
13250       {
13251         CacheView
13252           *image_view;
13253
13254         q=p;
13255         while ((*q != '\n') && (*q != '\0'))
13256           q++;
13257         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13258         p=q;
13259         if (IsPathAccessible(filename) != MagickFalse)
13260           {
13261             tile++;
13262             continue;
13263           }
13264         /*
13265           Overwrite tile with background color.
13266         */
13267         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13268         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13269         image_view=AcquireAuthenticCacheView(image,exception);
13270         (void) GetOneCacheViewVirtualPixelInfo(image_view,0,0,&pixel,exception);
13271         for (i=0; i < (int) height; i++)
13272         {
13273           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13274             y_offset+i,width,1,exception);
13275           if (s == (Quantum *) NULL)
13276             break;
13277           for (j=0; j < (int) width; j++)
13278           {
13279             SetPixelInfoPixel(image,&pixel,s);
13280             s+=GetPixelChannels(image);
13281           }
13282           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13283             break;
13284         }
13285         image_view=DestroyCacheView(image_view);
13286         tile++;
13287       }
13288       windows->image.window_changes.width=(int) image->columns;
13289       windows->image.window_changes.height=(int) image->rows;
13290       XConfigureImageColormap(display,resource_info,windows,image,exception);
13291       (void) XConfigureImage(display,resource_info,windows,image,exception);
13292       break;
13293     }
13294     default:
13295       break;
13296   }
13297   XSetCursorState(display,windows,MagickFalse);
13298   return(tile_image);
13299 }
13300 \f
13301 /*
13302 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13303 %                                                                             %
13304 %                                                                             %
13305 %                                                                             %
13306 +   X T r a n s l a t e I m a g e                                             %
13307 %                                                                             %
13308 %                                                                             %
13309 %                                                                             %
13310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13311 %
13312 %  XTranslateImage() translates the image within an Image window by one pixel
13313 %  as specified by the key symbol.  If the image has a montage string the
13314 %  translation is respect to the width and height contained within the string.
13315 %
13316 %  The format of the XTranslateImage method is:
13317 %
13318 %      void XTranslateImage(Display *display,XWindows *windows,
13319 %        Image *image,const KeySym key_symbol)
13320 %
13321 %  A description of each parameter follows:
13322 %
13323 %    o display: Specifies a connection to an X server; returned from
13324 %      XOpenDisplay.
13325 %
13326 %    o windows: Specifies a pointer to a XWindows structure.
13327 %
13328 %    o image: the image.
13329 %
13330 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13331 %      to trim.
13332 %
13333 */
13334 static void XTranslateImage(Display *display,XWindows *windows,
13335   Image *image,const KeySym key_symbol)
13336 {
13337   char
13338     text[MaxTextExtent];
13339
13340   int
13341     x,
13342     y;
13343
13344   unsigned int
13345     x_offset,
13346     y_offset;
13347
13348   /*
13349     User specified a pan position offset.
13350   */
13351   x_offset=windows->image.width;
13352   y_offset=windows->image.height;
13353   if (image->montage != (char *) NULL)
13354     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13355   switch ((int) key_symbol)
13356   {
13357     case XK_Home:
13358     case XK_KP_Home:
13359     {
13360       windows->image.x=(int) windows->image.width/2;
13361       windows->image.y=(int) windows->image.height/2;
13362       break;
13363     }
13364     case XK_Left:
13365     case XK_KP_Left:
13366     {
13367       windows->image.x-=x_offset;
13368       break;
13369     }
13370     case XK_Next:
13371     case XK_Up:
13372     case XK_KP_Up:
13373     {
13374       windows->image.y-=y_offset;
13375       break;
13376     }
13377     case XK_Right:
13378     case XK_KP_Right:
13379     {
13380       windows->image.x+=x_offset;
13381       break;
13382     }
13383     case XK_Prior:
13384     case XK_Down:
13385     case XK_KP_Down:
13386     {
13387       windows->image.y+=y_offset;
13388       break;
13389     }
13390     default:
13391       return;
13392   }
13393   /*
13394     Check boundary conditions.
13395   */
13396   if (windows->image.x < 0)
13397     windows->image.x=0;
13398   else
13399     if ((int) (windows->image.x+windows->image.width) >
13400         windows->image.ximage->width)
13401       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13402   if (windows->image.y < 0)
13403     windows->image.y=0;
13404   else
13405     if ((int) (windows->image.y+windows->image.height) >
13406         windows->image.ximage->height)
13407       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13408   /*
13409     Refresh Image window.
13410   */
13411   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13412     windows->image.width,windows->image.height,windows->image.x,
13413     windows->image.y);
13414   XInfoWidget(display,windows,text);
13415   XCheckRefreshWindows(display,windows);
13416   XDrawPanRectangle(display,windows);
13417   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13418   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13419 }
13420 \f
13421 /*
13422 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13423 %                                                                             %
13424 %                                                                             %
13425 %                                                                             %
13426 +   X T r i m I m a g e                                                       %
13427 %                                                                             %
13428 %                                                                             %
13429 %                                                                             %
13430 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13431 %
13432 %  XTrimImage() trims the edges from the Image window.
13433 %
13434 %  The format of the XTrimImage method is:
13435 %
13436 %      MagickBooleanType XTrimImage(Display *display,
13437 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
13438 %        ExceptionInfo *exception)
13439 %
13440 %  A description of each parameter follows:
13441 %
13442 %    o display: Specifies a connection to an X server; returned from
13443 %      XOpenDisplay.
13444 %
13445 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13446 %
13447 %    o windows: Specifies a pointer to a XWindows structure.
13448 %
13449 %    o image: the image.
13450 %
13451 %    o exception: return any errors or warnings in this structure.
13452 %
13453 */
13454 static MagickBooleanType XTrimImage(Display *display,
13455   XResourceInfo *resource_info,XWindows *windows,Image *image,
13456   ExceptionInfo *exception)
13457 {
13458   RectangleInfo
13459     trim_info;
13460
13461   register int
13462     x,
13463     y;
13464
13465   size_t
13466     background,
13467     pixel;
13468
13469   /*
13470     Trim edges from image.
13471   */
13472   XSetCursorState(display,windows,MagickTrue);
13473   XCheckRefreshWindows(display,windows);
13474   /*
13475     Crop the left edge.
13476   */
13477   background=XGetPixel(windows->image.ximage,0,0);
13478   trim_info.width=(size_t) windows->image.ximage->width;
13479   for (x=0; x < windows->image.ximage->width; x++)
13480   {
13481     for (y=0; y < windows->image.ximage->height; y++)
13482     {
13483       pixel=XGetPixel(windows->image.ximage,x,y);
13484       if (pixel != background)
13485         break;
13486     }
13487     if (y < windows->image.ximage->height)
13488       break;
13489   }
13490   trim_info.x=(ssize_t) x;
13491   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13492     {
13493       XSetCursorState(display,windows,MagickFalse);
13494       return(MagickFalse);
13495     }
13496   /*
13497     Crop the right edge.
13498   */
13499   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13500   for (x=windows->image.ximage->width-1; x != 0; x--)
13501   {
13502     for (y=0; y < windows->image.ximage->height; y++)
13503     {
13504       pixel=XGetPixel(windows->image.ximage,x,y);
13505       if (pixel != background)
13506         break;
13507     }
13508     if (y < windows->image.ximage->height)
13509       break;
13510   }
13511   trim_info.width=(size_t) (x-trim_info.x+1);
13512   /*
13513     Crop the top edge.
13514   */
13515   background=XGetPixel(windows->image.ximage,0,0);
13516   trim_info.height=(size_t) windows->image.ximage->height;
13517   for (y=0; y < windows->image.ximage->height; y++)
13518   {
13519     for (x=0; x < windows->image.ximage->width; x++)
13520     {
13521       pixel=XGetPixel(windows->image.ximage,x,y);
13522       if (pixel != background)
13523         break;
13524     }
13525     if (x < windows->image.ximage->width)
13526       break;
13527   }
13528   trim_info.y=(ssize_t) y;
13529   /*
13530     Crop the bottom edge.
13531   */
13532   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13533   for (y=windows->image.ximage->height-1; y != 0; y--)
13534   {
13535     for (x=0; x < windows->image.ximage->width; x++)
13536     {
13537       pixel=XGetPixel(windows->image.ximage,x,y);
13538       if (pixel != background)
13539         break;
13540     }
13541     if (x < windows->image.ximage->width)
13542       break;
13543   }
13544   trim_info.height=(size_t) y-trim_info.y+1;
13545   if (((unsigned int) trim_info.width != windows->image.width) ||
13546       ((unsigned int) trim_info.height != windows->image.height))
13547     {
13548       /*
13549         Reconfigure Image window as defined by the trimming rectangle.
13550       */
13551       XSetCropGeometry(display,windows,&trim_info,image);
13552       windows->image.window_changes.width=(int) trim_info.width;
13553       windows->image.window_changes.height=(int) trim_info.height;
13554       (void) XConfigureImage(display,resource_info,windows,image,exception);
13555     }
13556   XSetCursorState(display,windows,MagickFalse);
13557   return(MagickTrue);
13558 }
13559 \f
13560 /*
13561 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13562 %                                                                             %
13563 %                                                                             %
13564 %                                                                             %
13565 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13566 %                                                                             %
13567 %                                                                             %
13568 %                                                                             %
13569 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13570 %
13571 %  XVisualDirectoryImage() creates a Visual Image Directory.
13572 %
13573 %  The format of the XVisualDirectoryImage method is:
13574 %
13575 %      Image *XVisualDirectoryImage(Display *display,
13576 %        XResourceInfo *resource_info,XWindows *windows,
13577 %        ExceptionInfo *exception)
13578 %
13579 %  A description of each parameter follows:
13580 %
13581 %    o display: Specifies a connection to an X server; returned from
13582 %      XOpenDisplay.
13583 %
13584 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13585 %
13586 %    o windows: Specifies a pointer to a XWindows structure.
13587 %
13588 %    o exception: return any errors or warnings in this structure.
13589 %
13590 */
13591 static Image *XVisualDirectoryImage(Display *display,
13592   XResourceInfo *resource_info,XWindows *windows,ExceptionInfo *exception)
13593 {
13594 #define TileImageTag  "Scale/Image"
13595 #define XClientName  "montage"
13596
13597   char
13598     **filelist;
13599
13600   Image
13601     *images,
13602     *montage_image,
13603     *next_image,
13604     *thumbnail_image;
13605
13606   ImageInfo
13607     *read_info;
13608
13609   int
13610     number_files;
13611
13612   MagickBooleanType
13613     backdrop;
13614
13615   MagickStatusType
13616     status;
13617
13618   MontageInfo
13619     *montage_info;
13620
13621   RectangleInfo
13622     geometry;
13623
13624   register int
13625     i;
13626
13627   static char
13628     filename[MaxTextExtent] = "\0",
13629     filenames[MaxTextExtent] = "*";
13630
13631   XResourceInfo
13632     background_resources;
13633
13634   /*
13635     Request file name from user.
13636   */
13637   XFileBrowserWidget(display,windows,"Directory",filenames);
13638   if (*filenames == '\0')
13639     return((Image *) NULL);
13640   /*
13641     Expand the filenames.
13642   */
13643   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13644   if (filelist == (char **) NULL)
13645     {
13646       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13647         filenames);
13648       return((Image *) NULL);
13649     }
13650   number_files=1;
13651   filelist[0]=filenames;
13652   status=ExpandFilenames(&number_files,&filelist);
13653   if ((status == MagickFalse) || (number_files == 0))
13654     {
13655       if (number_files == 0)
13656         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13657       else
13658         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13659           filenames);
13660       return((Image *) NULL);
13661     }
13662   /*
13663     Set image background resources.
13664   */
13665   background_resources=(*resource_info);
13666   background_resources.window_id=AcquireString("");
13667   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13668     "0x%lx",windows->image.id);
13669   background_resources.backdrop=MagickTrue;
13670   /*
13671     Read each image and convert them to a tile.
13672   */
13673   backdrop=(windows->visual_info->klass == TrueColor) ||
13674     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13675   read_info=CloneImageInfo(resource_info->image_info);
13676   (void) SetImageOption(read_info,"jpeg:size","120x120");
13677   (void) CloneString(&read_info->size,DefaultTileGeometry);
13678   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13679     (void *) NULL);
13680   images=NewImageList();
13681   XSetCursorState(display,windows,MagickTrue);
13682   XCheckRefreshWindows(display,windows);
13683   for (i=0; i < (int) number_files; i++)
13684   {
13685     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13686     filelist[i]=DestroyString(filelist[i]);
13687     *read_info->magick='\0';
13688     next_image=ReadImage(read_info,exception);
13689     CatchException(exception);
13690     if (next_image != (Image *) NULL)
13691       {
13692         (void) DeleteImageProperty(next_image,"label");
13693         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13694           read_info,next_image,DefaultTileLabel,exception),exception);
13695         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13696           exception);
13697         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13698           geometry.height,exception);
13699         if (thumbnail_image != (Image *) NULL)
13700           {
13701             next_image=DestroyImage(next_image);
13702             next_image=thumbnail_image;
13703           }
13704         if (backdrop)
13705           {
13706             (void) XDisplayBackgroundImage(display,&background_resources,
13707               next_image,exception);
13708             XSetCursorState(display,windows,MagickTrue);
13709           }
13710         AppendImageToList(&images,next_image);
13711         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13712           {
13713             MagickBooleanType
13714               proceed;
13715
13716             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13717               (MagickSizeType) number_files);
13718             if (proceed == MagickFalse)
13719               break;
13720           }
13721       }
13722   }
13723   filelist=(char **) RelinquishMagickMemory(filelist);
13724   if (images == (Image *) NULL)
13725     {
13726       read_info=DestroyImageInfo(read_info);
13727       XSetCursorState(display,windows,MagickFalse);
13728       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13729       return((Image *) NULL);
13730     }
13731   /*
13732     Create the Visual Image Directory.
13733   */
13734   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13735   montage_info->pointsize=10;
13736   if (resource_info->font != (char *) NULL)
13737     (void) CloneString(&montage_info->font,resource_info->font);
13738   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13739   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13740     images),exception);
13741   images=DestroyImageList(images);
13742   montage_info=DestroyMontageInfo(montage_info);
13743   read_info=DestroyImageInfo(read_info);
13744   XSetCursorState(display,windows,MagickFalse);
13745   if (montage_image == (Image *) NULL)
13746     return(montage_image);
13747   XClientMessage(display,windows->image.id,windows->im_protocols,
13748     windows->im_next_image,CurrentTime);
13749   return(montage_image);
13750 }
13751 \f
13752 /*
13753 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13754 %                                                                             %
13755 %                                                                             %
13756 %                                                                             %
13757 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13758 %                                                                             %
13759 %                                                                             %
13760 %                                                                             %
13761 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13762 %
13763 %  XDisplayBackgroundImage() displays an image in the background of a window.
13764 %
13765 %  The format of the XDisplayBackgroundImage method is:
13766 %
13767 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13768 %        XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13769 %
13770 %  A description of each parameter follows:
13771 %
13772 %    o display: Specifies a connection to an X server;  returned from
13773 %      XOpenDisplay.
13774 %
13775 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13776 %
13777 %    o image: the image.
13778 %
13779 %    o exception: return any errors or warnings in this structure.
13780 %
13781 */
13782 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13783   XResourceInfo *resource_info,Image *image,ExceptionInfo *exception)
13784 {
13785   char
13786     geometry[MaxTextExtent],
13787     visual_type[MaxTextExtent];
13788
13789   int
13790     height,
13791     status,
13792     width;
13793
13794   RectangleInfo
13795     geometry_info;
13796
13797   static XPixelInfo
13798     pixel;
13799
13800   static XStandardColormap
13801     *map_info;
13802
13803   static XVisualInfo
13804     *visual_info = (XVisualInfo *) NULL;
13805
13806   static XWindowInfo
13807     window_info;
13808
13809   size_t
13810     delay;
13811
13812   Window
13813     root_window;
13814
13815   XGCValues
13816     context_values;
13817
13818   XResourceInfo
13819     resources;
13820
13821   XWindowAttributes
13822     window_attributes;
13823
13824   /*
13825     Determine target window.
13826   */
13827   assert(image != (Image *) NULL);
13828   assert(image->signature == MagickSignature);
13829   if (image->debug != MagickFalse)
13830     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13831   resources=(*resource_info);
13832   window_info.id=(Window) NULL;
13833   root_window=XRootWindow(display,XDefaultScreen(display));
13834   if (LocaleCompare(resources.window_id,"root") == 0)
13835     window_info.id=root_window;
13836   else
13837     {
13838       if (isdigit((unsigned char) *resources.window_id) != 0)
13839         window_info.id=XWindowByID(display,root_window,
13840           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13841       if (window_info.id == (Window) NULL)
13842         window_info.id=XWindowByName(display,root_window,resources.window_id);
13843     }
13844   if (window_info.id == (Window) NULL)
13845     {
13846       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13847         resources.window_id);
13848       return(MagickFalse);
13849     }
13850   /*
13851     Determine window visual id.
13852   */
13853   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13854   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13855   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13856   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13857   if (status != 0)
13858     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13859       XVisualIDFromVisual(window_attributes.visual));
13860   if (visual_info == (XVisualInfo *) NULL)
13861     {
13862       /*
13863         Allocate standard colormap.
13864       */
13865       map_info=XAllocStandardColormap();
13866       if (map_info == (XStandardColormap *) NULL)
13867         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13868           image->filename);
13869       map_info->colormap=(Colormap) NULL;
13870       pixel.pixels=(unsigned long *) NULL;
13871       /*
13872         Initialize visual info.
13873       */
13874       resources.map_type=(char *) NULL;
13875       resources.visual_type=visual_type;
13876       visual_info=XBestVisualInfo(display,map_info,&resources);
13877       if (visual_info == (XVisualInfo *) NULL)
13878         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13879           resources.visual_type);
13880       /*
13881         Initialize window info.
13882       */
13883       window_info.ximage=(XImage *) NULL;
13884       window_info.matte_image=(XImage *) NULL;
13885       window_info.pixmap=(Pixmap) NULL;
13886       window_info.matte_pixmap=(Pixmap) NULL;
13887     }
13888   /*
13889     Free previous root colors.
13890   */
13891   if (window_info.id == root_window)
13892     (void) XDestroyWindowColors(display,root_window);
13893   /*
13894     Initialize Standard Colormap.
13895   */
13896   resources.colormap=SharedColormap;
13897   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel,
13898     exception);
13899   /*
13900     Graphic context superclass.
13901   */
13902   context_values.background=pixel.background_color.pixel;
13903   context_values.foreground=pixel.foreground_color.pixel;
13904   pixel.annotate_context=XCreateGC(display,window_info.id,
13905     (size_t) (GCBackground | GCForeground),&context_values);
13906   if (pixel.annotate_context == (GC) NULL)
13907     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13908       image->filename);
13909   /*
13910     Initialize Image window attributes.
13911   */
13912   window_info.name=AcquireString("\0");
13913   window_info.icon_name=AcquireString("\0");
13914   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13915     &resources,&window_info);
13916   /*
13917     Create the X image.
13918   */
13919   window_info.width=(unsigned int) image->columns;
13920   window_info.height=(unsigned int) image->rows;
13921   if ((image->columns != window_info.width) ||
13922       (image->rows != window_info.height))
13923     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13924       image->filename);
13925   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13926     window_attributes.width,window_attributes.height);
13927   geometry_info.width=window_info.width;
13928   geometry_info.height=window_info.height;
13929   geometry_info.x=(ssize_t) window_info.x;
13930   geometry_info.y=(ssize_t) window_info.y;
13931   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13932     &geometry_info.width,&geometry_info.height);
13933   window_info.width=(unsigned int) geometry_info.width;
13934   window_info.height=(unsigned int) geometry_info.height;
13935   window_info.x=(int) geometry_info.x;
13936   window_info.y=(int) geometry_info.y;
13937   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13938     window_info.height,exception);
13939   if (status == MagickFalse)
13940     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13941       image->filename);
13942   window_info.x=0;
13943   window_info.y=0;
13944   if (image->debug != MagickFalse)
13945     {
13946       (void) LogMagickEvent(X11Event,GetMagickModule(),
13947         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13948         (double) image->columns,(double) image->rows);
13949       if (image->colors != 0)
13950         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13951           image->colors);
13952       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13953     }
13954   /*
13955     Adjust image dimensions as specified by backdrop or geometry options.
13956   */
13957   width=(int) window_info.width;
13958   height=(int) window_info.height;
13959   if (resources.backdrop != MagickFalse)
13960     {
13961       /*
13962         Center image on window.
13963       */
13964       window_info.x=(window_attributes.width/2)-
13965         (window_info.ximage->width/2);
13966       window_info.y=(window_attributes.height/2)-
13967         (window_info.ximage->height/2);
13968       width=window_attributes.width;
13969       height=window_attributes.height;
13970     }
13971   if ((resources.image_geometry != (char *) NULL) &&
13972       (*resources.image_geometry != '\0'))
13973     {
13974       char
13975         default_geometry[MaxTextExtent];
13976
13977       int
13978         flags,
13979         gravity;
13980
13981       XSizeHints
13982         *size_hints;
13983
13984       /*
13985         User specified geometry.
13986       */
13987       size_hints=XAllocSizeHints();
13988       if (size_hints == (XSizeHints *) NULL)
13989         ThrowXWindowFatalException(ResourceLimitFatalError,
13990           "MemoryAllocationFailed",image->filename);
13991       size_hints->flags=0L;
13992       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13993         width,height);
13994       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13995         default_geometry,window_info.border_width,size_hints,&window_info.x,
13996         &window_info.y,&width,&height,&gravity);
13997       if (flags & (XValue | YValue))
13998         {
13999           width=window_attributes.width;
14000           height=window_attributes.height;
14001         }
14002       (void) XFree((void *) size_hints);
14003     }
14004   /*
14005     Create the X pixmap.
14006   */
14007   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
14008     (unsigned int) height,window_info.depth);
14009   if (window_info.pixmap == (Pixmap) NULL)
14010     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
14011       image->filename);
14012   /*
14013     Display pixmap on the window.
14014   */
14015   if (((unsigned int) width > window_info.width) ||
14016       ((unsigned int) height > window_info.height))
14017     (void) XFillRectangle(display,window_info.pixmap,
14018       window_info.annotate_context,0,0,(unsigned int) width,
14019       (unsigned int) height);
14020   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
14021     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
14022     window_info.width,(unsigned int) window_info.height);
14023   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
14024   (void) XClearWindow(display,window_info.id);
14025   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
14026   XDelay(display,delay == 0UL ? 10UL : delay);
14027   (void) XSync(display,MagickFalse);
14028   return(window_info.id == root_window ? MagickTrue : MagickFalse);
14029 }
14030 \f
14031 /*
14032 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14033 %                                                                             %
14034 %                                                                             %
14035 %                                                                             %
14036 +   X D i s p l a y I m a g e                                                 %
14037 %                                                                             %
14038 %                                                                             %
14039 %                                                                             %
14040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
14041 %
14042 %  XDisplayImage() displays an image via X11.  A new image is created and
14043 %  returned if the user interactively transforms the displayed image.
14044 %
14045 %  The format of the XDisplayImage method is:
14046 %
14047 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14048 %        char **argv,int argc,Image **image,size_t *state,
14049 %        ExceptionInfo *exception)
14050 %
14051 %  A description of each parameter follows:
14052 %
14053 %    o nexus:  Method XDisplayImage returns an image when the
14054 %      user chooses 'Open Image' from the command menu or picks a tile
14055 %      from the image directory.  Otherwise a null image is returned.
14056 %
14057 %    o display: Specifies a connection to an X server;  returned from
14058 %      XOpenDisplay.
14059 %
14060 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
14061 %
14062 %    o argv: Specifies the application's argument list.
14063 %
14064 %    o argc: Specifies the number of arguments.
14065 %
14066 %    o image: Specifies an address to an address of an Image structure;
14067 %
14068 %    o exception: return any errors or warnings in this structure.
14069 %
14070 */
14071 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
14072   char **argv,int argc,Image **image,size_t *state,ExceptionInfo *exception)
14073 {
14074 #define MagnifySize  256  /* must be a power of 2 */
14075 #define MagickMenus  10
14076 #define MagickTitle  "Commands"
14077
14078   static const char
14079     *CommandMenu[] =
14080     {
14081       "File",
14082       "Edit",
14083       "View",
14084       "Transform",
14085       "Enhance",
14086       "Effects",
14087       "F/X",
14088       "Image Edit",
14089       "Miscellany",
14090       "Help",
14091       (char *) NULL
14092     },
14093     *FileMenu[] =
14094     {
14095       "Open...",
14096       "Next",
14097       "Former",
14098       "Select...",
14099       "Save...",
14100       "Print...",
14101       "Delete...",
14102       "New...",
14103       "Visual Directory...",
14104       "Quit",
14105       (char *) NULL
14106     },
14107     *EditMenu[] =
14108     {
14109       "Undo",
14110       "Redo",
14111       "Cut",
14112       "Copy",
14113       "Paste",
14114       (char *) NULL
14115     },
14116     *ViewMenu[] =
14117     {
14118       "Half Size",
14119       "Original Size",
14120       "Double Size",
14121       "Resize...",
14122       "Apply",
14123       "Refresh",
14124       "Restore",
14125       (char *) NULL
14126     },
14127     *TransformMenu[] =
14128     {
14129       "Crop",
14130       "Chop",
14131       "Flop",
14132       "Flip",
14133       "Rotate Right",
14134       "Rotate Left",
14135       "Rotate...",
14136       "Shear...",
14137       "Roll...",
14138       "Trim Edges",
14139       (char *) NULL
14140     },
14141     *EnhanceMenu[] =
14142     {
14143       "Hue...",
14144       "Saturation...",
14145       "Brightness...",
14146       "Gamma...",
14147       "Spiff",
14148       "Dull",
14149       "Contrast Stretch...",
14150       "Sigmoidal Contrast...",
14151       "Normalize",
14152       "Equalize",
14153       "Negate",
14154       "Grayscale",
14155       "Map...",
14156       "Quantize...",
14157       (char *) NULL
14158     },
14159     *EffectsMenu[] =
14160     {
14161       "Despeckle",
14162       "Emboss",
14163       "Reduce Noise",
14164       "Add Noise...",
14165       "Sharpen...",
14166       "Blur...",
14167       "Threshold...",
14168       "Edge Detect...",
14169       "Spread...",
14170       "Shade...",
14171       "Raise...",
14172       "Segment...",
14173       (char *) NULL
14174     },
14175     *FXMenu[] =
14176     {
14177       "Solarize...",
14178       "Sepia Tone...",
14179       "Swirl...",
14180       "Implode...",
14181       "Vignette...",
14182       "Wave...",
14183       "Oil Paint...",
14184       "Charcoal Draw...",
14185       (char *) NULL
14186     },
14187     *ImageEditMenu[] =
14188     {
14189       "Annotate...",
14190       "Draw...",
14191       "Color...",
14192       "Matte...",
14193       "Composite...",
14194       "Add Border...",
14195       "Add Frame...",
14196       "Comment...",
14197       "Launch...",
14198       "Region of Interest...",
14199       (char *) NULL
14200     },
14201     *MiscellanyMenu[] =
14202     {
14203       "Image Info",
14204       "Zoom Image",
14205       "Show Preview...",
14206       "Show Histogram",
14207       "Show Matte",
14208       "Background...",
14209       "Slide Show...",
14210       "Preferences...",
14211       (char *) NULL
14212     },
14213     *HelpMenu[] =
14214     {
14215       "Overview",
14216       "Browse Documentation",
14217       "About Display",
14218       (char *) NULL
14219     },
14220     *ShortCutsMenu[] =
14221     {
14222       "Next",
14223       "Former",
14224       "Open...",
14225       "Save...",
14226       "Print...",
14227       "Undo",
14228       "Restore",
14229       "Image Info",
14230       "Quit",
14231       (char *) NULL
14232     },
14233     *VirtualMenu[] =
14234     {
14235       "Image Info",
14236       "Print",
14237       "Next",
14238       "Quit",
14239       (char *) NULL
14240     };
14241
14242   static const char
14243     **Menus[MagickMenus] =
14244     {
14245       FileMenu,
14246       EditMenu,
14247       ViewMenu,
14248       TransformMenu,
14249       EnhanceMenu,
14250       EffectsMenu,
14251       FXMenu,
14252       ImageEditMenu,
14253       MiscellanyMenu,
14254       HelpMenu
14255     };
14256
14257   static CommandType
14258     CommandMenus[] =
14259     {
14260       NullCommand,
14261       NullCommand,
14262       NullCommand,
14263       NullCommand,
14264       NullCommand,
14265       NullCommand,
14266       NullCommand,
14267       NullCommand,
14268       NullCommand,
14269       NullCommand,
14270     },
14271     FileCommands[] =
14272     {
14273       OpenCommand,
14274       NextCommand,
14275       FormerCommand,
14276       SelectCommand,
14277       SaveCommand,
14278       PrintCommand,
14279       DeleteCommand,
14280       NewCommand,
14281       VisualDirectoryCommand,
14282       QuitCommand
14283     },
14284     EditCommands[] =
14285     {
14286       UndoCommand,
14287       RedoCommand,
14288       CutCommand,
14289       CopyCommand,
14290       PasteCommand
14291     },
14292     ViewCommands[] =
14293     {
14294       HalfSizeCommand,
14295       OriginalSizeCommand,
14296       DoubleSizeCommand,
14297       ResizeCommand,
14298       ApplyCommand,
14299       RefreshCommand,
14300       RestoreCommand
14301     },
14302     TransformCommands[] =
14303     {
14304       CropCommand,
14305       ChopCommand,
14306       FlopCommand,
14307       FlipCommand,
14308       RotateRightCommand,
14309       RotateLeftCommand,
14310       RotateCommand,
14311       ShearCommand,
14312       RollCommand,
14313       TrimCommand
14314     },
14315     EnhanceCommands[] =
14316     {
14317       HueCommand,
14318       SaturationCommand,
14319       BrightnessCommand,
14320       GammaCommand,
14321       SpiffCommand,
14322       DullCommand,
14323       ContrastStretchCommand,
14324       SigmoidalContrastCommand,
14325       NormalizeCommand,
14326       EqualizeCommand,
14327       NegateCommand,
14328       GrayscaleCommand,
14329       MapCommand,
14330       QuantizeCommand
14331     },
14332     EffectsCommands[] =
14333     {
14334       DespeckleCommand,
14335       EmbossCommand,
14336       ReduceNoiseCommand,
14337       AddNoiseCommand,
14338       SharpenCommand,
14339       BlurCommand,
14340       ThresholdCommand,
14341       EdgeDetectCommand,
14342       SpreadCommand,
14343       ShadeCommand,
14344       RaiseCommand,
14345       SegmentCommand
14346     },
14347     FXCommands[] =
14348     {
14349       SolarizeCommand,
14350       SepiaToneCommand,
14351       SwirlCommand,
14352       ImplodeCommand,
14353       VignetteCommand,
14354       WaveCommand,
14355       OilPaintCommand,
14356       CharcoalDrawCommand
14357     },
14358     ImageEditCommands[] =
14359     {
14360       AnnotateCommand,
14361       DrawCommand,
14362       ColorCommand,
14363       MatteCommand,
14364       CompositeCommand,
14365       AddBorderCommand,
14366       AddFrameCommand,
14367       CommentCommand,
14368       LaunchCommand,
14369       RegionofInterestCommand
14370     },
14371     MiscellanyCommands[] =
14372     {
14373       InfoCommand,
14374       ZoomCommand,
14375       ShowPreviewCommand,
14376       ShowHistogramCommand,
14377       ShowMatteCommand,
14378       BackgroundCommand,
14379       SlideShowCommand,
14380       PreferencesCommand
14381     },
14382     HelpCommands[] =
14383     {
14384       HelpCommand,
14385       BrowseDocumentationCommand,
14386       VersionCommand
14387     },
14388     ShortCutsCommands[] =
14389     {
14390       NextCommand,
14391       FormerCommand,
14392       OpenCommand,
14393       SaveCommand,
14394       PrintCommand,
14395       UndoCommand,
14396       RestoreCommand,
14397       InfoCommand,
14398       QuitCommand
14399     },
14400     VirtualCommands[] =
14401     {
14402       InfoCommand,
14403       PrintCommand,
14404       NextCommand,
14405       QuitCommand
14406     };
14407
14408   static CommandType
14409     *Commands[MagickMenus] =
14410     {
14411       FileCommands,
14412       EditCommands,
14413       ViewCommands,
14414       TransformCommands,
14415       EnhanceCommands,
14416       EffectsCommands,
14417       FXCommands,
14418       ImageEditCommands,
14419       MiscellanyCommands,
14420       HelpCommands
14421     };
14422
14423   char
14424     command[MaxTextExtent],
14425     *directory,
14426     geometry[MaxTextExtent],
14427     resource_name[MaxTextExtent];
14428
14429   CommandType
14430     command_type;
14431
14432   Image
14433     *display_image,
14434     *nexus;
14435
14436   int
14437     entry,
14438     id;
14439
14440   KeySym
14441     key_symbol;
14442
14443   MagickStatusType
14444     context_mask,
14445     status;
14446
14447   RectangleInfo
14448     geometry_info;
14449
14450   register int
14451     i;
14452
14453   static char
14454     working_directory[MaxTextExtent];
14455
14456   static XPoint
14457     vid_info;
14458
14459   static XWindowInfo
14460     *magick_windows[MaxXWindows];
14461
14462   static unsigned int
14463     number_windows;
14464
14465   struct stat
14466     attributes;
14467
14468   time_t
14469     timer,
14470     timestamp,
14471     update_time;
14472
14473   unsigned int
14474     height,
14475     width;
14476
14477   size_t
14478     delay;
14479
14480   WarningHandler
14481     warning_handler;
14482
14483   Window
14484     root_window;
14485
14486   XClassHint
14487     *class_hints;
14488
14489   XEvent
14490     event;
14491
14492   XFontStruct
14493     *font_info;
14494
14495   XGCValues
14496     context_values;
14497
14498   XPixelInfo
14499     *icon_pixel,
14500     *pixel;
14501
14502   XResourceInfo
14503     *icon_resources;
14504
14505   XStandardColormap
14506     *icon_map,
14507     *map_info;
14508
14509   XVisualInfo
14510     *icon_visual,
14511     *visual_info;
14512
14513   XWindowChanges
14514     window_changes;
14515
14516   XWindows
14517     *windows;
14518
14519   XWMHints
14520     *manager_hints;
14521
14522   assert(image != (Image **) NULL);
14523   assert((*image)->signature == MagickSignature);
14524   if ((*image)->debug != MagickFalse)
14525     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14526   display_image=(*image);
14527   warning_handler=(WarningHandler) NULL;
14528   windows=XSetWindows((XWindows *) ~0);
14529   if (windows != (XWindows *) NULL)
14530     {
14531       int
14532         status;
14533
14534       status=chdir(working_directory);
14535       if (status == -1)
14536         (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
14537           "UnableToOpenFile","%s",working_directory);
14538       warning_handler=resource_info->display_warnings ?
14539         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14540       warning_handler=resource_info->display_warnings ?
14541         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14542     }
14543   else
14544     {
14545       /*
14546         Allocate windows structure.
14547       */
14548       resource_info->colors=display_image->colors;
14549       windows=XSetWindows(XInitializeWindows(display,resource_info));
14550       if (windows == (XWindows *) NULL)
14551         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14552           (*image)->filename);
14553       /*
14554         Initialize window id's.
14555       */
14556       number_windows=0;
14557       magick_windows[number_windows++]=(&windows->icon);
14558       magick_windows[number_windows++]=(&windows->backdrop);
14559       magick_windows[number_windows++]=(&windows->image);
14560       magick_windows[number_windows++]=(&windows->info);
14561       magick_windows[number_windows++]=(&windows->command);
14562       magick_windows[number_windows++]=(&windows->widget);
14563       magick_windows[number_windows++]=(&windows->popup);
14564       magick_windows[number_windows++]=(&windows->magnify);
14565       magick_windows[number_windows++]=(&windows->pan);
14566       for (i=0; i < (int) number_windows; i++)
14567         magick_windows[i]->id=(Window) NULL;
14568       vid_info.x=0;
14569       vid_info.y=0;
14570     }
14571   /*
14572     Initialize font info.
14573   */
14574   if (windows->font_info != (XFontStruct *) NULL)
14575     (void) XFreeFont(display,windows->font_info);
14576   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14577   if (windows->font_info == (XFontStruct *) NULL)
14578     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14579       resource_info->font);
14580   /*
14581     Initialize Standard Colormap.
14582   */
14583   map_info=windows->map_info;
14584   icon_map=windows->icon_map;
14585   visual_info=windows->visual_info;
14586   icon_visual=windows->icon_visual;
14587   pixel=windows->pixel_info;
14588   icon_pixel=windows->icon_pixel;
14589   font_info=windows->font_info;
14590   icon_resources=windows->icon_resources;
14591   class_hints=windows->class_hints;
14592   manager_hints=windows->manager_hints;
14593   root_window=XRootWindow(display,visual_info->screen);
14594   nexus=NewImageList();
14595   if (display_image->debug != MagickFalse)
14596     {
14597       (void) LogMagickEvent(X11Event,GetMagickModule(),
14598         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14599         (double) display_image->scene,(double) display_image->columns,
14600         (double) display_image->rows);
14601       if (display_image->colors != 0)
14602         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14603           display_image->colors);
14604       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14605         display_image->magick);
14606     }
14607   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14608     map_info,pixel,exception);
14609   display_image->taint=MagickFalse;
14610   /*
14611     Initialize graphic context.
14612   */
14613   windows->context.id=(Window) NULL;
14614   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14615     resource_info,&windows->context);
14616   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14617   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14618   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14619   manager_hints->flags=InputHint | StateHint;
14620   manager_hints->input=MagickFalse;
14621   manager_hints->initial_state=WithdrawnState;
14622   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14623     &windows->context);
14624   if (display_image->debug != MagickFalse)
14625     (void) LogMagickEvent(X11Event,GetMagickModule(),
14626       "Window id: 0x%lx (context)",windows->context.id);
14627   context_values.background=pixel->background_color.pixel;
14628   context_values.font=font_info->fid;
14629   context_values.foreground=pixel->foreground_color.pixel;
14630   context_values.graphics_exposures=MagickFalse;
14631   context_mask=(MagickStatusType)
14632     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14633   if (pixel->annotate_context != (GC) NULL)
14634     (void) XFreeGC(display,pixel->annotate_context);
14635   pixel->annotate_context=XCreateGC(display,windows->context.id,
14636     context_mask,&context_values);
14637   if (pixel->annotate_context == (GC) NULL)
14638     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14639       display_image->filename);
14640   context_values.background=pixel->depth_color.pixel;
14641   if (pixel->widget_context != (GC) NULL)
14642     (void) XFreeGC(display,pixel->widget_context);
14643   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14644     &context_values);
14645   if (pixel->widget_context == (GC) NULL)
14646     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14647       display_image->filename);
14648   context_values.background=pixel->foreground_color.pixel;
14649   context_values.foreground=pixel->background_color.pixel;
14650   context_values.plane_mask=context_values.background ^
14651     context_values.foreground;
14652   if (pixel->highlight_context != (GC) NULL)
14653     (void) XFreeGC(display,pixel->highlight_context);
14654   pixel->highlight_context=XCreateGC(display,windows->context.id,
14655     (size_t) (context_mask | GCPlaneMask),&context_values);
14656   if (pixel->highlight_context == (GC) NULL)
14657     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14658       display_image->filename);
14659   (void) XDestroyWindow(display,windows->context.id);
14660   /*
14661     Initialize icon window.
14662   */
14663   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14664     icon_resources,&windows->icon);
14665   windows->icon.geometry=resource_info->icon_geometry;
14666   XBestIconSize(display,&windows->icon,display_image);
14667   windows->icon.attributes.colormap=XDefaultColormap(display,
14668     icon_visual->screen);
14669   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14670   manager_hints->flags=InputHint | StateHint;
14671   manager_hints->input=MagickFalse;
14672   manager_hints->initial_state=IconicState;
14673   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14674     &windows->icon);
14675   if (display_image->debug != MagickFalse)
14676     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14677       windows->icon.id);
14678   /*
14679     Initialize graphic context for icon window.
14680   */
14681   if (icon_pixel->annotate_context != (GC) NULL)
14682     (void) XFreeGC(display,icon_pixel->annotate_context);
14683   context_values.background=icon_pixel->background_color.pixel;
14684   context_values.foreground=icon_pixel->foreground_color.pixel;
14685   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14686     (size_t) (GCBackground | GCForeground),&context_values);
14687   if (icon_pixel->annotate_context == (GC) NULL)
14688     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14689       display_image->filename);
14690   windows->icon.annotate_context=icon_pixel->annotate_context;
14691   /*
14692     Initialize Image window.
14693   */
14694   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14695     &windows->image);
14696   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14697   if (resource_info->use_shared_memory == MagickFalse)
14698     windows->image.shared_memory=MagickFalse;
14699   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14700     {
14701       char
14702         *title;
14703
14704       title=InterpretImageProperties(resource_info->image_info,display_image,
14705         resource_info->title,exception);
14706       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14707       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14708       title=DestroyString(title);
14709     }
14710   else
14711     {
14712       char
14713         filename[MaxTextExtent];
14714
14715       /*
14716         Window name is the base of the filename.
14717       */
14718       GetPathComponent(display_image->magick_filename,TailPath,filename);
14719       if (display_image->scene == 0)
14720         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14721           "%s: %s",MagickPackageName,filename);
14722       else
14723         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14724           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14725           (double) display_image->scene,(double) GetImageListLength(
14726           display_image));
14727       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14728     }
14729   if (resource_info->immutable)
14730     windows->image.immutable=MagickTrue;
14731   windows->image.use_pixmap=resource_info->use_pixmap;
14732   windows->image.geometry=resource_info->image_geometry;
14733   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14734     XDisplayWidth(display,visual_info->screen),
14735     XDisplayHeight(display,visual_info->screen));
14736   geometry_info.width=display_image->columns;
14737   geometry_info.height=display_image->rows;
14738   geometry_info.x=0;
14739   geometry_info.y=0;
14740   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14741     &geometry_info.width,&geometry_info.height);
14742   windows->image.width=(unsigned int) geometry_info.width;
14743   windows->image.height=(unsigned int) geometry_info.height;
14744   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14745     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14746     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14747     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14748   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14749     resource_info,&windows->backdrop);
14750   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14751     {
14752       /*
14753         Initialize backdrop window.
14754       */
14755       windows->backdrop.x=0;
14756       windows->backdrop.y=0;
14757       (void) CloneString(&windows->backdrop.name,"Backdrop");
14758       windows->backdrop.flags=(size_t) (USSize | USPosition);
14759       windows->backdrop.width=(unsigned int)
14760         XDisplayWidth(display,visual_info->screen);
14761       windows->backdrop.height=(unsigned int)
14762         XDisplayHeight(display,visual_info->screen);
14763       windows->backdrop.border_width=0;
14764       windows->backdrop.immutable=MagickTrue;
14765       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14766         ButtonReleaseMask;
14767       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14768         StructureNotifyMask;
14769       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14770       manager_hints->icon_window=windows->icon.id;
14771       manager_hints->input=MagickTrue;
14772       manager_hints->initial_state=resource_info->iconic ? IconicState :
14773         NormalState;
14774       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14775         &windows->backdrop);
14776       if (display_image->debug != MagickFalse)
14777         (void) LogMagickEvent(X11Event,GetMagickModule(),
14778           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14779       (void) XMapWindow(display,windows->backdrop.id);
14780       (void) XClearWindow(display,windows->backdrop.id);
14781       if (windows->image.id != (Window) NULL)
14782         {
14783           (void) XDestroyWindow(display,windows->image.id);
14784           windows->image.id=(Window) NULL;
14785         }
14786       /*
14787         Position image in the center the backdrop.
14788       */
14789       windows->image.flags|=USPosition;
14790       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14791         (windows->image.width/2);
14792       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14793         (windows->image.height/2);
14794     }
14795   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14796   manager_hints->icon_window=windows->icon.id;
14797   manager_hints->input=MagickTrue;
14798   manager_hints->initial_state=resource_info->iconic ? IconicState :
14799     NormalState;
14800   if (windows->group_leader.id != (Window) NULL)
14801     {
14802       /*
14803         Follow the leader.
14804       */
14805       manager_hints->flags|=WindowGroupHint;
14806       manager_hints->window_group=windows->group_leader.id;
14807       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14808       if (display_image->debug != MagickFalse)
14809         (void) LogMagickEvent(X11Event,GetMagickModule(),
14810           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14811     }
14812   XMakeWindow(display,
14813     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14814     argv,argc,class_hints,manager_hints,&windows->image);
14815   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14816     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14817   if (windows->group_leader.id != (Window) NULL)
14818     (void) XSetTransientForHint(display,windows->image.id,
14819       windows->group_leader.id);
14820   if (display_image->debug != MagickFalse)
14821     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14822       windows->image.id);
14823   /*
14824     Initialize Info widget.
14825   */
14826   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14827     &windows->info);
14828   (void) CloneString(&windows->info.name,"Info");
14829   (void) CloneString(&windows->info.icon_name,"Info");
14830   windows->info.border_width=1;
14831   windows->info.x=2;
14832   windows->info.y=2;
14833   windows->info.flags|=PPosition;
14834   windows->info.attributes.win_gravity=UnmapGravity;
14835   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14836     StructureNotifyMask;
14837   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14838   manager_hints->input=MagickFalse;
14839   manager_hints->initial_state=NormalState;
14840   manager_hints->window_group=windows->image.id;
14841   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14842     &windows->info);
14843   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14844     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14845   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14846     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14847   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14848   if (windows->image.mapped != MagickFalse)
14849     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14850   if (display_image->debug != MagickFalse)
14851     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14852       windows->info.id);
14853   /*
14854     Initialize Command widget.
14855   */
14856   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14857     resource_info,&windows->command);
14858   windows->command.data=MagickMenus;
14859   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14860   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14861     resource_info->client_name);
14862   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14863     resource_name,"geometry",(char *) NULL);
14864   (void) CloneString(&windows->command.name,MagickTitle);
14865   windows->command.border_width=0;
14866   windows->command.flags|=PPosition;
14867   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14868     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14869     OwnerGrabButtonMask | StructureNotifyMask;
14870   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14871   manager_hints->input=MagickTrue;
14872   manager_hints->initial_state=NormalState;
14873   manager_hints->window_group=windows->image.id;
14874   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14875     &windows->command);
14876   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14877     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14878     HighlightHeight);
14879   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14880     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14881   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14882   if (windows->command.mapped != MagickFalse)
14883     (void) XMapRaised(display,windows->command.id);
14884   if (display_image->debug != MagickFalse)
14885     (void) LogMagickEvent(X11Event,GetMagickModule(),
14886       "Window id: 0x%lx (command)",windows->command.id);
14887   /*
14888     Initialize Widget window.
14889   */
14890   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14891     resource_info,&windows->widget);
14892   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14893     resource_info->client_name);
14894   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14895     resource_name,"geometry",(char *) NULL);
14896   windows->widget.border_width=0;
14897   windows->widget.flags|=PPosition;
14898   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14899     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14900     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14901     StructureNotifyMask;
14902   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14903   manager_hints->input=MagickTrue;
14904   manager_hints->initial_state=NormalState;
14905   manager_hints->window_group=windows->image.id;
14906   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14907     &windows->widget);
14908   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14909     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14910   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14911     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14912   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14913   if (display_image->debug != MagickFalse)
14914     (void) LogMagickEvent(X11Event,GetMagickModule(),
14915       "Window id: 0x%lx (widget)",windows->widget.id);
14916   /*
14917     Initialize popup window.
14918   */
14919   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14920     resource_info,&windows->popup);
14921   windows->popup.border_width=0;
14922   windows->popup.flags|=PPosition;
14923   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14924     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14925     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14926   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14927   manager_hints->input=MagickTrue;
14928   manager_hints->initial_state=NormalState;
14929   manager_hints->window_group=windows->image.id;
14930   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14931     &windows->popup);
14932   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14933     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14934   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14935     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14936   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14937   if (display_image->debug != MagickFalse)
14938     (void) LogMagickEvent(X11Event,GetMagickModule(),
14939       "Window id: 0x%lx (pop up)",windows->popup.id);
14940   /*
14941     Initialize Magnify window and cursor.
14942   */
14943   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14944     resource_info,&windows->magnify);
14945   if (resource_info->use_shared_memory == MagickFalse)
14946     windows->magnify.shared_memory=MagickFalse;
14947   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14948     resource_info->client_name);
14949   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14950     resource_name,"geometry",(char *) NULL);
14951   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14952     resource_info->magnify);
14953   if (windows->magnify.cursor != (Cursor) NULL)
14954     (void) XFreeCursor(display,windows->magnify.cursor);
14955   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14956     map_info->colormap,resource_info->background_color,
14957     resource_info->foreground_color);
14958   if (windows->magnify.cursor == (Cursor) NULL)
14959     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14960       display_image->filename);
14961   windows->magnify.width=MagnifySize;
14962   windows->magnify.height=MagnifySize;
14963   windows->magnify.flags|=PPosition;
14964   windows->magnify.min_width=MagnifySize;
14965   windows->magnify.min_height=MagnifySize;
14966   windows->magnify.width_inc=MagnifySize;
14967   windows->magnify.height_inc=MagnifySize;
14968   windows->magnify.data=resource_info->magnify;
14969   windows->magnify.attributes.cursor=windows->magnify.cursor;
14970   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14971     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14972     StructureNotifyMask;
14973   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14974   manager_hints->input=MagickTrue;
14975   manager_hints->initial_state=NormalState;
14976   manager_hints->window_group=windows->image.id;
14977   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14978     &windows->magnify);
14979   if (display_image->debug != MagickFalse)
14980     (void) LogMagickEvent(X11Event,GetMagickModule(),
14981       "Window id: 0x%lx (magnify)",windows->magnify.id);
14982   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14983   /*
14984     Initialize panning window.
14985   */
14986   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14987     resource_info,&windows->pan);
14988   (void) CloneString(&windows->pan.name,"Pan Icon");
14989   windows->pan.width=windows->icon.width;
14990   windows->pan.height=windows->icon.height;
14991   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14992     resource_info->client_name);
14993   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14994     resource_name,"geometry",(char *) NULL);
14995   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14996     &windows->pan.width,&windows->pan.height);
14997   windows->pan.flags|=PPosition;
14998   windows->pan.immutable=MagickTrue;
14999   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
15000     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
15001     StructureNotifyMask;
15002   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
15003   manager_hints->input=MagickFalse;
15004   manager_hints->initial_state=NormalState;
15005   manager_hints->window_group=windows->image.id;
15006   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
15007     &windows->pan);
15008   if (display_image->debug != MagickFalse)
15009     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
15010       windows->pan.id);
15011   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
15012   if (windows->info.mapped != MagickFalse)
15013     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15014   if ((windows->image.mapped == MagickFalse) ||
15015       (windows->backdrop.id != (Window) NULL))
15016     (void) XMapWindow(display,windows->image.id);
15017   /*
15018     Set our progress monitor and warning handlers.
15019   */
15020   if (warning_handler == (WarningHandler) NULL)
15021     {
15022       warning_handler=resource_info->display_warnings ?
15023         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
15024       warning_handler=resource_info->display_warnings ?
15025         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
15026     }
15027   /*
15028     Initialize Image and Magnify X images.
15029   */
15030   windows->image.x=0;
15031   windows->image.y=0;
15032   windows->magnify.shape=MagickFalse;
15033   width=(unsigned int) display_image->columns;
15034   height=(unsigned int) display_image->rows;
15035   if ((display_image->columns != width) || (display_image->rows != height))
15036     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15037       display_image->filename);
15038   status=XMakeImage(display,resource_info,&windows->image,display_image,
15039     width,height,exception);
15040   if (status == MagickFalse)
15041     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15042       display_image->filename);
15043   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
15044     windows->magnify.width,windows->magnify.height,exception);
15045   if (status == MagickFalse)
15046     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
15047       display_image->filename);
15048   if (windows->magnify.mapped != MagickFalse)
15049     (void) XMapRaised(display,windows->magnify.id);
15050   if (windows->pan.mapped != MagickFalse)
15051     (void) XMapRaised(display,windows->pan.id);
15052   windows->image.window_changes.width=(int) display_image->columns;
15053   windows->image.window_changes.height=(int) display_image->rows;
15054   (void) XConfigureImage(display,resource_info,windows,display_image,exception);
15055   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15056   (void) XSync(display,MagickFalse);
15057   /*
15058     Respond to events.
15059   */
15060   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
15061   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15062   update_time=0;
15063   if (resource_info->update != MagickFalse)
15064     {
15065       MagickBooleanType
15066         status;
15067
15068       /*
15069         Determine when file data was last modified.
15070       */
15071       status=GetPathAttributes(display_image->filename,&attributes);
15072       if (status != MagickFalse)
15073         update_time=attributes.st_mtime;
15074     }
15075   *state&=(~FormerImageState);
15076   *state&=(~MontageImageState);
15077   *state&=(~NextImageState);
15078   do
15079   {
15080     /*
15081       Handle a window event.
15082     */
15083     if (windows->image.mapped != MagickFalse)
15084       if ((display_image->delay != 0) || (resource_info->update != 0))
15085         {
15086           if (timer < time((time_t *) NULL))
15087             {
15088               if (resource_info->update == MagickFalse)
15089                 *state|=NextImageState | ExitState;
15090               else
15091                 {
15092                   MagickBooleanType
15093                     status;
15094
15095                   /*
15096                     Determine if image file was modified.
15097                   */
15098                   status=GetPathAttributes(display_image->filename,&attributes);
15099                   if (status != MagickFalse)
15100                     if (update_time != attributes.st_mtime)
15101                       {
15102                         /*
15103                           Redisplay image.
15104                         */
15105                         (void) FormatLocaleString(
15106                           resource_info->image_info->filename,MaxTextExtent,
15107                           "%s:%s",display_image->magick,
15108                           display_image->filename);
15109                         nexus=ReadImage(resource_info->image_info,exception);
15110                         if (nexus != (Image *) NULL)
15111                           {
15112                             nexus=DestroyImage(nexus);
15113                             *state|=NextImageState | ExitState;
15114                           }
15115                       }
15116                   delay=display_image->delay/MagickMax(
15117                     display_image->ticks_per_second,1L);
15118                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15119                 }
15120             }
15121           if (XEventsQueued(display,QueuedAfterFlush) == 0)
15122             {
15123               /*
15124                 Do not block if delay > 0.
15125               */
15126               XDelay(display,SuspendTime << 2);
15127               continue;
15128             }
15129         }
15130     timestamp=time((time_t *) NULL);
15131     (void) XNextEvent(display,&event);
15132     if (windows->image.stasis == MagickFalse)
15133       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15134         MagickTrue : MagickFalse;
15135     if (windows->magnify.stasis == MagickFalse)
15136       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
15137         MagickTrue : MagickFalse;
15138     if (event.xany.window == windows->command.id)
15139       {
15140         /*
15141           Select a command from the Command widget.
15142         */
15143         id=XCommandWidget(display,windows,CommandMenu,&event);
15144         if (id < 0)
15145           continue;
15146         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
15147         command_type=CommandMenus[id];
15148         if (id < MagickMenus)
15149           {
15150             /*
15151               Select a command from a pop-up menu.
15152             */
15153             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
15154               command);
15155             if (entry < 0)
15156               continue;
15157             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15158             command_type=Commands[id][entry];
15159           }
15160         if (command_type != NullCommand)
15161           nexus=XMagickCommand(display,resource_info,windows,command_type,
15162             &display_image,exception);
15163         continue;
15164       }
15165     switch (event.type)
15166     {
15167       case ButtonPress:
15168       {
15169         if (display_image->debug != MagickFalse)
15170           (void) LogMagickEvent(X11Event,GetMagickModule(),
15171             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15172             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15173         if ((event.xbutton.button == Button3) &&
15174             (event.xbutton.state & Mod1Mask))
15175           {
15176             /*
15177               Convert Alt-Button3 to Button2.
15178             */
15179             event.xbutton.button=Button2;
15180             event.xbutton.state&=(~Mod1Mask);
15181           }
15182         if (event.xbutton.window == windows->backdrop.id)
15183           {
15184             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15185               event.xbutton.time);
15186             break;
15187           }
15188         if (event.xbutton.window == windows->image.id)
15189           {
15190             switch (event.xbutton.button)
15191             {
15192               case Button1:
15193               {
15194                 if (resource_info->immutable)
15195                   {
15196                     /*
15197                       Select a command from the Virtual menu.
15198                     */
15199                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15200                       command);
15201                     if (entry >= 0)
15202                       nexus=XMagickCommand(display,resource_info,windows,
15203                         VirtualCommands[entry],&display_image,exception);
15204                     break;
15205                   }
15206                 /*
15207                   Map/unmap Command widget.
15208                 */
15209                 if (windows->command.mapped != MagickFalse)
15210                   (void) XWithdrawWindow(display,windows->command.id,
15211                     windows->command.screen);
15212                 else
15213                   {
15214                     (void) XCommandWidget(display,windows,CommandMenu,
15215                       (XEvent *) NULL);
15216                     (void) XMapRaised(display,windows->command.id);
15217                   }
15218                 break;
15219               }
15220               case Button2:
15221               {
15222                 /*
15223                   User pressed the image magnify button.
15224                 */
15225                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15226                   &display_image,exception);
15227                 XMagnifyImage(display,windows,&event,exception);
15228                 break;
15229               }
15230               case Button3:
15231               {
15232                 if (resource_info->immutable)
15233                   {
15234                     /*
15235                       Select a command from the Virtual menu.
15236                     */
15237                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15238                       command);
15239                     if (entry >= 0)
15240                       nexus=XMagickCommand(display,resource_info,windows,
15241                         VirtualCommands[entry],&display_image,exception);
15242                     break;
15243                   }
15244                 if (display_image->montage != (char *) NULL)
15245                   {
15246                     /*
15247                       Open or delete a tile from a visual image directory.
15248                     */
15249                     nexus=XTileImage(display,resource_info,windows,
15250                       display_image,&event,exception);
15251                     if (nexus != (Image *) NULL)
15252                       *state|=MontageImageState | NextImageState | ExitState;
15253                     vid_info.x=(short int) windows->image.x;
15254                     vid_info.y=(short int) windows->image.y;
15255                     break;
15256                   }
15257                 /*
15258                   Select a command from the Short Cuts menu.
15259                 */
15260                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15261                   command);
15262                 if (entry >= 0)
15263                   nexus=XMagickCommand(display,resource_info,windows,
15264                     ShortCutsCommands[entry],&display_image,exception);
15265                 break;
15266               }
15267               case Button4:
15268               {
15269                 /*
15270                   Wheel up.
15271                 */
15272                 XTranslateImage(display,windows,*image,XK_Up);
15273                 break;
15274               }
15275               case Button5:
15276               {
15277                 /*
15278                   Wheel down.
15279                 */
15280                 XTranslateImage(display,windows,*image,XK_Down);
15281                 break;
15282               }
15283               default:
15284                 break;
15285             }
15286             break;
15287           }
15288         if (event.xbutton.window == windows->magnify.id)
15289           {
15290             int
15291               factor;
15292
15293             static const char
15294               *MagnifyMenu[] =
15295               {
15296                 "2",
15297                 "4",
15298                 "5",
15299                 "6",
15300                 "7",
15301                 "8",
15302                 "9",
15303                 "3",
15304                 (char *) NULL,
15305               };
15306
15307             static KeySym
15308               MagnifyCommands[] =
15309               {
15310                 XK_2,
15311                 XK_4,
15312                 XK_5,
15313                 XK_6,
15314                 XK_7,
15315                 XK_8,
15316                 XK_9,
15317                 XK_3
15318               };
15319
15320             /*
15321               Select a magnify factor from the pop-up menu.
15322             */
15323             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15324             if (factor >= 0)
15325               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor],
15326                 exception);
15327             break;
15328           }
15329         if (event.xbutton.window == windows->pan.id)
15330           {
15331             switch (event.xbutton.button)
15332             {
15333               case Button4:
15334               {
15335                 /*
15336                   Wheel up.
15337                 */
15338                 XTranslateImage(display,windows,*image,XK_Up);
15339                 break;
15340               }
15341               case Button5:
15342               {
15343                 /*
15344                   Wheel down.
15345                 */
15346                 XTranslateImage(display,windows,*image,XK_Down);
15347                 break;
15348               }
15349               default:
15350               {
15351                 XPanImage(display,windows,&event,exception);
15352                 break;
15353               }
15354             }
15355             break;
15356           }
15357         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15358           1L);
15359         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15360         break;
15361       }
15362       case ButtonRelease:
15363       {
15364         if (display_image->debug != MagickFalse)
15365           (void) LogMagickEvent(X11Event,GetMagickModule(),
15366             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15367             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15368         break;
15369       }
15370       case ClientMessage:
15371       {
15372         if (display_image->debug != MagickFalse)
15373           (void) LogMagickEvent(X11Event,GetMagickModule(),
15374             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15375             event.xclient.message_type,event.xclient.format,(unsigned long)
15376             event.xclient.data.l[0]);
15377         if (event.xclient.message_type == windows->im_protocols)
15378           {
15379             if (*event.xclient.data.l == (long) windows->im_update_widget)
15380               {
15381                 (void) CloneString(&windows->command.name,MagickTitle);
15382                 windows->command.data=MagickMenus;
15383                 (void) XCommandWidget(display,windows,CommandMenu,
15384                   (XEvent *) NULL);
15385                 break;
15386               }
15387             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15388               {
15389                 /*
15390                   Update graphic context and window colormap.
15391                 */
15392                 for (i=0; i < (int) number_windows; i++)
15393                 {
15394                   if (magick_windows[i]->id == windows->icon.id)
15395                     continue;
15396                   context_values.background=pixel->background_color.pixel;
15397                   context_values.foreground=pixel->foreground_color.pixel;
15398                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15399                     context_mask,&context_values);
15400                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15401                     context_mask,&context_values);
15402                   context_values.background=pixel->foreground_color.pixel;
15403                   context_values.foreground=pixel->background_color.pixel;
15404                   context_values.plane_mask=context_values.background ^
15405                     context_values.foreground;
15406                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15407                     (size_t) (context_mask | GCPlaneMask),
15408                     &context_values);
15409                   magick_windows[i]->attributes.background_pixel=
15410                     pixel->background_color.pixel;
15411                   magick_windows[i]->attributes.border_pixel=
15412                     pixel->border_color.pixel;
15413                   magick_windows[i]->attributes.colormap=map_info->colormap;
15414                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15415                     (unsigned long) magick_windows[i]->mask,
15416                     &magick_windows[i]->attributes);
15417                 }
15418                 if (windows->pan.mapped != MagickFalse)
15419                   {
15420                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15421                       windows->pan.pixmap);
15422                     (void) XClearWindow(display,windows->pan.id);
15423                     XDrawPanRectangle(display,windows);
15424                   }
15425                 if (windows->backdrop.id != (Window) NULL)
15426                   (void) XInstallColormap(display,map_info->colormap);
15427                 break;
15428               }
15429             if (*event.xclient.data.l == (long) windows->im_former_image)
15430               {
15431                 *state|=FormerImageState | ExitState;
15432                 break;
15433               }
15434             if (*event.xclient.data.l == (long) windows->im_next_image)
15435               {
15436                 *state|=NextImageState | ExitState;
15437                 break;
15438               }
15439             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15440               {
15441                 *state|=RetainColorsState;
15442                 break;
15443               }
15444             if (*event.xclient.data.l == (long) windows->im_exit)
15445               {
15446                 *state|=ExitState;
15447                 break;
15448               }
15449             break;
15450           }
15451         if (event.xclient.message_type == windows->dnd_protocols)
15452           {
15453             Atom
15454               selection,
15455               type;
15456
15457             int
15458               format,
15459               status;
15460
15461             unsigned char
15462               *data;
15463
15464             unsigned long
15465               after,
15466               length;
15467
15468             /*
15469               Display image named by the Drag-and-Drop selection.
15470             */
15471             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15472               break;
15473             selection=XInternAtom(display,"DndSelection",MagickFalse);
15474             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15475               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15476               &length,&after,&data);
15477             if ((status != Success) || (length == 0))
15478               break;
15479             if (*event.xclient.data.l == 2)
15480               {
15481                 /*
15482                   Offix DND.
15483                 */
15484                 (void) CopyMagickString(resource_info->image_info->filename,
15485                   (char *) data,MaxTextExtent);
15486               }
15487             else
15488               {
15489                 /*
15490                   XDND.
15491                 */
15492                 if (strncmp((char *) data, "file:", 5) != 0)
15493                   {
15494                     (void) XFree((void *) data);
15495                     break;
15496                   }
15497                 (void) CopyMagickString(resource_info->image_info->filename,
15498                   ((char *) data)+5,MaxTextExtent);
15499               }
15500             nexus=ReadImage(resource_info->image_info,exception);
15501             CatchException(exception);
15502             if (nexus != (Image *) NULL)
15503               *state|=NextImageState | ExitState;
15504             (void) XFree((void *) data);
15505             break;
15506           }
15507         /*
15508           If client window delete message, exit.
15509         */
15510         if (event.xclient.message_type != windows->wm_protocols)
15511           break;
15512         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15513           break;
15514         (void) XWithdrawWindow(display,event.xclient.window,
15515           visual_info->screen);
15516         if (event.xclient.window == windows->image.id)
15517           {
15518             *state|=ExitState;
15519             break;
15520           }
15521         if (event.xclient.window == windows->pan.id)
15522           {
15523             /*
15524               Restore original image size when pan window is deleted.
15525             */
15526             windows->image.window_changes.width=windows->image.ximage->width;
15527             windows->image.window_changes.height=windows->image.ximage->height;
15528             (void) XConfigureImage(display,resource_info,windows,
15529               display_image,exception);
15530           }
15531         break;
15532       }
15533       case ConfigureNotify:
15534       {
15535         if (display_image->debug != MagickFalse)
15536           (void) LogMagickEvent(X11Event,GetMagickModule(),
15537             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15538             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15539             event.xconfigure.y,event.xconfigure.send_event);
15540         if (event.xconfigure.window == windows->image.id)
15541           {
15542             /*
15543               Image window has a new configuration.
15544             */
15545             if (event.xconfigure.send_event != 0)
15546               {
15547                 XWindowChanges
15548                   window_changes;
15549
15550                 /*
15551                   Position the transient windows relative of the Image window.
15552                 */
15553                 if (windows->command.geometry == (char *) NULL)
15554                   if (windows->command.mapped == MagickFalse)
15555                     {
15556                       windows->command.x=event.xconfigure.x-
15557                         windows->command.width-25;
15558                       windows->command.y=event.xconfigure.y;
15559                       XConstrainWindowPosition(display,&windows->command);
15560                       window_changes.x=windows->command.x;
15561                       window_changes.y=windows->command.y;
15562                       (void) XReconfigureWMWindow(display,windows->command.id,
15563                         windows->command.screen,(unsigned int) (CWX | CWY),
15564                         &window_changes);
15565                     }
15566                 if (windows->widget.geometry == (char *) NULL)
15567                   if (windows->widget.mapped == MagickFalse)
15568                     {
15569                       windows->widget.x=event.xconfigure.x+
15570                         event.xconfigure.width/10;
15571                       windows->widget.y=event.xconfigure.y+
15572                         event.xconfigure.height/10;
15573                       XConstrainWindowPosition(display,&windows->widget);
15574                       window_changes.x=windows->widget.x;
15575                       window_changes.y=windows->widget.y;
15576                       (void) XReconfigureWMWindow(display,windows->widget.id,
15577                         windows->widget.screen,(unsigned int) (CWX | CWY),
15578                         &window_changes);
15579                     }
15580                 if (windows->magnify.geometry == (char *) NULL)
15581                   if (windows->magnify.mapped == MagickFalse)
15582                     {
15583                       windows->magnify.x=event.xconfigure.x+
15584                         event.xconfigure.width+25;
15585                       windows->magnify.y=event.xconfigure.y;
15586                       XConstrainWindowPosition(display,&windows->magnify);
15587                       window_changes.x=windows->magnify.x;
15588                       window_changes.y=windows->magnify.y;
15589                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15590                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15591                         &window_changes);
15592                     }
15593                 if (windows->pan.geometry == (char *) NULL)
15594                   if (windows->pan.mapped == MagickFalse)
15595                     {
15596                       windows->pan.x=event.xconfigure.x+
15597                         event.xconfigure.width+25;
15598                       windows->pan.y=event.xconfigure.y+
15599                         windows->magnify.height+50;
15600                       XConstrainWindowPosition(display,&windows->pan);
15601                       window_changes.x=windows->pan.x;
15602                       window_changes.y=windows->pan.y;
15603                       (void) XReconfigureWMWindow(display,windows->pan.id,
15604                         windows->pan.screen,(unsigned int) (CWX | CWY),
15605                         &window_changes);
15606                     }
15607               }
15608             if ((event.xconfigure.width == (int) windows->image.width) &&
15609                 (event.xconfigure.height == (int) windows->image.height))
15610               break;
15611             windows->image.width=(unsigned int) event.xconfigure.width;
15612             windows->image.height=(unsigned int) event.xconfigure.height;
15613             windows->image.x=0;
15614             windows->image.y=0;
15615             if (display_image->montage != (char *) NULL)
15616               {
15617                 windows->image.x=vid_info.x;
15618                 windows->image.y=vid_info.y;
15619               }
15620             if ((windows->image.mapped != MagickFalse) &&
15621                 (windows->image.stasis != MagickFalse))
15622               {
15623                 /*
15624                   Update image window configuration.
15625                 */
15626                 windows->image.window_changes.width=event.xconfigure.width;
15627                 windows->image.window_changes.height=event.xconfigure.height;
15628                 (void) XConfigureImage(display,resource_info,windows,
15629                   display_image,exception);
15630               }
15631             /*
15632               Update pan window configuration.
15633             */
15634             if ((event.xconfigure.width < windows->image.ximage->width) ||
15635                 (event.xconfigure.height < windows->image.ximage->height))
15636               {
15637                 (void) XMapRaised(display,windows->pan.id);
15638                 XDrawPanRectangle(display,windows);
15639               }
15640             else
15641               if (windows->pan.mapped != MagickFalse)
15642                 (void) XWithdrawWindow(display,windows->pan.id,
15643                   windows->pan.screen);
15644             break;
15645           }
15646         if (event.xconfigure.window == windows->magnify.id)
15647           {
15648             unsigned int
15649               magnify;
15650
15651             /*
15652               Magnify window has a new configuration.
15653             */
15654             windows->magnify.width=(unsigned int) event.xconfigure.width;
15655             windows->magnify.height=(unsigned int) event.xconfigure.height;
15656             if (windows->magnify.mapped == MagickFalse)
15657               break;
15658             magnify=1;
15659             while ((int) magnify <= event.xconfigure.width)
15660               magnify<<=1;
15661             while ((int) magnify <= event.xconfigure.height)
15662               magnify<<=1;
15663             magnify>>=1;
15664             if (((int) magnify != event.xconfigure.width) ||
15665                 ((int) magnify != event.xconfigure.height))
15666               {
15667                 window_changes.width=(int) magnify;
15668                 window_changes.height=(int) magnify;
15669                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15670                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15671                   &window_changes);
15672                 break;
15673               }
15674             if ((windows->magnify.mapped != MagickFalse) &&
15675                 (windows->magnify.stasis != MagickFalse))
15676               {
15677                 status=XMakeImage(display,resource_info,&windows->magnify,
15678                   display_image,windows->magnify.width,windows->magnify.height,
15679                   exception);
15680                 XMakeMagnifyImage(display,windows,exception);
15681               }
15682             break;
15683           }
15684         if ((windows->magnify.mapped != MagickFalse) &&
15685             (event.xconfigure.window == windows->pan.id))
15686           {
15687             /*
15688               Pan icon window has a new configuration.
15689             */
15690             if (event.xconfigure.send_event != 0)
15691               {
15692                 windows->pan.x=event.xconfigure.x;
15693                 windows->pan.y=event.xconfigure.y;
15694               }
15695             windows->pan.width=(unsigned int) event.xconfigure.width;
15696             windows->pan.height=(unsigned int) event.xconfigure.height;
15697             break;
15698           }
15699         if (event.xconfigure.window == windows->icon.id)
15700           {
15701             /*
15702               Icon window has a new configuration.
15703             */
15704             windows->icon.width=(unsigned int) event.xconfigure.width;
15705             windows->icon.height=(unsigned int) event.xconfigure.height;
15706             break;
15707           }
15708         break;
15709       }
15710       case DestroyNotify:
15711       {
15712         /*
15713           Group leader has exited.
15714         */
15715         if (display_image->debug != MagickFalse)
15716           (void) LogMagickEvent(X11Event,GetMagickModule(),
15717             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15718         if (event.xdestroywindow.window == windows->group_leader.id)
15719           {
15720             *state|=ExitState;
15721             break;
15722           }
15723         break;
15724       }
15725       case EnterNotify:
15726       {
15727         /*
15728           Selectively install colormap.
15729         */
15730         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15731           if (event.xcrossing.mode != NotifyUngrab)
15732             XInstallColormap(display,map_info->colormap);
15733         break;
15734       }
15735       case Expose:
15736       {
15737         if (display_image->debug != MagickFalse)
15738           (void) LogMagickEvent(X11Event,GetMagickModule(),
15739             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15740             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15741             event.xexpose.y);
15742         /*
15743           Refresh windows that are now exposed.
15744         */
15745         if ((event.xexpose.window == windows->image.id) &&
15746             (windows->image.mapped != MagickFalse))
15747           {
15748             XRefreshWindow(display,&windows->image,&event);
15749             delay=display_image->delay/MagickMax(
15750               display_image->ticks_per_second,1L);
15751             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15752             break;
15753           }
15754         if ((event.xexpose.window == windows->magnify.id) &&
15755             (windows->magnify.mapped != MagickFalse))
15756           {
15757             XMakeMagnifyImage(display,windows,exception);
15758             break;
15759           }
15760         if (event.xexpose.window == windows->pan.id)
15761           {
15762             XDrawPanRectangle(display,windows);
15763             break;
15764           }
15765         if (event.xexpose.window == windows->icon.id)
15766           {
15767             XRefreshWindow(display,&windows->icon,&event);
15768             break;
15769           }
15770         break;
15771       }
15772       case KeyPress:
15773       {
15774         int
15775           length;
15776
15777         /*
15778           Respond to a user key press.
15779         */
15780         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15781           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15782         *(command+length)='\0';
15783         if (display_image->debug != MagickFalse)
15784           (void) LogMagickEvent(X11Event,GetMagickModule(),
15785             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15786             key_symbol,command);
15787         if (event.xkey.window == windows->image.id)
15788           {
15789             command_type=XImageWindowCommand(display,resource_info,windows,
15790               event.xkey.state,key_symbol,&display_image,exception);
15791             if (command_type != NullCommand)
15792               nexus=XMagickCommand(display,resource_info,windows,command_type,
15793                 &display_image,exception);
15794           }
15795         if (event.xkey.window == windows->magnify.id)
15796           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol,
15797             exception);
15798         if (event.xkey.window == windows->pan.id)
15799           {
15800             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15801               (void) XWithdrawWindow(display,windows->pan.id,
15802                 windows->pan.screen);
15803             else
15804               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15805                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15806                   "Help Viewer - Image Pan",ImagePanHelp);
15807               else
15808                 XTranslateImage(display,windows,*image,key_symbol);
15809           }
15810         delay=display_image->delay/MagickMax(
15811           display_image->ticks_per_second,1L);
15812         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15813         break;
15814       }
15815       case KeyRelease:
15816       {
15817         /*
15818           Respond to a user key release.
15819         */
15820         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15821           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15822         if (display_image->debug != MagickFalse)
15823           (void) LogMagickEvent(X11Event,GetMagickModule(),
15824             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15825         break;
15826       }
15827       case LeaveNotify:
15828       {
15829         /*
15830           Selectively uninstall colormap.
15831         */
15832         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15833           if (event.xcrossing.mode != NotifyUngrab)
15834             XUninstallColormap(display,map_info->colormap);
15835         break;
15836       }
15837       case MapNotify:
15838       {
15839         if (display_image->debug != MagickFalse)
15840           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15841             event.xmap.window);
15842         if (event.xmap.window == windows->backdrop.id)
15843           {
15844             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15845               CurrentTime);
15846             windows->backdrop.mapped=MagickTrue;
15847             break;
15848           }
15849         if (event.xmap.window == windows->image.id)
15850           {
15851             if (windows->backdrop.id != (Window) NULL)
15852               (void) XInstallColormap(display,map_info->colormap);
15853             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15854               {
15855                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15856                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15857               }
15858             if (((int) windows->image.width < windows->image.ximage->width) ||
15859                 ((int) windows->image.height < windows->image.ximage->height))
15860               (void) XMapRaised(display,windows->pan.id);
15861             windows->image.mapped=MagickTrue;
15862             break;
15863           }
15864         if (event.xmap.window == windows->magnify.id)
15865           {
15866             XMakeMagnifyImage(display,windows,exception);
15867             windows->magnify.mapped=MagickTrue;
15868             (void) XWithdrawWindow(display,windows->info.id,
15869               windows->info.screen);
15870             break;
15871           }
15872         if (event.xmap.window == windows->pan.id)
15873           {
15874             XMakePanImage(display,resource_info,windows,display_image,
15875               exception);
15876             windows->pan.mapped=MagickTrue;
15877             break;
15878           }
15879         if (event.xmap.window == windows->info.id)
15880           {
15881             windows->info.mapped=MagickTrue;
15882             break;
15883           }
15884         if (event.xmap.window == windows->icon.id)
15885           {
15886             MagickBooleanType
15887               taint;
15888
15889             /*
15890               Create an icon image.
15891             */
15892             taint=display_image->taint;
15893             XMakeStandardColormap(display,icon_visual,icon_resources,
15894               display_image,icon_map,icon_pixel,exception);
15895             (void) XMakeImage(display,icon_resources,&windows->icon,
15896               display_image,windows->icon.width,windows->icon.height,
15897               exception);
15898             display_image->taint=taint;
15899             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15900               windows->icon.pixmap);
15901             (void) XClearWindow(display,windows->icon.id);
15902             (void) XWithdrawWindow(display,windows->info.id,
15903               windows->info.screen);
15904             windows->icon.mapped=MagickTrue;
15905             break;
15906           }
15907         if (event.xmap.window == windows->command.id)
15908           {
15909             windows->command.mapped=MagickTrue;
15910             break;
15911           }
15912         if (event.xmap.window == windows->popup.id)
15913           {
15914             windows->popup.mapped=MagickTrue;
15915             break;
15916           }
15917         if (event.xmap.window == windows->widget.id)
15918           {
15919             windows->widget.mapped=MagickTrue;
15920             break;
15921           }
15922         break;
15923       }
15924       case MappingNotify:
15925       {
15926         (void) XRefreshKeyboardMapping(&event.xmapping);
15927         break;
15928       }
15929       case NoExpose:
15930         break;
15931       case PropertyNotify:
15932       {
15933         Atom
15934           type;
15935
15936         int
15937           format,
15938           status;
15939
15940         unsigned char
15941           *data;
15942
15943         unsigned long
15944           after,
15945           length;
15946
15947         if (display_image->debug != MagickFalse)
15948           (void) LogMagickEvent(X11Event,GetMagickModule(),
15949             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15950             event.xproperty.atom,event.xproperty.state);
15951         if (event.xproperty.atom != windows->im_remote_command)
15952           break;
15953         /*
15954           Display image named by the remote command protocol.
15955         */
15956         status=XGetWindowProperty(display,event.xproperty.window,
15957           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15958           AnyPropertyType,&type,&format,&length,&after,&data);
15959         if ((status != Success) || (length == 0))
15960           break;
15961         if (LocaleCompare((char *) data,"-quit") == 0)
15962           {
15963             XClientMessage(display,windows->image.id,windows->im_protocols,
15964               windows->im_exit,CurrentTime);
15965             (void) XFree((void *) data);
15966             break;
15967           }
15968         (void) CopyMagickString(resource_info->image_info->filename,
15969           (char *) data,MaxTextExtent);
15970         (void) XFree((void *) data);
15971         nexus=ReadImage(resource_info->image_info,exception);
15972         CatchException(exception);
15973         if (nexus != (Image *) NULL)
15974           *state|=NextImageState | ExitState;
15975         break;
15976       }
15977       case ReparentNotify:
15978       {
15979         if (display_image->debug != MagickFalse)
15980           (void) LogMagickEvent(X11Event,GetMagickModule(),
15981             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15982             event.xreparent.window);
15983         break;
15984       }
15985       case UnmapNotify:
15986       {
15987         if (display_image->debug != MagickFalse)
15988           (void) LogMagickEvent(X11Event,GetMagickModule(),
15989             "Unmap Notify: 0x%lx",event.xunmap.window);
15990         if (event.xunmap.window == windows->backdrop.id)
15991           {
15992             windows->backdrop.mapped=MagickFalse;
15993             break;
15994           }
15995         if (event.xunmap.window == windows->image.id)
15996           {
15997             windows->image.mapped=MagickFalse;
15998             break;
15999           }
16000         if (event.xunmap.window == windows->magnify.id)
16001           {
16002             windows->magnify.mapped=MagickFalse;
16003             break;
16004           }
16005         if (event.xunmap.window == windows->pan.id)
16006           {
16007             windows->pan.mapped=MagickFalse;
16008             break;
16009           }
16010         if (event.xunmap.window == windows->info.id)
16011           {
16012             windows->info.mapped=MagickFalse;
16013             break;
16014           }
16015         if (event.xunmap.window == windows->icon.id)
16016           {
16017             if (map_info->colormap == icon_map->colormap)
16018               XConfigureImageColormap(display,resource_info,windows,
16019                 display_image,exception);
16020             (void) XFreeStandardColormap(display,icon_visual,icon_map,
16021               icon_pixel);
16022             windows->icon.mapped=MagickFalse;
16023             break;
16024           }
16025         if (event.xunmap.window == windows->command.id)
16026           {
16027             windows->command.mapped=MagickFalse;
16028             break;
16029           }
16030         if (event.xunmap.window == windows->popup.id)
16031           {
16032             if (windows->backdrop.id != (Window) NULL)
16033               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16034                 CurrentTime);
16035             windows->popup.mapped=MagickFalse;
16036             break;
16037           }
16038         if (event.xunmap.window == windows->widget.id)
16039           {
16040             if (windows->backdrop.id != (Window) NULL)
16041               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
16042                 CurrentTime);
16043             windows->widget.mapped=MagickFalse;
16044             break;
16045           }
16046         break;
16047       }
16048       default:
16049       {
16050         if (display_image->debug != MagickFalse)
16051           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
16052             event.type);
16053         break;
16054       }
16055     }
16056   } while (!(*state & ExitState));
16057   if ((*state & ExitState) == 0)
16058     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
16059       &display_image,exception);
16060   else
16061     if (resource_info->confirm_edit != MagickFalse)
16062       {
16063         /*
16064           Query user if image has changed.
16065         */
16066         if ((resource_info->immutable == MagickFalse) &&
16067             (display_image->taint != MagickFalse))
16068           {
16069             int
16070               status;
16071
16072             status=XConfirmWidget(display,windows,"Your image changed.",
16073               "Do you want to save it");
16074             if (status == 0)
16075               *state&=(~ExitState);
16076             else
16077               if (status > 0)
16078                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
16079                   &display_image,exception);
16080           }
16081       }
16082   if ((windows->visual_info->klass == GrayScale) ||
16083       (windows->visual_info->klass == PseudoColor) ||
16084       (windows->visual_info->klass == DirectColor))
16085     {
16086       /*
16087         Withdraw pan and Magnify window.
16088       */
16089       if (windows->info.mapped != MagickFalse)
16090         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
16091       if (windows->magnify.mapped != MagickFalse)
16092         (void) XWithdrawWindow(display,windows->magnify.id,
16093           windows->magnify.screen);
16094       if (windows->command.mapped != MagickFalse)
16095         (void) XWithdrawWindow(display,windows->command.id,
16096           windows->command.screen);
16097     }
16098   if (windows->pan.mapped != MagickFalse)
16099     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
16100   if (resource_info->backdrop == MagickFalse)
16101     if (windows->backdrop.mapped)
16102       {
16103         (void) XWithdrawWindow(display,windows->backdrop.id,
16104           windows->backdrop.screen);
16105         (void) XDestroyWindow(display,windows->backdrop.id);
16106         windows->backdrop.id=(Window) NULL;
16107         (void) XWithdrawWindow(display,windows->image.id,
16108           windows->image.screen);
16109         (void) XDestroyWindow(display,windows->image.id);
16110         windows->image.id=(Window) NULL;
16111       }
16112   XSetCursorState(display,windows,MagickTrue);
16113   XCheckRefreshWindows(display,windows);
16114   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
16115     *state&=(~ExitState);
16116   if (*state & ExitState)
16117     {
16118       /*
16119         Free Standard Colormap.
16120       */
16121       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
16122       if (resource_info->map_type == (char *) NULL)
16123         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
16124       /*
16125         Free X resources.
16126       */
16127       if (resource_info->copy_image != (Image *) NULL)
16128         {
16129           resource_info->copy_image=DestroyImage(resource_info->copy_image);
16130           resource_info->copy_image=NewImageList();
16131         }
16132       DestroyXResources();
16133     }
16134   (void) XSync(display,MagickFalse);
16135   /*
16136     Restore our progress monitor and warning handlers.
16137   */
16138   (void) SetErrorHandler(warning_handler);
16139   (void) SetWarningHandler(warning_handler);
16140   /*
16141     Change to home directory.
16142   */
16143   directory=getcwd(working_directory,MaxTextExtent);
16144   (void) directory;
16145   {
16146     int
16147       status;
16148
16149     status=chdir(resource_info->home_directory);
16150     if (status == -1)
16151       (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
16152         "UnableToOpenFile","%s",resource_info->home_directory);
16153   }
16154   *image=display_image;
16155   return(nexus);
16156 }
16157 #else
16158 \f
16159 /*
16160 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16161 %                                                                             %
16162 %                                                                             %
16163 %                                                                             %
16164 +   D i s p l a y I m a g e s                                                 %
16165 %                                                                             %
16166 %                                                                             %
16167 %                                                                             %
16168 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16169 %
16170 %  DisplayImages() displays an image sequence to any X window screen.  It
16171 %  returns a value other than 0 if successful.  Check the exception member
16172 %  of image to determine the reason for any failure.
16173 %
16174 %  The format of the DisplayImages method is:
16175 %
16176 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16177 %        Image *images,ExceptionInfo *exception)
16178 %
16179 %  A description of each parameter follows:
16180 %
16181 %    o image_info: the image info.
16182 %
16183 %    o image: the image.
16184 %
16185 %    o exception: return any errors or warnings in this structure.
16186 %
16187 */
16188 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16189   Image *image,ExceptionInfo *exception)
16190 {
16191   assert(image_info != (const ImageInfo *) NULL);
16192   assert(image_info->signature == MagickSignature);
16193   assert(image != (Image *) NULL);
16194   assert(image->signature == MagickSignature);
16195   if (image->debug != MagickFalse)
16196     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16197   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16198     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image->filename);
16199   return(MagickFalse);
16200 }
16201 \f
16202 /*
16203 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16204 %                                                                             %
16205 %                                                                             %
16206 %                                                                             %
16207 +   R e m o t e D i s p l a y C o m m a n d                                   %
16208 %                                                                             %
16209 %                                                                             %
16210 %                                                                             %
16211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16212 %
16213 %  RemoteDisplayCommand() encourages a remote display program to display the
16214 %  specified image filename.
16215 %
16216 %  The format of the RemoteDisplayCommand method is:
16217 %
16218 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16219 %        const char *window,const char *filename,ExceptionInfo *exception)
16220 %
16221 %  A description of each parameter follows:
16222 %
16223 %    o image_info: the image info.
16224 %
16225 %    o window: Specifies the name or id of an X window.
16226 %
16227 %    o filename: the name of the image filename to display.
16228 %
16229 %    o exception: return any errors or warnings in this structure.
16230 %
16231 */
16232 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16233   const char *window,const char *filename,ExceptionInfo *exception)
16234 {
16235   assert(image_info != (const ImageInfo *) NULL);
16236   assert(image_info->signature == MagickSignature);
16237   assert(filename != (char *) NULL);
16238   (void) window;
16239   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16240   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16241     "DelegateLibrarySupportNotBuiltIn","'%s' (X11)",image_info->filename);
16242   return(MagickFalse);
16243 }
16244 #endif