]> granicus.if.org Git - imagemagick/blob - magick/display.c
(no commit message)
[imagemagick] / magick / 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-2010 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 "magick/studio.h"
43 #include "magick/artifact.h"
44 #include "magick/blob.h"
45 #include "magick/cache.h"
46 #include "magick/client.h"
47 #include "magick/color.h"
48 #include "magick/colorspace.h"
49 #include "magick/composite.h"
50 #include "magick/constitute.h"
51 #include "magick/decorate.h"
52 #include "magick/delegate.h"
53 #include "magick/display.h"
54 #include "magick/display-private.h"
55 #include "magick/draw.h"
56 #include "magick/effect.h"
57 #include "magick/enhance.h"
58 #include "magick/exception.h"
59 #include "magick/exception-private.h"
60 #include "magick/fx.h"
61 #include "magick/geometry.h"
62 #include "magick/image.h"
63 #include "magick/image-private.h"
64 #include "magick/list.h"
65 #include "magick/log.h"
66 #include "magick/magick.h"
67 #include "magick/memory_.h"
68 #include "magick/monitor.h"
69 #include "magick/monitor-private.h"
70 #include "magick/montage.h"
71 #include "magick/option.h"
72 #include "magick/paint.h"
73 #include "magick/pixel.h"
74 #include "magick/pixel-private.h"
75 #include "magick/PreRvIcccm.h"
76 #include "magick/property.h"
77 #include "magick/quantum.h"
78 #include "magick/resize.h"
79 #include "magick/resource_.h"
80 #include "magick/shear.h"
81 #include "magick/segment.h"
82 #include "magick/string_.h"
83 #include "magick/string-private.h"
84 #include "magick/transform.h"
85 #include "magick/threshold.h"
86 #include "magick/utility.h"
87 #include "magick/version.h"
88 #include "magick/widget.h"
89 #include "magick/xwindow-private.h"
90 \f
91 #if defined(MAGICKCORE_X11_DELEGATE)
92 /*
93   Define declarations.
94 */
95 #define MaxColors  MagickMin(windows->visual_info->colormap_size,256L)
96 \f
97 /*
98   Constant declarations.
99 */
100 static const unsigned char
101   HighlightBitmap[8] =
102   {
103     0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55
104   },
105   ShadowBitmap[8] =
106   {
107     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
108   };
109
110 static const char
111   *PageSizes[] =
112   {
113     "Letter",
114     "Tabloid",
115     "Ledger",
116     "Legal",
117     "Statement",
118     "Executive",
119     "A3",
120     "A4",
121     "A5",
122     "B4",
123     "B5",
124     "Folio",
125     "Quarto",
126     "10x14",
127     (char *) NULL
128   };
129 \f
130 /*
131   Help widget declarations.
132 */
133 static const char
134   *ImageAnnotateHelp[] =
135   {
136     "In annotate mode, the Command widget has these options:",
137     "",
138     "    Font Name",
139     "      fixed",
140     "      variable",
141     "      5x8",
142     "      6x10",
143     "      7x13bold",
144     "      8x13bold",
145     "      9x15bold",
146     "      10x20",
147     "      12x24",
148     "      Browser...",
149     "    Font Color",
150     "      black",
151     "      blue",
152     "      cyan",
153     "      green",
154     "      gray",
155     "      red",
156     "      magenta",
157     "      yellow",
158     "      white",
159     "      transparent",
160     "      Browser...",
161     "    Font Color",
162     "      black",
163     "      blue",
164     "      cyan",
165     "      green",
166     "      gray",
167     "      red",
168     "      magenta",
169     "      yellow",
170     "      white",
171     "      transparent",
172     "      Browser...",
173     "    Rotate Text",
174     "      -90",
175     "      -45",
176     "      -30",
177     "      0",
178     "      30",
179     "      45",
180     "      90",
181     "      180",
182     "      Dialog...",
183     "    Help",
184     "    Dismiss",
185     "",
186     "Choose a font name from the Font Name sub-menu.  Additional",
187     "font names can be specified with the font browser.  You can",
188     "change the menu names by setting the X resources font1",
189     "through font9.",
190     "",
191     "Choose a font color from the Font Color sub-menu.",
192     "Additional font colors can be specified with the color",
193     "browser.  You can change the menu colors by setting the X",
194     "resources pen1 through pen9.",
195     "",
196     "If you select the color browser and press Grab, you can",
197     "choose the font color by moving the pointer to the desired",
198     "color on the screen and press any button.",
199     "",
200     "If you choose to rotate the text, choose Rotate Text from the",
201     "menu and select an angle.  Typically you will only want to",
202     "rotate one line of text at a time.  Depending on the angle you",
203     "choose, subsequent lines may end up overwriting each other.",
204     "",
205     "Choosing a font and its color is optional.  The default font",
206     "is fixed and the default color is black.  However, you must",
207     "choose a location to begin entering text and press button 1.",
208     "An underscore character will appear at the location of the",
209     "pointer.  The cursor changes to a pencil to indicate you are",
210     "in text mode.  To exit immediately, press Dismiss.",
211     "",
212     "In text mode, any key presses will display the character at",
213     "the location of the underscore and advance the underscore",
214     "cursor.  Enter your text and once completed press Apply to",
215     "finish your image annotation.  To correct errors press BACK",
216     "SPACE.  To delete an entire line of text, press DELETE.  Any",
217     "text that exceeds the boundaries of the image window is",
218     "automagically continued onto the next line.",
219     "",
220     "The actual color you request for the font is saved in the",
221     "image.  However, the color that appears in your image window",
222     "may be different.  For example, on a monochrome screen the",
223     "text will appear black or white even if you choose the color",
224     "red as the font color.  However, the image saved to a file",
225     "with -write is written with red lettering.  To assure the",
226     "correct color text in the final image, any PseudoClass image",
227     "is promoted to DirectClass (see miff(5)).  To force a",
228     "PseudoClass image to remain PseudoClass, use -colors.",
229     (char *) NULL,
230   },
231   *ImageChopHelp[] =
232   {
233     "In chop mode, the Command widget has these options:",
234     "",
235     "    Direction",
236     "      horizontal",
237     "      vertical",
238     "    Help",
239     "    Dismiss",
240     "",
241     "If the you choose the horizontal direction (this the",
242     "default), the area of the image between the two horizontal",
243     "endpoints of the chop line is removed.  Otherwise, the area",
244     "of the image between the two vertical endpoints of the chop",
245     "line is removed.",
246     "",
247     "Select a location within the image window to begin your chop,",
248     "press and hold any button.  Next, move the pointer to",
249     "another location in the image.  As you move a line will",
250     "connect the initial location and the pointer.  When you",
251     "release the button, the area within the image to chop is",
252     "determined by which direction you choose from the Command",
253     "widget.",
254     "",
255     "To cancel the image chopping, move the pointer back to the",
256     "starting point of the line and release the button.",
257     (char *) NULL,
258   },
259   *ImageColorEditHelp[] =
260   {
261     "In color edit mode, the Command widget has these options:",
262     "",
263     "    Method",
264     "      point",
265     "      replace",
266     "      floodfill",
267     "      filltoborder",
268     "      reset",
269     "    Pixel Color",
270     "      black",
271     "      blue",
272     "      cyan",
273     "      green",
274     "      gray",
275     "      red",
276     "      magenta",
277     "      yellow",
278     "      white",
279     "      Browser...",
280     "    Border Color",
281     "      black",
282     "      blue",
283     "      cyan",
284     "      green",
285     "      gray",
286     "      red",
287     "      magenta",
288     "      yellow",
289     "      white",
290     "      Browser...",
291     "    Fuzz",
292     "      0%",
293     "      2%",
294     "      5%",
295     "      10%",
296     "      15%",
297     "      Dialog...",
298     "    Undo",
299     "    Help",
300     "    Dismiss",
301     "",
302     "Choose a color editing method from the Method sub-menu",
303     "of the Command widget.  The point method recolors any pixel",
304     "selected with the pointer until the button is released.  The",
305     "replace method recolors any pixel that matches the color of",
306     "the pixel you select with a button press.  Floodfill recolors",
307     "any pixel that matches the color of the pixel you select with",
308     "a button press and is a neighbor.  Whereas filltoborder recolors",
309     "any neighbor pixel that is not the border color.  Finally reset",
310     "changes the entire image to the designated color.",
311     "",
312     "Next, choose a pixel color from the Pixel Color sub-menu.",
313     "Additional pixel colors can be specified with the color",
314     "browser.  You can change the menu colors by setting the X",
315     "resources pen1 through pen9.",
316     "",
317     "Now press button 1 to select a pixel within the image window",
318     "to change its color.  Additional pixels may be recolored as",
319     "prescribed by the method you choose.",
320     "",
321     "If the Magnify widget is mapped, it can be helpful in positioning",
322     "your pointer within the image (refer to button 2).",
323     "",
324     "The actual color you request for the pixels is saved in the",
325     "image.  However, the color that appears in your image window",
326     "may be different.  For example, on a monochrome screen the",
327     "pixel will appear black or white even if you choose the",
328     "color red as the pixel color.  However, the image saved to a",
329     "file with -write is written with red pixels.  To assure the",
330     "correct color text in the final image, any PseudoClass image",
331     "is promoted to DirectClass (see miff(5)).  To force a",
332     "PseudoClass image to remain PseudoClass, use -colors.",
333     (char *) NULL,
334   },
335   *ImageCompositeHelp[] =
336   {
337     "First a widget window is displayed requesting you to enter an",
338     "image name. Press Composite, Grab or type a file name.",
339     "Press Cancel if you choose not to create a composite image.",
340     "When you choose Grab, move the pointer to the desired window",
341     "and press any button.",
342     "",
343     "If the Composite image does not have any matte information,",
344     "you are informed and the file browser is displayed again.",
345     "Enter the name of a mask image.  The image is typically",
346     "grayscale and the same size as the composite image.  If the",
347     "image is not grayscale, it is converted to grayscale and the",
348     "resulting intensities are used as matte information.",
349     "",
350     "A small window appears showing the location of the cursor in",
351     "the image window. You are now in composite mode.  To exit",
352     "immediately, press Dismiss.  In composite mode, the Command",
353     "widget has these options:",
354     "",
355     "    Operators",
356     "      Over",
357     "      In",
358     "      Out",
359     "      Atop",
360     "      Xor",
361     "      Plus",
362     "      Minus",
363     "      Add",
364     "      Subtract",
365     "      Difference",
366     "      Multiply",
367     "      Bumpmap",
368     "      Copy",
369     "      CopyRed",
370     "      CopyGreen",
371     "      CopyBlue",
372     "      CopyOpacity",
373     "      Clear",
374     "    Dissolve",
375     "    Displace",
376     "    Help",
377     "    Dismiss",
378     "",
379     "Choose a composite operation from the Operators sub-menu of",
380     "the Command widget.  How each operator behaves is described",
381     "below.  Image window is the image currently displayed on",
382     "your X server and image is the image obtained with the File",
383     "Browser widget.",
384     "",
385     "Over     The result is the union of the two image shapes,",
386     "         with image obscuring image window in the region of",
387     "         overlap.",
388     "",
389     "In       The result is simply image cut by the shape of",
390     "         image window.  None of the image data of image",
391     "         window is in the result.",
392     "",
393     "Out      The resulting image is image with the shape of",
394     "         image window cut out.",
395     "",
396     "Atop     The result is the same shape as image image window,",
397     "         with image obscuring image window where the image",
398     "         shapes overlap.  Note this differs from over",
399     "         because the portion of image outside image window's",
400     "         shape does not appear in the result.",
401     "",
402     "Xor      The result is the image data from both image and",
403     "         image window that is outside the overlap region.",
404     "         The overlap region is blank.",
405     "",
406     "Plus     The result is just the sum of the image data.",
407     "         Output values are cropped to QuantumRange (no overflow).",
408     "",
409     "Minus    The result of image - image window, with underflow",
410     "         cropped to zero.",
411     "",
412     "Add      The result of image + image window, with overflow",
413     "         wrapping around (mod 256).",
414     "",
415     "Subtract The result of image - image window, with underflow",
416     "         wrapping around (mod 256).  The add and subtract",
417     "         operators can be used to perform reversible",
418     "         transformations.",
419     "",
420     "Difference",
421     "         The result of abs(image - image window).  This",
422     "         useful for comparing two very similar images.",
423     "",
424     "Multiply",
425     "         The result of image * image window.  This",
426     "         useful for the creation of drop-shadows.",
427     "",
428     "Bumpmap  The result of surface normals from image * image",
429     "         window.",
430     "",
431     "Copy     The resulting image is image window replaced with",
432     "         image.  Here the matte information is ignored.",
433     "",
434     "CopyRed  The red layer of the image window is replace with",
435     "         the red layer of the image.  The other layers are",
436     "         untouched.",
437     "",
438     "CopyGreen",
439     "         The green layer of the image window is replace with",
440     "         the green layer of the image.  The other layers are",
441     "         untouched.",
442     "",
443     "CopyBlue The blue layer of the image window is replace with",
444     "         the blue layer of the image.  The other layers are",
445     "         untouched.",
446     "",
447     "CopyOpacity",
448     "         The matte layer of the image window is replace with",
449     "         the matte layer of the image.  The other layers are",
450     "         untouched.",
451     "",
452     "The image compositor requires a matte, or alpha channel in",
453     "the image for some operations.  This extra channel usually",
454     "defines a mask which represents a sort of a cookie-cutter",
455     "for the image.  This the case when matte is opaque (full",
456     "coverage) for pixels inside the shape, zero outside, and",
457     "between 0 and QuantumRange on the boundary.  If image does not",
458     "have a matte channel, it is initialized with 0 for any pixel",
459     "matching in color to pixel location (0,0), otherwise QuantumRange.",
460     "",
461     "If you choose Dissolve, the composite operator becomes Over.  The",
462     "image matte channel percent transparency is initialized to factor.",
463     "The image window is initialized to (100-factor). Where factor is the",
464     "value you specify in the Dialog widget.",
465     "",
466     "Displace shifts the image pixels as defined by a displacement",
467     "map.  With this option, image is used as a displacement map.",
468     "Black, within the displacement map, is a maximum positive",
469     "displacement.  White is a maximum negative displacement and",
470     "middle gray is neutral.  The displacement is scaled to determine",
471     "the pixel shift.  By default, the displacement applies in both the",
472     "horizontal and vertical directions.  However, if you specify a mask,",
473     "image is the horizontal X displacement and mask the vertical Y",
474     "displacement.",
475     "",
476     "Note that matte information for image window is not retained",
477     "for colormapped X server visuals (e.g. StaticColor,",
478     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
479     "behavior may require a TrueColor or DirectColor visual or a",
480     "Standard Colormap.",
481     "",
482     "Choosing a composite operator is optional.  The default",
483     "operator is replace.  However, you must choose a location to",
484     "composite your image and press button 1.  Press and hold the",
485     "button before releasing and an outline of the image will",
486     "appear to help you identify your location.",
487     "",
488     "The actual colors of the composite image is saved.  However,",
489     "the color that appears in image window may be different.",
490     "For example, on a monochrome screen image window will appear",
491     "black or white even though your composited image may have",
492     "many colors.  If the image is saved to a file it is written",
493     "with the correct colors.  To assure the correct colors are",
494     "saved in the final image, any PseudoClass image is promoted",
495     "to DirectClass (see miff(5)).  To force a PseudoClass image",
496     "to remain PseudoClass, use -colors.",
497     (char *) NULL,
498   },
499   *ImageCutHelp[] =
500   {
501     "In cut mode, the Command widget has these options:",
502     "",
503     "    Help",
504     "    Dismiss",
505     "",
506     "To define a cut region, press button 1 and drag.  The",
507     "cut region is defined by a highlighted rectangle that",
508     "expands or contracts as it follows the pointer.  Once you",
509     "are satisfied with the cut region, release the button.",
510     "You are now in rectify mode.  In rectify mode, the Command",
511     "widget has these options:",
512     "",
513     "    Cut",
514     "    Help",
515     "    Dismiss",
516     "",
517     "You can make adjustments by moving the pointer to one of the",
518     "cut rectangle corners, pressing a button, and dragging.",
519     "Finally, press Cut to commit your copy region.  To",
520     "exit without cutting the image, press Dismiss.",
521     (char *) NULL,
522   },
523   *ImageCopyHelp[] =
524   {
525     "In copy mode, the Command widget has these options:",
526     "",
527     "    Help",
528     "    Dismiss",
529     "",
530     "To define a copy region, press button 1 and drag.  The",
531     "copy region is defined by a highlighted rectangle that",
532     "expands or contracts as it follows the pointer.  Once you",
533     "are satisfied with the copy region, release the button.",
534     "You are now in rectify mode.  In rectify mode, the Command",
535     "widget has these options:",
536     "",
537     "    Copy",
538     "    Help",
539     "    Dismiss",
540     "",
541     "You can make adjustments by moving the pointer to one of the",
542     "copy rectangle corners, pressing a button, and dragging.",
543     "Finally, press Copy to commit your copy region.  To",
544     "exit without copying the image, press Dismiss.",
545     (char *) NULL,
546   },
547   *ImageCropHelp[] =
548   {
549     "In crop mode, the Command widget has these options:",
550     "",
551     "    Help",
552     "    Dismiss",
553     "",
554     "To define a cropping region, press button 1 and drag.  The",
555     "cropping region is defined by a highlighted rectangle that",
556     "expands or contracts as it follows the pointer.  Once you",
557     "are satisfied with the cropping region, release the button.",
558     "You are now in rectify mode.  In rectify mode, the Command",
559     "widget has these options:",
560     "",
561     "    Crop",
562     "    Help",
563     "    Dismiss",
564     "",
565     "You can make adjustments by moving the pointer to one of the",
566     "cropping rectangle corners, pressing a button, and dragging.",
567     "Finally, press Crop to commit your cropping region.  To",
568     "exit without cropping the image, press Dismiss.",
569     (char *) NULL,
570   },
571   *ImageDrawHelp[] =
572   {
573     "The cursor changes to a crosshair to indicate you are in",
574     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
575     "the Command widget has these options:",
576     "",
577     "    Element",
578     "      point",
579     "      line",
580     "      rectangle",
581     "      fill rectangle",
582     "      circle",
583     "      fill circle",
584     "      ellipse",
585     "      fill ellipse",
586     "      polygon",
587     "      fill polygon",
588     "    Color",
589     "      black",
590     "      blue",
591     "      cyan",
592     "      green",
593     "      gray",
594     "      red",
595     "      magenta",
596     "      yellow",
597     "      white",
598     "      transparent",
599     "      Browser...",
600     "    Stipple",
601     "      Brick",
602     "      Diagonal",
603     "      Scales",
604     "      Vertical",
605     "      Wavy",
606     "      Translucent",
607     "      Opaque",
608     "      Open...",
609     "    Width",
610     "      1",
611     "      2",
612     "      4",
613     "      8",
614     "      16",
615     "      Dialog...",
616     "    Undo",
617     "    Help",
618     "    Dismiss",
619     "",
620     "Choose a drawing primitive from the Element sub-menu.",
621     "",
622     "Choose a color from the Color sub-menu.  Additional",
623     "colors can be specified with the color browser.",
624     "",
625     "If you choose the color browser and press Grab, you can",
626     "select the color by moving the pointer to the desired",
627     "color on the screen and press any button.  The transparent",
628     "color updates the image matte channel and is useful for",
629     "image compositing.",
630     "",
631     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
632     "Additional stipples can be specified with the file browser.",
633     "Stipples obtained from the file browser must be on disk in the",
634     "X11 bitmap format.",
635     "",
636     "Choose a width, if appropriate, from the Width sub-menu.  To",
637     "choose a specific width select the Dialog widget.",
638     "",
639     "Choose a point in the Image window and press button 1 and",
640     "hold.  Next, move the pointer to another location in the",
641     "image.  As you move, a line connects the initial location and",
642     "the pointer.  When you release the button, the image is",
643     "updated with the primitive you just drew.  For polygons, the",
644     "image is updated when you press and release the button without",
645     "moving the pointer.",
646     "",
647     "To cancel image drawing, move the pointer back to the",
648     "starting point of the line and release the button.",
649     (char *) NULL,
650   },
651   *DisplayHelp[] =
652   {
653     "BUTTONS",
654     "  The effects of each button press is described below.  Three",
655     "  buttons are required.  If you have a two button mouse,",
656     "  button 1 and 3 are returned.  Press ALT and button 3 to",
657     "  simulate button 2.",
658     "",
659     "  1    Press this button to map or unmap the Command widget.",
660     "",
661     "  2    Press and drag to define a region of the image to",
662     "       magnify.",
663     "",
664     "  3    Press and drag to choose from a select set of commands.",
665     "       This button behaves differently if the image being",
666     "       displayed is a visual image directory.  Here, choose a",
667     "       particular tile of the directory and press this button and",
668     "       drag to select a command from a pop-up menu.  Choose from",
669     "       these menu items:",
670     "",
671     "           Open",
672     "           Next",
673     "           Former",
674     "           Delete",
675     "           Update",
676     "",
677     "       If you choose Open, the image represented by the tile is",
678     "       displayed.  To return to the visual image directory, choose",
679     "       Next from the Command widget.  Next and Former moves to the",
680     "       next or former image respectively.  Choose Delete to delete",
681     "       a particular image tile.  Finally, choose Update to",
682     "       synchronize all the image tiles with their respective",
683     "       images.",
684     "",
685     "COMMAND WIDGET",
686     "  The Command widget lists a number of sub-menus and commands.",
687     "  They are",
688     "",
689     "      File",
690     "        Open...",
691     "        Next",
692     "        Former",
693     "        Select...",
694     "        Save...",
695     "        Print...",
696     "        Delete...",
697     "        New...",
698     "        Visual Directory...",
699     "        Quit",
700     "      Edit",
701     "        Undo",
702     "        Redo",
703     "        Cut",
704     "        Copy",
705     "        Paste",
706     "      View",
707     "        Half Size",
708     "        Original Size",
709     "        Double Size",
710     "        Resize...",
711     "        Apply",
712     "        Refresh",
713     "        Restore",
714     "      Transform",
715     "        Crop",
716     "        Chop",
717     "        Flop",
718     "        Flip",
719     "        Rotate Right",
720     "        Rotate Left",
721     "        Rotate...",
722     "        Shear...",
723     "        Roll...",
724     "        Trim Edges",
725     "      Enhance",
726     "        Brightness...",
727     "        Saturation...",
728     "        Hue...",
729     "        Gamma...",
730     "        Sharpen...",
731     "        Dull",
732     "        Contrast Stretch...",
733     "        Sigmoidal Contrast...",
734     "        Normalize",
735     "        Equalize",
736     "        Negate",
737     "        Grayscale",
738     "        Map...",
739     "        Quantize...",
740     "      Effects",
741     "        Despeckle",
742     "        Emboss",
743     "        Reduce Noise",
744     "        Add Noise",
745     "        Sharpen...",
746     "        Blur...",
747     "        Threshold...",
748     "        Edge Detect...",
749     "        Spread...",
750     "        Shade...",
751     "        Painting...",
752     "        Segment...",
753     "      F/X",
754     "        Solarize...",
755     "        Sepia Tone...",
756     "        Swirl...",
757     "        Implode...",
758     "        Vignette...",
759     "        Wave...",
760     "        Oil Painting...",
761     "        Charcoal Drawing...",
762     "      Image Edit",
763     "        Annotate...",
764     "        Draw...",
765     "        Color...",
766     "        Matte...",
767     "        Composite...",
768     "        Add Border...",
769     "        Add Frame...",
770     "        Comment...",
771     "        Launch...",
772     "        Region of Interest...",
773     "      Miscellany",
774     "        Image Info",
775     "        Zoom Image",
776     "        Show Preview...",
777     "        Show Histogram",
778     "        Show Matte",
779     "        Background...",
780     "        Slide Show",
781     "        Preferences...",
782     "      Help",
783     "        Overview",
784     "        Browse Documentation",
785     "        About Display",
786     "",
787     "  Menu items with a indented triangle have a sub-menu.  They",
788     "  are represented above as the indented items.  To access a",
789     "  sub-menu item, move the pointer to the appropriate menu and",
790     "  press a button and drag.  When you find the desired sub-menu",
791     "  item, release the button and the command is executed.  Move",
792     "  the pointer away from the sub-menu if you decide not to",
793     "  execute a particular command.",
794     "",
795     "KEYBOARD ACCELERATORS",
796     "  Accelerators are one or two key presses that effect a",
797     "  particular command.  The keyboard accelerators that",
798     "  display(1) understands is:",
799     "",
800     "  Ctl+O     Press to open an image from a file.",
801     "",
802     "  space     Press to display the next image.",
803     "",
804     "            If the image is a multi-paged document such as a Postscript",
805     "            document, you can skip ahead several pages by preceding",
806     "            this command with a number.  For example to display the",
807     "            third page beyond the current page, press 3<space>.",
808     "",
809     "  backspace Press to display the former image.",
810     "",
811     "            If the image is a multi-paged document such as a Postscript",
812     "            document, you can skip behind several pages by preceding",
813     "            this command with a number.  For example to display the",
814     "            third page preceding the current page, press 3<backspace>.",
815     "",
816     "  Ctl+S     Press to write the image to a file.",
817     "",
818     "  Ctl+P     Press to print the image to a Postscript printer.",
819     "",
820     "  Ctl+D     Press to delete an image file.",
821     "",
822     "  Ctl+N     Press to create a blank canvas.",
823     "",
824     "  Ctl+Q     Press to discard all images and exit program.",
825     "",
826     "  Ctl+Z     Press to undo last image transformation.",
827     "",
828     "  Ctl+R     Press to redo last image transformation.",
829     "",
830     "  Ctl+X     Press to cut a region of the image.",
831     "",
832     "  Ctl+C     Press to copy a region of the image.",
833     "",
834     "  Ctl+V     Press to paste a region to the image.",
835     "",
836     "  <         Press to half the image size.",
837     "",
838     "  -         Press to return to the original image size.",
839     "",
840     "  >         Press to double the image size.",
841     "",
842     "  %         Press to resize the image to a width and height you",
843     "            specify.",
844     "",
845     "Cmd-A       Press to make any image transformations permanent."
846     "",
847     "            By default, any image size transformations are applied",
848     "            to the original image to create the image displayed on",
849     "            the X server.  However, the transformations are not",
850     "            permanent (i.e. the original image does not change",
851     "            size only the X image does).  For example, if you",
852     "            press > the X image will appear to double in size,",
853     "            but the original image will in fact remain the same size.",
854     "            To force the original image to double in size, press >",
855     "            followed by Cmd-A.",
856     "",
857     "  @         Press to refresh the image window.",
858     "",
859     "  C         Press to cut out a rectangular region of the image.",
860     "",
861     "  [         Press to chop the image.",
862     "",
863     "  H         Press to flop image in the horizontal direction.",
864     "",
865     "  V         Press to flip image in the vertical direction.",
866     "",
867     "  /         Press to rotate the image 90 degrees clockwise.",
868     "",
869     " \\         Press to rotate the image 90 degrees counter-clockwise.",
870     "",
871     "  *         Press to rotate the image the number of degrees you",
872     "            specify.",
873     "",
874     "  S         Press to shear the image the number of degrees you",
875     "            specify.",
876     "",
877     "  R         Press to roll the image.",
878     "",
879     "  T         Press to trim the image edges.",
880     "",
881     "  Shft-H    Press to vary the image hue.",
882     "",
883     "  Shft-S    Press to vary the color saturation.",
884     "",
885     "  Shft-L    Press to vary the color brightness.",
886     "",
887     "  Shft-G    Press to gamma correct the image.",
888     "",
889     "  Shft-C    Press to sharpen the image contrast.",
890     "",
891     "  Shft-Z    Press to dull the image contrast.",
892     "",
893     "  =         Press to perform histogram equalization on the image.",
894     "",
895     "  Shft-N    Press to perform histogram normalization on the image.",
896     "",
897     "  Shft-~    Press to negate the colors of the image.",
898     "",
899     "  .         Press to convert the image colors to gray.",
900     "",
901     "  Shft-#    Press to set the maximum number of unique colors in the",
902     "            image.",
903     "",
904     "  F2        Press to reduce the speckles in an image.",
905     "",
906     "  F3        Press to eliminate peak noise from an image.",
907     "",
908     "  F4        Press to add noise to an image.",
909     "",
910     "  F5        Press to sharpen an image.",
911     "",
912     "  F6        Press to delete an image file.",
913     "",
914     "  F7        Press to threshold the image.",
915     "",
916     "  F8        Press to detect edges within an image.",
917     "",
918     "  F9        Press to emboss an image.",
919     "",
920     "  F10       Press to displace pixels by a random amount.",
921     "",
922     "  F11       Press to negate all pixels above the threshold level.",
923     "",
924     "  F12       Press to shade the image using a distant light source.",
925     "",
926     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
927     "",
928     "  F14       Press to segment the image by color.",
929     "",
930     "  Meta-S    Press to swirl image pixels about the center.",
931     "",
932     "  Meta-I    Press to implode image pixels about the center.",
933     "",
934     "  Meta-W    Press to alter an image along a sine wave.",
935     "",
936     "  Meta-P    Press to simulate an oil painting.",
937     "",
938     "  Meta-C    Press to simulate a charcoal drawing.",
939     "",
940     "  Alt-A     Press to annotate the image with text.",
941     "",
942     "  Alt-D     Press to draw on an image.",
943     "",
944     "  Alt-P     Press to edit an image pixel color.",
945     "",
946     "  Alt-M     Press to edit the image matte information.",
947     "",
948     "  Alt-V     Press to composite the image with another.",
949     "",
950     "  Alt-B     Press to add a border to the image.",
951     "",
952     "  Alt-F     Press to add an ornamental border to the image.",
953     "",
954     "  Alt-Shft-!",
955     "            Press to add an image comment.",
956     "",
957     "  Ctl-A     Press to apply image processing techniques to a region",
958     "            of interest.",
959     "",
960     "  Shft-?    Press to display information about the image.",
961     "",
962     "  Shft-+    Press to map the zoom image window.",
963     "",
964     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
965     "",
966     "  F1        Press to display helpful information about display(1).",
967     "",
968     "  Find      Press to browse documentation about ImageMagick.",
969     "",
970     "  1-9       Press to change the level of magnification.",
971     "",
972     "  Use the arrow keys to move the image one pixel up, down,",
973     "  left, or right within the magnify window.  Be sure to first",
974     "  map the magnify window by pressing button 2.",
975     "",
976     "  Press ALT and one of the arrow keys to trim off one pixel",
977     "  from any side of the image.",
978     (char *) NULL,
979   },
980   *ImageMatteEditHelp[] =
981   {
982     "Matte information within an image is useful for some",
983     "operations such as image compositing (See IMAGE",
984     "COMPOSITING).  This extra channel usually defines a mask",
985     "which represents a sort of a cookie-cutter for the image.",
986     "This the case when matte is opaque (full coverage) for",
987     "pixels inside the shape, zero outside, and between 0 and",
988     "QuantumRange on the boundary.",
989     "",
990     "A small window appears showing the location of the cursor in",
991     "the image window. You are now in matte edit mode.  To exit",
992     "immediately, press Dismiss.  In matte edit mode, the Command",
993     "widget has these options:",
994     "",
995     "    Method",
996     "      point",
997     "      replace",
998     "      floodfill",
999     "      filltoborder",
1000     "      reset",
1001     "    Border Color",
1002     "      black",
1003     "      blue",
1004     "      cyan",
1005     "      green",
1006     "      gray",
1007     "      red",
1008     "      magenta",
1009     "      yellow",
1010     "      white",
1011     "      Browser...",
1012     "    Fuzz",
1013     "      0%",
1014     "      2%",
1015     "      5%",
1016     "      10%",
1017     "      15%",
1018     "      Dialog...",
1019     "    Matte",
1020     "      Opaque",
1021     "      Transparent",
1022     "      Dialog...",
1023     "    Undo",
1024     "    Help",
1025     "    Dismiss",
1026     "",
1027     "Choose a matte editing method from the Method sub-menu of",
1028     "the Command widget.  The point method changes the matte value",
1029     "of any pixel selected with the pointer until the button is",
1030     "is released.  The replace method changes the matte value of",
1031     "any pixel that matches the color of the pixel you select with",
1032     "a button press.  Floodfill changes the matte value of any pixel",
1033     "that matches the color of the pixel you select with a button",
1034     "press and is a neighbor.  Whereas filltoborder changes the matte",
1035     "value any neighbor pixel that is not the border color.  Finally",
1036     "reset changes the entire image to the designated matte value.",
1037     "",
1038     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1039     "select the Dialog entry.  Here a dialog appears requesting a matte",
1040     "value.  The value you select is assigned as the opacity value of the",
1041     "selected pixel or pixels.",
1042     "",
1043     "Now, press any button to select a pixel within the image",
1044     "window to change its matte value.",
1045     "",
1046     "If the Magnify widget is mapped, it can be helpful in positioning",
1047     "your pointer within the image (refer to button 2).",
1048     "",
1049     "Matte information is only valid in a DirectClass image.",
1050     "Therefore, any PseudoClass image is promoted to DirectClass",
1051     "(see miff(5)).  Note that matte information for PseudoClass",
1052     "is not retained for colormapped X server visuals (e.g.",
1053     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1054     "immediately save your image to a file (refer to Write).",
1055     "Correct matte editing behavior may require a TrueColor or",
1056     "DirectColor visual or a Standard Colormap.",
1057     (char *) NULL,
1058   },
1059   *ImagePanHelp[] =
1060   {
1061     "When an image exceeds the width or height of the X server",
1062     "screen, display maps a small panning icon.  The rectangle",
1063     "within the panning icon shows the area that is currently",
1064     "displayed in the image window.  To pan about the image,",
1065     "press any button and drag the pointer within the panning",
1066     "icon.  The pan rectangle moves with the pointer and the",
1067     "image window is updated to reflect the location of the",
1068     "rectangle within the panning icon.  When you have selected",
1069     "the area of the image you wish to view, release the button.",
1070     "",
1071     "Use the arrow keys to pan the image one pixel up, down,",
1072     "left, or right within the image window.",
1073     "",
1074     "The panning icon is withdrawn if the image becomes smaller",
1075     "than the dimensions of the X server screen.",
1076     (char *) NULL,
1077   },
1078   *ImagePasteHelp[] =
1079   {
1080     "A small window appears showing the location of the cursor in",
1081     "the image window. You are now in paste mode.  To exit",
1082     "immediately, press Dismiss.  In paste mode, the Command",
1083     "widget has these options:",
1084     "",
1085     "    Operators",
1086     "      over",
1087     "      in",
1088     "      out",
1089     "      atop",
1090     "      xor",
1091     "      plus",
1092     "      minus",
1093     "      add",
1094     "      subtract",
1095     "      difference",
1096     "      replace",
1097     "    Help",
1098     "    Dismiss",
1099     "",
1100     "Choose a composite operation from the Operators sub-menu of",
1101     "the Command widget.  How each operator behaves is described",
1102     "below.  Image window is the image currently displayed on",
1103     "your X server and image is the image obtained with the File",
1104     "Browser widget.",
1105     "",
1106     "Over     The result is the union of the two image shapes,",
1107     "         with image obscuring image window in the region of",
1108     "         overlap.",
1109     "",
1110     "In       The result is simply image cut by the shape of",
1111     "         image window.  None of the image data of image",
1112     "         window is in the result.",
1113     "",
1114     "Out      The resulting image is image with the shape of",
1115     "         image window cut out.",
1116     "",
1117     "Atop     The result is the same shape as image image window,",
1118     "         with image obscuring image window where the image",
1119     "         shapes overlap.  Note this differs from over",
1120     "         because the portion of image outside image window's",
1121     "         shape does not appear in the result.",
1122     "",
1123     "Xor      The result is the image data from both image and",
1124     "         image window that is outside the overlap region.",
1125     "         The overlap region is blank.",
1126     "",
1127     "Plus     The result is just the sum of the image data.",
1128     "         Output values are cropped to QuantumRange (no overflow).",
1129     "         This operation is independent of the matte",
1130     "         channels.",
1131     "",
1132     "Minus    The result of image - image window, with underflow",
1133     "         cropped to zero.",
1134     "",
1135     "Add      The result of image + image window, with overflow",
1136     "         wrapping around (mod 256).",
1137     "",
1138     "Subtract The result of image - image window, with underflow",
1139     "         wrapping around (mod 256).  The add and subtract",
1140     "         operators can be used to perform reversible",
1141     "         transformations.",
1142     "",
1143     "Difference",
1144     "         The result of abs(image - image window).  This",
1145     "         useful for comparing two very similar images.",
1146     "",
1147     "Copy     The resulting image is image window replaced with",
1148     "         image.  Here the matte information is ignored.",
1149     "",
1150     "CopyRed  The red layer of the image window is replace with",
1151     "         the red layer of the image.  The other layers are",
1152     "         untouched.",
1153     "",
1154     "CopyGreen",
1155     "         The green layer of the image window is replace with",
1156     "         the green layer of the image.  The other layers are",
1157     "         untouched.",
1158     "",
1159     "CopyBlue The blue layer of the image window is replace with",
1160     "         the blue layer of the image.  The other layers are",
1161     "         untouched.",
1162     "",
1163     "CopyOpacity",
1164     "         The matte layer of the image window is replace with",
1165     "         the matte layer of the image.  The other layers are",
1166     "         untouched.",
1167     "",
1168     "The image compositor requires a matte, or alpha channel in",
1169     "the image for some operations.  This extra channel usually",
1170     "defines a mask which represents a sort of a cookie-cutter",
1171     "for the image.  This the case when matte is opaque (full",
1172     "coverage) for pixels inside the shape, zero outside, and",
1173     "between 0 and QuantumRange on the boundary.  If image does not",
1174     "have a matte channel, it is initialized with 0 for any pixel",
1175     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1176     "",
1177     "Note that matte information for image window is not retained",
1178     "for colormapped X server visuals (e.g. StaticColor,",
1179     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1180     "behavior may require a TrueColor or DirectColor visual or a",
1181     "Standard Colormap.",
1182     "",
1183     "Choosing a composite operator is optional.  The default",
1184     "operator is replace.  However, you must choose a location to",
1185     "paste your image and press button 1.  Press and hold the",
1186     "button before releasing and an outline of the image will",
1187     "appear to help you identify your location.",
1188     "",
1189     "The actual colors of the pasted image is saved.  However,",
1190     "the color that appears in image window may be different.",
1191     "For example, on a monochrome screen image window will appear",
1192     "black or white even though your pasted image may have",
1193     "many colors.  If the image is saved to a file it is written",
1194     "with the correct colors.  To assure the correct colors are",
1195     "saved in the final image, any PseudoClass image is promoted",
1196     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1197     "to remain PseudoClass, use -colors.",
1198     (char *) NULL,
1199   },
1200   *ImageROIHelp[] =
1201   {
1202     "In region of interest mode, the Command widget has these",
1203     "options:",
1204     "",
1205     "    Help",
1206     "    Dismiss",
1207     "",
1208     "To define a region of interest, press button 1 and drag.",
1209     "The region of interest is defined by a highlighted rectangle",
1210     "that expands or contracts as it follows the pointer.  Once",
1211     "you are satisfied with the region of interest, release the",
1212     "button.  You are now in apply mode.  In apply mode the",
1213     "Command widget has these options:",
1214     "",
1215     "      File",
1216     "        Save...",
1217     "        Print...",
1218     "      Edit",
1219     "        Undo",
1220     "        Redo",
1221     "      Transform",
1222     "        Flop",
1223     "        Flip",
1224     "        Rotate Right",
1225     "        Rotate Left",
1226     "      Enhance",
1227     "        Hue...",
1228     "        Saturation...",
1229     "        Brightness...",
1230     "        Gamma...",
1231     "        Spiff",
1232     "        Dull",
1233     "        Contrast Stretch",
1234     "        Sigmoidal Contrast...",
1235     "        Normalize",
1236     "        Equalize",
1237     "        Negate",
1238     "        Grayscale",
1239     "        Map...",
1240     "        Quantize...",
1241     "      Effects",
1242     "        Despeckle",
1243     "        Emboss",
1244     "        Reduce Noise",
1245     "        Sharpen...",
1246     "        Blur...",
1247     "        Threshold...",
1248     "        Edge Detect...",
1249     "        Spread...",
1250     "        Shade...",
1251     "        Raise...",
1252     "        Segment...",
1253     "      F/X",
1254     "        Solarize...",
1255     "        Sepia Tone...",
1256     "        Swirl...",
1257     "        Implode...",
1258     "        Vignette...",
1259     "        Wave...",
1260     "        Oil Painting...",
1261     "        Charcoal Drawing...",
1262     "      Miscellany",
1263     "        Image Info",
1264     "        Zoom Image",
1265     "        Show Preview...",
1266     "        Show Histogram",
1267     "        Show Matte",
1268     "      Help",
1269     "      Dismiss",
1270     "",
1271     "You can make adjustments to the region of interest by moving",
1272     "the pointer to one of the rectangle corners, pressing a",
1273     "button, and dragging.  Finally, choose an image processing",
1274     "technique from the Command widget.  You can choose more than",
1275     "one image processing technique to apply to an area.",
1276     "Alternatively, you can move the region of interest before",
1277     "applying another image processing technique.  To exit, press",
1278     "Dismiss.",
1279     (char *) NULL,
1280   },
1281   *ImageRotateHelp[] =
1282   {
1283     "In rotate mode, the Command widget has these options:",
1284     "",
1285     "    Pixel Color",
1286     "      black",
1287     "      blue",
1288     "      cyan",
1289     "      green",
1290     "      gray",
1291     "      red",
1292     "      magenta",
1293     "      yellow",
1294     "      white",
1295     "      Browser...",
1296     "    Direction",
1297     "      horizontal",
1298     "      vertical",
1299     "    Help",
1300     "    Dismiss",
1301     "",
1302     "Choose a background color from the Pixel Color sub-menu.",
1303     "Additional background colors can be specified with the color",
1304     "browser.  You can change the menu colors by setting the X",
1305     "resources pen1 through pen9.",
1306     "",
1307     "If you choose the color browser and press Grab, you can",
1308     "select the background color by moving the pointer to the",
1309     "desired color on the screen and press any button.",
1310     "",
1311     "Choose a point in the image window and press this button and",
1312     "hold.  Next, move the pointer to another location in the",
1313     "image.  As you move a line connects the initial location and",
1314     "the pointer.  When you release the button, the degree of",
1315     "image rotation is determined by the slope of the line you",
1316     "just drew.  The slope is relative to the direction you",
1317     "choose from the Direction sub-menu of the Command widget.",
1318     "",
1319     "To cancel the image rotation, move the pointer back to the",
1320     "starting point of the line and release the button.",
1321     (char *) NULL,
1322   };
1323 \f
1324 /*
1325   Enumeration declarations.
1326 */
1327 typedef enum
1328 {
1329   CopyMode,
1330   CropMode,
1331   CutMode
1332 } ClipboardMode;
1333
1334 typedef enum
1335 {
1336   OpenCommand,
1337   NextCommand,
1338   FormerCommand,
1339   SelectCommand,
1340   SaveCommand,
1341   PrintCommand,
1342   DeleteCommand,
1343   NewCommand,
1344   VisualDirectoryCommand,
1345   QuitCommand,
1346   UndoCommand,
1347   RedoCommand,
1348   CutCommand,
1349   CopyCommand,
1350   PasteCommand,
1351   HalfSizeCommand,
1352   OriginalSizeCommand,
1353   DoubleSizeCommand,
1354   ResizeCommand,
1355   ApplyCommand,
1356   RefreshCommand,
1357   RestoreCommand,
1358   CropCommand,
1359   ChopCommand,
1360   FlopCommand,
1361   FlipCommand,
1362   RotateRightCommand,
1363   RotateLeftCommand,
1364   RotateCommand,
1365   ShearCommand,
1366   RollCommand,
1367   TrimCommand,
1368   HueCommand,
1369   SaturationCommand,
1370   BrightnessCommand,
1371   GammaCommand,
1372   SpiffCommand,
1373   DullCommand,
1374   ContrastStretchCommand,
1375   SigmoidalContrastCommand,
1376   NormalizeCommand,
1377   EqualizeCommand,
1378   NegateCommand,
1379   GrayscaleCommand,
1380   MapCommand,
1381   QuantizeCommand,
1382   DespeckleCommand,
1383   EmbossCommand,
1384   ReduceNoiseCommand,
1385   AddNoiseCommand,
1386   SharpenCommand,
1387   BlurCommand,
1388   ThresholdCommand,
1389   EdgeDetectCommand,
1390   SpreadCommand,
1391   ShadeCommand,
1392   RaiseCommand,
1393   SegmentCommand,
1394   SolarizeCommand,
1395   SepiaToneCommand,
1396   SwirlCommand,
1397   ImplodeCommand,
1398   VignetteCommand,
1399   WaveCommand,
1400   OilPaintCommand,
1401   CharcoalDrawCommand,
1402   AnnotateCommand,
1403   DrawCommand,
1404   ColorCommand,
1405   MatteCommand,
1406   CompositeCommand,
1407   AddBorderCommand,
1408   AddFrameCommand,
1409   CommentCommand,
1410   LaunchCommand,
1411   RegionofInterestCommand,
1412   ROIHelpCommand,
1413   ROIDismissCommand,
1414   InfoCommand,
1415   ZoomCommand,
1416   ShowPreviewCommand,
1417   ShowHistogramCommand,
1418   ShowMatteCommand,
1419   BackgroundCommand,
1420   SlideShowCommand,
1421   PreferencesCommand,
1422   HelpCommand,
1423   BrowseDocumentationCommand,
1424   VersionCommand,
1425   SaveToUndoBufferCommand,
1426   FreeBuffersCommand,
1427   NullCommand
1428 } CommandType;
1429
1430 typedef enum
1431 {
1432   AnnotateNameCommand,
1433   AnnotateFontColorCommand,
1434   AnnotateBackgroundColorCommand,
1435   AnnotateRotateCommand,
1436   AnnotateHelpCommand,
1437   AnnotateDismissCommand,
1438   TextHelpCommand,
1439   TextApplyCommand,
1440   ChopDirectionCommand,
1441   ChopHelpCommand,
1442   ChopDismissCommand,
1443   HorizontalChopCommand,
1444   VerticalChopCommand,
1445   ColorEditMethodCommand,
1446   ColorEditColorCommand,
1447   ColorEditBorderCommand,
1448   ColorEditFuzzCommand,
1449   ColorEditUndoCommand,
1450   ColorEditHelpCommand,
1451   ColorEditDismissCommand,
1452   CompositeOperatorsCommand,
1453   CompositeDissolveCommand,
1454   CompositeDisplaceCommand,
1455   CompositeHelpCommand,
1456   CompositeDismissCommand,
1457   CropHelpCommand,
1458   CropDismissCommand,
1459   RectifyCopyCommand,
1460   RectifyHelpCommand,
1461   RectifyDismissCommand,
1462   DrawElementCommand,
1463   DrawColorCommand,
1464   DrawStippleCommand,
1465   DrawWidthCommand,
1466   DrawUndoCommand,
1467   DrawHelpCommand,
1468   DrawDismissCommand,
1469   MatteEditMethod,
1470   MatteEditBorderCommand,
1471   MatteEditFuzzCommand,
1472   MatteEditValueCommand,
1473   MatteEditUndoCommand,
1474   MatteEditHelpCommand,
1475   MatteEditDismissCommand,
1476   PasteOperatorsCommand,
1477   PasteHelpCommand,
1478   PasteDismissCommand,
1479   RotateColorCommand,
1480   RotateDirectionCommand,
1481   RotateCropCommand,
1482   RotateSharpenCommand,
1483   RotateHelpCommand,
1484   RotateDismissCommand,
1485   HorizontalRotateCommand,
1486   VerticalRotateCommand,
1487   TileLoadCommand,
1488   TileNextCommand,
1489   TileFormerCommand,
1490   TileDeleteCommand,
1491   TileUpdateCommand
1492 } ModeType;
1493 \f
1494 /*
1495   Stipples.
1496 */
1497 #define BricksWidth  20
1498 #define BricksHeight  20
1499 #define DiagonalWidth  16
1500 #define DiagonalHeight  16
1501 #define HighlightWidth  8
1502 #define HighlightHeight  8
1503 #define ScalesWidth  16
1504 #define ScalesHeight  16
1505 #define ShadowWidth  8
1506 #define ShadowHeight  8
1507 #define VerticalWidth  16
1508 #define VerticalHeight  16
1509 #define WavyWidth  16
1510 #define WavyHeight  16
1511 \f
1512 /*
1513   Constant declaration.
1514 */
1515 static const int
1516   RoiDelta = 8;
1517
1518 static const unsigned char
1519   BricksBitmap[] =
1520   {
1521     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1522     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1523     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1524     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1525     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1526   },
1527   DiagonalBitmap[] =
1528   {
1529     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1530     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1531     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1532   },
1533   ScalesBitmap[] =
1534   {
1535     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1536     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1537     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1538   },
1539   VerticalBitmap[] =
1540   {
1541     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1542     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1543     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1544   },
1545   WavyBitmap[] =
1546   {
1547     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1548     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1549     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1550   };
1551 \f
1552 /*
1553   Function prototypes.
1554 */
1555 static CommandType
1556   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1557     const MagickStatusType,KeySym,Image **);
1558
1559 static Image
1560   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1561     Image **),
1562   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1563   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
1564   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
1565
1566 static MagickBooleanType
1567   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
1568   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1569   XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
1570   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
1571   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
1572   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1573   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
1574   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
1575   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1576   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
1577   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
1578   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
1579   XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
1580   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
1581   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
1582
1583 static void
1584   XDrawPanRectangle(Display *,XWindows *),
1585   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **),
1586   XMagnifyImage(Display *,XWindows *,XEvent *),
1587   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
1588   XPanImage(Display *,XWindows *,XEvent *),
1589   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1590     const KeySym),
1591   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1592   XScreenEvent(Display *,XWindows *,XEvent *),
1593   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1594 \f
1595 /*
1596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1597 %                                                                             %
1598 %                                                                             %
1599 %                                                                             %
1600 %   D i s p l a y I m a g e s                                                 %
1601 %                                                                             %
1602 %                                                                             %
1603 %                                                                             %
1604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1605 %
1606 %  DisplayImages() displays an image sequence to any X window screen.  It
1607 %  returns a value other than 0 if successful.  Check the exception member
1608 %  of image to determine the reason for any failure.
1609 %
1610 %  The format of the DisplayImages method is:
1611 %
1612 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1613 %        Image *images)
1614 %
1615 %  A description of each parameter follows:
1616 %
1617 %    o image_info: the image info.
1618 %
1619 %    o image: the image.
1620 %
1621 */
1622 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1623   Image *images)
1624 {
1625   char
1626     *argv[1];
1627
1628   Display
1629     *display;
1630
1631   Image
1632     *image;
1633
1634   register ssize_t
1635     i;
1636
1637   size_t
1638     state;
1639
1640   XrmDatabase
1641     resource_database;
1642
1643   XResourceInfo
1644     resource_info;
1645
1646   assert(image_info != (const ImageInfo *) NULL);
1647   assert(image_info->signature == MagickSignature);
1648   assert(images != (Image *) NULL);
1649   assert(images->signature == MagickSignature);
1650   if (images->debug != MagickFalse)
1651     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1652   display=XOpenDisplay(image_info->server_name);
1653   if (display == (Display *) NULL)
1654     {
1655       (void) ThrowMagickException(&images->exception,GetMagickModule(),
1656         XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
1657         image_info->server_name));
1658       return(MagickFalse);
1659     }
1660   if (images->exception.severity != UndefinedException)
1661     CatchException(&images->exception);
1662   (void) XSetErrorHandler(XError);
1663   resource_database=XGetResourceDatabase(display,GetClientName());
1664   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1665   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1666   if (image_info->page != (char *) NULL)
1667     resource_info.image_geometry=AcquireString(image_info->page);
1668   resource_info.immutable=MagickTrue;
1669   argv[0]=AcquireString(GetClientName());
1670   state=DefaultState;
1671   for (i=0; (state & ExitState) == 0; i++)
1672   {
1673     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1674       break;
1675     image=GetImageFromList(images,i % GetImageListLength(images));
1676     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
1677   }
1678   argv[0]=DestroyString(argv[0]);
1679   (void) XCloseDisplay(display);
1680   XDestroyResourceInfo(&resource_info);
1681   if (images->exception.severity != UndefinedException)
1682     return(MagickFalse);
1683   return(MagickTrue);
1684 }
1685 \f
1686 /*
1687 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1688 %                                                                             %
1689 %                                                                             %
1690 %                                                                             %
1691 %   R e m o t e D i s p l a y C o m m a n d                                   %
1692 %                                                                             %
1693 %                                                                             %
1694 %                                                                             %
1695 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1696 %
1697 %  RemoteDisplayCommand() encourages a remote display program to display the
1698 %  specified image filename.
1699 %
1700 %  The format of the RemoteDisplayCommand method is:
1701 %
1702 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1703 %        const char *window,const char *filename,ExceptionInfo *exception)
1704 %
1705 %  A description of each parameter follows:
1706 %
1707 %    o image_info: the image info.
1708 %
1709 %    o window: Specifies the name or id of an X window.
1710 %
1711 %    o filename: the name of the image filename to display.
1712 %
1713 %    o exception: return any errors or warnings in this structure.
1714 %
1715 */
1716 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1717   const char *window,const char *filename,ExceptionInfo *exception)
1718 {
1719   Display
1720     *display;
1721
1722   MagickStatusType
1723     status;
1724
1725   assert(image_info != (const ImageInfo *) NULL);
1726   assert(image_info->signature == MagickSignature);
1727   assert(filename != (char *) NULL);
1728   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1729   display=XOpenDisplay(image_info->server_name);
1730   if (display == (Display *) NULL)
1731     {
1732       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1733         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1734       return(MagickFalse);
1735     }
1736   (void) XSetErrorHandler(XError);
1737   status=XRemoteCommand(display,window,filename);
1738   (void) XCloseDisplay(display);
1739   return(status != 0 ? MagickTrue : MagickFalse);
1740 }
1741 \f
1742 /*
1743 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1744 %                                                                             %
1745 %                                                                             %
1746 %                                                                             %
1747 +   X A n n o t a t e E d i t I m a g e                                       %
1748 %                                                                             %
1749 %                                                                             %
1750 %                                                                             %
1751 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1752 %
1753 %  XAnnotateEditImage() annotates the image with text.
1754 %
1755 %  The format of the XAnnotateEditImage method is:
1756 %
1757 %      MagickBooleanType XAnnotateEditImage(Display *display,
1758 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
1759 %
1760 %  A description of each parameter follows:
1761 %
1762 %    o display: Specifies a connection to an X server;  returned from
1763 %      XOpenDisplay.
1764 %
1765 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1766 %
1767 %    o windows: Specifies a pointer to a XWindows structure.
1768 %
1769 %    o image: the image; returned from ReadImage.
1770 %
1771 */
1772
1773 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1774 {
1775   if (x > y)
1776     return(x);
1777   return(y);
1778 }
1779
1780 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1781 {
1782   if (x < y)
1783     return(x);
1784   return(y);
1785 }
1786
1787 static MagickBooleanType XAnnotateEditImage(Display *display,
1788   XResourceInfo *resource_info,XWindows *windows,Image *image)
1789 {
1790   static const char
1791     *AnnotateMenu[] =
1792     {
1793       "Font Name",
1794       "Font Color",
1795       "Box Color",
1796       "Rotate Text",
1797       "Help",
1798       "Dismiss",
1799       (char *) NULL
1800     },
1801     *TextMenu[] =
1802     {
1803       "Help",
1804       "Apply",
1805       (char *) NULL
1806     };
1807
1808   static const ModeType
1809     AnnotateCommands[] =
1810     {
1811       AnnotateNameCommand,
1812       AnnotateFontColorCommand,
1813       AnnotateBackgroundColorCommand,
1814       AnnotateRotateCommand,
1815       AnnotateHelpCommand,
1816       AnnotateDismissCommand
1817     },
1818     TextCommands[] =
1819     {
1820       TextHelpCommand,
1821       TextApplyCommand
1822     };
1823
1824   static MagickBooleanType
1825     transparent_box = MagickTrue,
1826     transparent_pen = MagickFalse;
1827
1828   static MagickRealType
1829     degrees = 0.0;
1830
1831   static unsigned int
1832     box_id = MaxNumberPens-2,
1833     font_id = 0,
1834     pen_id = 0;
1835
1836   char
1837     command[MaxTextExtent],
1838     text[MaxTextExtent];
1839
1840   const char
1841     *ColorMenu[MaxNumberPens+1];
1842
1843   Cursor
1844     cursor;
1845
1846   GC
1847     annotate_context;
1848
1849   int
1850     id,
1851     pen_number,
1852     status,
1853     x,
1854     y;
1855
1856   KeySym
1857     key_symbol;
1858
1859   register char
1860     *p;
1861
1862   register ssize_t
1863     i;
1864
1865   unsigned int
1866     height,
1867     width;
1868
1869   size_t
1870     state;
1871
1872   XAnnotateInfo
1873     *annotate_info,
1874     *previous_info;
1875
1876   XColor
1877     color;
1878
1879   XFontStruct
1880     *font_info;
1881
1882   XEvent
1883     event,
1884     text_event;
1885
1886   /*
1887     Map Command widget.
1888   */
1889   (void) CloneString(&windows->command.name,"Annotate");
1890   windows->command.data=4;
1891   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1892   (void) XMapRaised(display,windows->command.id);
1893   XClientMessage(display,windows->image.id,windows->im_protocols,
1894     windows->im_update_widget,CurrentTime);
1895   /*
1896     Track pointer until button 1 is pressed.
1897   */
1898   XQueryPosition(display,windows->image.id,&x,&y);
1899   (void) XSelectInput(display,windows->image.id,
1900     windows->image.attributes.event_mask | PointerMotionMask);
1901   cursor=XCreateFontCursor(display,XC_left_side);
1902   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1903   state=DefaultState;
1904   do
1905   {
1906     if (windows->info.mapped != MagickFalse)
1907       {
1908         /*
1909           Display pointer position.
1910         */
1911         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
1912           x+windows->image.x,y+windows->image.y);
1913         XInfoWidget(display,windows,text);
1914       }
1915     /*
1916       Wait for next event.
1917     */
1918     XScreenEvent(display,windows,&event);
1919     if (event.xany.window == windows->command.id)
1920       {
1921         /*
1922           Select a command from the Command widget.
1923         */
1924         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1925         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1926         if (id < 0)
1927           continue;
1928         switch (AnnotateCommands[id])
1929         {
1930           case AnnotateNameCommand:
1931           {
1932             const char
1933               *FontMenu[MaxNumberFonts];
1934
1935             int
1936               font_number;
1937
1938             /*
1939               Initialize menu selections.
1940             */
1941             for (i=0; i < MaxNumberFonts; i++)
1942               FontMenu[i]=resource_info->font_name[i];
1943             FontMenu[MaxNumberFonts-2]="Browser...";
1944             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1945             /*
1946               Select a font name from the pop-up menu.
1947             */
1948             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1949               (const char **) FontMenu,command);
1950             if (font_number < 0)
1951               break;
1952             if (font_number == (MaxNumberFonts-2))
1953               {
1954                 static char
1955                   font_name[MaxTextExtent] = "fixed";
1956
1957                 /*
1958                   Select a font name from a browser.
1959                 */
1960                 resource_info->font_name[font_number]=font_name;
1961                 XFontBrowserWidget(display,windows,"Select",font_name);
1962                 if (*font_name == '\0')
1963                   break;
1964               }
1965             /*
1966               Initialize font info.
1967             */
1968             font_info=XLoadQueryFont(display,resource_info->font_name[
1969               font_number]);
1970             if (font_info == (XFontStruct *) NULL)
1971               {
1972                 XNoticeWidget(display,windows,"Unable to load font:",
1973                   resource_info->font_name[font_number]);
1974                 break;
1975               }
1976             font_id=(unsigned int) font_number;
1977             (void) XFreeFont(display,font_info);
1978             break;
1979           }
1980           case AnnotateFontColorCommand:
1981           {
1982             /*
1983               Initialize menu selections.
1984             */
1985             for (i=0; i < (int) (MaxNumberPens-2); i++)
1986               ColorMenu[i]=resource_info->pen_colors[i];
1987             ColorMenu[MaxNumberPens-2]="transparent";
1988             ColorMenu[MaxNumberPens-1]="Browser...";
1989             ColorMenu[MaxNumberPens]=(const char *) NULL;
1990             /*
1991               Select a pen color from the pop-up menu.
1992             */
1993             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
1994               (const char **) ColorMenu,command);
1995             if (pen_number < 0)
1996               break;
1997             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
1998               MagickFalse;
1999             if (transparent_pen != MagickFalse)
2000               break;
2001             if (pen_number == (MaxNumberPens-1))
2002               {
2003                 static char
2004                   color_name[MaxTextExtent] = "gray";
2005
2006                 /*
2007                   Select a pen color from a dialog.
2008                 */
2009                 resource_info->pen_colors[pen_number]=color_name;
2010                 XColorBrowserWidget(display,windows,"Select",color_name);
2011                 if (*color_name == '\0')
2012                   break;
2013               }
2014             /*
2015               Set pen color.
2016             */
2017             (void) XParseColor(display,windows->map_info->colormap,
2018               resource_info->pen_colors[pen_number],&color);
2019             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2020               (unsigned int) MaxColors,&color);
2021             windows->pixel_info->pen_colors[pen_number]=color;
2022             pen_id=(unsigned int) pen_number;
2023             break;
2024           }
2025           case AnnotateBackgroundColorCommand:
2026           {
2027             /*
2028               Initialize menu selections.
2029             */
2030             for (i=0; i < (int) (MaxNumberPens-2); i++)
2031               ColorMenu[i]=resource_info->pen_colors[i];
2032             ColorMenu[MaxNumberPens-2]="transparent";
2033             ColorMenu[MaxNumberPens-1]="Browser...";
2034             ColorMenu[MaxNumberPens]=(const char *) NULL;
2035             /*
2036               Select a pen color from the pop-up menu.
2037             */
2038             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2039               (const char **) ColorMenu,command);
2040             if (pen_number < 0)
2041               break;
2042             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2043               MagickFalse;
2044             if (transparent_box != MagickFalse)
2045               break;
2046             if (pen_number == (MaxNumberPens-1))
2047               {
2048                 static char
2049                   color_name[MaxTextExtent] = "gray";
2050
2051                 /*
2052                   Select a pen color from a dialog.
2053                 */
2054                 resource_info->pen_colors[pen_number]=color_name;
2055                 XColorBrowserWidget(display,windows,"Select",color_name);
2056                 if (*color_name == '\0')
2057                   break;
2058               }
2059             /*
2060               Set pen color.
2061             */
2062             (void) XParseColor(display,windows->map_info->colormap,
2063               resource_info->pen_colors[pen_number],&color);
2064             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2065               (unsigned int) MaxColors,&color);
2066             windows->pixel_info->pen_colors[pen_number]=color;
2067             box_id=(unsigned int) pen_number;
2068             break;
2069           }
2070           case AnnotateRotateCommand:
2071           {
2072             int
2073               entry;
2074
2075             static char
2076               angle[MaxTextExtent] = "30.0";
2077
2078             static const char
2079               *RotateMenu[] =
2080               {
2081                 "-90",
2082                 "-45",
2083                 "-30",
2084                 "0",
2085                 "30",
2086                 "45",
2087                 "90",
2088                 "180",
2089                 "Dialog...",
2090                 (char *) NULL,
2091               };
2092
2093             /*
2094               Select a command from the pop-up menu.
2095             */
2096             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2097               command);
2098             if (entry < 0)
2099               break;
2100             if (entry != 8)
2101               {
2102                 degrees=StringToDouble(RotateMenu[entry]);
2103                 break;
2104               }
2105             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2106               angle);
2107             if (*angle == '\0')
2108               break;
2109             degrees=StringToDouble(angle);
2110             break;
2111           }
2112           case AnnotateHelpCommand:
2113           {
2114             XTextViewWidget(display,resource_info,windows,MagickFalse,
2115               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2116             break;
2117           }
2118           case AnnotateDismissCommand:
2119           {
2120             /*
2121               Prematurely exit.
2122             */
2123             state|=EscapeState;
2124             state|=ExitState;
2125             break;
2126           }
2127           default:
2128             break;
2129         }
2130         continue;
2131       }
2132     switch (event.type)
2133     {
2134       case ButtonPress:
2135       {
2136         if (event.xbutton.button != Button1)
2137           break;
2138         if (event.xbutton.window != windows->image.id)
2139           break;
2140         /*
2141           Change to text entering mode.
2142         */
2143         x=event.xbutton.x;
2144         y=event.xbutton.y;
2145         state|=ExitState;
2146         break;
2147       }
2148       case ButtonRelease:
2149         break;
2150       case Expose:
2151         break;
2152       case KeyPress:
2153       {
2154         if (event.xkey.window != windows->image.id)
2155           break;
2156         /*
2157           Respond to a user key press.
2158         */
2159         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2160           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2161         switch ((int) key_symbol)
2162         {
2163           case XK_Escape:
2164           case XK_F20:
2165           {
2166             /*
2167               Prematurely exit.
2168             */
2169             state|=EscapeState;
2170             state|=ExitState;
2171             break;
2172           }
2173           case XK_F1:
2174           case XK_Help:
2175           {
2176             XTextViewWidget(display,resource_info,windows,MagickFalse,
2177               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2178             break;
2179           }
2180           default:
2181           {
2182             (void) XBell(display,0);
2183             break;
2184           }
2185         }
2186         break;
2187       }
2188       case MotionNotify:
2189       {
2190         /*
2191           Map and unmap Info widget as cursor crosses its boundaries.
2192         */
2193         x=event.xmotion.x;
2194         y=event.xmotion.y;
2195         if (windows->info.mapped != MagickFalse)
2196           {
2197             if ((x < (int) (windows->info.x+windows->info.width)) &&
2198                 (y < (int) (windows->info.y+windows->info.height)))
2199               (void) XWithdrawWindow(display,windows->info.id,
2200                 windows->info.screen);
2201           }
2202         else
2203           if ((x > (int) (windows->info.x+windows->info.width)) ||
2204               (y > (int) (windows->info.y+windows->info.height)))
2205             (void) XMapWindow(display,windows->info.id);
2206         break;
2207       }
2208       default:
2209         break;
2210     }
2211   } while ((state & ExitState) == 0);
2212   (void) XSelectInput(display,windows->image.id,
2213     windows->image.attributes.event_mask);
2214   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2215   if ((state & EscapeState) != 0)
2216     return(MagickTrue);
2217   /*
2218     Set font info and check boundary conditions.
2219   */
2220   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2221   if (font_info == (XFontStruct *) NULL)
2222     {
2223       XNoticeWidget(display,windows,"Unable to load font:",
2224         resource_info->font_name[font_id]);
2225       font_info=windows->font_info;
2226     }
2227   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2228     x=(int) windows->image.width-font_info->max_bounds.width;
2229   if (y < (int) (font_info->ascent+font_info->descent))
2230     y=(int) font_info->ascent+font_info->descent;
2231   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2232       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2233     return(MagickFalse);
2234   /*
2235     Initialize annotate structure.
2236   */
2237   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2238   if (annotate_info == (XAnnotateInfo *) NULL)
2239     return(MagickFalse);
2240   XGetAnnotateInfo(annotate_info);
2241   annotate_info->x=x;
2242   annotate_info->y=y;
2243   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2244     annotate_info->stencil=OpaqueStencil;
2245   else
2246     if (transparent_box == MagickFalse)
2247       annotate_info->stencil=BackgroundStencil;
2248     else
2249       annotate_info->stencil=ForegroundStencil;
2250   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2251   annotate_info->degrees=degrees;
2252   annotate_info->font_info=font_info;
2253   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2254     windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
2255     sizeof(*annotate_info->text));
2256   if (annotate_info->text == (char *) NULL)
2257     return(MagickFalse);
2258   /*
2259     Create cursor and set graphic context.
2260   */
2261   cursor=XCreateFontCursor(display,XC_pencil);
2262   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2263   annotate_context=windows->image.annotate_context;
2264   (void) XSetFont(display,annotate_context,font_info->fid);
2265   (void) XSetBackground(display,annotate_context,
2266     windows->pixel_info->pen_colors[box_id].pixel);
2267   (void) XSetForeground(display,annotate_context,
2268     windows->pixel_info->pen_colors[pen_id].pixel);
2269   /*
2270     Begin annotating the image with text.
2271   */
2272   (void) CloneString(&windows->command.name,"Text");
2273   windows->command.data=0;
2274   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2275   state=DefaultState;
2276   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2277   text_event.xexpose.width=(int) font_info->max_bounds.width;
2278   text_event.xexpose.height=font_info->max_bounds.ascent+
2279     font_info->max_bounds.descent;
2280   p=annotate_info->text;
2281   do
2282   {
2283     /*
2284       Display text cursor.
2285     */
2286     *p='\0';
2287     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2288     /*
2289       Wait for next event.
2290     */
2291     XScreenEvent(display,windows,&event);
2292     if (event.xany.window == windows->command.id)
2293       {
2294         /*
2295           Select a command from the Command widget.
2296         */
2297         (void) XSetBackground(display,annotate_context,
2298           windows->pixel_info->background_color.pixel);
2299         (void) XSetForeground(display,annotate_context,
2300           windows->pixel_info->foreground_color.pixel);
2301         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2302         (void) XSetBackground(display,annotate_context,
2303           windows->pixel_info->pen_colors[box_id].pixel);
2304         (void) XSetForeground(display,annotate_context,
2305           windows->pixel_info->pen_colors[pen_id].pixel);
2306         if (id < 0)
2307           continue;
2308         switch (TextCommands[id])
2309         {
2310           case TextHelpCommand:
2311           {
2312             XTextViewWidget(display,resource_info,windows,MagickFalse,
2313               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2314             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2315             break;
2316           }
2317           case TextApplyCommand:
2318           {
2319             /*
2320               Finished annotating.
2321             */
2322             annotate_info->width=(unsigned int) XTextWidth(font_info,
2323               annotate_info->text,(int) strlen(annotate_info->text));
2324             XRefreshWindow(display,&windows->image,&text_event);
2325             state|=ExitState;
2326             break;
2327           }
2328           default:
2329             break;
2330         }
2331         continue;
2332       }
2333     /*
2334       Erase text cursor.
2335     */
2336     text_event.xexpose.x=x;
2337     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2338     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2339       (unsigned int) text_event.xexpose.width,(unsigned int)
2340       text_event.xexpose.height,MagickFalse);
2341     XRefreshWindow(display,&windows->image,&text_event);
2342     switch (event.type)
2343     {
2344       case ButtonPress:
2345       {
2346         if (event.xbutton.window != windows->image.id)
2347           break;
2348         if (event.xbutton.button == Button2)
2349           {
2350             /*
2351               Request primary selection.
2352             */
2353             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2354               windows->image.id,CurrentTime);
2355             break;
2356           }
2357         break;
2358       }
2359       case Expose:
2360       {
2361         if (event.xexpose.count == 0)
2362           {
2363             XAnnotateInfo
2364               *text_info;
2365
2366             /*
2367               Refresh Image window.
2368             */
2369             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2370             text_info=annotate_info;
2371             while (text_info != (XAnnotateInfo *) NULL)
2372             {
2373               if (annotate_info->stencil == ForegroundStencil)
2374                 (void) XDrawString(display,windows->image.id,annotate_context,
2375                   text_info->x,text_info->y,text_info->text,
2376                   (int) strlen(text_info->text));
2377               else
2378                 (void) XDrawImageString(display,windows->image.id,
2379                   annotate_context,text_info->x,text_info->y,text_info->text,
2380                   (int) strlen(text_info->text));
2381               text_info=text_info->previous;
2382             }
2383             (void) XDrawString(display,windows->image.id,annotate_context,
2384               x,y,"_",1);
2385           }
2386         break;
2387       }
2388       case KeyPress:
2389       {
2390         int
2391           length;
2392
2393         if (event.xkey.window != windows->image.id)
2394           break;
2395         /*
2396           Respond to a user key press.
2397         */
2398         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2399           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2400         *(command+length)='\0';
2401         if (((event.xkey.state & ControlMask) != 0) ||
2402             ((event.xkey.state & Mod1Mask) != 0))
2403           state|=ModifierState;
2404         if ((state & ModifierState) != 0)
2405           switch ((int) key_symbol)
2406           {
2407             case XK_u:
2408             case XK_U:
2409             {
2410               key_symbol=DeleteCommand;
2411               break;
2412             }
2413             default:
2414               break;
2415           }
2416         switch ((int) key_symbol)
2417         {
2418           case XK_BackSpace:
2419           {
2420             /*
2421               Erase one character.
2422             */
2423             if (p == annotate_info->text)
2424               {
2425                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2426                   break;
2427                 else
2428                   {
2429                     /*
2430                       Go to end of the previous line of text.
2431                     */
2432                     annotate_info=annotate_info->previous;
2433                     p=annotate_info->text;
2434                     x=annotate_info->x+annotate_info->width;
2435                     y=annotate_info->y;
2436                     if (annotate_info->width != 0)
2437                       p+=strlen(annotate_info->text);
2438                     break;
2439                   }
2440               }
2441             p--;
2442             x-=XTextWidth(font_info,p,1);
2443             text_event.xexpose.x=x;
2444             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2445             XRefreshWindow(display,&windows->image,&text_event);
2446             break;
2447           }
2448           case XK_bracketleft:
2449           {
2450             key_symbol=XK_Escape;
2451             break;
2452           }
2453           case DeleteCommand:
2454           {
2455             /*
2456               Erase the entire line of text.
2457             */
2458             while (p != annotate_info->text)
2459             {
2460               p--;
2461               x-=XTextWidth(font_info,p,1);
2462               text_event.xexpose.x=x;
2463               XRefreshWindow(display,&windows->image,&text_event);
2464             }
2465             break;
2466           }
2467           case XK_Escape:
2468           case XK_F20:
2469           {
2470             /*
2471               Finished annotating.
2472             */
2473             annotate_info->width=(unsigned int) XTextWidth(font_info,
2474               annotate_info->text,(int) strlen(annotate_info->text));
2475             XRefreshWindow(display,&windows->image,&text_event);
2476             state|=ExitState;
2477             break;
2478           }
2479           default:
2480           {
2481             /*
2482               Draw a single character on the Image window.
2483             */
2484             if ((state & ModifierState) != 0)
2485               break;
2486             if (*command == '\0')
2487               break;
2488             *p=(*command);
2489             if (annotate_info->stencil == ForegroundStencil)
2490               (void) XDrawString(display,windows->image.id,annotate_context,
2491                 x,y,p,1);
2492             else
2493               (void) XDrawImageString(display,windows->image.id,
2494                 annotate_context,x,y,p,1);
2495             x+=XTextWidth(font_info,p,1);
2496             p++;
2497             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2498               break;
2499           }
2500           case XK_Return:
2501           case XK_KP_Enter:
2502           {
2503             /*
2504               Advance to the next line of text.
2505             */
2506             *p='\0';
2507             annotate_info->width=(unsigned int) XTextWidth(font_info,
2508               annotate_info->text,(int) strlen(annotate_info->text));
2509             if (annotate_info->next != (XAnnotateInfo *) NULL)
2510               {
2511                 /*
2512                   Line of text already exists.
2513                 */
2514                 annotate_info=annotate_info->next;
2515                 x=annotate_info->x;
2516                 y=annotate_info->y;
2517                 p=annotate_info->text;
2518                 break;
2519               }
2520             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2521               sizeof(*annotate_info->next));
2522             if (annotate_info->next == (XAnnotateInfo *) NULL)
2523               return(MagickFalse);
2524             *annotate_info->next=(*annotate_info);
2525             annotate_info->next->previous=annotate_info;
2526             annotate_info=annotate_info->next;
2527             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2528               windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
2529               sizeof(*annotate_info->text));
2530             if (annotate_info->text == (char *) NULL)
2531               return(MagickFalse);
2532             annotate_info->y+=annotate_info->height;
2533             if (annotate_info->y > (int) windows->image.height)
2534               annotate_info->y=(int) annotate_info->height;
2535             annotate_info->next=(XAnnotateInfo *) NULL;
2536             x=annotate_info->x;
2537             y=annotate_info->y;
2538             p=annotate_info->text;
2539             break;
2540           }
2541         }
2542         break;
2543       }
2544       case KeyRelease:
2545       {
2546         /*
2547           Respond to a user key release.
2548         */
2549         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2550           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2551         state&=(~ModifierState);
2552         break;
2553       }
2554       case SelectionNotify:
2555       {
2556         Atom
2557           type;
2558
2559         int
2560           format;
2561
2562         unsigned char
2563           *data;
2564
2565         unsigned long
2566           after,
2567           length;
2568
2569         /*
2570           Obtain response from primary selection.
2571         */
2572         if (event.xselection.property == (Atom) None)
2573           break;
2574         status=XGetWindowProperty(display,event.xselection.requestor,
2575           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2576           &type,&format,&length,&after,&data);
2577         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2578             (length == 0))
2579           break;
2580         /*
2581           Annotate Image window with primary selection.
2582         */
2583         for (i=0; i < (ssize_t) length; i++)
2584         {
2585           if ((char) data[i] != '\n')
2586             {
2587               /*
2588                 Draw a single character on the Image window.
2589               */
2590               *p=(char) data[i];
2591               (void) XDrawString(display,windows->image.id,annotate_context,
2592                 x,y,p,1);
2593               x+=XTextWidth(font_info,p,1);
2594               p++;
2595               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2596                 continue;
2597             }
2598           /*
2599             Advance to the next line of text.
2600           */
2601           *p='\0';
2602           annotate_info->width=(unsigned int) XTextWidth(font_info,
2603             annotate_info->text,(int) strlen(annotate_info->text));
2604           if (annotate_info->next != (XAnnotateInfo *) NULL)
2605             {
2606               /*
2607                 Line of text already exists.
2608               */
2609               annotate_info=annotate_info->next;
2610               x=annotate_info->x;
2611               y=annotate_info->y;
2612               p=annotate_info->text;
2613               continue;
2614             }
2615           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2616             sizeof(*annotate_info->next));
2617           if (annotate_info->next == (XAnnotateInfo *) NULL)
2618             return(MagickFalse);
2619           *annotate_info->next=(*annotate_info);
2620           annotate_info->next->previous=annotate_info;
2621           annotate_info=annotate_info->next;
2622           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2623             windows->image.width/MagickMax(font_info->min_bounds.width,1)+2UL,
2624             sizeof(*annotate_info->text));
2625           if (annotate_info->text == (char *) NULL)
2626             return(MagickFalse);
2627           annotate_info->y+=annotate_info->height;
2628           if (annotate_info->y > (int) windows->image.height)
2629             annotate_info->y=(int) annotate_info->height;
2630           annotate_info->next=(XAnnotateInfo *) NULL;
2631           x=annotate_info->x;
2632           y=annotate_info->y;
2633           p=annotate_info->text;
2634         }
2635         (void) XFree((void *) data);
2636         break;
2637       }
2638       default:
2639         break;
2640     }
2641   } while ((state & ExitState) == 0);
2642   (void) XFreeCursor(display,cursor);
2643   /*
2644     Annotation is relative to image configuration.
2645   */
2646   width=(unsigned int) image->columns;
2647   height=(unsigned int) image->rows;
2648   x=0;
2649   y=0;
2650   if (windows->image.crop_geometry != (char *) NULL)
2651     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2652   /*
2653     Initialize annotated image.
2654   */
2655   XSetCursorState(display,windows,MagickTrue);
2656   XCheckRefreshWindows(display,windows);
2657   while (annotate_info != (XAnnotateInfo *) NULL)
2658   {
2659     if (annotate_info->width == 0)
2660       {
2661         /*
2662           No text on this line--  go to the next line of text.
2663         */
2664         previous_info=annotate_info->previous;
2665         annotate_info->text=(char *)
2666           RelinquishMagickMemory(annotate_info->text);
2667         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2668         annotate_info=previous_info;
2669         continue;
2670       }
2671     /*
2672       Determine pixel index for box and pen color.
2673     */
2674     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2675     if (windows->pixel_info->colors != 0)
2676       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2677         if (windows->pixel_info->pixels[i] ==
2678             windows->pixel_info->pen_colors[box_id].pixel)
2679           {
2680             windows->pixel_info->box_index=(unsigned short) i;
2681             break;
2682           }
2683     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2684     if (windows->pixel_info->colors != 0)
2685       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2686         if (windows->pixel_info->pixels[i] ==
2687             windows->pixel_info->pen_colors[pen_id].pixel)
2688           {
2689             windows->pixel_info->pen_index=(unsigned short) i;
2690             break;
2691           }
2692     /*
2693       Define the annotate geometry string.
2694     */
2695     annotate_info->x=(int)
2696       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2697     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2698       windows->image.y)/windows->image.ximage->height;
2699     (void) FormatMagickString(annotate_info->geometry,MaxTextExtent,
2700       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2701       height*annotate_info->height/windows->image.ximage->height,
2702       annotate_info->x+x,annotate_info->y+y);
2703     /*
2704       Annotate image with text.
2705     */
2706     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2707     if (status == 0)
2708       return(MagickFalse);
2709     /*
2710       Free up memory.
2711     */
2712     previous_info=annotate_info->previous;
2713     annotate_info->text=DestroyString(annotate_info->text);
2714     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2715     annotate_info=previous_info;
2716   }
2717   (void) XSetForeground(display,annotate_context,
2718     windows->pixel_info->foreground_color.pixel);
2719   (void) XSetBackground(display,annotate_context,
2720     windows->pixel_info->background_color.pixel);
2721   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2722   XSetCursorState(display,windows,MagickFalse);
2723   (void) XFreeFont(display,font_info);
2724   /*
2725     Update image configuration.
2726   */
2727   XConfigureImageColormap(display,resource_info,windows,image);
2728   (void) XConfigureImage(display,resource_info,windows,image);
2729   return(MagickTrue);
2730 }
2731 \f
2732 /*
2733 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2734 %                                                                             %
2735 %                                                                             %
2736 %                                                                             %
2737 +   X B a c k g r o u n d I m a g e                                           %
2738 %                                                                             %
2739 %                                                                             %
2740 %                                                                             %
2741 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2742 %
2743 %  XBackgroundImage() displays the image in the background of a window.
2744 %
2745 %  The format of the XBackgroundImage method is:
2746 %
2747 %      MagickBooleanType XBackgroundImage(Display *display,
2748 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
2749 %
2750 %  A description of each parameter follows:
2751 %
2752 %    o display: Specifies a connection to an X server; returned from
2753 %      XOpenDisplay.
2754 %
2755 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2756 %
2757 %    o windows: Specifies a pointer to a XWindows structure.
2758 %
2759 %    o image: the image.
2760 %
2761 */
2762 static MagickBooleanType XBackgroundImage(Display *display,
2763   XResourceInfo *resource_info,XWindows *windows,Image **image)
2764 {
2765 #define BackgroundImageTag  "Background/Image"
2766
2767   int
2768     status;
2769
2770   static char
2771     window_id[MaxTextExtent] = "root";
2772
2773   XResourceInfo
2774     background_resources;
2775
2776   /*
2777     Put image in background.
2778   */
2779   status=XDialogWidget(display,windows,"Background",
2780     "Enter window id (id 0x00 selects window with pointer):",window_id);
2781   if (*window_id == '\0')
2782     return(MagickFalse);
2783   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
2784   XInfoWidget(display,windows,BackgroundImageTag);
2785   XSetCursorState(display,windows,MagickTrue);
2786   XCheckRefreshWindows(display,windows);
2787   background_resources=(*resource_info);
2788   background_resources.window_id=window_id;
2789   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2790   status=XDisplayBackgroundImage(display,&background_resources,*image);
2791   if (status != MagickFalse)
2792     XClientMessage(display,windows->image.id,windows->im_protocols,
2793       windows->im_retain_colors,CurrentTime);
2794   XSetCursorState(display,windows,MagickFalse);
2795   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
2796   return(MagickTrue);
2797 }
2798 \f
2799 /*
2800 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2801 %                                                                             %
2802 %                                                                             %
2803 %                                                                             %
2804 +   X C h o p I m a g e                                                       %
2805 %                                                                             %
2806 %                                                                             %
2807 %                                                                             %
2808 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2809 %
2810 %  XChopImage() chops the X image.
2811 %
2812 %  The format of the XChopImage method is:
2813 %
2814 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2815 %      XWindows *windows,Image **image)
2816 %
2817 %  A description of each parameter follows:
2818 %
2819 %    o display: Specifies a connection to an X server; returned from
2820 %      XOpenDisplay.
2821 %
2822 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2823 %
2824 %    o windows: Specifies a pointer to a XWindows structure.
2825 %
2826 %    o image: the image.
2827 %
2828 */
2829 static MagickBooleanType XChopImage(Display *display,
2830   XResourceInfo *resource_info,XWindows *windows,Image **image)
2831 {
2832   static const char
2833     *ChopMenu[] =
2834     {
2835       "Direction",
2836       "Help",
2837       "Dismiss",
2838       (char *) NULL
2839     };
2840
2841   static ModeType
2842     direction = HorizontalChopCommand;
2843
2844   static const ModeType
2845     ChopCommands[] =
2846     {
2847       ChopDirectionCommand,
2848       ChopHelpCommand,
2849       ChopDismissCommand
2850     },
2851     DirectionCommands[] =
2852     {
2853       HorizontalChopCommand,
2854       VerticalChopCommand
2855     };
2856
2857   char
2858     text[MaxTextExtent];
2859
2860   Image
2861     *chop_image;
2862
2863   int
2864     id,
2865     x,
2866     y;
2867
2868   MagickRealType
2869     scale_factor;
2870
2871   RectangleInfo
2872     chop_info;
2873
2874   unsigned int
2875     distance,
2876     height,
2877     width;
2878
2879   size_t
2880     state;
2881
2882   XEvent
2883     event;
2884
2885   XSegment
2886     segment_info;
2887
2888   /*
2889     Map Command widget.
2890   */
2891   (void) CloneString(&windows->command.name,"Chop");
2892   windows->command.data=1;
2893   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2894   (void) XMapRaised(display,windows->command.id);
2895   XClientMessage(display,windows->image.id,windows->im_protocols,
2896     windows->im_update_widget,CurrentTime);
2897   /*
2898     Track pointer until button 1 is pressed.
2899   */
2900   XQueryPosition(display,windows->image.id,&x,&y);
2901   (void) XSelectInput(display,windows->image.id,
2902     windows->image.attributes.event_mask | PointerMotionMask);
2903   state=DefaultState;
2904   do
2905   {
2906     if (windows->info.mapped != MagickFalse)
2907       {
2908         /*
2909           Display pointer position.
2910         */
2911         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
2912           x+windows->image.x,y+windows->image.y);
2913         XInfoWidget(display,windows,text);
2914       }
2915     /*
2916       Wait for next event.
2917     */
2918     XScreenEvent(display,windows,&event);
2919     if (event.xany.window == windows->command.id)
2920       {
2921         /*
2922           Select a command from the Command widget.
2923         */
2924         id=XCommandWidget(display,windows,ChopMenu,&event);
2925         if (id < 0)
2926           continue;
2927         switch (ChopCommands[id])
2928         {
2929           case ChopDirectionCommand:
2930           {
2931             char
2932               command[MaxTextExtent];
2933
2934             static const char
2935               *Directions[] =
2936               {
2937                 "horizontal",
2938                 "vertical",
2939                 (char *) NULL,
2940               };
2941
2942             /*
2943               Select a command from the pop-up menu.
2944             */
2945             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2946             if (id >= 0)
2947               direction=DirectionCommands[id];
2948             break;
2949           }
2950           case ChopHelpCommand:
2951           {
2952             XTextViewWidget(display,resource_info,windows,MagickFalse,
2953               "Help Viewer - Image Chop",ImageChopHelp);
2954             break;
2955           }
2956           case ChopDismissCommand:
2957           {
2958             /*
2959               Prematurely exit.
2960             */
2961             state|=EscapeState;
2962             state|=ExitState;
2963             break;
2964           }
2965           default:
2966             break;
2967         }
2968         continue;
2969       }
2970     switch (event.type)
2971     {
2972       case ButtonPress:
2973       {
2974         if (event.xbutton.button != Button1)
2975           break;
2976         if (event.xbutton.window != windows->image.id)
2977           break;
2978         /*
2979           User has committed to start point of chopping line.
2980         */
2981         segment_info.x1=(short int) event.xbutton.x;
2982         segment_info.x2=(short int) event.xbutton.x;
2983         segment_info.y1=(short int) event.xbutton.y;
2984         segment_info.y2=(short int) event.xbutton.y;
2985         state|=ExitState;
2986         break;
2987       }
2988       case ButtonRelease:
2989         break;
2990       case Expose:
2991         break;
2992       case KeyPress:
2993       {
2994         char
2995           command[MaxTextExtent];
2996
2997         KeySym
2998           key_symbol;
2999
3000         if (event.xkey.window != windows->image.id)
3001           break;
3002         /*
3003           Respond to a user key press.
3004         */
3005         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3006           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3007         switch ((int) key_symbol)
3008         {
3009           case XK_Escape:
3010           case XK_F20:
3011           {
3012             /*
3013               Prematurely exit.
3014             */
3015             state|=EscapeState;
3016             state|=ExitState;
3017             break;
3018           }
3019           case XK_F1:
3020           case XK_Help:
3021           {
3022             (void) XSetFunction(display,windows->image.highlight_context,
3023               GXcopy);
3024             XTextViewWidget(display,resource_info,windows,MagickFalse,
3025               "Help Viewer - Image Chop",ImageChopHelp);
3026             (void) XSetFunction(display,windows->image.highlight_context,
3027               GXinvert);
3028             break;
3029           }
3030           default:
3031           {
3032             (void) XBell(display,0);
3033             break;
3034           }
3035         }
3036         break;
3037       }
3038       case MotionNotify:
3039       {
3040         /*
3041           Map and unmap Info widget as text cursor crosses its boundaries.
3042         */
3043         x=event.xmotion.x;
3044         y=event.xmotion.y;
3045         if (windows->info.mapped != MagickFalse)
3046           {
3047             if ((x < (int) (windows->info.x+windows->info.width)) &&
3048                 (y < (int) (windows->info.y+windows->info.height)))
3049               (void) XWithdrawWindow(display,windows->info.id,
3050                 windows->info.screen);
3051           }
3052         else
3053           if ((x > (int) (windows->info.x+windows->info.width)) ||
3054               (y > (int) (windows->info.y+windows->info.height)))
3055             (void) XMapWindow(display,windows->info.id);
3056       }
3057     }
3058   } while ((state & ExitState) == 0);
3059   (void) XSelectInput(display,windows->image.id,
3060     windows->image.attributes.event_mask);
3061   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3062   if ((state & EscapeState) != 0)
3063     return(MagickTrue);
3064   /*
3065     Draw line as pointer moves until the mouse button is released.
3066   */
3067   chop_info.width=0;
3068   chop_info.height=0;
3069   chop_info.x=0;
3070   chop_info.y=0;
3071   distance=0;
3072   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3073   state=DefaultState;
3074   do
3075   {
3076     if (distance > 9)
3077       {
3078         /*
3079           Display info and draw chopping line.
3080         */
3081         if (windows->info.mapped == MagickFalse)
3082           (void) XMapWindow(display,windows->info.id);
3083         (void) FormatMagickString(text,MaxTextExtent,
3084           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3085           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3086         XInfoWidget(display,windows,text);
3087         XHighlightLine(display,windows->image.id,
3088           windows->image.highlight_context,&segment_info);
3089       }
3090     else
3091       if (windows->info.mapped != MagickFalse)
3092         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3093     /*
3094       Wait for next event.
3095     */
3096     XScreenEvent(display,windows,&event);
3097     if (distance > 9)
3098       XHighlightLine(display,windows->image.id,
3099         windows->image.highlight_context,&segment_info);
3100     switch (event.type)
3101     {
3102       case ButtonPress:
3103       {
3104         segment_info.x2=(short int) event.xmotion.x;
3105         segment_info.y2=(short int) event.xmotion.y;
3106         break;
3107       }
3108       case ButtonRelease:
3109       {
3110         /*
3111           User has committed to chopping line.
3112         */
3113         segment_info.x2=(short int) event.xbutton.x;
3114         segment_info.y2=(short int) event.xbutton.y;
3115         state|=ExitState;
3116         break;
3117       }
3118       case Expose:
3119         break;
3120       case MotionNotify:
3121       {
3122         segment_info.x2=(short int) event.xmotion.x;
3123         segment_info.y2=(short int) event.xmotion.y;
3124       }
3125       default:
3126         break;
3127     }
3128     /*
3129       Check boundary conditions.
3130     */
3131     if (segment_info.x2 < 0)
3132       segment_info.x2=0;
3133     else
3134       if (segment_info.x2 > windows->image.ximage->width)
3135         segment_info.x2=windows->image.ximage->width;
3136     if (segment_info.y2 < 0)
3137       segment_info.y2=0;
3138     else
3139       if (segment_info.y2 > windows->image.ximage->height)
3140         segment_info.y2=windows->image.ximage->height;
3141     distance=(unsigned int)
3142       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3143        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3144     /*
3145       Compute chopping geometry.
3146     */
3147     if (direction == HorizontalChopCommand)
3148       {
3149         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3150         chop_info.x=windows->image.x+segment_info.x1;
3151         chop_info.height=0;
3152         chop_info.y=0;
3153         if (segment_info.x1 > (int) segment_info.x2)
3154           {
3155             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3156             chop_info.x=windows->image.x+segment_info.x2;
3157           }
3158       }
3159     else
3160       {
3161         chop_info.width=0;
3162         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3163         chop_info.x=0;
3164         chop_info.y=windows->image.y+segment_info.y1;
3165         if (segment_info.y1 > segment_info.y2)
3166           {
3167             chop_info.height=(size_t)
3168               (segment_info.y1-segment_info.y2+1);
3169             chop_info.y=windows->image.y+segment_info.y2;
3170           }
3171       }
3172   } while ((state & ExitState) == 0);
3173   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3174   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3175   if (distance <= 9)
3176     return(MagickTrue);
3177   /*
3178     Image chopping is relative to image configuration.
3179   */
3180   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
3181   XSetCursorState(display,windows,MagickTrue);
3182   XCheckRefreshWindows(display,windows);
3183   windows->image.window_changes.width=windows->image.ximage->width-
3184     (unsigned int) chop_info.width;
3185   windows->image.window_changes.height=windows->image.ximage->height-
3186     (unsigned int) chop_info.height;
3187   width=(unsigned int) (*image)->columns;
3188   height=(unsigned int) (*image)->rows;
3189   x=0;
3190   y=0;
3191   if (windows->image.crop_geometry != (char *) NULL)
3192     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3193   scale_factor=(MagickRealType) width/windows->image.ximage->width;
3194   chop_info.x+=x;
3195   chop_info.x=(int) (scale_factor*chop_info.x+0.5);
3196   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3197   scale_factor=(MagickRealType) height/windows->image.ximage->height;
3198   chop_info.y+=y;
3199   chop_info.y=(int) (scale_factor*chop_info.y+0.5);
3200   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3201   /*
3202     Chop image.
3203   */
3204   chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
3205   XSetCursorState(display,windows,MagickFalse);
3206   if (chop_image == (Image *) NULL)
3207     return(MagickFalse);
3208   *image=DestroyImage(*image);
3209   *image=chop_image;
3210   /*
3211     Update image configuration.
3212   */
3213   XConfigureImageColormap(display,resource_info,windows,*image);
3214   (void) XConfigureImage(display,resource_info,windows,*image);
3215   return(MagickTrue);
3216 }
3217 \f
3218 /*
3219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3220 %                                                                             %
3221 %                                                                             %
3222 %                                                                             %
3223 +   X C o l o r E d i t I m a g e                                             %
3224 %                                                                             %
3225 %                                                                             %
3226 %                                                                             %
3227 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3228 %
3229 %  XColorEditImage() allows the user to interactively change the color of one
3230 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3231 %
3232 %  The format of the XColorEditImage method is:
3233 %
3234 %      MagickBooleanType XColorEditImage(Display *display,
3235 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
3236 %
3237 %  A description of each parameter follows:
3238 %
3239 %    o display: Specifies a connection to an X server;  returned from
3240 %      XOpenDisplay.
3241 %
3242 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3243 %
3244 %    o windows: Specifies a pointer to a XWindows structure.
3245 %
3246 %    o image: the image; returned from ReadImage.
3247 %
3248 */
3249
3250
3251 static MagickBooleanType XColorEditImage(Display *display,
3252   XResourceInfo *resource_info,XWindows *windows,Image **image)
3253 {
3254   static const char
3255     *ColorEditMenu[] =
3256     {
3257       "Method",
3258       "Pixel Color",
3259       "Border Color",
3260       "Fuzz",
3261       "Undo",
3262       "Help",
3263       "Dismiss",
3264       (char *) NULL
3265     };
3266
3267   static const ModeType
3268     ColorEditCommands[] =
3269     {
3270       ColorEditMethodCommand,
3271       ColorEditColorCommand,
3272       ColorEditBorderCommand,
3273       ColorEditFuzzCommand,
3274       ColorEditUndoCommand,
3275       ColorEditHelpCommand,
3276       ColorEditDismissCommand
3277     };
3278
3279   static PaintMethod
3280     method = PointMethod;
3281
3282   static unsigned int
3283     pen_id = 0;
3284
3285   static XColor
3286     border_color = { 0, 0, 0, 0, 0, 0 };
3287
3288   char
3289     command[MaxTextExtent],
3290     text[MaxTextExtent];
3291
3292   Cursor
3293     cursor;
3294
3295   ExceptionInfo
3296     *exception;
3297
3298   int
3299     entry,
3300     id,
3301     x,
3302     x_offset,
3303     y,
3304     y_offset;
3305
3306   register PixelPacket
3307     *q;
3308
3309   register ssize_t
3310     i;
3311
3312   unsigned int
3313     height,
3314     width;
3315
3316   size_t
3317     state;
3318
3319   XColor
3320     color;
3321
3322   XEvent
3323     event;
3324
3325   /*
3326     Map Command widget.
3327   */
3328   (void) CloneString(&windows->command.name,"Color Edit");
3329   windows->command.data=4;
3330   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3331   (void) XMapRaised(display,windows->command.id);
3332   XClientMessage(display,windows->image.id,windows->im_protocols,
3333     windows->im_update_widget,CurrentTime);
3334   /*
3335     Make cursor.
3336   */
3337   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3338     resource_info->background_color,resource_info->foreground_color);
3339   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3340   /*
3341     Track pointer until button 1 is pressed.
3342   */
3343   XQueryPosition(display,windows->image.id,&x,&y);
3344   (void) XSelectInput(display,windows->image.id,
3345     windows->image.attributes.event_mask | PointerMotionMask);
3346   state=DefaultState;
3347   do
3348   {
3349     if (windows->info.mapped != MagickFalse)
3350       {
3351         /*
3352           Display pointer position.
3353         */
3354         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
3355           x+windows->image.x,y+windows->image.y);
3356         XInfoWidget(display,windows,text);
3357       }
3358     /*
3359       Wait for next event.
3360     */
3361     XScreenEvent(display,windows,&event);
3362     if (event.xany.window == windows->command.id)
3363       {
3364         /*
3365           Select a command from the Command widget.
3366         */
3367         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3368         if (id < 0)
3369           {
3370             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3371             continue;
3372           }
3373         switch (ColorEditCommands[id])
3374         {
3375           case ColorEditMethodCommand:
3376           {
3377             char
3378               **methods;
3379
3380             /*
3381               Select a method from the pop-up menu.
3382             */
3383             methods=(char **) GetMagickOptions(MagickMethodOptions);
3384             if (methods == (char **) NULL)
3385               break;
3386             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3387               (const char **) methods,command);
3388             if (entry >= 0)
3389               method=(PaintMethod) ParseMagickOption(MagickMethodOptions,
3390                 MagickFalse,methods[entry]);
3391             methods=DestroyStringList(methods);
3392             break;
3393           }
3394           case ColorEditColorCommand:
3395           {
3396             const char
3397               *ColorMenu[MaxNumberPens];
3398
3399             int
3400               pen_number;
3401
3402             /*
3403               Initialize menu selections.
3404             */
3405             for (i=0; i < (int) (MaxNumberPens-2); i++)
3406               ColorMenu[i]=resource_info->pen_colors[i];
3407             ColorMenu[MaxNumberPens-2]="Browser...";
3408             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3409             /*
3410               Select a pen color from the pop-up menu.
3411             */
3412             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3413               (const char **) ColorMenu,command);
3414             if (pen_number < 0)
3415               break;
3416             if (pen_number == (MaxNumberPens-2))
3417               {
3418                 static char
3419                   color_name[MaxTextExtent] = "gray";
3420
3421                 /*
3422                   Select a pen color from a dialog.
3423                 */
3424                 resource_info->pen_colors[pen_number]=color_name;
3425                 XColorBrowserWidget(display,windows,"Select",color_name);
3426                 if (*color_name == '\0')
3427                   break;
3428               }
3429             /*
3430               Set pen color.
3431             */
3432             (void) XParseColor(display,windows->map_info->colormap,
3433               resource_info->pen_colors[pen_number],&color);
3434             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3435               (unsigned int) MaxColors,&color);
3436             windows->pixel_info->pen_colors[pen_number]=color;
3437             pen_id=(unsigned int) pen_number;
3438             break;
3439           }
3440           case ColorEditBorderCommand:
3441           {
3442             const char
3443               *ColorMenu[MaxNumberPens];
3444
3445             int
3446               pen_number;
3447
3448             /*
3449               Initialize menu selections.
3450             */
3451             for (i=0; i < (int) (MaxNumberPens-2); i++)
3452               ColorMenu[i]=resource_info->pen_colors[i];
3453             ColorMenu[MaxNumberPens-2]="Browser...";
3454             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3455             /*
3456               Select a pen color from the pop-up menu.
3457             */
3458             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3459               (const char **) ColorMenu,command);
3460             if (pen_number < 0)
3461               break;
3462             if (pen_number == (MaxNumberPens-2))
3463               {
3464                 static char
3465                   color_name[MaxTextExtent] = "gray";
3466
3467                 /*
3468                   Select a pen color from a dialog.
3469                 */
3470                 resource_info->pen_colors[pen_number]=color_name;
3471                 XColorBrowserWidget(display,windows,"Select",color_name);
3472                 if (*color_name == '\0')
3473                   break;
3474               }
3475             /*
3476               Set border color.
3477             */
3478             (void) XParseColor(display,windows->map_info->colormap,
3479               resource_info->pen_colors[pen_number],&border_color);
3480             break;
3481           }
3482           case ColorEditFuzzCommand:
3483           {
3484             static char
3485               fuzz[MaxTextExtent];
3486
3487             static const char
3488               *FuzzMenu[] =
3489               {
3490                 "0%",
3491                 "2%",
3492                 "5%",
3493                 "10%",
3494                 "15%",
3495                 "Dialog...",
3496                 (char *) NULL,
3497               };
3498
3499             /*
3500               Select a command from the pop-up menu.
3501             */
3502             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3503               command);
3504             if (entry < 0)
3505               break;
3506             if (entry != 5)
3507               {
3508                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
3509                   QuantumRange+1.0);
3510                 break;
3511               }
3512             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3513             (void) XDialogWidget(display,windows,"Ok",
3514               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3515             if (*fuzz == '\0')
3516               break;
3517             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3518             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
3519             break;
3520           }
3521           case ColorEditUndoCommand:
3522           {
3523             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3524               image);
3525             break;
3526           }
3527           case ColorEditHelpCommand:
3528           default:
3529           {
3530             XTextViewWidget(display,resource_info,windows,MagickFalse,
3531               "Help Viewer - Image Annotation",ImageColorEditHelp);
3532             break;
3533           }
3534           case ColorEditDismissCommand:
3535           {
3536             /*
3537               Prematurely exit.
3538             */
3539             state|=EscapeState;
3540             state|=ExitState;
3541             break;
3542           }
3543         }
3544         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3545         continue;
3546       }
3547     switch (event.type)
3548     {
3549       case ButtonPress:
3550       {
3551         if (event.xbutton.button != Button1)
3552           break;
3553         if ((event.xbutton.window != windows->image.id) &&
3554             (event.xbutton.window != windows->magnify.id))
3555           break;
3556         /*
3557           exit loop.
3558         */
3559         x=event.xbutton.x;
3560         y=event.xbutton.y;
3561         (void) XMagickCommand(display,resource_info,windows,
3562           SaveToUndoBufferCommand,image);
3563         state|=UpdateConfigurationState;
3564         break;
3565       }
3566       case ButtonRelease:
3567       {
3568         if (event.xbutton.button != Button1)
3569           break;
3570         if ((event.xbutton.window != windows->image.id) &&
3571             (event.xbutton.window != windows->magnify.id))
3572           break;
3573         /*
3574           Update colormap information.
3575         */
3576         x=event.xbutton.x;
3577         y=event.xbutton.y;
3578         XConfigureImageColormap(display,resource_info,windows,*image);
3579         (void) XConfigureImage(display,resource_info,windows,*image);
3580         XInfoWidget(display,windows,text);
3581         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3582         state&=(~UpdateConfigurationState);
3583         break;
3584       }
3585       case Expose:
3586         break;
3587       case KeyPress:
3588       {
3589         KeySym
3590           key_symbol;
3591
3592         if (event.xkey.window == windows->magnify.id)
3593           {
3594             Window
3595               window;
3596
3597             window=windows->magnify.id;
3598             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3599           }
3600         if (event.xkey.window != windows->image.id)
3601           break;
3602         /*
3603           Respond to a user key press.
3604         */
3605         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3606           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3607         switch ((int) key_symbol)
3608         {
3609           case XK_Escape:
3610           case XK_F20:
3611           {
3612             /*
3613               Prematurely exit.
3614             */
3615             state|=ExitState;
3616             break;
3617           }
3618           case XK_F1:
3619           case XK_Help:
3620           {
3621             XTextViewWidget(display,resource_info,windows,MagickFalse,
3622               "Help Viewer - Image Annotation",ImageColorEditHelp);
3623             break;
3624           }
3625           default:
3626           {
3627             (void) XBell(display,0);
3628             break;
3629           }
3630         }
3631         break;
3632       }
3633       case MotionNotify:
3634       {
3635         /*
3636           Map and unmap Info widget as cursor crosses its boundaries.
3637         */
3638         x=event.xmotion.x;
3639         y=event.xmotion.y;
3640         if (windows->info.mapped != MagickFalse)
3641           {
3642             if ((x < (int) (windows->info.x+windows->info.width)) &&
3643                 (y < (int) (windows->info.y+windows->info.height)))
3644               (void) XWithdrawWindow(display,windows->info.id,
3645                 windows->info.screen);
3646           }
3647         else
3648           if ((x > (int) (windows->info.x+windows->info.width)) ||
3649               (y > (int) (windows->info.y+windows->info.height)))
3650             (void) XMapWindow(display,windows->info.id);
3651         break;
3652       }
3653       default:
3654         break;
3655     }
3656     if (event.xany.window == windows->magnify.id)
3657       {
3658         x=windows->magnify.x-windows->image.x;
3659         y=windows->magnify.y-windows->image.y;
3660       }
3661     x_offset=x;
3662     y_offset=y;
3663     if ((state & UpdateConfigurationState) != 0)
3664       {
3665         int
3666           x,
3667           y;
3668
3669         /*
3670           Pixel edit is relative to image configuration.
3671         */
3672         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3673           MagickTrue);
3674         color=windows->pixel_info->pen_colors[pen_id];
3675         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3676         width=(unsigned int) (*image)->columns;
3677         height=(unsigned int) (*image)->rows;
3678         x=0;
3679         y=0;
3680         if (windows->image.crop_geometry != (char *) NULL)
3681           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3682             &width,&height);
3683         x_offset=(int)
3684           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3685         y_offset=(int)
3686           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3687         if ((x_offset < 0) || (y_offset < 0))
3688           continue;
3689         if ((x_offset >= (ssize_t) (*image)->columns) ||
3690             (y_offset >= (ssize_t) (*image)->rows))
3691           continue;
3692         exception=(&(*image)->exception);
3693         switch (method)
3694         {
3695           case PointMethod:
3696           default:
3697           {
3698             /*
3699               Update color information using point algorithm.
3700             */
3701             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3702               return(MagickFalse);
3703             q=GetAuthenticPixels(*image,x_offset,y_offset,1,1,exception);
3704             if (q == (PixelPacket *) NULL)
3705               break;
3706             q->red=ScaleShortToQuantum(color.red);
3707             q->green=ScaleShortToQuantum(color.green);
3708             q->blue=ScaleShortToQuantum(color.blue);
3709             (void) SyncAuthenticPixels(*image,&(*image)->exception);
3710             break;
3711           }
3712           case ReplaceMethod:
3713           {
3714             PixelPacket
3715               target;
3716
3717             /*
3718               Update color information using replace algorithm.
3719             */
3720             (void) GetOneVirtualPixel(*image,x_offset,y_offset,&target,
3721               &(*image)->exception);
3722             if ((*image)->storage_class == DirectClass)
3723               {
3724                 for (y=0; y < (ssize_t) (*image)->rows; y++)
3725                 {
3726                   q=GetAuthenticPixels(*image,0,y,(*image)->columns,1,
3727                     exception);
3728                   if (q == (PixelPacket *) NULL)
3729                     break;
3730                   for (x=0; x < (int) (*image)->columns; x++)
3731                   {
3732                     if (IsColorSimilar(*image,q,&target))
3733                       {
3734                         q->red=ScaleShortToQuantum(color.red);
3735                         q->green=ScaleShortToQuantum(color.green);
3736                         q->blue=ScaleShortToQuantum(color.blue);
3737                       }
3738                     q++;
3739                   }
3740                   if (SyncAuthenticPixels(*image,exception) == MagickFalse)
3741                     break;
3742                 }
3743               }
3744             else
3745               {
3746                 for (i=0; i < (int) (*image)->colors; i++)
3747                   if (IsColorSimilar(*image,(*image)->colormap+i,&target))
3748                     {
3749                       (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
3750                       (*image)->colormap[i].green=ScaleShortToQuantum(
3751                         color.green);
3752                       (*image)->colormap[i].blue=ScaleShortToQuantum(
3753                         color.blue);
3754                     }
3755                 (void) SyncImage(*image);
3756               }
3757             break;
3758           }
3759           case FloodfillMethod:
3760           case FillToBorderMethod:
3761           {
3762             DrawInfo
3763               *draw_info;
3764
3765             MagickPixelPacket
3766               target;
3767
3768             /*
3769               Update color information using floodfill algorithm.
3770             */
3771             (void) GetOneVirtualMagickPixel(*image,x_offset,y_offset,&target,
3772               exception);
3773             if (method == FillToBorderMethod)
3774               {
3775                 target.red=(MagickRealType)
3776                   ScaleShortToQuantum(border_color.red);
3777                 target.green=(MagickRealType)
3778                   ScaleShortToQuantum(border_color.green);
3779                 target.blue=(MagickRealType)
3780                   ScaleShortToQuantum(border_color.blue);
3781               }
3782             draw_info=CloneDrawInfo(resource_info->image_info,
3783               (DrawInfo *) NULL);
3784             (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3785               &draw_info->fill,exception);
3786             (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
3787               x_offset,y_offset,method == FloodfillMethod ? MagickFalse :
3788               MagickTrue);
3789             draw_info=DestroyDrawInfo(draw_info);
3790             break;
3791           }
3792           case ResetMethod:
3793           {
3794             /*
3795               Update color information using reset algorithm.
3796             */
3797             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3798               return(MagickFalse);
3799             for (y=0; y < (ssize_t) (*image)->rows; y++)
3800             {
3801               q=QueueAuthenticPixels(*image,0,y,(*image)->columns,1,exception);
3802               if (q == (PixelPacket *) NULL)
3803                 break;
3804               for (x=0; x < (int) (*image)->columns; x++)
3805               {
3806                 q->red=ScaleShortToQuantum(color.red);
3807                 q->green=ScaleShortToQuantum(color.green);
3808                 q->blue=ScaleShortToQuantum(color.blue);
3809                 q++;
3810               }
3811               if (SyncAuthenticPixels(*image,exception) == MagickFalse)
3812                 break;
3813             }
3814             break;
3815           }
3816         }
3817         state&=(~UpdateConfigurationState);
3818       }
3819   } while ((state & ExitState) == 0);
3820   (void) XSelectInput(display,windows->image.id,
3821     windows->image.attributes.event_mask);
3822   XSetCursorState(display,windows,MagickFalse);
3823   (void) XFreeCursor(display,cursor);
3824   return(MagickTrue);
3825 }
3826 \f
3827 /*
3828 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3829 %                                                                             %
3830 %                                                                             %
3831 %                                                                             %
3832 +   X C o m p o s i t e I m a g e                                             %
3833 %                                                                             %
3834 %                                                                             %
3835 %                                                                             %
3836 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3837 %
3838 %  XCompositeImage() requests an image name from the user, reads the image and
3839 %  composites it with the X window image at a location the user chooses with
3840 %  the pointer.
3841 %
3842 %  The format of the XCompositeImage method is:
3843 %
3844 %      MagickBooleanType XCompositeImage(Display *display,
3845 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
3846 %
3847 %  A description of each parameter follows:
3848 %
3849 %    o display: Specifies a connection to an X server;  returned from
3850 %      XOpenDisplay.
3851 %
3852 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3853 %
3854 %    o windows: Specifies a pointer to a XWindows structure.
3855 %
3856 %    o image: the image; returned from ReadImage.
3857 %
3858 */
3859 static MagickBooleanType XCompositeImage(Display *display,
3860   XResourceInfo *resource_info,XWindows *windows,Image *image)
3861 {
3862   static char
3863     displacement_geometry[MaxTextExtent] = "30x30",
3864     filename[MaxTextExtent] = "\0";
3865
3866   static const char
3867     *CompositeMenu[] =
3868     {
3869       "Operators",
3870       "Dissolve",
3871       "Displace",
3872       "Help",
3873       "Dismiss",
3874       (char *) NULL
3875     };
3876
3877   static CompositeOperator
3878     compose = CopyCompositeOp;
3879
3880   static const ModeType
3881     CompositeCommands[] =
3882     {
3883       CompositeOperatorsCommand,
3884       CompositeDissolveCommand,
3885       CompositeDisplaceCommand,
3886       CompositeHelpCommand,
3887       CompositeDismissCommand
3888     };
3889
3890   char
3891     text[MaxTextExtent];
3892
3893   Cursor
3894     cursor;
3895
3896   Image
3897     *composite_image;
3898
3899   int
3900     entry,
3901     id,
3902     x,
3903     y;
3904
3905   MagickRealType
3906     blend,
3907     scale_factor;
3908
3909   RectangleInfo
3910     highlight_info,
3911     composite_info;
3912
3913   unsigned int
3914     height,
3915     width;
3916
3917   size_t
3918     state;
3919
3920   XEvent
3921     event;
3922
3923   /*
3924     Request image file name from user.
3925   */
3926   XFileBrowserWidget(display,windows,"Composite",filename);
3927   if (*filename == '\0')
3928     return(MagickTrue);
3929   /*
3930     Read image.
3931   */
3932   XSetCursorState(display,windows,MagickTrue);
3933   XCheckRefreshWindows(display,windows);
3934   (void) CopyMagickString(resource_info->image_info->filename,filename,
3935     MaxTextExtent);
3936   composite_image=ReadImage(resource_info->image_info,&image->exception);
3937   CatchException(&image->exception);
3938   XSetCursorState(display,windows,MagickFalse);
3939   if (composite_image == (Image *) NULL)
3940     return(MagickFalse);
3941   /*
3942     Map Command widget.
3943   */
3944   (void) CloneString(&windows->command.name,"Composite");
3945   windows->command.data=1;
3946   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3947   (void) XMapRaised(display,windows->command.id);
3948   XClientMessage(display,windows->image.id,windows->im_protocols,
3949     windows->im_update_widget,CurrentTime);
3950   /*
3951     Track pointer until button 1 is pressed.
3952   */
3953   XQueryPosition(display,windows->image.id,&x,&y);
3954   (void) XSelectInput(display,windows->image.id,
3955     windows->image.attributes.event_mask | PointerMotionMask);
3956   composite_info.x=windows->image.x+x;
3957   composite_info.y=windows->image.y+y;
3958   composite_info.width=0;
3959   composite_info.height=0;
3960   cursor=XCreateFontCursor(display,XC_ul_angle);
3961   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3962   blend=0.0;
3963   state=DefaultState;
3964   do
3965   {
3966     if (windows->info.mapped != MagickFalse)
3967       {
3968         /*
3969           Display pointer position.
3970         */
3971         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
3972           (long) composite_info.x,(long) composite_info.y);
3973         XInfoWidget(display,windows,text);
3974       }
3975     highlight_info=composite_info;
3976     highlight_info.x=composite_info.x-windows->image.x;
3977     highlight_info.y=composite_info.y-windows->image.y;
3978     XHighlightRectangle(display,windows->image.id,
3979       windows->image.highlight_context,&highlight_info);
3980     /*
3981       Wait for next event.
3982     */
3983     XScreenEvent(display,windows,&event);
3984     XHighlightRectangle(display,windows->image.id,
3985       windows->image.highlight_context,&highlight_info);
3986     if (event.xany.window == windows->command.id)
3987       {
3988         /*
3989           Select a command from the Command widget.
3990         */
3991         id=XCommandWidget(display,windows,CompositeMenu,&event);
3992         if (id < 0)
3993           continue;
3994         switch (CompositeCommands[id])
3995         {
3996           case CompositeOperatorsCommand:
3997           {
3998             char
3999               command[MaxTextExtent],
4000               **operators;
4001
4002             /*
4003               Select a command from the pop-up menu.
4004             */
4005             operators=GetMagickOptions(MagickComposeOptions);
4006             if (operators == (char **) NULL)
4007               break;
4008             entry=XMenuWidget(display,windows,CompositeMenu[id],
4009               (const char **) operators,command);
4010             if (entry >= 0)
4011               compose=(CompositeOperator) ParseMagickOption(
4012                 MagickComposeOptions,MagickFalse,operators[entry]);
4013             operators=DestroyStringList(operators);
4014             break;
4015           }
4016           case CompositeDissolveCommand:
4017           {
4018             static char
4019               factor[MaxTextExtent] = "20.0";
4020
4021             /*
4022               Dissolve the two images a given percent.
4023             */
4024             (void) XSetFunction(display,windows->image.highlight_context,
4025               GXcopy);
4026             (void) XDialogWidget(display,windows,"Dissolve",
4027               "Enter the blend factor (0.0 - 99.9%):",factor);
4028             (void) XSetFunction(display,windows->image.highlight_context,
4029               GXinvert);
4030             if (*factor == '\0')
4031               break;
4032             blend=StringToDouble(factor);
4033             compose=DissolveCompositeOp;
4034             break;
4035           }
4036           case CompositeDisplaceCommand:
4037           {
4038             /*
4039               Get horizontal and vertical scale displacement geometry.
4040             */
4041             (void) XSetFunction(display,windows->image.highlight_context,
4042               GXcopy);
4043             (void) XDialogWidget(display,windows,"Displace",
4044               "Enter the horizontal and vertical scale:",displacement_geometry);
4045             (void) XSetFunction(display,windows->image.highlight_context,
4046               GXinvert);
4047             if (*displacement_geometry == '\0')
4048               break;
4049             compose=DisplaceCompositeOp;
4050             break;
4051           }
4052           case CompositeHelpCommand:
4053           {
4054             (void) XSetFunction(display,windows->image.highlight_context,
4055               GXcopy);
4056             XTextViewWidget(display,resource_info,windows,MagickFalse,
4057               "Help Viewer - Image Composite",ImageCompositeHelp);
4058             (void) XSetFunction(display,windows->image.highlight_context,
4059               GXinvert);
4060             break;
4061           }
4062           case CompositeDismissCommand:
4063           {
4064             /*
4065               Prematurely exit.
4066             */
4067             state|=EscapeState;
4068             state|=ExitState;
4069             break;
4070           }
4071           default:
4072             break;
4073         }
4074         continue;
4075       }
4076     switch (event.type)
4077     {
4078       case ButtonPress:
4079       {
4080         if (image->debug != MagickFalse)
4081           (void) LogMagickEvent(X11Event,GetMagickModule(),
4082             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4083             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4084         if (event.xbutton.button != Button1)
4085           break;
4086         if (event.xbutton.window != windows->image.id)
4087           break;
4088         /*
4089           Change cursor.
4090         */
4091         composite_info.width=composite_image->columns;
4092         composite_info.height=composite_image->rows;
4093         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4094         composite_info.x=windows->image.x+event.xbutton.x;
4095         composite_info.y=windows->image.y+event.xbutton.y;
4096         break;
4097       }
4098       case ButtonRelease:
4099       {
4100         if (image->debug != MagickFalse)
4101           (void) LogMagickEvent(X11Event,GetMagickModule(),
4102             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4103             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4104         if (event.xbutton.button != Button1)
4105           break;
4106         if (event.xbutton.window != windows->image.id)
4107           break;
4108         if ((composite_info.width != 0) && (composite_info.height != 0))
4109           {
4110             /*
4111               User has selected the location of the composite image.
4112             */
4113             composite_info.x=windows->image.x+event.xbutton.x;
4114             composite_info.y=windows->image.y+event.xbutton.y;
4115             state|=ExitState;
4116           }
4117         break;
4118       }
4119       case Expose:
4120         break;
4121       case KeyPress:
4122       {
4123         char
4124           command[MaxTextExtent];
4125
4126         KeySym
4127           key_symbol;
4128
4129         int
4130           length;
4131
4132         if (event.xkey.window != windows->image.id)
4133           break;
4134         /*
4135           Respond to a user key press.
4136         */
4137         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4138           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4139         *(command+length)='\0';
4140         if (image->debug != MagickFalse)
4141           (void) LogMagickEvent(X11Event,GetMagickModule(),
4142             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4143         switch ((int) key_symbol)
4144         {
4145           case XK_Escape:
4146           case XK_F20:
4147           {
4148             /*
4149               Prematurely exit.
4150             */
4151             composite_image=DestroyImage(composite_image);
4152             state|=EscapeState;
4153             state|=ExitState;
4154             break;
4155           }
4156           case XK_F1:
4157           case XK_Help:
4158           {
4159             (void) XSetFunction(display,windows->image.highlight_context,
4160               GXcopy);
4161             XTextViewWidget(display,resource_info,windows,MagickFalse,
4162               "Help Viewer - Image Composite",ImageCompositeHelp);
4163             (void) XSetFunction(display,windows->image.highlight_context,
4164               GXinvert);
4165             break;
4166           }
4167           default:
4168           {
4169             (void) XBell(display,0);
4170             break;
4171           }
4172         }
4173         break;
4174       }
4175       case MotionNotify:
4176       {
4177         /*
4178           Map and unmap Info widget as text cursor crosses its boundaries.
4179         */
4180         x=event.xmotion.x;
4181         y=event.xmotion.y;
4182         if (windows->info.mapped != MagickFalse)
4183           {
4184             if ((x < (int) (windows->info.x+windows->info.width)) &&
4185                 (y < (int) (windows->info.y+windows->info.height)))
4186               (void) XWithdrawWindow(display,windows->info.id,
4187                 windows->info.screen);
4188           }
4189         else
4190           if ((x > (int) (windows->info.x+windows->info.width)) ||
4191               (y > (int) (windows->info.y+windows->info.height)))
4192             (void) XMapWindow(display,windows->info.id);
4193         composite_info.x=windows->image.x+x;
4194         composite_info.y=windows->image.y+y;
4195         break;
4196       }
4197       default:
4198       {
4199         if (image->debug != MagickFalse)
4200           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4201             event.type);
4202         break;
4203       }
4204     }
4205   } while ((state & ExitState) == 0);
4206   (void) XSelectInput(display,windows->image.id,
4207     windows->image.attributes.event_mask);
4208   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4209   XSetCursorState(display,windows,MagickFalse);
4210   (void) XFreeCursor(display,cursor);
4211   if ((state & EscapeState) != 0)
4212     return(MagickTrue);
4213   /*
4214     Image compositing is relative to image configuration.
4215   */
4216   XSetCursorState(display,windows,MagickTrue);
4217   XCheckRefreshWindows(display,windows);
4218   width=(unsigned int) image->columns;
4219   height=(unsigned int) image->rows;
4220   x=0;
4221   y=0;
4222   if (windows->image.crop_geometry != (char *) NULL)
4223     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4224   scale_factor=(MagickRealType) width/windows->image.ximage->width;
4225   composite_info.x+=x;
4226   composite_info.x=(int) (scale_factor*composite_info.x+0.5);
4227   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4228   scale_factor=(MagickRealType) height/windows->image.ximage->height;
4229   composite_info.y+=y;
4230   composite_info.y=(int) (scale_factor*composite_info.y+0.5);
4231   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4232   if ((composite_info.width != composite_image->columns) ||
4233       (composite_info.height != composite_image->rows))
4234     {
4235       Image
4236         *resize_image;
4237
4238       /*
4239         Scale composite image.
4240       */
4241       resize_image=ResizeImage(composite_image,composite_info.width,
4242         composite_info.height,composite_image->filter,composite_image->blur,
4243         &image->exception);
4244       composite_image=DestroyImage(composite_image);
4245       if (resize_image == (Image *) NULL)
4246         {
4247           XSetCursorState(display,windows,MagickFalse);
4248           return(MagickFalse);
4249         }
4250       composite_image=resize_image;
4251     }
4252   if (compose == DisplaceCompositeOp)
4253     (void) SetImageArtifact(composite_image,"compose:args",
4254       displacement_geometry);
4255   if (blend != 0.0)
4256     {
4257       ExceptionInfo
4258         *exception;
4259
4260       int
4261         y;
4262
4263       Quantum
4264         opacity;
4265
4266       register int
4267         x;
4268
4269       register PixelPacket
4270         *q;
4271
4272       /*
4273         Create mattes for blending.
4274       */
4275       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4276       opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4277         ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4278       if (SetImageStorageClass(image,DirectClass) == MagickFalse)
4279         return(MagickFalse);
4280       image->matte=MagickTrue;
4281       exception=(&image->exception);
4282       for (y=0; y < (ssize_t) image->rows; y++)
4283       {
4284         q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
4285         if (q == (PixelPacket *) NULL)
4286           break;
4287         for (x=0; x < (int) image->columns; x++)
4288         {
4289           q->opacity=opacity;
4290           q++;
4291         }
4292         if (SyncAuthenticPixels(image,exception) == MagickFalse)
4293           break;
4294       }
4295     }
4296   /*
4297     Composite image with X Image window.
4298   */
4299   (void) CompositeImage(image,compose,composite_image,composite_info.x,
4300     composite_info.y);
4301   composite_image=DestroyImage(composite_image);
4302   XSetCursorState(display,windows,MagickFalse);
4303   /*
4304     Update image configuration.
4305   */
4306   XConfigureImageColormap(display,resource_info,windows,image);
4307   (void) XConfigureImage(display,resource_info,windows,image);
4308   return(MagickTrue);
4309 }
4310 \f
4311 /*
4312 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4313 %                                                                             %
4314 %                                                                             %
4315 %                                                                             %
4316 +   X C o n f i g u r e I m a g e                                             %
4317 %                                                                             %
4318 %                                                                             %
4319 %                                                                             %
4320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4321 %
4322 %  XConfigureImage() creates a new X image.  It also notifies the window
4323 %  manager of the new image size and configures the transient widows.
4324 %
4325 %  The format of the XConfigureImage method is:
4326 %
4327 %      MagickBooleanType XConfigureImage(Display *display,
4328 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
4329 %
4330 %  A description of each parameter follows:
4331 %
4332 %    o display: Specifies a connection to an X server; returned from
4333 %      XOpenDisplay.
4334 %
4335 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4336 %
4337 %    o windows: Specifies a pointer to a XWindows structure.
4338 %
4339 %    o image: the image.
4340 %
4341 %
4342 */
4343 static MagickBooleanType XConfigureImage(Display *display,
4344   XResourceInfo *resource_info,XWindows *windows,Image *image)
4345 {
4346   char
4347     geometry[MaxTextExtent];
4348
4349   ssize_t
4350     x,
4351     y;
4352
4353   MagickStatusType
4354     status;
4355
4356   size_t
4357     mask,
4358     height,
4359     width;
4360
4361   XSizeHints
4362     *size_hints;
4363
4364   XWindowChanges
4365     window_changes;
4366
4367   /*
4368     Dismiss if window dimensions are zero.
4369   */
4370   width=(unsigned int) windows->image.window_changes.width;
4371   height=(unsigned int) windows->image.window_changes.height;
4372   if (image->debug != MagickFalse)
4373     (void) LogMagickEvent(X11Event,GetMagickModule(),
4374       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4375       windows->image.ximage->height,(double) width,(double) height);
4376   if ((width*height) == 0)
4377     return(MagickTrue);
4378   x=0;
4379   y=0;
4380   /*
4381     Resize image to fit Image window dimensions.
4382   */
4383   XSetCursorState(display,windows,MagickTrue);
4384   (void) XFlush(display);
4385   if (((int) width != windows->image.ximage->width) ||
4386       ((int) height != windows->image.ximage->height))
4387     image->taint=MagickTrue;
4388   windows->magnify.x=(int)
4389     width*windows->magnify.x/windows->image.ximage->width;
4390   windows->magnify.y=(int)
4391     height*windows->magnify.y/windows->image.ximage->height;
4392   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4393   windows->image.y=(int)
4394     (height*windows->image.y/windows->image.ximage->height);
4395   status=XMakeImage(display,resource_info,&windows->image,image,
4396     (unsigned int) width,(unsigned int) height);
4397   if (status == MagickFalse)
4398     XNoticeWidget(display,windows,"Unable to configure X image:",
4399       windows->image.name);
4400   /*
4401     Notify window manager of the new configuration.
4402   */
4403   if (resource_info->image_geometry != (char *) NULL)
4404     (void) FormatMagickString(geometry,MaxTextExtent,"%s>!",
4405       resource_info->image_geometry);
4406   else
4407     (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4408       XDisplayWidth(display,windows->image.screen),
4409       XDisplayHeight(display,windows->image.screen));
4410   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4411   window_changes.width=(int) width;
4412   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4413     window_changes.width=XDisplayWidth(display,windows->image.screen);
4414   window_changes.height=(int) height;
4415   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4416     window_changes.height=XDisplayHeight(display,windows->image.screen);
4417   mask=(size_t) (CWWidth | CWHeight);
4418   if (resource_info->backdrop)
4419     {
4420       mask|=CWX | CWY;
4421       window_changes.x=(int)
4422         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4423       window_changes.y=(int)
4424         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4425     }
4426   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4427     (unsigned int) mask,&window_changes);
4428   (void) XClearWindow(display,windows->image.id);
4429   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4430   /*
4431     Update Magnify window configuration.
4432   */
4433   if (windows->magnify.mapped != MagickFalse)
4434     XMakeMagnifyImage(display,windows);
4435   windows->pan.crop_geometry=windows->image.crop_geometry;
4436   XBestIconSize(display,&windows->pan,image);
4437   while (((windows->pan.width << 1) < MaxIconSize) &&
4438          ((windows->pan.height << 1) < MaxIconSize))
4439   {
4440     windows->pan.width<<=1;
4441     windows->pan.height<<=1;
4442   }
4443   if (windows->pan.geometry != (char *) NULL)
4444     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4445       &windows->pan.width,&windows->pan.height);
4446   window_changes.width=(int) windows->pan.width;
4447   window_changes.height=(int) windows->pan.height;
4448   size_hints=XAllocSizeHints();
4449   if (size_hints != (XSizeHints *) NULL)
4450     {
4451       /*
4452         Set new size hints.
4453       */
4454       size_hints->flags=PSize | PMinSize | PMaxSize;
4455       size_hints->width=window_changes.width;
4456       size_hints->height=window_changes.height;
4457       size_hints->min_width=size_hints->width;
4458       size_hints->min_height=size_hints->height;
4459       size_hints->max_width=size_hints->width;
4460       size_hints->max_height=size_hints->height;
4461       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4462       (void) XFree((void *) size_hints);
4463     }
4464   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4465     (unsigned int) (CWWidth | CWHeight),&window_changes);
4466   /*
4467     Update icon window configuration.
4468   */
4469   windows->icon.crop_geometry=windows->image.crop_geometry;
4470   XBestIconSize(display,&windows->icon,image);
4471   window_changes.width=(int) windows->icon.width;
4472   window_changes.height=(int) windows->icon.height;
4473   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4474     (unsigned int) (CWWidth | CWHeight),&window_changes);
4475   XSetCursorState(display,windows,MagickFalse);
4476   return(status != 0 ? MagickTrue : MagickFalse);
4477 }
4478 \f
4479 /*
4480 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4481 %                                                                             %
4482 %                                                                             %
4483 %                                                                             %
4484 +   X C r o p I m a g e                                                       %
4485 %                                                                             %
4486 %                                                                             %
4487 %                                                                             %
4488 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4489 %
4490 %  XCropImage() allows the user to select a region of the image and crop, copy,
4491 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4492 %  the image with XPasteImage.
4493 %
4494 %  The format of the XCropImage method is:
4495 %
4496 %      MagickBooleanType XCropImage(Display *display,
4497 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4498 %        const ClipboardMode mode)
4499 %
4500 %  A description of each parameter follows:
4501 %
4502 %    o display: Specifies a connection to an X server; returned from
4503 %      XOpenDisplay.
4504 %
4505 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4506 %
4507 %    o windows: Specifies a pointer to a XWindows structure.
4508 %
4509 %    o image: the image; returned from ReadImage.
4510 %
4511 %    o mode: This unsigned value specified whether the image should be
4512 %      cropped, copied, or cut.
4513 %
4514 */
4515 static MagickBooleanType XCropImage(Display *display,
4516   XResourceInfo *resource_info,XWindows *windows,Image *image,
4517   const ClipboardMode mode)
4518 {
4519   static const char
4520     *CropModeMenu[] =
4521     {
4522       "Help",
4523       "Dismiss",
4524       (char *) NULL
4525     },
4526     *RectifyModeMenu[] =
4527     {
4528       "Crop",
4529       "Help",
4530       "Dismiss",
4531       (char *) NULL
4532     };
4533
4534   static const ModeType
4535     CropCommands[] =
4536     {
4537       CropHelpCommand,
4538       CropDismissCommand
4539     },
4540     RectifyCommands[] =
4541     {
4542       RectifyCopyCommand,
4543       RectifyHelpCommand,
4544       RectifyDismissCommand
4545     };
4546
4547   char
4548     command[MaxTextExtent],
4549     text[MaxTextExtent];
4550
4551   Cursor
4552     cursor;
4553
4554   ExceptionInfo
4555     *exception;
4556
4557   int
4558     id,
4559     x,
4560     y;
4561
4562   KeySym
4563     key_symbol;
4564
4565   Image
4566     *crop_image;
4567
4568   MagickRealType
4569     scale_factor;
4570
4571   RectangleInfo
4572     crop_info,
4573     highlight_info;
4574
4575   register PixelPacket
4576     *q;
4577
4578   unsigned int
4579     height,
4580     width;
4581
4582   size_t
4583     state;
4584
4585   XEvent
4586     event;
4587
4588   /*
4589     Map Command widget.
4590   */
4591   switch (mode)
4592   {
4593     case CopyMode:
4594     {
4595       (void) CloneString(&windows->command.name,"Copy");
4596       break;
4597     }
4598     case CropMode:
4599     {
4600       (void) CloneString(&windows->command.name,"Crop");
4601       break;
4602     }
4603     case CutMode:
4604     {
4605       (void) CloneString(&windows->command.name,"Cut");
4606       break;
4607     }
4608   }
4609   RectifyModeMenu[0]=windows->command.name;
4610   windows->command.data=0;
4611   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4612   (void) XMapRaised(display,windows->command.id);
4613   XClientMessage(display,windows->image.id,windows->im_protocols,
4614     windows->im_update_widget,CurrentTime);
4615   /*
4616     Track pointer until button 1 is pressed.
4617   */
4618   XQueryPosition(display,windows->image.id,&x,&y);
4619   (void) XSelectInput(display,windows->image.id,
4620     windows->image.attributes.event_mask | PointerMotionMask);
4621   crop_info.x=windows->image.x+x;
4622   crop_info.y=windows->image.y+y;
4623   crop_info.width=0;
4624   crop_info.height=0;
4625   cursor=XCreateFontCursor(display,XC_fleur);
4626   state=DefaultState;
4627   do
4628   {
4629     if (windows->info.mapped != MagickFalse)
4630       {
4631         /*
4632           Display pointer position.
4633         */
4634         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
4635           (long) crop_info.x,(long) crop_info.y);
4636         XInfoWidget(display,windows,text);
4637       }
4638     /*
4639       Wait for next event.
4640     */
4641     XScreenEvent(display,windows,&event);
4642     if (event.xany.window == windows->command.id)
4643       {
4644         /*
4645           Select a command from the Command widget.
4646         */
4647         id=XCommandWidget(display,windows,CropModeMenu,&event);
4648         if (id < 0)
4649           continue;
4650         switch (CropCommands[id])
4651         {
4652           case CropHelpCommand:
4653           {
4654             switch (mode)
4655             {
4656               case CopyMode:
4657               {
4658                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4659                   "Help Viewer - Image Copy",ImageCopyHelp);
4660                 break;
4661               }
4662               case CropMode:
4663               {
4664                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4665                   "Help Viewer - Image Crop",ImageCropHelp);
4666                 break;
4667               }
4668               case CutMode:
4669               {
4670                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4671                   "Help Viewer - Image Cut",ImageCutHelp);
4672                 break;
4673               }
4674             }
4675             break;
4676           }
4677           case CropDismissCommand:
4678           {
4679             /*
4680               Prematurely exit.
4681             */
4682             state|=EscapeState;
4683             state|=ExitState;
4684             break;
4685           }
4686           default:
4687             break;
4688         }
4689         continue;
4690       }
4691     switch (event.type)
4692     {
4693       case ButtonPress:
4694       {
4695         if (event.xbutton.button != Button1)
4696           break;
4697         if (event.xbutton.window != windows->image.id)
4698           break;
4699         /*
4700           Note first corner of cropping rectangle-- exit loop.
4701         */
4702         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4703         crop_info.x=windows->image.x+event.xbutton.x;
4704         crop_info.y=windows->image.y+event.xbutton.y;
4705         state|=ExitState;
4706         break;
4707       }
4708       case ButtonRelease:
4709         break;
4710       case Expose:
4711         break;
4712       case KeyPress:
4713       {
4714         if (event.xkey.window != windows->image.id)
4715           break;
4716         /*
4717           Respond to a user key press.
4718         */
4719         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4720           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4721         switch ((int) key_symbol)
4722         {
4723           case XK_Escape:
4724           case XK_F20:
4725           {
4726             /*
4727               Prematurely exit.
4728             */
4729             state|=EscapeState;
4730             state|=ExitState;
4731             break;
4732           }
4733           case XK_F1:
4734           case XK_Help:
4735           {
4736             switch (mode)
4737             {
4738               case CopyMode:
4739               {
4740                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4741                   "Help Viewer - Image Copy",ImageCopyHelp);
4742                 break;
4743               }
4744               case CropMode:
4745               {
4746                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4747                   "Help Viewer - Image Crop",ImageCropHelp);
4748                 break;
4749               }
4750               case CutMode:
4751               {
4752                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4753                   "Help Viewer - Image Cut",ImageCutHelp);
4754                 break;
4755               }
4756             }
4757             break;
4758           }
4759           default:
4760           {
4761             (void) XBell(display,0);
4762             break;
4763           }
4764         }
4765         break;
4766       }
4767       case MotionNotify:
4768       {
4769         if (event.xmotion.window != windows->image.id)
4770           break;
4771         /*
4772           Map and unmap Info widget as text cursor crosses its boundaries.
4773         */
4774         x=event.xmotion.x;
4775         y=event.xmotion.y;
4776         if (windows->info.mapped != MagickFalse)
4777           {
4778             if ((x < (int) (windows->info.x+windows->info.width)) &&
4779                 (y < (int) (windows->info.y+windows->info.height)))
4780               (void) XWithdrawWindow(display,windows->info.id,
4781                 windows->info.screen);
4782           }
4783         else
4784           if ((x > (int) (windows->info.x+windows->info.width)) ||
4785               (y > (int) (windows->info.y+windows->info.height)))
4786             (void) XMapWindow(display,windows->info.id);
4787         crop_info.x=windows->image.x+x;
4788         crop_info.y=windows->image.y+y;
4789         break;
4790       }
4791       default:
4792         break;
4793     }
4794   } while ((state & ExitState) == 0);
4795   (void) XSelectInput(display,windows->image.id,
4796     windows->image.attributes.event_mask);
4797   if ((state & EscapeState) != 0)
4798     {
4799       /*
4800         User want to exit without cropping.
4801       */
4802       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4803       (void) XFreeCursor(display,cursor);
4804       return(MagickTrue);
4805     }
4806   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4807   do
4808   {
4809     /*
4810       Size rectangle as pointer moves until the mouse button is released.
4811     */
4812     x=(int) crop_info.x;
4813     y=(int) crop_info.y;
4814     crop_info.width=0;
4815     crop_info.height=0;
4816     state=DefaultState;
4817     do
4818     {
4819       highlight_info=crop_info;
4820       highlight_info.x=crop_info.x-windows->image.x;
4821       highlight_info.y=crop_info.y-windows->image.y;
4822       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4823         {
4824           /*
4825             Display info and draw cropping rectangle.
4826           */
4827           if (windows->info.mapped == MagickFalse)
4828             (void) XMapWindow(display,windows->info.id);
4829           (void) FormatMagickString(text,MaxTextExtent,
4830             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4831             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4832           XInfoWidget(display,windows,text);
4833           XHighlightRectangle(display,windows->image.id,
4834             windows->image.highlight_context,&highlight_info);
4835         }
4836       else
4837         if (windows->info.mapped != MagickFalse)
4838           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4839       /*
4840         Wait for next event.
4841       */
4842       XScreenEvent(display,windows,&event);
4843       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4844         XHighlightRectangle(display,windows->image.id,
4845           windows->image.highlight_context,&highlight_info);
4846       switch (event.type)
4847       {
4848         case ButtonPress:
4849         {
4850           crop_info.x=windows->image.x+event.xbutton.x;
4851           crop_info.y=windows->image.y+event.xbutton.y;
4852           break;
4853         }
4854         case ButtonRelease:
4855         {
4856           /*
4857             User has committed to cropping rectangle.
4858           */
4859           crop_info.x=windows->image.x+event.xbutton.x;
4860           crop_info.y=windows->image.y+event.xbutton.y;
4861           XSetCursorState(display,windows,MagickFalse);
4862           state|=ExitState;
4863           windows->command.data=0;
4864           (void) XCommandWidget(display,windows,RectifyModeMenu,
4865             (XEvent *) NULL);
4866           break;
4867         }
4868         case Expose:
4869           break;
4870         case MotionNotify:
4871         {
4872           crop_info.x=windows->image.x+event.xmotion.x;
4873           crop_info.y=windows->image.y+event.xmotion.y;
4874         }
4875         default:
4876           break;
4877       }
4878       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4879           ((state & ExitState) != 0))
4880         {
4881           /*
4882             Check boundary conditions.
4883           */
4884           if (crop_info.x < 0)
4885             crop_info.x=0;
4886           else
4887             if (crop_info.x > (int) windows->image.ximage->width)
4888               crop_info.x=windows->image.ximage->width;
4889           if ((int) crop_info.x < x)
4890             crop_info.width=(unsigned int) (x-crop_info.x);
4891           else
4892             {
4893               crop_info.width=(unsigned int) (crop_info.x-x);
4894               crop_info.x=x;
4895             }
4896           if (crop_info.y < 0)
4897             crop_info.y=0;
4898           else
4899             if (crop_info.y > (int) windows->image.ximage->height)
4900               crop_info.y=windows->image.ximage->height;
4901           if ((int) crop_info.y < y)
4902             crop_info.height=(unsigned int) (y-crop_info.y);
4903           else
4904             {
4905               crop_info.height=(unsigned int) (crop_info.y-y);
4906               crop_info.y=y;
4907             }
4908         }
4909     } while ((state & ExitState) == 0);
4910     /*
4911       Wait for user to grab a corner of the rectangle or press return.
4912     */
4913     state=DefaultState;
4914     (void) XMapWindow(display,windows->info.id);
4915     do
4916     {
4917       if (windows->info.mapped != MagickFalse)
4918         {
4919           /*
4920             Display pointer position.
4921           */
4922           (void) FormatMagickString(text,MaxTextExtent,
4923             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4924             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4925           XInfoWidget(display,windows,text);
4926         }
4927       highlight_info=crop_info;
4928       highlight_info.x=crop_info.x-windows->image.x;
4929       highlight_info.y=crop_info.y-windows->image.y;
4930       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4931         {
4932           state|=EscapeState;
4933           state|=ExitState;
4934           break;
4935         }
4936       XHighlightRectangle(display,windows->image.id,
4937         windows->image.highlight_context,&highlight_info);
4938       XScreenEvent(display,windows,&event);
4939       if (event.xany.window == windows->command.id)
4940         {
4941           /*
4942             Select a command from the Command widget.
4943           */
4944           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4945           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4946           (void) XSetFunction(display,windows->image.highlight_context,
4947             GXinvert);
4948           XHighlightRectangle(display,windows->image.id,
4949             windows->image.highlight_context,&highlight_info);
4950           if (id >= 0)
4951             switch (RectifyCommands[id])
4952             {
4953               case RectifyCopyCommand:
4954               {
4955                 state|=ExitState;
4956                 break;
4957               }
4958               case RectifyHelpCommand:
4959               {
4960                 (void) XSetFunction(display,windows->image.highlight_context,
4961                   GXcopy);
4962                 switch (mode)
4963                 {
4964                   case CopyMode:
4965                   {
4966                     XTextViewWidget(display,resource_info,windows,MagickFalse,
4967                       "Help Viewer - Image Copy",ImageCopyHelp);
4968                     break;
4969                   }
4970                   case CropMode:
4971                   {
4972                     XTextViewWidget(display,resource_info,windows,MagickFalse,
4973                       "Help Viewer - Image Crop",ImageCropHelp);
4974                     break;
4975                   }
4976                   case CutMode:
4977                   {
4978                     XTextViewWidget(display,resource_info,windows,MagickFalse,
4979                       "Help Viewer - Image Cut",ImageCutHelp);
4980                     break;
4981                   }
4982                 }
4983                 (void) XSetFunction(display,windows->image.highlight_context,
4984                   GXinvert);
4985                 break;
4986               }
4987               case RectifyDismissCommand:
4988               {
4989                 /*
4990                   Prematurely exit.
4991                 */
4992                 state|=EscapeState;
4993                 state|=ExitState;
4994                 break;
4995               }
4996               default:
4997                 break;
4998             }
4999           continue;
5000         }
5001       XHighlightRectangle(display,windows->image.id,
5002         windows->image.highlight_context,&highlight_info);
5003       switch (event.type)
5004       {
5005         case ButtonPress:
5006         {
5007           if (event.xbutton.button != Button1)
5008             break;
5009           if (event.xbutton.window != windows->image.id)
5010             break;
5011           x=windows->image.x+event.xbutton.x;
5012           y=windows->image.y+event.xbutton.y;
5013           if ((x < (int) (crop_info.x+RoiDelta)) &&
5014               (x > (int) (crop_info.x-RoiDelta)) &&
5015               (y < (int) (crop_info.y+RoiDelta)) &&
5016               (y > (int) (crop_info.y-RoiDelta)))
5017             {
5018               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5019               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5020               state|=UpdateConfigurationState;
5021               break;
5022             }
5023           if ((x < (int) (crop_info.x+RoiDelta)) &&
5024               (x > (int) (crop_info.x-RoiDelta)) &&
5025               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5026               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5027             {
5028               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5029               state|=UpdateConfigurationState;
5030               break;
5031             }
5032           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5033               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5034               (y < (int) (crop_info.y+RoiDelta)) &&
5035               (y > (int) (crop_info.y-RoiDelta)))
5036             {
5037               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5038               state|=UpdateConfigurationState;
5039               break;
5040             }
5041           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5042               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5043               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5044               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5045             {
5046               state|=UpdateConfigurationState;
5047               break;
5048             }
5049         }
5050         case ButtonRelease:
5051         {
5052           if (event.xbutton.window == windows->pan.id)
5053             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5054                 (highlight_info.y != crop_info.y-windows->image.y))
5055               XHighlightRectangle(display,windows->image.id,
5056                 windows->image.highlight_context,&highlight_info);
5057           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5058             event.xbutton.time);
5059           break;
5060         }
5061         case Expose:
5062         {
5063           if (event.xexpose.window == windows->image.id)
5064             if (event.xexpose.count == 0)
5065               {
5066                 event.xexpose.x=(int) highlight_info.x;
5067                 event.xexpose.y=(int) highlight_info.y;
5068                 event.xexpose.width=(int) highlight_info.width;
5069                 event.xexpose.height=(int) highlight_info.height;
5070                 XRefreshWindow(display,&windows->image,&event);
5071               }
5072           if (event.xexpose.window == windows->info.id)
5073             if (event.xexpose.count == 0)
5074               XInfoWidget(display,windows,text);
5075           break;
5076         }
5077         case KeyPress:
5078         {
5079           if (event.xkey.window != windows->image.id)
5080             break;
5081           /*
5082             Respond to a user key press.
5083           */
5084           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5085             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5086           switch ((int) key_symbol)
5087           {
5088             case XK_Escape:
5089             case XK_F20:
5090               state|=EscapeState;
5091             case XK_Return:
5092             {
5093               state|=ExitState;
5094               break;
5095             }
5096             case XK_Home:
5097             case XK_KP_Home:
5098             {
5099               crop_info.x=(ssize_t) (windows->image.width/2L-crop_info.width/2L);
5100               crop_info.y=(ssize_t) (windows->image.height/2L-crop_info.height/2L);
5101               break;
5102             }
5103             case XK_Left:
5104             case XK_KP_Left:
5105             {
5106               crop_info.x--;
5107               break;
5108             }
5109             case XK_Up:
5110             case XK_KP_Up:
5111             case XK_Next:
5112             {
5113               crop_info.y--;
5114               break;
5115             }
5116             case XK_Right:
5117             case XK_KP_Right:
5118             {
5119               crop_info.x++;
5120               break;
5121             }
5122             case XK_Prior:
5123             case XK_Down:
5124             case XK_KP_Down:
5125             {
5126               crop_info.y++;
5127               break;
5128             }
5129             case XK_F1:
5130             case XK_Help:
5131             {
5132               (void) XSetFunction(display,windows->image.highlight_context,
5133                 GXcopy);
5134               switch (mode)
5135               {
5136                 case CopyMode:
5137                 {
5138                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5139                     "Help Viewer - Image Copy",ImageCopyHelp);
5140                   break;
5141                 }
5142                 case CropMode:
5143                 {
5144                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5145                     "Help Viewer - Image Cropg",ImageCropHelp);
5146                   break;
5147                 }
5148                 case CutMode:
5149                 {
5150                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5151                     "Help Viewer - Image Cutg",ImageCutHelp);
5152                   break;
5153                 }
5154               }
5155               (void) XSetFunction(display,windows->image.highlight_context,
5156                 GXinvert);
5157               break;
5158             }
5159             default:
5160             {
5161               (void) XBell(display,0);
5162               break;
5163             }
5164           }
5165           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5166             event.xkey.time);
5167           break;
5168         }
5169         case KeyRelease:
5170           break;
5171         case MotionNotify:
5172         {
5173           if (event.xmotion.window != windows->image.id)
5174             break;
5175           /*
5176             Map and unmap Info widget as text cursor crosses its boundaries.
5177           */
5178           x=event.xmotion.x;
5179           y=event.xmotion.y;
5180           if (windows->info.mapped != MagickFalse)
5181             {
5182               if ((x < (int) (windows->info.x+windows->info.width)) &&
5183                   (y < (int) (windows->info.y+windows->info.height)))
5184                 (void) XWithdrawWindow(display,windows->info.id,
5185                   windows->info.screen);
5186             }
5187           else
5188             if ((x > (int) (windows->info.x+windows->info.width)) ||
5189                 (y > (int) (windows->info.y+windows->info.height)))
5190               (void) XMapWindow(display,windows->info.id);
5191           crop_info.x=windows->image.x+event.xmotion.x;
5192           crop_info.y=windows->image.y+event.xmotion.y;
5193           break;
5194         }
5195         case SelectionRequest:
5196         {
5197           XSelectionEvent
5198             notify;
5199
5200           XSelectionRequestEvent
5201             *request;
5202
5203           /*
5204             Set primary selection.
5205           */
5206           (void) FormatMagickString(text,MaxTextExtent,
5207             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5208             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5209           request=(&(event.xselectionrequest));
5210           (void) XChangeProperty(request->display,request->requestor,
5211             request->property,request->target,8,PropModeReplace,
5212             (unsigned char *) text,(int) strlen(text));
5213           notify.type=SelectionNotify;
5214           notify.display=request->display;
5215           notify.requestor=request->requestor;
5216           notify.selection=request->selection;
5217           notify.target=request->target;
5218           notify.time=request->time;
5219           if (request->property == None)
5220             notify.property=request->target;
5221           else
5222             notify.property=request->property;
5223           (void) XSendEvent(request->display,request->requestor,False,0,
5224             (XEvent *) &notify);
5225         }
5226         default:
5227           break;
5228       }
5229       if ((state & UpdateConfigurationState) != 0)
5230         {
5231           (void) XPutBackEvent(display,&event);
5232           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5233           break;
5234         }
5235     } while ((state & ExitState) == 0);
5236   } while ((state & ExitState) == 0);
5237   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5238   XSetCursorState(display,windows,MagickFalse);
5239   if ((state & EscapeState) != 0)
5240     return(MagickTrue);
5241   if (mode == CropMode)
5242     if (((int) crop_info.width != windows->image.ximage->width) ||
5243         ((int) crop_info.height != windows->image.ximage->height))
5244       {
5245         /*
5246           Reconfigure Image window as defined by cropping rectangle.
5247         */
5248         XSetCropGeometry(display,windows,&crop_info,image);
5249         windows->image.window_changes.width=(int) crop_info.width;
5250         windows->image.window_changes.height=(int) crop_info.height;
5251         (void) XConfigureImage(display,resource_info,windows,image);
5252         return(MagickTrue);
5253       }
5254   /*
5255     Copy image before applying image transforms.
5256   */
5257   XSetCursorState(display,windows,MagickTrue);
5258   XCheckRefreshWindows(display,windows);
5259   width=(unsigned int) image->columns;
5260   height=(unsigned int) image->rows;
5261   x=0;
5262   y=0;
5263   if (windows->image.crop_geometry != (char *) NULL)
5264     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5265   scale_factor=(MagickRealType) width/windows->image.ximage->width;
5266   crop_info.x+=x;
5267   crop_info.x=(int) (scale_factor*crop_info.x+0.5);
5268   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5269   scale_factor=(MagickRealType) height/windows->image.ximage->height;
5270   crop_info.y+=y;
5271   crop_info.y=(int) (scale_factor*crop_info.y+0.5);
5272   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5273   crop_image=CropImage(image,&crop_info,&image->exception);
5274   XSetCursorState(display,windows,MagickFalse);
5275   if (crop_image == (Image *) NULL)
5276     return(MagickFalse);
5277   if (resource_info->copy_image != (Image *) NULL)
5278     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5279   resource_info->copy_image=crop_image;
5280   if (mode == CopyMode)
5281     {
5282       (void) XConfigureImage(display,resource_info,windows,image);
5283       return(MagickTrue);
5284     }
5285   /*
5286     Cut image.
5287   */
5288   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
5289     return(MagickFalse);
5290   image->matte=MagickTrue;
5291   exception=(&image->exception);
5292   for (y=0; y < (ssize_t) crop_info.height; y++)
5293   {
5294     q=GetAuthenticPixels(image,crop_info.x,y+crop_info.y,crop_info.width,1,
5295       exception);
5296     if (q == (PixelPacket *) NULL)
5297       break;
5298     for (x=0; x < (int) crop_info.width; x++)
5299     {
5300       q->opacity=(Quantum) TransparentOpacity;
5301       q++;
5302     }
5303     if (SyncAuthenticPixels(image,exception) == MagickFalse)
5304       break;
5305   }
5306   /*
5307     Update image configuration.
5308   */
5309   XConfigureImageColormap(display,resource_info,windows,image);
5310   (void) XConfigureImage(display,resource_info,windows,image);
5311   return(MagickTrue);
5312 }
5313 \f
5314 /*
5315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5316 %                                                                             %
5317 %                                                                             %
5318 %                                                                             %
5319 +   X D r a w I m a g e                                                       %
5320 %                                                                             %
5321 %                                                                             %
5322 %                                                                             %
5323 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5324 %
5325 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5326 %  the image.
5327 %
5328 %  The format of the XDrawEditImage method is:
5329 %
5330 %      MagickBooleanType XDrawEditImage(Display *display,
5331 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
5332 %
5333 %  A description of each parameter follows:
5334 %
5335 %    o display: Specifies a connection to an X server; returned from
5336 %      XOpenDisplay.
5337 %
5338 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5339 %
5340 %    o windows: Specifies a pointer to a XWindows structure.
5341 %
5342 %    o image: the image.
5343 %
5344 */
5345 static MagickBooleanType XDrawEditImage(Display *display,
5346   XResourceInfo *resource_info,XWindows *windows,Image **image)
5347 {
5348   static const char
5349     *DrawMenu[] =
5350     {
5351       "Element",
5352       "Color",
5353       "Stipple",
5354       "Width",
5355       "Undo",
5356       "Help",
5357       "Dismiss",
5358       (char *) NULL
5359     };
5360
5361   static ElementType
5362     element = PointElement;
5363
5364   static const ModeType
5365     DrawCommands[] =
5366     {
5367       DrawElementCommand,
5368       DrawColorCommand,
5369       DrawStippleCommand,
5370       DrawWidthCommand,
5371       DrawUndoCommand,
5372       DrawHelpCommand,
5373       DrawDismissCommand
5374     };
5375
5376   static Pixmap
5377     stipple = (Pixmap) NULL;
5378
5379   static unsigned int
5380     pen_id = 0,
5381     line_width = 1;
5382
5383   char
5384     command[MaxTextExtent],
5385     text[MaxTextExtent];
5386
5387   Cursor
5388     cursor;
5389
5390   int
5391     entry,
5392     id,
5393     number_coordinates,
5394     x,
5395     y;
5396
5397   MagickRealType
5398     degrees;
5399
5400   MagickStatusType
5401     status;
5402
5403   RectangleInfo
5404     rectangle_info;
5405
5406   register int
5407     i;
5408
5409   unsigned int
5410     distance,
5411     height,
5412     max_coordinates,
5413     width;
5414
5415   size_t
5416     state;
5417
5418   Window
5419     root_window;
5420
5421   XDrawInfo
5422     draw_info;
5423
5424   XEvent
5425     event;
5426
5427   XPoint
5428     *coordinate_info;
5429
5430   XSegment
5431     line_info;
5432
5433   /*
5434     Allocate polygon info.
5435   */
5436   max_coordinates=2048;
5437   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5438     sizeof(*coordinate_info));
5439   if (coordinate_info == (XPoint *) NULL)
5440     {
5441       (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
5442         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5443       return(MagickFalse);
5444     }
5445   /*
5446     Map Command widget.
5447   */
5448   (void) CloneString(&windows->command.name,"Draw");
5449   windows->command.data=4;
5450   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5451   (void) XMapRaised(display,windows->command.id);
5452   XClientMessage(display,windows->image.id,windows->im_protocols,
5453     windows->im_update_widget,CurrentTime);
5454   /*
5455     Wait for first button press.
5456   */
5457   root_window=XRootWindow(display,XDefaultScreen(display));
5458   draw_info.stencil=OpaqueStencil;
5459   status=MagickTrue;
5460   cursor=XCreateFontCursor(display,XC_tcross);
5461   for ( ; ; )
5462   {
5463     XQueryPosition(display,windows->image.id,&x,&y);
5464     (void) XSelectInput(display,windows->image.id,
5465       windows->image.attributes.event_mask | PointerMotionMask);
5466     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5467     state=DefaultState;
5468     do
5469     {
5470       if (windows->info.mapped != MagickFalse)
5471         {
5472           /*
5473             Display pointer position.
5474           */
5475           (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
5476             x+windows->image.x,y+windows->image.y);
5477           XInfoWidget(display,windows,text);
5478         }
5479       /*
5480         Wait for next event.
5481       */
5482       XScreenEvent(display,windows,&event);
5483       if (event.xany.window == windows->command.id)
5484         {
5485           /*
5486             Select a command from the Command widget.
5487           */
5488           id=XCommandWidget(display,windows,DrawMenu,&event);
5489           if (id < 0)
5490             continue;
5491           switch (DrawCommands[id])
5492           {
5493             case DrawElementCommand:
5494             {
5495               static const char
5496                 *Elements[] =
5497                 {
5498                   "point",
5499                   "line",
5500                   "rectangle",
5501                   "fill rectangle",
5502                   "circle",
5503                   "fill circle",
5504                   "ellipse",
5505                   "fill ellipse",
5506                   "polygon",
5507                   "fill polygon",
5508                   (char *) NULL,
5509                 };
5510
5511               /*
5512                 Select a command from the pop-up menu.
5513               */
5514               element=(ElementType) (XMenuWidget(display,windows,
5515                 DrawMenu[id],Elements,command)+1);
5516               break;
5517             }
5518             case DrawColorCommand:
5519             {
5520               const char
5521                 *ColorMenu[MaxNumberPens+1];
5522
5523               int
5524                 pen_number;
5525
5526               MagickBooleanType
5527                 transparent;
5528
5529               XColor
5530                 color;
5531
5532               /*
5533                 Initialize menu selections.
5534               */
5535               for (i=0; i < (int) (MaxNumberPens-2); i++)
5536                 ColorMenu[i]=resource_info->pen_colors[i];
5537               ColorMenu[MaxNumberPens-2]="transparent";
5538               ColorMenu[MaxNumberPens-1]="Browser...";
5539               ColorMenu[MaxNumberPens]=(char *) NULL;
5540               /*
5541                 Select a pen color from the pop-up menu.
5542               */
5543               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5544                 (const char **) ColorMenu,command);
5545               if (pen_number < 0)
5546                 break;
5547               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5548                 MagickFalse;
5549               if (transparent != MagickFalse)
5550                 {
5551                   draw_info.stencil=TransparentStencil;
5552                   break;
5553                 }
5554               if (pen_number == (MaxNumberPens-1))
5555                 {
5556                   static char
5557                     color_name[MaxTextExtent] = "gray";
5558
5559                   /*
5560                     Select a pen color from a dialog.
5561                   */
5562                   resource_info->pen_colors[pen_number]=color_name;
5563                   XColorBrowserWidget(display,windows,"Select",color_name);
5564                   if (*color_name == '\0')
5565                     break;
5566                 }
5567               /*
5568                 Set pen color.
5569               */
5570               (void) XParseColor(display,windows->map_info->colormap,
5571                 resource_info->pen_colors[pen_number],&color);
5572               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5573                 (unsigned int) MaxColors,&color);
5574               windows->pixel_info->pen_colors[pen_number]=color;
5575               pen_id=(unsigned int) pen_number;
5576               draw_info.stencil=OpaqueStencil;
5577               break;
5578             }
5579             case DrawStippleCommand:
5580             {
5581               Image
5582                 *stipple_image;
5583
5584               ImageInfo
5585                 *image_info;
5586
5587               int
5588                 status;
5589
5590               static char
5591                 filename[MaxTextExtent] = "\0";
5592
5593               static const char
5594                 *StipplesMenu[] =
5595                 {
5596                   "Brick",
5597                   "Diagonal",
5598                   "Scales",
5599                   "Vertical",
5600                   "Wavy",
5601                   "Translucent",
5602                   "Opaque",
5603                   (char *) NULL,
5604                   (char *) NULL,
5605                 };
5606
5607               /*
5608                 Select a command from the pop-up menu.
5609               */
5610               StipplesMenu[7]="Open...";
5611               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5612                 command);
5613               if (entry < 0)
5614                 break;
5615               if (stipple != (Pixmap) NULL)
5616                 (void) XFreePixmap(display,stipple);
5617               stipple=(Pixmap) NULL;
5618               if (entry == 6)
5619                 break;
5620               if (entry != 7)
5621                 {
5622                   switch (entry)
5623                   {
5624                     case 0:
5625                     {
5626                       stipple=XCreateBitmapFromData(display,root_window,
5627                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5628                       break;
5629                     }
5630                     case 1:
5631                     {
5632                       stipple=XCreateBitmapFromData(display,root_window,
5633                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5634                       break;
5635                     }
5636                     case 2:
5637                     {
5638                       stipple=XCreateBitmapFromData(display,root_window,
5639                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5640                       break;
5641                     }
5642                     case 3:
5643                     {
5644                       stipple=XCreateBitmapFromData(display,root_window,
5645                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5646                       break;
5647                     }
5648                     case 4:
5649                     {
5650                       stipple=XCreateBitmapFromData(display,root_window,
5651                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5652                       break;
5653                     }
5654                     case 5:
5655                     default:
5656                     {
5657                       stipple=XCreateBitmapFromData(display,root_window,
5658                         (char *) HighlightBitmap,HighlightWidth,
5659                         HighlightHeight);
5660                       break;
5661                     }
5662                   }
5663                   break;
5664                 }
5665               XFileBrowserWidget(display,windows,"Stipple",filename);
5666               if (*filename == '\0')
5667                 break;
5668               /*
5669                 Read image.
5670               */
5671               XSetCursorState(display,windows,MagickTrue);
5672               XCheckRefreshWindows(display,windows);
5673               image_info=AcquireImageInfo();
5674               (void) CopyMagickString(image_info->filename,filename,
5675                 MaxTextExtent);
5676               stipple_image=ReadImage(image_info,&(*image)->exception);
5677               CatchException(&(*image)->exception);
5678               XSetCursorState(display,windows,MagickFalse);
5679               if (stipple_image == (Image *) NULL)
5680                 break;
5681               (void) AcquireUniqueFileResource(filename);
5682               (void) FormatMagickString(stipple_image->filename,MaxTextExtent,
5683                 "xbm:%s",filename);
5684               (void) WriteImage(image_info,stipple_image);
5685               stipple_image=DestroyImage(stipple_image);
5686               image_info=DestroyImageInfo(image_info);
5687               status=XReadBitmapFile(display,root_window,filename,&width,
5688                 &height,&stipple,&x,&y);
5689               (void) RelinquishUniqueFileResource(filename);
5690               if ((status != BitmapSuccess) != 0)
5691                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5692                   filename);
5693               break;
5694             }
5695             case DrawWidthCommand:
5696             {
5697               static char
5698                 width[MaxTextExtent] = "0";
5699
5700               static const char
5701                 *WidthsMenu[] =
5702                 {
5703                   "1",
5704                   "2",
5705                   "4",
5706                   "8",
5707                   "16",
5708                   "Dialog...",
5709                   (char *) NULL,
5710                 };
5711
5712               /*
5713                 Select a command from the pop-up menu.
5714               */
5715               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5716                 command);
5717               if (entry < 0)
5718                 break;
5719               if (entry != 5)
5720                 {
5721                   line_width=(unsigned int) StringToUnsignedLong(WidthsMenu[entry]);
5722                   break;
5723                 }
5724               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5725                 width);
5726               if (*width == '\0')
5727                 break;
5728               line_width=(unsigned int) StringToUnsignedLong(width);
5729               break;
5730             }
5731             case DrawUndoCommand:
5732             {
5733               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5734                 image);
5735               break;
5736             }
5737             case DrawHelpCommand:
5738             {
5739               XTextViewWidget(display,resource_info,windows,MagickFalse,
5740                 "Help Viewer - Image Rotation",ImageDrawHelp);
5741               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5742               break;
5743             }
5744             case DrawDismissCommand:
5745             {
5746               /*
5747                 Prematurely exit.
5748               */
5749               state|=EscapeState;
5750               state|=ExitState;
5751               break;
5752             }
5753             default:
5754               break;
5755           }
5756           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5757           continue;
5758         }
5759       switch (event.type)
5760       {
5761         case ButtonPress:
5762         {
5763           if (event.xbutton.button != Button1)
5764             break;
5765           if (event.xbutton.window != windows->image.id)
5766             break;
5767           /*
5768             exit loop.
5769           */
5770           x=event.xbutton.x;
5771           y=event.xbutton.y;
5772           state|=ExitState;
5773           break;
5774         }
5775         case ButtonRelease:
5776           break;
5777         case Expose:
5778           break;
5779         case KeyPress:
5780         {
5781           KeySym
5782             key_symbol;
5783
5784           if (event.xkey.window != windows->image.id)
5785             break;
5786           /*
5787             Respond to a user key press.
5788           */
5789           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5790             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5791           switch ((int) key_symbol)
5792           {
5793             case XK_Escape:
5794             case XK_F20:
5795             {
5796               /*
5797                 Prematurely exit.
5798               */
5799               state|=EscapeState;
5800               state|=ExitState;
5801               break;
5802             }
5803             case XK_F1:
5804             case XK_Help:
5805             {
5806               XTextViewWidget(display,resource_info,windows,MagickFalse,
5807                 "Help Viewer - Image Rotation",ImageDrawHelp);
5808               break;
5809             }
5810             default:
5811             {
5812               (void) XBell(display,0);
5813               break;
5814             }
5815           }
5816           break;
5817         }
5818         case MotionNotify:
5819         {
5820           /*
5821             Map and unmap Info widget as text cursor crosses its boundaries.
5822           */
5823           x=event.xmotion.x;
5824           y=event.xmotion.y;
5825           if (windows->info.mapped != MagickFalse)
5826             {
5827               if ((x < (int) (windows->info.x+windows->info.width)) &&
5828                   (y < (int) (windows->info.y+windows->info.height)))
5829                 (void) XWithdrawWindow(display,windows->info.id,
5830                   windows->info.screen);
5831             }
5832           else
5833             if ((x > (int) (windows->info.x+windows->info.width)) ||
5834                 (y > (int) (windows->info.y+windows->info.height)))
5835               (void) XMapWindow(display,windows->info.id);
5836           break;
5837         }
5838       }
5839     } while ((state & ExitState) == 0);
5840     (void) XSelectInput(display,windows->image.id,
5841       windows->image.attributes.event_mask);
5842     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5843     if ((state & EscapeState) != 0)
5844       break;
5845     /*
5846       Draw element as pointer moves until the button is released.
5847     */
5848     distance=0;
5849     degrees=0.0;
5850     line_info.x1=x;
5851     line_info.y1=y;
5852     line_info.x2=x;
5853     line_info.y2=y;
5854     rectangle_info.x=x;
5855     rectangle_info.y=y;
5856     rectangle_info.width=0;
5857     rectangle_info.height=0;
5858     number_coordinates=1;
5859     coordinate_info->x=x;
5860     coordinate_info->y=y;
5861     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5862     state=DefaultState;
5863     do
5864     {
5865       switch (element)
5866       {
5867         case PointElement:
5868         default:
5869         {
5870           if (number_coordinates > 1)
5871             {
5872               (void) XDrawLines(display,windows->image.id,
5873                 windows->image.highlight_context,coordinate_info,
5874                 number_coordinates,CoordModeOrigin);
5875               (void) FormatMagickString(text,MaxTextExtent," %+d%+d",
5876                 coordinate_info[number_coordinates-1].x,
5877                 coordinate_info[number_coordinates-1].y);
5878               XInfoWidget(display,windows,text);
5879             }
5880           break;
5881         }
5882         case LineElement:
5883         {
5884           if (distance > 9)
5885             {
5886               /*
5887                 Display angle of the line.
5888               */
5889               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5890                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5891               (void) FormatMagickString(text,MaxTextExtent," %g",
5892                 (double) degrees);
5893               XInfoWidget(display,windows,text);
5894               XHighlightLine(display,windows->image.id,
5895                 windows->image.highlight_context,&line_info);
5896             }
5897           else
5898             if (windows->info.mapped != MagickFalse)
5899               (void) XWithdrawWindow(display,windows->info.id,
5900                 windows->info.screen);
5901           break;
5902         }
5903         case RectangleElement:
5904         case FillRectangleElement:
5905         {
5906           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5907             {
5908               /*
5909                 Display info and draw drawing rectangle.
5910               */
5911               (void) FormatMagickString(text,MaxTextExtent,
5912                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5913                 (double) rectangle_info.height,(double) rectangle_info.x,
5914                 (double) rectangle_info.y);
5915               XInfoWidget(display,windows,text);
5916               XHighlightRectangle(display,windows->image.id,
5917                 windows->image.highlight_context,&rectangle_info);
5918             }
5919           else
5920             if (windows->info.mapped != MagickFalse)
5921               (void) XWithdrawWindow(display,windows->info.id,
5922                 windows->info.screen);
5923           break;
5924         }
5925         case CircleElement:
5926         case FillCircleElement:
5927         case EllipseElement:
5928         case FillEllipseElement:
5929         {
5930           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5931             {
5932               /*
5933                 Display info and draw drawing rectangle.
5934               */
5935               (void) FormatMagickString(text,MaxTextExtent,
5936                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5937                 (double) rectangle_info.height,(double) rectangle_info.x,
5938                 (double) rectangle_info.y);
5939               XInfoWidget(display,windows,text);
5940               XHighlightEllipse(display,windows->image.id,
5941                 windows->image.highlight_context,&rectangle_info);
5942             }
5943           else
5944             if (windows->info.mapped != MagickFalse)
5945               (void) XWithdrawWindow(display,windows->info.id,
5946                 windows->info.screen);
5947           break;
5948         }
5949         case PolygonElement:
5950         case FillPolygonElement:
5951         {
5952           if (number_coordinates > 1)
5953             (void) XDrawLines(display,windows->image.id,
5954               windows->image.highlight_context,coordinate_info,
5955               number_coordinates,CoordModeOrigin);
5956           if (distance > 9)
5957             {
5958               /*
5959                 Display angle of the line.
5960               */
5961               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5962                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5963               (void) FormatMagickString(text,MaxTextExtent," %g",
5964                 (double) degrees);
5965               XInfoWidget(display,windows,text);
5966               XHighlightLine(display,windows->image.id,
5967                 windows->image.highlight_context,&line_info);
5968             }
5969           else
5970             if (windows->info.mapped != MagickFalse)
5971               (void) XWithdrawWindow(display,windows->info.id,
5972                 windows->info.screen);
5973           break;
5974         }
5975       }
5976       /*
5977         Wait for next event.
5978       */
5979       XScreenEvent(display,windows,&event);
5980       switch (element)
5981       {
5982         case PointElement:
5983         default:
5984         {
5985           if (number_coordinates > 1)
5986             (void) XDrawLines(display,windows->image.id,
5987               windows->image.highlight_context,coordinate_info,
5988               number_coordinates,CoordModeOrigin);
5989           break;
5990         }
5991         case LineElement:
5992         {
5993           if (distance > 9)
5994             XHighlightLine(display,windows->image.id,
5995               windows->image.highlight_context,&line_info);
5996           break;
5997         }
5998         case RectangleElement:
5999         case FillRectangleElement:
6000         {
6001           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6002             XHighlightRectangle(display,windows->image.id,
6003               windows->image.highlight_context,&rectangle_info);
6004           break;
6005         }
6006         case CircleElement:
6007         case FillCircleElement:
6008         case EllipseElement:
6009         case FillEllipseElement:
6010         {
6011           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6012             XHighlightEllipse(display,windows->image.id,
6013               windows->image.highlight_context,&rectangle_info);
6014           break;
6015         }
6016         case PolygonElement:
6017         case FillPolygonElement:
6018         {
6019           if (number_coordinates > 1)
6020             (void) XDrawLines(display,windows->image.id,
6021               windows->image.highlight_context,coordinate_info,
6022               number_coordinates,CoordModeOrigin);
6023           if (distance > 9)
6024             XHighlightLine(display,windows->image.id,
6025               windows->image.highlight_context,&line_info);
6026           break;
6027         }
6028       }
6029       switch (event.type)
6030       {
6031         case ButtonPress:
6032           break;
6033         case ButtonRelease:
6034         {
6035           /*
6036             User has committed to element.
6037           */
6038           line_info.x2=event.xbutton.x;
6039           line_info.y2=event.xbutton.y;
6040           rectangle_info.x=event.xbutton.x;
6041           rectangle_info.y=event.xbutton.y;
6042           coordinate_info[number_coordinates].x=event.xbutton.x;
6043           coordinate_info[number_coordinates].y=event.xbutton.y;
6044           if (((element != PolygonElement) &&
6045                (element != FillPolygonElement)) || (distance <= 9))
6046             {
6047               state|=ExitState;
6048               break;
6049             }
6050           number_coordinates++;
6051           if (number_coordinates < (int) max_coordinates)
6052             {
6053               line_info.x1=event.xbutton.x;
6054               line_info.y1=event.xbutton.y;
6055               break;
6056             }
6057           max_coordinates<<=1;
6058           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6059             max_coordinates,sizeof(*coordinate_info));
6060           if (coordinate_info == (XPoint *) NULL)
6061             (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6062               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6063           break;
6064         }
6065         case Expose:
6066           break;
6067         case MotionNotify:
6068         {
6069           if (event.xmotion.window != windows->image.id)
6070             break;
6071           if (element != PointElement)
6072             {
6073               line_info.x2=event.xmotion.x;
6074               line_info.y2=event.xmotion.y;
6075               rectangle_info.x=event.xmotion.x;
6076               rectangle_info.y=event.xmotion.y;
6077               break;
6078             }
6079           coordinate_info[number_coordinates].x=event.xbutton.x;
6080           coordinate_info[number_coordinates].y=event.xbutton.y;
6081           number_coordinates++;
6082           if (number_coordinates < (int) max_coordinates)
6083             break;
6084           max_coordinates<<=1;
6085           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6086             max_coordinates,sizeof(*coordinate_info));
6087           if (coordinate_info == (XPoint *) NULL)
6088             (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6089               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6090           break;
6091         }
6092         default:
6093           break;
6094       }
6095       /*
6096         Check boundary conditions.
6097       */
6098       if (line_info.x2 < 0)
6099         line_info.x2=0;
6100       else
6101         if (line_info.x2 > (int) windows->image.width)
6102           line_info.x2=(short) windows->image.width;
6103       if (line_info.y2 < 0)
6104         line_info.y2=0;
6105       else
6106         if (line_info.y2 > (int) windows->image.height)
6107           line_info.y2=(short) windows->image.height;
6108       distance=(unsigned int)
6109         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6110          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6111       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6112           ((state & ExitState) != 0))
6113         {
6114           if (rectangle_info.x < 0)
6115             rectangle_info.x=0;
6116           else
6117             if (rectangle_info.x > (int) windows->image.width)
6118               rectangle_info.x=(ssize_t) windows->image.width;
6119           if ((int) rectangle_info.x < x)
6120             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6121           else
6122             {
6123               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6124               rectangle_info.x=x;
6125             }
6126           if (rectangle_info.y < 0)
6127             rectangle_info.y=0;
6128           else
6129             if (rectangle_info.y > (int) windows->image.height)
6130               rectangle_info.y=(ssize_t) windows->image.height;
6131           if ((int) rectangle_info.y < y)
6132             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6133           else
6134             {
6135               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6136               rectangle_info.y=y;
6137             }
6138         }
6139     } while ((state & ExitState) == 0);
6140     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6141     if ((element == PointElement) || (element == PolygonElement) ||
6142         (element == FillPolygonElement))
6143       {
6144         /*
6145           Determine polygon bounding box.
6146         */
6147         rectangle_info.x=coordinate_info->x;
6148         rectangle_info.y=coordinate_info->y;
6149         x=coordinate_info->x;
6150         y=coordinate_info->y;
6151         for (i=1; i < number_coordinates; i++)
6152         {
6153           if (coordinate_info[i].x > x)
6154             x=coordinate_info[i].x;
6155           if (coordinate_info[i].y > y)
6156             y=coordinate_info[i].y;
6157           if (coordinate_info[i].x < rectangle_info.x)
6158             rectangle_info.x=MagickMax(coordinate_info[i].x,0);
6159           if (coordinate_info[i].y < rectangle_info.y)
6160             rectangle_info.y=MagickMax(coordinate_info[i].y,0);
6161         }
6162         rectangle_info.width=(size_t) (x-rectangle_info.x);
6163         rectangle_info.height=(size_t) (y-rectangle_info.y);
6164         for (i=0; i < number_coordinates; i++)
6165         {
6166           coordinate_info[i].x-=rectangle_info.x;
6167           coordinate_info[i].y-=rectangle_info.y;
6168         }
6169       }
6170     else
6171       if (distance <= 9)
6172         continue;
6173       else
6174         if ((element == RectangleElement) ||
6175             (element == CircleElement) || (element == EllipseElement))
6176           {
6177             rectangle_info.width--;
6178             rectangle_info.height--;
6179           }
6180     /*
6181       Drawing is relative to image configuration.
6182     */
6183     draw_info.x=(int) rectangle_info.x;
6184     draw_info.y=(int) rectangle_info.y;
6185     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6186       image);
6187     width=(unsigned int) (*image)->columns;
6188     height=(unsigned int) (*image)->rows;
6189     x=0;
6190     y=0;
6191     if (windows->image.crop_geometry != (char *) NULL)
6192       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6193     draw_info.x+=windows->image.x-(line_width/2);
6194     if (draw_info.x < 0)
6195       draw_info.x=0;
6196     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6197     draw_info.y+=windows->image.y-(line_width/2);
6198     if (draw_info.y < 0)
6199       draw_info.y=0;
6200     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6201     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6202     if (draw_info.width > (unsigned int) (*image)->columns)
6203       draw_info.width=(unsigned int) (*image)->columns;
6204     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6205     if (draw_info.height > (unsigned int) (*image)->rows)
6206       draw_info.height=(unsigned int) (*image)->rows;
6207     (void) FormatMagickString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6208       width*draw_info.width/windows->image.ximage->width,
6209       height*draw_info.height/windows->image.ximage->height,
6210       draw_info.x+x,draw_info.y+y);
6211     /*
6212       Initialize drawing attributes.
6213     */
6214     draw_info.degrees=0.0;
6215     draw_info.element=element;
6216     draw_info.stipple=stipple;
6217     draw_info.line_width=line_width;
6218     draw_info.line_info=line_info;
6219     if (line_info.x1 > (int) (line_width/2))
6220       draw_info.line_info.x1=(short) line_width/2;
6221     if (line_info.y1 > (int) (line_width/2))
6222       draw_info.line_info.y1=(short) line_width/2;
6223     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6224     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6225     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6226       {
6227         draw_info.line_info.x2=(-draw_info.line_info.x2);
6228         draw_info.line_info.y2=(-draw_info.line_info.y2);
6229       }
6230     if (draw_info.line_info.x2 < 0)
6231       {
6232         draw_info.line_info.x2=(-draw_info.line_info.x2);
6233         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6234       }
6235     if (draw_info.line_info.y2 < 0)
6236       {
6237         draw_info.line_info.y2=(-draw_info.line_info.y2);
6238         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6239       }
6240     draw_info.rectangle_info=rectangle_info;
6241     if (draw_info.rectangle_info.x > (int) (line_width/2))
6242       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6243     if (draw_info.rectangle_info.y > (int) (line_width/2))
6244       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6245     draw_info.number_coordinates=(unsigned int) number_coordinates;
6246     draw_info.coordinate_info=coordinate_info;
6247     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6248     /*
6249       Draw element on image.
6250     */
6251     XSetCursorState(display,windows,MagickTrue);
6252     XCheckRefreshWindows(display,windows);
6253     status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6254     XSetCursorState(display,windows,MagickFalse);
6255     /*
6256       Update image colormap and return to image drawing.
6257     */
6258     XConfigureImageColormap(display,resource_info,windows,*image);
6259     (void) XConfigureImage(display,resource_info,windows,*image);
6260   }
6261   XSetCursorState(display,windows,MagickFalse);
6262   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6263   return(status != 0 ? MagickTrue : MagickFalse);
6264 }
6265 \f
6266 /*
6267 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6268 %                                                                             %
6269 %                                                                             %
6270 %                                                                             %
6271 +   X D r a w P a n R e c t a n g l e                                         %
6272 %                                                                             %
6273 %                                                                             %
6274 %                                                                             %
6275 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6276 %
6277 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6278 %  displays a zoom image and the rectangle shows which portion of the image is
6279 %  displayed in the Image window.
6280 %
6281 %  The format of the XDrawPanRectangle method is:
6282 %
6283 %      XDrawPanRectangle(Display *display,XWindows *windows)
6284 %
6285 %  A description of each parameter follows:
6286 %
6287 %    o display: Specifies a connection to an X server;  returned from
6288 %      XOpenDisplay.
6289 %
6290 %    o windows: Specifies a pointer to a XWindows structure.
6291 %
6292 */
6293 static void XDrawPanRectangle(Display *display,XWindows *windows)
6294 {
6295   MagickRealType
6296     scale_factor;
6297
6298   RectangleInfo
6299     highlight_info;
6300
6301   /*
6302     Determine dimensions of the panning rectangle.
6303   */
6304   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6305   highlight_info.x=(int) (scale_factor*windows->image.x+0.5);
6306   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6307   scale_factor=(MagickRealType)
6308     windows->pan.height/windows->image.ximage->height;
6309   highlight_info.y=(int) (scale_factor*windows->image.y+0.5);
6310   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6311   /*
6312     Display the panning rectangle.
6313   */
6314   (void) XClearWindow(display,windows->pan.id);
6315   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6316     &highlight_info);
6317 }
6318 \f
6319 /*
6320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6321 %                                                                             %
6322 %                                                                             %
6323 %                                                                             %
6324 +   X I m a g e C a c h e                                                     %
6325 %                                                                             %
6326 %                                                                             %
6327 %                                                                             %
6328 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6329 %
6330 %  XImageCache() handles the creation, manipulation, and destruction of the
6331 %  image cache (undo and redo buffers).
6332 %
6333 %  The format of the XImageCache method is:
6334 %
6335 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6336 %        XWindows *windows,const CommandType command,Image **image)
6337 %
6338 %  A description of each parameter follows:
6339 %
6340 %    o display: Specifies a connection to an X server; returned from
6341 %      XOpenDisplay.
6342 %
6343 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6344 %
6345 %    o windows: Specifies a pointer to a XWindows structure.
6346 %
6347 %    o command: Specifies a command to perform.
6348 %
6349 %    o image: the image;  XImageCache
6350 %      may transform the image and return a new image pointer.
6351 %
6352 */
6353 static void XImageCache(Display *display,XResourceInfo *resource_info,
6354   XWindows *windows,const CommandType command,Image **image)
6355 {
6356   Image
6357     *cache_image;
6358
6359   static Image
6360     *redo_image = (Image *) NULL,
6361     *undo_image = (Image *) NULL;
6362
6363   switch (command)
6364   {
6365     case FreeBuffersCommand:
6366     {
6367       /*
6368         Free memory from the undo and redo cache.
6369       */
6370       while (undo_image != (Image *) NULL)
6371       {
6372         cache_image=undo_image;
6373         undo_image=GetPreviousImageInList(undo_image);
6374         cache_image->list=DestroyImage(cache_image->list);
6375         cache_image=DestroyImage(cache_image);
6376       }
6377       undo_image=NewImageList();
6378       if (redo_image != (Image *) NULL)
6379         redo_image=DestroyImage(redo_image);
6380       redo_image=NewImageList();
6381       return;
6382     }
6383     case UndoCommand:
6384     {
6385       /*
6386         Undo the last image transformation.
6387       */
6388       if (undo_image == (Image *) NULL)
6389         {
6390           (void) XBell(display,0);
6391           return;
6392         }
6393       cache_image=undo_image;
6394       undo_image=GetPreviousImageInList(undo_image);
6395       windows->image.window_changes.width=(int) cache_image->columns;
6396       windows->image.window_changes.height=(int) cache_image->rows;
6397       if (windows->image.crop_geometry != (char *) NULL)
6398         windows->image.crop_geometry=(char *)
6399           RelinquishMagickMemory(windows->image.crop_geometry);
6400       windows->image.crop_geometry=cache_image->geometry;
6401       if (redo_image != (Image *) NULL)
6402         redo_image=DestroyImage(redo_image);
6403       redo_image=(*image);
6404       *image=cache_image->list;
6405       cache_image=DestroyImage(cache_image);
6406       if (windows->image.orphan != MagickFalse)
6407         return;
6408       XConfigureImageColormap(display,resource_info,windows,*image);
6409       (void) XConfigureImage(display,resource_info,windows,*image);
6410       return;
6411     }
6412     case CutCommand:
6413     case PasteCommand:
6414     case ApplyCommand:
6415     case HalfSizeCommand:
6416     case OriginalSizeCommand:
6417     case DoubleSizeCommand:
6418     case ResizeCommand:
6419     case TrimCommand:
6420     case CropCommand:
6421     case ChopCommand:
6422     case FlipCommand:
6423     case FlopCommand:
6424     case RotateRightCommand:
6425     case RotateLeftCommand:
6426     case RotateCommand:
6427     case ShearCommand:
6428     case RollCommand:
6429     case NegateCommand:
6430     case ContrastStretchCommand:
6431     case SigmoidalContrastCommand:
6432     case NormalizeCommand:
6433     case EqualizeCommand:
6434     case HueCommand:
6435     case SaturationCommand:
6436     case BrightnessCommand:
6437     case GammaCommand:
6438     case SpiffCommand:
6439     case DullCommand:
6440     case GrayscaleCommand:
6441     case MapCommand:
6442     case QuantizeCommand:
6443     case DespeckleCommand:
6444     case EmbossCommand:
6445     case ReduceNoiseCommand:
6446     case AddNoiseCommand:
6447     case SharpenCommand:
6448     case BlurCommand:
6449     case ThresholdCommand:
6450     case EdgeDetectCommand:
6451     case SpreadCommand:
6452     case ShadeCommand:
6453     case RaiseCommand:
6454     case SegmentCommand:
6455     case SolarizeCommand:
6456     case SepiaToneCommand:
6457     case SwirlCommand:
6458     case ImplodeCommand:
6459     case VignetteCommand:
6460     case WaveCommand:
6461     case OilPaintCommand:
6462     case CharcoalDrawCommand:
6463     case AnnotateCommand:
6464     case AddBorderCommand:
6465     case AddFrameCommand:
6466     case CompositeCommand:
6467     case CommentCommand:
6468     case LaunchCommand:
6469     case RegionofInterestCommand:
6470     case SaveToUndoBufferCommand:
6471     case RedoCommand:
6472     {
6473       Image
6474         *previous_image;
6475
6476       ssize_t
6477         bytes;
6478
6479       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6480       if (undo_image != (Image *) NULL)
6481         {
6482           /*
6483             Ensure the undo stash.has enough memory available.
6484           */
6485           previous_image=undo_image;
6486           while (previous_image != (Image *) NULL)
6487           {
6488             bytes+=previous_image->list->columns*previous_image->list->rows*
6489               sizeof(PixelPacket);
6490             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6491               {
6492                 previous_image=GetPreviousImageInList(previous_image);
6493                 continue;
6494               }
6495             bytes-=previous_image->list->columns*previous_image->list->rows*
6496               sizeof(PixelPacket);
6497             if (previous_image == undo_image)
6498               undo_image=NewImageList();
6499             else
6500               previous_image->next->previous=NewImageList();
6501             break;
6502           }
6503           while (previous_image != (Image *) NULL)
6504           {
6505             /*
6506               Delete any excess memory from undo cache.
6507             */
6508             cache_image=previous_image;
6509             previous_image=GetPreviousImageInList(previous_image);
6510             cache_image->list=DestroyImage(cache_image->list);
6511             cache_image=DestroyImage(cache_image);
6512           }
6513         }
6514       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6515         break;
6516       /*
6517         Save image before transformations are applied.
6518       */
6519       cache_image=AcquireImage((ImageInfo *) NULL);
6520       if (cache_image == (Image *) NULL)
6521         break;
6522       XSetCursorState(display,windows,MagickTrue);
6523       XCheckRefreshWindows(display,windows);
6524       cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6525       XSetCursorState(display,windows,MagickFalse);
6526       if (cache_image->list == (Image *) NULL)
6527         {
6528           cache_image=DestroyImage(cache_image);
6529           break;
6530         }
6531       cache_image->columns=(size_t) windows->image.ximage->width;
6532       cache_image->rows=(size_t) windows->image.ximage->height;
6533       cache_image->geometry=windows->image.crop_geometry;
6534       if (windows->image.crop_geometry != (char *) NULL)
6535         {
6536           cache_image->geometry=AcquireString((char *) NULL);
6537           (void) CopyMagickString(cache_image->geometry,
6538             windows->image.crop_geometry,MaxTextExtent);
6539         }
6540       if (undo_image == (Image *) NULL)
6541         {
6542           undo_image=cache_image;
6543           break;
6544         }
6545       undo_image->next=cache_image;
6546       undo_image->next->previous=undo_image;
6547       undo_image=undo_image->next;
6548       break;
6549     }
6550     default:
6551       break;
6552   }
6553   if (command == RedoCommand)
6554     {
6555       /*
6556         Redo the last image transformation.
6557       */
6558       if (redo_image == (Image *) NULL)
6559         {
6560           (void) XBell(display,0);
6561           return;
6562         }
6563       windows->image.window_changes.width=(int) redo_image->columns;
6564       windows->image.window_changes.height=(int) redo_image->rows;
6565       if (windows->image.crop_geometry != (char *) NULL)
6566         windows->image.crop_geometry=(char *)
6567           RelinquishMagickMemory(windows->image.crop_geometry);
6568       windows->image.crop_geometry=redo_image->geometry;
6569       *image=DestroyImage(*image);
6570       *image=redo_image;
6571       redo_image=NewImageList();
6572       if (windows->image.orphan != MagickFalse)
6573         return;
6574       XConfigureImageColormap(display,resource_info,windows,*image);
6575       (void) XConfigureImage(display,resource_info,windows,*image);
6576       return;
6577     }
6578   if (command != InfoCommand)
6579     return;
6580   /*
6581     Display image info.
6582   */
6583   XSetCursorState(display,windows,MagickTrue);
6584   XCheckRefreshWindows(display,windows);
6585   XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6586   XSetCursorState(display,windows,MagickFalse);
6587 }
6588 \f
6589 /*
6590 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6591 %                                                                             %
6592 %                                                                             %
6593 %                                                                             %
6594 +   X I m a g e W i n d o w C o m m a n d                                     %
6595 %                                                                             %
6596 %                                                                             %
6597 %                                                                             %
6598 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6599 %
6600 %  XImageWindowCommand() makes a transform to the image or Image window as
6601 %  specified by a user menu button or keyboard command.
6602 %
6603 %  The format of the XMagickCommand method is:
6604 %
6605 %      CommandType XImageWindowCommand(Display *display,
6606 %        XResourceInfo *resource_info,XWindows *windows,
6607 %        const MagickStatusType state,KeySym key_symbol,Image **image)
6608 %
6609 %  A description of each parameter follows:
6610 %
6611 %    o nexus:  Method XImageWindowCommand returns an image when the
6612 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6613 %      image is returned.
6614 %
6615 %    o display: Specifies a connection to an X server; returned from
6616 %      XOpenDisplay.
6617 %
6618 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6619 %
6620 %    o windows: Specifies a pointer to a XWindows structure.
6621 %
6622 %    o state: key mask.
6623 %
6624 %    o key_symbol: Specifies a command to perform.
6625 %
6626 %    o image: the image;  XImageWIndowCommand
6627 %      may transform the image and return a new image pointer.
6628 %
6629 */
6630 static CommandType XImageWindowCommand(Display *display,
6631   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6632   KeySym key_symbol,Image **image)
6633 {
6634   static char
6635     delta[MaxTextExtent] = "";
6636
6637   static const char
6638     Digits[] = "01234567890";
6639
6640   static KeySym
6641     last_symbol = XK_0;
6642
6643   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6644     {
6645       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6646         {
6647           *delta='\0';
6648           resource_info->quantum=1;
6649         }
6650       last_symbol=key_symbol;
6651       delta[strlen(delta)+1]='\0';
6652       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6653       resource_info->quantum=StringToLong(delta);
6654       return(NullCommand);
6655     }
6656   last_symbol=key_symbol;
6657   if (resource_info->immutable)
6658     {
6659       /*
6660         Virtual image window has a restricted command set.
6661       */
6662       switch (key_symbol)
6663       {
6664         case XK_question:
6665           return(InfoCommand);
6666         case XK_p:
6667         case XK_Print:
6668           return(PrintCommand);
6669         case XK_space:
6670           return(NextCommand);
6671         case XK_q:
6672         case XK_Escape:
6673           return(QuitCommand);
6674         default:
6675           break;
6676       }
6677       return(NullCommand);
6678     }
6679   switch ((int) key_symbol)
6680   {
6681     case XK_o:
6682     {
6683       if ((state & ControlMask) == 0)
6684         break;
6685       return(OpenCommand);
6686     }
6687     case XK_space:
6688       return(NextCommand);
6689     case XK_BackSpace:
6690       return(FormerCommand);
6691     case XK_s:
6692     {
6693       if ((state & Mod1Mask) != 0)
6694         return(SwirlCommand);
6695       if ((state & ControlMask) == 0)
6696         return(ShearCommand);
6697       return(SaveCommand);
6698     }
6699     case XK_p:
6700     case XK_Print:
6701     {
6702       if ((state & Mod1Mask) != 0)
6703         return(OilPaintCommand);
6704       if ((state & Mod4Mask) != 0)
6705         return(ColorCommand);
6706       if ((state & ControlMask) == 0)
6707         return(NullCommand);
6708       return(PrintCommand);
6709     }
6710     case XK_d:
6711     {
6712       if ((state & Mod4Mask) != 0)
6713         return(DrawCommand);
6714       if ((state & ControlMask) == 0)
6715         return(NullCommand);
6716       return(DeleteCommand);
6717     }
6718     case XK_Select:
6719     {
6720       if ((state & ControlMask) == 0)
6721         return(NullCommand);
6722       return(SelectCommand);
6723     }
6724     case XK_n:
6725     {
6726       if ((state & ControlMask) == 0)
6727         return(NullCommand);
6728       return(NewCommand);
6729     }
6730     case XK_q:
6731     case XK_Escape:
6732       return(QuitCommand);
6733     case XK_z:
6734     case XK_Undo:
6735     {
6736       if ((state & ControlMask) == 0)
6737         return(NullCommand);
6738       return(UndoCommand);
6739     }
6740     case XK_r:
6741     case XK_Redo:
6742     {
6743       if ((state & ControlMask) == 0)
6744         return(RollCommand);
6745       return(RedoCommand);
6746     }
6747     case XK_x:
6748     {
6749       if ((state & ControlMask) == 0)
6750         return(NullCommand);
6751       return(CutCommand);
6752     }
6753     case XK_c:
6754     {
6755       if ((state & Mod1Mask) != 0)
6756         return(CharcoalDrawCommand);
6757       if ((state & ControlMask) == 0)
6758         return(CropCommand);
6759       return(CopyCommand);
6760     }
6761     case XK_v:
6762     case XK_Insert:
6763     {
6764       if ((state & Mod4Mask) != 0)
6765         return(CompositeCommand);
6766       if ((state & ControlMask) == 0)
6767         return(FlipCommand);
6768       return(PasteCommand);
6769     }
6770     case XK_less:
6771       return(HalfSizeCommand);
6772     case XK_minus:
6773       return(OriginalSizeCommand);
6774     case XK_greater:
6775       return(DoubleSizeCommand);
6776     case XK_percent:
6777       return(ResizeCommand);
6778     case XK_at:
6779       return(RefreshCommand);
6780     case XK_bracketleft:
6781       return(ChopCommand);
6782     case XK_h:
6783       return(FlopCommand);
6784     case XK_slash:
6785       return(RotateRightCommand);
6786     case XK_backslash:
6787       return(RotateLeftCommand);
6788     case XK_asterisk:
6789       return(RotateCommand);
6790     case XK_t:
6791       return(TrimCommand);
6792     case XK_H:
6793       return(HueCommand);
6794     case XK_S:
6795       return(SaturationCommand);
6796     case XK_L:
6797       return(BrightnessCommand);
6798     case XK_G:
6799       return(GammaCommand);
6800     case XK_C:
6801       return(SpiffCommand);
6802     case XK_Z:
6803       return(DullCommand);
6804     case XK_N:
6805       return(NormalizeCommand);
6806     case XK_equal:
6807       return(EqualizeCommand);
6808     case XK_asciitilde:
6809       return(NegateCommand);
6810     case XK_period:
6811       return(GrayscaleCommand);
6812     case XK_numbersign:
6813       return(QuantizeCommand);
6814     case XK_F2:
6815       return(DespeckleCommand);
6816     case XK_F3:
6817       return(EmbossCommand);
6818     case XK_F4:
6819       return(ReduceNoiseCommand);
6820     case XK_F5:
6821       return(AddNoiseCommand);
6822     case XK_F6:
6823       return(SharpenCommand);
6824     case XK_F7:
6825       return(BlurCommand);
6826     case XK_F8:
6827       return(ThresholdCommand);
6828     case XK_F9:
6829       return(EdgeDetectCommand);
6830     case XK_F10:
6831       return(SpreadCommand);
6832     case XK_F11:
6833       return(ShadeCommand);
6834     case XK_F12:
6835       return(RaiseCommand);
6836     case XK_F13:
6837       return(SegmentCommand);
6838     case XK_i:
6839     {
6840       if ((state & Mod1Mask) == 0)
6841         return(NullCommand);
6842       return(ImplodeCommand);
6843     }
6844     case XK_w:
6845     {
6846       if ((state & Mod1Mask) == 0)
6847         return(NullCommand);
6848       return(WaveCommand);
6849     }
6850     case XK_m:
6851     {
6852       if ((state & Mod4Mask) == 0)
6853         return(NullCommand);
6854       return(MatteCommand);
6855     }
6856     case XK_b:
6857     {
6858       if ((state & Mod4Mask) == 0)
6859         return(NullCommand);
6860       return(AddBorderCommand);
6861     }
6862     case XK_f:
6863     {
6864       if ((state & Mod4Mask) == 0)
6865         return(NullCommand);
6866       return(AddFrameCommand);
6867     }
6868     case XK_exclam:
6869     {
6870       if ((state & Mod4Mask) == 0)
6871         return(NullCommand);
6872       return(CommentCommand);
6873     }
6874     case XK_a:
6875     {
6876       if ((state & Mod1Mask) != 0)
6877         return(ApplyCommand);
6878       if ((state & Mod4Mask) != 0)
6879         return(AnnotateCommand);
6880       if ((state & ControlMask) == 0)
6881         return(NullCommand);
6882       return(RegionofInterestCommand);
6883     }
6884     case XK_question:
6885       return(InfoCommand);
6886     case XK_plus:
6887       return(ZoomCommand);
6888     case XK_P:
6889     {
6890       if ((state & ShiftMask) == 0)
6891         return(NullCommand);
6892       return(ShowPreviewCommand);
6893     }
6894     case XK_Execute:
6895       return(LaunchCommand);
6896     case XK_F1:
6897       return(HelpCommand);
6898     case XK_Find:
6899       return(BrowseDocumentationCommand);
6900     case XK_Menu:
6901     {
6902       (void) XMapRaised(display,windows->command.id);
6903       return(NullCommand);
6904     }
6905     case XK_Next:
6906     case XK_Prior:
6907     case XK_Home:
6908     case XK_KP_Home:
6909     {
6910       XTranslateImage(display,windows,*image,key_symbol);
6911       return(NullCommand);
6912     }
6913     case XK_Up:
6914     case XK_KP_Up:
6915     case XK_Down:
6916     case XK_KP_Down:
6917     case XK_Left:
6918     case XK_KP_Left:
6919     case XK_Right:
6920     case XK_KP_Right:
6921     {
6922       if ((state & Mod1Mask) != 0)
6923         {
6924           RectangleInfo
6925             crop_info;
6926
6927           /*
6928             Trim one pixel from edge of image.
6929           */
6930           crop_info.x=0;
6931           crop_info.y=0;
6932           crop_info.width=(size_t) windows->image.ximage->width;
6933           crop_info.height=(size_t) windows->image.ximage->height;
6934           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6935             {
6936               if (resource_info->quantum >= (int) crop_info.height)
6937                 resource_info->quantum=(int) crop_info.height-1;
6938               crop_info.height-=resource_info->quantum;
6939             }
6940           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6941             {
6942               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6943                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6944               crop_info.y+=resource_info->quantum;
6945               crop_info.height-=resource_info->quantum;
6946             }
6947           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6948             {
6949               if (resource_info->quantum >= (int) crop_info.width)
6950                 resource_info->quantum=(int) crop_info.width-1;
6951               crop_info.width-=resource_info->quantum;
6952             }
6953           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6954             {
6955               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
6956                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
6957               crop_info.x+=resource_info->quantum;
6958               crop_info.width-=resource_info->quantum;
6959             }
6960           if ((int) (windows->image.x+windows->image.width) >
6961               (int) crop_info.width)
6962             windows->image.x=(int) (crop_info.width-windows->image.width);
6963           if ((int) (windows->image.y+windows->image.height) >
6964               (int) crop_info.height)
6965             windows->image.y=(int) (crop_info.height-windows->image.height);
6966           XSetCropGeometry(display,windows,&crop_info,*image);
6967           windows->image.window_changes.width=(int) crop_info.width;
6968           windows->image.window_changes.height=(int) crop_info.height;
6969           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
6970           (void) XConfigureImage(display,resource_info,windows,*image);
6971           return(NullCommand);
6972         }
6973       XTranslateImage(display,windows,*image,key_symbol);
6974       return(NullCommand);
6975     }
6976     default:
6977       return(NullCommand);
6978   }
6979   return(NullCommand);
6980 }
6981 \f
6982 /*
6983 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6984 %                                                                             %
6985 %                                                                             %
6986 %                                                                             %
6987 +   X M a g i c k C o m m a n d                                               %
6988 %                                                                             %
6989 %                                                                             %
6990 %                                                                             %
6991 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6992 %
6993 %  XMagickCommand() makes a transform to the image or Image window as
6994 %  specified by a user menu button or keyboard command.
6995 %
6996 %  The format of the XMagickCommand method is:
6997 %
6998 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
6999 %        XWindows *windows,const CommandType command,Image **image)
7000 %
7001 %  A description of each parameter follows:
7002 %
7003 %    o nexus:  Method XMagickCommand returns an image when the
7004 %      user chooses 'Load Image' from the command menu.  Otherwise a null
7005 %      image is returned.
7006 %
7007 %    o display: Specifies a connection to an X server; returned from
7008 %      XOpenDisplay.
7009 %
7010 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7011 %
7012 %    o windows: Specifies a pointer to a XWindows structure.
7013 %
7014 %    o command: Specifies a command to perform.
7015 %
7016 %    o image: the image;  XMagickCommand
7017 %      may transform the image and return a new image pointer.
7018 %
7019 */
7020 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7021   XWindows *windows,const CommandType command,Image **image)
7022 {
7023   char
7024     filename[MaxTextExtent],
7025     geometry[MaxTextExtent],
7026     modulate_factors[MaxTextExtent];
7027
7028   GeometryInfo
7029     geometry_info;
7030
7031   Image
7032     *nexus;
7033
7034   ImageInfo
7035     *image_info;
7036
7037   int
7038     x,
7039     y;
7040
7041   MagickStatusType
7042     flags,
7043     status;
7044
7045   QuantizeInfo
7046     quantize_info;
7047
7048   RectangleInfo
7049     page_geometry;
7050
7051   register int
7052     i;
7053
7054   static char
7055     color[MaxTextExtent] = "gray";
7056
7057   unsigned int
7058     height,
7059     width;
7060
7061   /*
7062     Process user command.
7063   */
7064   XCheckRefreshWindows(display,windows);
7065   XImageCache(display,resource_info,windows,command,image);
7066   nexus=NewImageList();
7067   windows->image.window_changes.width=windows->image.ximage->width;
7068   windows->image.window_changes.height=windows->image.ximage->height;
7069   image_info=CloneImageInfo(resource_info->image_info);
7070   SetGeometryInfo(&geometry_info);
7071   GetQuantizeInfo(&quantize_info);
7072   switch (command)
7073   {
7074     case OpenCommand:
7075     {
7076       /*
7077         Load image.
7078       */
7079       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7080       break;
7081     }
7082     case NextCommand:
7083     {
7084       /*
7085         Display next image.
7086       */
7087       for (i=0; i < resource_info->quantum; i++)
7088         XClientMessage(display,windows->image.id,windows->im_protocols,
7089           windows->im_next_image,CurrentTime);
7090       break;
7091     }
7092     case FormerCommand:
7093     {
7094       /*
7095         Display former image.
7096       */
7097       for (i=0; i < resource_info->quantum; i++)
7098         XClientMessage(display,windows->image.id,windows->im_protocols,
7099           windows->im_former_image,CurrentTime);
7100       break;
7101     }
7102     case SelectCommand:
7103     {
7104       int
7105         status;
7106
7107       /*
7108         Select image.
7109       */
7110       status=chdir(resource_info->home_directory);
7111       if (status == -1)
7112         (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7113           FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7114       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7115       break;
7116     }
7117     case SaveCommand:
7118     {
7119       /*
7120         Save image.
7121       */
7122       status=XSaveImage(display,resource_info,windows,*image);
7123       if (status == MagickFalse)
7124         {
7125           XNoticeWidget(display,windows,"Unable to write X image:",
7126             (*image)->filename);
7127           break;
7128         }
7129       break;
7130     }
7131     case PrintCommand:
7132     {
7133       /*
7134         Print image.
7135       */
7136       status=XPrintImage(display,resource_info,windows,*image);
7137       if (status == MagickFalse)
7138         {
7139           XNoticeWidget(display,windows,"Unable to print X image:",
7140             (*image)->filename);
7141           break;
7142         }
7143       break;
7144     }
7145     case DeleteCommand:
7146     {
7147       static char
7148         filename[MaxTextExtent] = "\0";
7149
7150       /*
7151         Delete image file.
7152       */
7153       XFileBrowserWidget(display,windows,"Delete",filename);
7154       if (*filename == '\0')
7155         break;
7156       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
7157       if (status != MagickFalse)
7158         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7159       break;
7160     }
7161     case NewCommand:
7162     {
7163       int
7164         status;
7165
7166       static char
7167         color[MaxTextExtent] = "gray",
7168         geometry[MaxTextExtent] = "640x480";
7169
7170       static const char
7171         *format = "gradient";
7172
7173       /*
7174         Query user for canvas geometry.
7175       */
7176       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7177         geometry);
7178       if (*geometry == '\0')
7179         break;
7180       if (status == 0)
7181         format="xc";
7182       XColorBrowserWidget(display,windows,"Select",color);
7183       if (*color == '\0')
7184         break;
7185       /*
7186         Create canvas.
7187       */
7188       (void) FormatMagickString(image_info->filename,MaxTextExtent,
7189         "%s:%s",format,color);
7190       (void) CloneString(&image_info->size,geometry);
7191       nexus=ReadImage(image_info,&(*image)->exception);
7192       CatchException(&(*image)->exception);
7193       XClientMessage(display,windows->image.id,windows->im_protocols,
7194         windows->im_next_image,CurrentTime);
7195       break;
7196     }
7197     case VisualDirectoryCommand:
7198     {
7199       /*
7200         Visual Image directory.
7201       */
7202       nexus=XVisualDirectoryImage(display,resource_info,windows);
7203       break;
7204     }
7205     case QuitCommand:
7206     {
7207       /*
7208         exit program.
7209       */
7210       if (resource_info->confirm_exit == MagickFalse)
7211         XClientMessage(display,windows->image.id,windows->im_protocols,
7212           windows->im_exit,CurrentTime);
7213       else
7214         {
7215           int
7216             status;
7217
7218           /*
7219             Confirm program exit.
7220           */
7221           status=XConfirmWidget(display,windows,"Do you really want to exit",
7222             resource_info->client_name);
7223           if (status > 0)
7224             XClientMessage(display,windows->image.id,windows->im_protocols,
7225               windows->im_exit,CurrentTime);
7226         }
7227       break;
7228     }
7229     case CutCommand:
7230     {
7231       /*
7232         Cut image.
7233       */
7234       (void) XCropImage(display,resource_info,windows,*image,CutMode);
7235       break;
7236     }
7237     case CopyCommand:
7238     {
7239       /*
7240         Copy image.
7241       */
7242       (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7243       break;
7244     }
7245     case PasteCommand:
7246     {
7247       /*
7248         Paste image.
7249       */
7250       status=XPasteImage(display,resource_info,windows,*image);
7251       if (status == MagickFalse)
7252         {
7253           XNoticeWidget(display,windows,"Unable to paste X image",
7254             (*image)->filename);
7255           break;
7256         }
7257       break;
7258     }
7259     case HalfSizeCommand:
7260     {
7261       /*
7262         Half image size.
7263       */
7264       windows->image.window_changes.width=windows->image.ximage->width/2;
7265       windows->image.window_changes.height=windows->image.ximage->height/2;
7266       (void) XConfigureImage(display,resource_info,windows,*image);
7267       break;
7268     }
7269     case OriginalSizeCommand:
7270     {
7271       /*
7272         Original image size.
7273       */
7274       windows->image.window_changes.width=(int) (*image)->columns;
7275       windows->image.window_changes.height=(int) (*image)->rows;
7276       (void) XConfigureImage(display,resource_info,windows,*image);
7277       break;
7278     }
7279     case DoubleSizeCommand:
7280     {
7281       /*
7282         Double the image size.
7283       */
7284       windows->image.window_changes.width=windows->image.ximage->width << 1;
7285       windows->image.window_changes.height=windows->image.ximage->height << 1;
7286       (void) XConfigureImage(display,resource_info,windows,*image);
7287       break;
7288     }
7289     case ResizeCommand:
7290     {
7291       int
7292         status;
7293
7294       ssize_t
7295         x,
7296         y;
7297
7298       size_t
7299         height,
7300         width;
7301
7302       /*
7303         Resize image.
7304       */
7305       width=(size_t) windows->image.ximage->width;
7306       height=(size_t) windows->image.ximage->height;
7307       x=0;
7308       y=0;
7309       (void) FormatMagickString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7310         (double) width,(double) height);
7311       status=XDialogWidget(display,windows,"Resize",
7312         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7313       if (*geometry == '\0')
7314         break;
7315       if (status == 0)
7316         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7317       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7318       windows->image.window_changes.width=(int) width;
7319       windows->image.window_changes.height=(int) height;
7320       (void) XConfigureImage(display,resource_info,windows,*image);
7321       break;
7322     }
7323     case ApplyCommand:
7324     {
7325       char
7326         image_geometry[MaxTextExtent];
7327
7328       if ((windows->image.crop_geometry == (char *) NULL) &&
7329           ((int) (*image)->columns == windows->image.ximage->width) &&
7330           ((int) (*image)->rows == windows->image.ximage->height))
7331         break;
7332       /*
7333         Apply size transforms to image.
7334       */
7335       XSetCursorState(display,windows,MagickTrue);
7336       XCheckRefreshWindows(display,windows);
7337       /*
7338         Crop and/or scale displayed image.
7339       */
7340       (void) FormatMagickString(image_geometry,MaxTextExtent,"%dx%d!",
7341         windows->image.ximage->width,windows->image.ximage->height);
7342       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7343       if (windows->image.crop_geometry != (char *) NULL)
7344         windows->image.crop_geometry=(char *)
7345           RelinquishMagickMemory(windows->image.crop_geometry);
7346       windows->image.x=0;
7347       windows->image.y=0;
7348       XConfigureImageColormap(display,resource_info,windows,*image);
7349       (void) XConfigureImage(display,resource_info,windows,*image);
7350       break;
7351     }
7352     case RefreshCommand:
7353     {
7354       (void) XConfigureImage(display,resource_info,windows,*image);
7355       break;
7356     }
7357     case RestoreCommand:
7358     {
7359       /*
7360         Restore Image window to its original size.
7361       */
7362       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7363           (windows->image.height == (unsigned int) (*image)->rows) &&
7364           (windows->image.crop_geometry == (char *) NULL))
7365         {
7366           (void) XBell(display,0);
7367           break;
7368         }
7369       windows->image.window_changes.width=(int) (*image)->columns;
7370       windows->image.window_changes.height=(int) (*image)->rows;
7371       if (windows->image.crop_geometry != (char *) NULL)
7372         {
7373           windows->image.crop_geometry=(char *)
7374             RelinquishMagickMemory(windows->image.crop_geometry);
7375           windows->image.crop_geometry=(char *) NULL;
7376           windows->image.x=0;
7377           windows->image.y=0;
7378         }
7379       XConfigureImageColormap(display,resource_info,windows,*image);
7380       (void) XConfigureImage(display,resource_info,windows,*image);
7381       break;
7382     }
7383     case CropCommand:
7384     {
7385       /*
7386         Crop image.
7387       */
7388       (void) XCropImage(display,resource_info,windows,*image,CropMode);
7389       break;
7390     }
7391     case ChopCommand:
7392     {
7393       /*
7394         Chop image.
7395       */
7396       status=XChopImage(display,resource_info,windows,image);
7397       if (status == MagickFalse)
7398         {
7399           XNoticeWidget(display,windows,"Unable to cut X image",
7400             (*image)->filename);
7401           break;
7402         }
7403       break;
7404     }
7405     case FlopCommand:
7406     {
7407       Image
7408         *flop_image;
7409
7410       /*
7411         Flop image scanlines.
7412       */
7413       XSetCursorState(display,windows,MagickTrue);
7414       XCheckRefreshWindows(display,windows);
7415       flop_image=FlopImage(*image,&(*image)->exception);
7416       if (flop_image != (Image *) NULL)
7417         {
7418           *image=DestroyImage(*image);
7419           *image=flop_image;
7420         }
7421       CatchException(&(*image)->exception);
7422       XSetCursorState(display,windows,MagickFalse);
7423       if (windows->image.crop_geometry != (char *) NULL)
7424         {
7425           /*
7426             Flop crop geometry.
7427           */
7428           width=(unsigned int) (*image)->columns;
7429           height=(unsigned int) (*image)->rows;
7430           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7431             &width,&height);
7432           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
7433             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7434         }
7435       if (windows->image.orphan != MagickFalse)
7436         break;
7437       (void) XConfigureImage(display,resource_info,windows,*image);
7438       break;
7439     }
7440     case FlipCommand:
7441     {
7442       Image
7443         *flip_image;
7444
7445       /*
7446         Flip image scanlines.
7447       */
7448       XSetCursorState(display,windows,MagickTrue);
7449       XCheckRefreshWindows(display,windows);
7450       flip_image=FlipImage(*image,&(*image)->exception);
7451       if (flip_image != (Image *) NULL)
7452         {
7453           *image=DestroyImage(*image);
7454           *image=flip_image;
7455         }
7456       CatchException(&(*image)->exception);
7457       XSetCursorState(display,windows,MagickFalse);
7458       if (windows->image.crop_geometry != (char *) NULL)
7459         {
7460           /*
7461             Flip crop geometry.
7462           */
7463           width=(unsigned int) (*image)->columns;
7464           height=(unsigned int) (*image)->rows;
7465           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7466             &width,&height);
7467           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
7468             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7469         }
7470       if (windows->image.orphan != MagickFalse)
7471         break;
7472       (void) XConfigureImage(display,resource_info,windows,*image);
7473       break;
7474     }
7475     case RotateRightCommand:
7476     {
7477       /*
7478         Rotate image 90 degrees clockwise.
7479       */
7480       status=XRotateImage(display,resource_info,windows,90.0,image);
7481       if (status == MagickFalse)
7482         {
7483           XNoticeWidget(display,windows,"Unable to rotate X image",
7484             (*image)->filename);
7485           break;
7486         }
7487       break;
7488     }
7489     case RotateLeftCommand:
7490     {
7491       /*
7492         Rotate image 90 degrees counter-clockwise.
7493       */
7494       status=XRotateImage(display,resource_info,windows,-90.0,image);
7495       if (status == MagickFalse)
7496         {
7497           XNoticeWidget(display,windows,"Unable to rotate X image",
7498             (*image)->filename);
7499           break;
7500         }
7501       break;
7502     }
7503     case RotateCommand:
7504     {
7505       /*
7506         Rotate image.
7507       */
7508       status=XRotateImage(display,resource_info,windows,0.0,image);
7509       if (status == MagickFalse)
7510         {
7511           XNoticeWidget(display,windows,"Unable to rotate X image",
7512             (*image)->filename);
7513           break;
7514         }
7515       break;
7516     }
7517     case ShearCommand:
7518     {
7519       Image
7520         *shear_image;
7521
7522       static char
7523         geometry[MaxTextExtent] = "45.0x45.0";
7524
7525       /*
7526         Query user for shear color and geometry.
7527       */
7528       XColorBrowserWidget(display,windows,"Select",color);
7529       if (*color == '\0')
7530         break;
7531       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7532         geometry);
7533       if (*geometry == '\0')
7534         break;
7535       /*
7536         Shear image.
7537       */
7538       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7539       XSetCursorState(display,windows,MagickTrue);
7540       XCheckRefreshWindows(display,windows);
7541       (void) QueryColorDatabase(color,&(*image)->background_color,
7542         &(*image)->exception);
7543       flags=ParseGeometry(geometry,&geometry_info);
7544       if ((flags & SigmaValue) == 0)
7545         geometry_info.sigma=geometry_info.rho;
7546       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7547         &(*image)->exception);
7548       if (shear_image != (Image *) NULL)
7549         {
7550           *image=DestroyImage(*image);
7551           *image=shear_image;
7552         }
7553       CatchException(&(*image)->exception);
7554       XSetCursorState(display,windows,MagickFalse);
7555       if (windows->image.orphan != MagickFalse)
7556         break;
7557       windows->image.window_changes.width=(int) (*image)->columns;
7558       windows->image.window_changes.height=(int) (*image)->rows;
7559       XConfigureImageColormap(display,resource_info,windows,*image);
7560       (void) XConfigureImage(display,resource_info,windows,*image);
7561       break;
7562     }
7563     case RollCommand:
7564     {
7565       Image
7566         *roll_image;
7567
7568       static char
7569         geometry[MaxTextExtent] = "+2+2";
7570
7571       /*
7572         Query user for the roll geometry.
7573       */
7574       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7575         geometry);
7576       if (*geometry == '\0')
7577         break;
7578       /*
7579         Roll image.
7580       */
7581       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7582       XSetCursorState(display,windows,MagickTrue);
7583       XCheckRefreshWindows(display,windows);
7584       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7585         &(*image)->exception);
7586       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7587         &(*image)->exception);
7588       if (roll_image != (Image *) NULL)
7589         {
7590           *image=DestroyImage(*image);
7591           *image=roll_image;
7592         }
7593       CatchException(&(*image)->exception);
7594       XSetCursorState(display,windows,MagickFalse);
7595       if (windows->image.orphan != MagickFalse)
7596         break;
7597       windows->image.window_changes.width=(int) (*image)->columns;
7598       windows->image.window_changes.height=(int) (*image)->rows;
7599       XConfigureImageColormap(display,resource_info,windows,*image);
7600       (void) XConfigureImage(display,resource_info,windows,*image);
7601       break;
7602     }
7603     case TrimCommand:
7604     {
7605       static char
7606         fuzz[MaxTextExtent];
7607
7608       /*
7609         Query user for the fuzz factor.
7610       */
7611       (void) FormatMagickString(fuzz,MaxTextExtent,"%g%%",100.0*
7612         (*image)->fuzz/(QuantumRange+1.0));
7613       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7614       if (*fuzz == '\0')
7615         break;
7616       (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7617       /*
7618         Trim image.
7619       */
7620       status=XTrimImage(display,resource_info,windows,*image);
7621       if (status == MagickFalse)
7622         {
7623           XNoticeWidget(display,windows,"Unable to trim X image",
7624             (*image)->filename);
7625           break;
7626         }
7627       break;
7628     }
7629     case HueCommand:
7630     {
7631       static char
7632         hue_percent[MaxTextExtent] = "110";
7633
7634       /*
7635         Query user for percent hue change.
7636       */
7637       (void) XDialogWidget(display,windows,"Apply",
7638         "Enter percent change in image hue (0-200):",hue_percent);
7639       if (*hue_percent == '\0')
7640         break;
7641       /*
7642         Vary the image hue.
7643       */
7644       XSetCursorState(display,windows,MagickTrue);
7645       XCheckRefreshWindows(display,windows);
7646       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7647       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7648         MaxTextExtent);
7649       (void) ModulateImage(*image,modulate_factors);
7650       XSetCursorState(display,windows,MagickFalse);
7651       if (windows->image.orphan != MagickFalse)
7652         break;
7653       XConfigureImageColormap(display,resource_info,windows,*image);
7654       (void) XConfigureImage(display,resource_info,windows,*image);
7655       break;
7656     }
7657     case SaturationCommand:
7658     {
7659       static char
7660         saturation_percent[MaxTextExtent] = "110";
7661
7662       /*
7663         Query user for percent saturation change.
7664       */
7665       (void) XDialogWidget(display,windows,"Apply",
7666         "Enter percent change in color saturation (0-200):",saturation_percent);
7667       if (*saturation_percent == '\0')
7668         break;
7669       /*
7670         Vary color saturation.
7671       */
7672       XSetCursorState(display,windows,MagickTrue);
7673       XCheckRefreshWindows(display,windows);
7674       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7675       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7676         MaxTextExtent);
7677       (void) ModulateImage(*image,modulate_factors);
7678       XSetCursorState(display,windows,MagickFalse);
7679       if (windows->image.orphan != MagickFalse)
7680         break;
7681       XConfigureImageColormap(display,resource_info,windows,*image);
7682       (void) XConfigureImage(display,resource_info,windows,*image);
7683       break;
7684     }
7685     case BrightnessCommand:
7686     {
7687       static char
7688         brightness_percent[MaxTextExtent] = "110";
7689
7690       /*
7691         Query user for percent brightness change.
7692       */
7693       (void) XDialogWidget(display,windows,"Apply",
7694         "Enter percent change in color brightness (0-200):",brightness_percent);
7695       if (*brightness_percent == '\0')
7696         break;
7697       /*
7698         Vary the color brightness.
7699       */
7700       XSetCursorState(display,windows,MagickTrue);
7701       XCheckRefreshWindows(display,windows);
7702       (void) CopyMagickString(modulate_factors,brightness_percent,
7703         MaxTextExtent);
7704       (void) ModulateImage(*image,modulate_factors);
7705       XSetCursorState(display,windows,MagickFalse);
7706       if (windows->image.orphan != MagickFalse)
7707         break;
7708       XConfigureImageColormap(display,resource_info,windows,*image);
7709       (void) XConfigureImage(display,resource_info,windows,*image);
7710       break;
7711     }
7712     case GammaCommand:
7713     {
7714       static char
7715         factor[MaxTextExtent] = "1.6";
7716
7717       /*
7718         Query user for gamma value.
7719       */
7720       (void) XDialogWidget(display,windows,"Gamma",
7721         "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
7722       if (*factor == '\0')
7723         break;
7724       /*
7725         Gamma correct image.
7726       */
7727       XSetCursorState(display,windows,MagickTrue);
7728       XCheckRefreshWindows(display,windows);
7729       (void) GammaImage(*image,factor);
7730       XSetCursorState(display,windows,MagickFalse);
7731       if (windows->image.orphan != MagickFalse)
7732         break;
7733       XConfigureImageColormap(display,resource_info,windows,*image);
7734       (void) XConfigureImage(display,resource_info,windows,*image);
7735       break;
7736     }
7737     case SpiffCommand:
7738     {
7739       /*
7740         Sharpen the image contrast.
7741       */
7742       XSetCursorState(display,windows,MagickTrue);
7743       XCheckRefreshWindows(display,windows);
7744       (void) ContrastImage(*image,MagickTrue);
7745       XSetCursorState(display,windows,MagickFalse);
7746       if (windows->image.orphan != MagickFalse)
7747         break;
7748       XConfigureImageColormap(display,resource_info,windows,*image);
7749       (void) XConfigureImage(display,resource_info,windows,*image);
7750       break;
7751     }
7752     case DullCommand:
7753     {
7754       /*
7755         Dull the image contrast.
7756       */
7757       XSetCursorState(display,windows,MagickTrue);
7758       XCheckRefreshWindows(display,windows);
7759       (void) ContrastImage(*image,MagickFalse);
7760       XSetCursorState(display,windows,MagickFalse);
7761       if (windows->image.orphan != MagickFalse)
7762         break;
7763       XConfigureImageColormap(display,resource_info,windows,*image);
7764       (void) XConfigureImage(display,resource_info,windows,*image);
7765       break;
7766     }
7767     case ContrastStretchCommand:
7768     {
7769       double
7770         black_point,
7771         white_point;
7772
7773       static char
7774         levels[MaxTextExtent] = "1%";
7775
7776       /*
7777         Query user for gamma value.
7778       */
7779       (void) XDialogWidget(display,windows,"Contrast Stretch",
7780         "Enter black and white points:",levels);
7781       if (*levels == '\0')
7782         break;
7783       /*
7784         Contrast stretch image.
7785       */
7786       XSetCursorState(display,windows,MagickTrue);
7787       XCheckRefreshWindows(display,windows);
7788       flags=ParseGeometry(levels,&geometry_info);
7789       black_point=geometry_info.rho;
7790       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7791       if ((flags & PercentValue) != 0)
7792         {
7793           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7794           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7795         }
7796       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7797       (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7798         white_point);
7799       XSetCursorState(display,windows,MagickFalse);
7800       if (windows->image.orphan != MagickFalse)
7801         break;
7802       XConfigureImageColormap(display,resource_info,windows,*image);
7803       (void) XConfigureImage(display,resource_info,windows,*image);
7804       break;
7805     }
7806     case SigmoidalContrastCommand:
7807     {
7808       static char
7809         levels[MaxTextExtent] = "3x50%";
7810
7811       /*
7812         Query user for gamma value.
7813       */
7814       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7815         "Enter contrast and midpoint:",levels);
7816       if (*levels == '\0')
7817         break;
7818       /*
7819         Contrast stretch image.
7820       */
7821       XSetCursorState(display,windows,MagickTrue);
7822       XCheckRefreshWindows(display,windows);
7823       (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7824       XSetCursorState(display,windows,MagickFalse);
7825       if (windows->image.orphan != MagickFalse)
7826         break;
7827       XConfigureImageColormap(display,resource_info,windows,*image);
7828       (void) XConfigureImage(display,resource_info,windows,*image);
7829       break;
7830     }
7831     case NormalizeCommand:
7832     {
7833       /*
7834         Perform histogram normalization on the image.
7835       */
7836       XSetCursorState(display,windows,MagickTrue);
7837       XCheckRefreshWindows(display,windows);
7838       (void) NormalizeImage(*image);
7839       XSetCursorState(display,windows,MagickFalse);
7840       if (windows->image.orphan != MagickFalse)
7841         break;
7842       XConfigureImageColormap(display,resource_info,windows,*image);
7843       (void) XConfigureImage(display,resource_info,windows,*image);
7844       break;
7845     }
7846     case EqualizeCommand:
7847     {
7848       /*
7849         Perform histogram equalization on the image.
7850       */
7851       XSetCursorState(display,windows,MagickTrue);
7852       XCheckRefreshWindows(display,windows);
7853       (void) EqualizeImage(*image);
7854       XSetCursorState(display,windows,MagickFalse);
7855       if (windows->image.orphan != MagickFalse)
7856         break;
7857       XConfigureImageColormap(display,resource_info,windows,*image);
7858       (void) XConfigureImage(display,resource_info,windows,*image);
7859       break;
7860     }
7861     case NegateCommand:
7862     {
7863       /*
7864         Negate colors in image.
7865       */
7866       XSetCursorState(display,windows,MagickTrue);
7867       XCheckRefreshWindows(display,windows);
7868       (void) NegateImage(*image,MagickFalse);
7869       XSetCursorState(display,windows,MagickFalse);
7870       if (windows->image.orphan != MagickFalse)
7871         break;
7872       XConfigureImageColormap(display,resource_info,windows,*image);
7873       (void) XConfigureImage(display,resource_info,windows,*image);
7874       break;
7875     }
7876     case GrayscaleCommand:
7877     {
7878       /*
7879         Convert image to grayscale.
7880       */
7881       XSetCursorState(display,windows,MagickTrue);
7882       XCheckRefreshWindows(display,windows);
7883       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7884         GrayscaleType : GrayscaleMatteType);
7885       XSetCursorState(display,windows,MagickFalse);
7886       if (windows->image.orphan != MagickFalse)
7887         break;
7888       XConfigureImageColormap(display,resource_info,windows,*image);
7889       (void) XConfigureImage(display,resource_info,windows,*image);
7890       break;
7891     }
7892     case MapCommand:
7893     {
7894       Image
7895         *affinity_image;
7896
7897       static char
7898         filename[MaxTextExtent] = "\0";
7899
7900       /*
7901         Request image file name from user.
7902       */
7903       XFileBrowserWidget(display,windows,"Map",filename);
7904       if (*filename == '\0')
7905         break;
7906       /*
7907         Map image.
7908       */
7909       XSetCursorState(display,windows,MagickTrue);
7910       XCheckRefreshWindows(display,windows);
7911       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7912       affinity_image=ReadImage(image_info,&(*image)->exception);
7913       if (affinity_image != (Image *) NULL)
7914         {
7915           (void) RemapImage(&quantize_info,*image,affinity_image);
7916           affinity_image=DestroyImage(affinity_image);
7917         }
7918       CatchException(&(*image)->exception);
7919       XSetCursorState(display,windows,MagickFalse);
7920       if (windows->image.orphan != MagickFalse)
7921         break;
7922       XConfigureImageColormap(display,resource_info,windows,*image);
7923       (void) XConfigureImage(display,resource_info,windows,*image);
7924       break;
7925     }
7926     case QuantizeCommand:
7927     {
7928       int
7929         status;
7930
7931       static char
7932         colors[MaxTextExtent] = "256";
7933
7934       /*
7935         Query user for maximum number of colors.
7936       */
7937       status=XDialogWidget(display,windows,"Quantize",
7938         "Maximum number of colors:",colors);
7939       if (*colors == '\0')
7940         break;
7941       /*
7942         Color reduce the image.
7943       */
7944       XSetCursorState(display,windows,MagickTrue);
7945       XCheckRefreshWindows(display,windows);
7946       quantize_info.number_colors=StringToUnsignedLong(colors);
7947       quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7948       (void) QuantizeImage(&quantize_info,*image);
7949       XSetCursorState(display,windows,MagickFalse);
7950       if (windows->image.orphan != MagickFalse)
7951         break;
7952       XConfigureImageColormap(display,resource_info,windows,*image);
7953       (void) XConfigureImage(display,resource_info,windows,*image);
7954       break;
7955     }
7956     case DespeckleCommand:
7957     {
7958       Image
7959         *despeckle_image;
7960
7961       /*
7962         Despeckle image.
7963       */
7964       XSetCursorState(display,windows,MagickTrue);
7965       XCheckRefreshWindows(display,windows);
7966       despeckle_image=DespeckleImage(*image,&(*image)->exception);
7967       if (despeckle_image != (Image *) NULL)
7968         {
7969           *image=DestroyImage(*image);
7970           *image=despeckle_image;
7971         }
7972       CatchException(&(*image)->exception);
7973       XSetCursorState(display,windows,MagickFalse);
7974       if (windows->image.orphan != MagickFalse)
7975         break;
7976       XConfigureImageColormap(display,resource_info,windows,*image);
7977       (void) XConfigureImage(display,resource_info,windows,*image);
7978       break;
7979     }
7980     case EmbossCommand:
7981     {
7982       Image
7983         *emboss_image;
7984
7985       static char
7986         radius[MaxTextExtent] = "0.0x1.0";
7987
7988       /*
7989         Query user for emboss radius.
7990       */
7991       (void) XDialogWidget(display,windows,"Emboss",
7992         "Enter the emboss radius and standard deviation:",radius);
7993       if (*radius == '\0')
7994         break;
7995       /*
7996         Reduce noise in the image.
7997       */
7998       XSetCursorState(display,windows,MagickTrue);
7999       XCheckRefreshWindows(display,windows);
8000       flags=ParseGeometry(radius,&geometry_info);
8001       if ((flags & SigmaValue) == 0)
8002         geometry_info.sigma=1.0;
8003       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8004         &(*image)->exception);
8005       if (emboss_image != (Image *) NULL)
8006         {
8007           *image=DestroyImage(*image);
8008           *image=emboss_image;
8009         }
8010       CatchException(&(*image)->exception);
8011       XSetCursorState(display,windows,MagickFalse);
8012       if (windows->image.orphan != MagickFalse)
8013         break;
8014       XConfigureImageColormap(display,resource_info,windows,*image);
8015       (void) XConfigureImage(display,resource_info,windows,*image);
8016       break;
8017     }
8018     case ReduceNoiseCommand:
8019     {
8020       Image
8021         *noise_image;
8022
8023       static char
8024         radius[MaxTextExtent] = "0";
8025
8026       /*
8027         Query user for noise radius.
8028       */
8029       (void) XDialogWidget(display,windows,"Reduce Noise",
8030         "Enter the noise radius:",radius);
8031       if (*radius == '\0')
8032         break;
8033       /*
8034         Reduce noise in the image.
8035       */
8036       XSetCursorState(display,windows,MagickTrue);
8037       XCheckRefreshWindows(display,windows);
8038       flags=ParseGeometry(radius,&geometry_info);
8039       noise_image=ReduceNoiseImage(*image,geometry_info.rho,
8040         &(*image)->exception);
8041       if (noise_image != (Image *) NULL)
8042         {
8043           *image=DestroyImage(*image);
8044           *image=noise_image;
8045         }
8046       CatchException(&(*image)->exception);
8047       XSetCursorState(display,windows,MagickFalse);
8048       if (windows->image.orphan != MagickFalse)
8049         break;
8050       XConfigureImageColormap(display,resource_info,windows,*image);
8051       (void) XConfigureImage(display,resource_info,windows,*image);
8052       break;
8053     }
8054     case AddNoiseCommand:
8055     {
8056       char
8057         **noises;
8058
8059       Image
8060         *noise_image;
8061
8062       static char
8063         noise_type[MaxTextExtent] = "Gaussian";
8064
8065       /*
8066         Add noise to the image.
8067       */
8068       noises=GetMagickOptions(MagickNoiseOptions);
8069       if (noises == (char **) NULL)
8070         break;
8071       XListBrowserWidget(display,windows,&windows->widget,
8072         (const char **) noises,"Add Noise",
8073         "Select a type of noise to add to your image:",noise_type);
8074       noises=DestroyStringList(noises);
8075       if (*noise_type == '\0')
8076         break;
8077       XSetCursorState(display,windows,MagickTrue);
8078       XCheckRefreshWindows(display,windows);
8079       noise_image=AddNoiseImage(*image,(NoiseType) ParseMagickOption(
8080         MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8081       if (noise_image != (Image *) NULL)
8082         {
8083           *image=DestroyImage(*image);
8084           *image=noise_image;
8085         }
8086       CatchException(&(*image)->exception);
8087       XSetCursorState(display,windows,MagickFalse);
8088       if (windows->image.orphan != MagickFalse)
8089         break;
8090       XConfigureImageColormap(display,resource_info,windows,*image);
8091       (void) XConfigureImage(display,resource_info,windows,*image);
8092       break;
8093     }
8094     case SharpenCommand:
8095     {
8096       Image
8097         *sharp_image;
8098
8099       static char
8100         radius[MaxTextExtent] = "0.0x1.0";
8101
8102       /*
8103         Query user for sharpen radius.
8104       */
8105       (void) XDialogWidget(display,windows,"Sharpen",
8106         "Enter the sharpen radius and standard deviation:",radius);
8107       if (*radius == '\0')
8108         break;
8109       /*
8110         Sharpen image scanlines.
8111       */
8112       XSetCursorState(display,windows,MagickTrue);
8113       XCheckRefreshWindows(display,windows);
8114       flags=ParseGeometry(radius,&geometry_info);
8115       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8116         &(*image)->exception);
8117       if (sharp_image != (Image *) NULL)
8118         {
8119           *image=DestroyImage(*image);
8120           *image=sharp_image;
8121         }
8122       CatchException(&(*image)->exception);
8123       XSetCursorState(display,windows,MagickFalse);
8124       if (windows->image.orphan != MagickFalse)
8125         break;
8126       XConfigureImageColormap(display,resource_info,windows,*image);
8127       (void) XConfigureImage(display,resource_info,windows,*image);
8128       break;
8129     }
8130     case BlurCommand:
8131     {
8132       Image
8133         *blur_image;
8134
8135       static char
8136         radius[MaxTextExtent] = "0.0x1.0";
8137
8138       /*
8139         Query user for blur radius.
8140       */
8141       (void) XDialogWidget(display,windows,"Blur",
8142         "Enter the blur radius and standard deviation:",radius);
8143       if (*radius == '\0')
8144         break;
8145       /*
8146         Blur an image.
8147       */
8148       XSetCursorState(display,windows,MagickTrue);
8149       XCheckRefreshWindows(display,windows);
8150       flags=ParseGeometry(radius,&geometry_info);
8151       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8152         &(*image)->exception);
8153       if (blur_image != (Image *) NULL)
8154         {
8155           *image=DestroyImage(*image);
8156           *image=blur_image;
8157         }
8158       CatchException(&(*image)->exception);
8159       XSetCursorState(display,windows,MagickFalse);
8160       if (windows->image.orphan != MagickFalse)
8161         break;
8162       XConfigureImageColormap(display,resource_info,windows,*image);
8163       (void) XConfigureImage(display,resource_info,windows,*image);
8164       break;
8165     }
8166     case ThresholdCommand:
8167     {
8168       double
8169         threshold;
8170
8171       static char
8172         factor[MaxTextExtent] = "128";
8173
8174       /*
8175         Query user for threshold value.
8176       */
8177       (void) XDialogWidget(display,windows,"Threshold",
8178         "Enter threshold value:",factor);
8179       if (*factor == '\0')
8180         break;
8181       /*
8182         Gamma correct image.
8183       */
8184       XSetCursorState(display,windows,MagickTrue);
8185       XCheckRefreshWindows(display,windows);
8186       threshold=SiPrefixToDouble(factor,QuantumRange);
8187       (void) BilevelImage(*image,threshold);
8188       XSetCursorState(display,windows,MagickFalse);
8189       if (windows->image.orphan != MagickFalse)
8190         break;
8191       XConfigureImageColormap(display,resource_info,windows,*image);
8192       (void) XConfigureImage(display,resource_info,windows,*image);
8193       break;
8194     }
8195     case EdgeDetectCommand:
8196     {
8197       Image
8198         *edge_image;
8199
8200       static char
8201         radius[MaxTextExtent] = "0";
8202
8203       /*
8204         Query user for edge factor.
8205       */
8206       (void) XDialogWidget(display,windows,"Detect Edges",
8207         "Enter the edge detect radius:",radius);
8208       if (*radius == '\0')
8209         break;
8210       /*
8211         Detect edge in image.
8212       */
8213       XSetCursorState(display,windows,MagickTrue);
8214       XCheckRefreshWindows(display,windows);
8215       flags=ParseGeometry(radius,&geometry_info);
8216       edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8217       if (edge_image != (Image *) NULL)
8218         {
8219           *image=DestroyImage(*image);
8220           *image=edge_image;
8221         }
8222       CatchException(&(*image)->exception);
8223       XSetCursorState(display,windows,MagickFalse);
8224       if (windows->image.orphan != MagickFalse)
8225         break;
8226       XConfigureImageColormap(display,resource_info,windows,*image);
8227       (void) XConfigureImage(display,resource_info,windows,*image);
8228       break;
8229     }
8230     case SpreadCommand:
8231     {
8232       Image
8233         *spread_image;
8234
8235       static char
8236         amount[MaxTextExtent] = "2";
8237
8238       /*
8239         Query user for spread amount.
8240       */
8241       (void) XDialogWidget(display,windows,"Spread",
8242         "Enter the displacement amount:",amount);
8243       if (*amount == '\0')
8244         break;
8245       /*
8246         Displace image pixels by a random amount.
8247       */
8248       XSetCursorState(display,windows,MagickTrue);
8249       XCheckRefreshWindows(display,windows);
8250       flags=ParseGeometry(amount,&geometry_info);
8251       spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8252       if (spread_image != (Image *) NULL)
8253         {
8254           *image=DestroyImage(*image);
8255           *image=spread_image;
8256         }
8257       CatchException(&(*image)->exception);
8258       XSetCursorState(display,windows,MagickFalse);
8259       if (windows->image.orphan != MagickFalse)
8260         break;
8261       XConfigureImageColormap(display,resource_info,windows,*image);
8262       (void) XConfigureImage(display,resource_info,windows,*image);
8263       break;
8264     }
8265     case ShadeCommand:
8266     {
8267       Image
8268         *shade_image;
8269
8270       int
8271         status;
8272
8273       static char
8274         geometry[MaxTextExtent] = "30x30";
8275
8276       /*
8277         Query user for the shade geometry.
8278       */
8279       status=XDialogWidget(display,windows,"Shade",
8280         "Enter the azimuth and elevation of the light source:",geometry);
8281       if (*geometry == '\0')
8282         break;
8283       /*
8284         Shade image pixels.
8285       */
8286       XSetCursorState(display,windows,MagickTrue);
8287       XCheckRefreshWindows(display,windows);
8288       flags=ParseGeometry(geometry,&geometry_info);
8289       if ((flags & SigmaValue) == 0)
8290         geometry_info.sigma=1.0;
8291       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8292         geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8293       if (shade_image != (Image *) NULL)
8294         {
8295           *image=DestroyImage(*image);
8296           *image=shade_image;
8297         }
8298       CatchException(&(*image)->exception);
8299       XSetCursorState(display,windows,MagickFalse);
8300       if (windows->image.orphan != MagickFalse)
8301         break;
8302       XConfigureImageColormap(display,resource_info,windows,*image);
8303       (void) XConfigureImage(display,resource_info,windows,*image);
8304       break;
8305     }
8306     case RaiseCommand:
8307     {
8308       static char
8309         bevel_width[MaxTextExtent] = "10";
8310
8311       /*
8312         Query user for bevel width.
8313       */
8314       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8315       if (*bevel_width == '\0')
8316         break;
8317       /*
8318         Raise an image.
8319       */
8320       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8321       XSetCursorState(display,windows,MagickTrue);
8322       XCheckRefreshWindows(display,windows);
8323       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8324         &(*image)->exception);
8325       (void) RaiseImage(*image,&page_geometry,MagickTrue);
8326       XSetCursorState(display,windows,MagickFalse);
8327       if (windows->image.orphan != MagickFalse)
8328         break;
8329       XConfigureImageColormap(display,resource_info,windows,*image);
8330       (void) XConfigureImage(display,resource_info,windows,*image);
8331       break;
8332     }
8333     case SegmentCommand:
8334     {
8335       static char
8336         threshold[MaxTextExtent] = "1.0x1.5";
8337
8338       /*
8339         Query user for smoothing threshold.
8340       */
8341       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8342         threshold);
8343       if (*threshold == '\0')
8344         break;
8345       /*
8346         Segment an image.
8347       */
8348       XSetCursorState(display,windows,MagickTrue);
8349       XCheckRefreshWindows(display,windows);
8350       flags=ParseGeometry(threshold,&geometry_info);
8351       if ((flags & SigmaValue) == 0)
8352         geometry_info.sigma=1.0;
8353       (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8354         geometry_info.sigma);
8355       XSetCursorState(display,windows,MagickFalse);
8356       if (windows->image.orphan != MagickFalse)
8357         break;
8358       XConfigureImageColormap(display,resource_info,windows,*image);
8359       (void) XConfigureImage(display,resource_info,windows,*image);
8360       break;
8361     }
8362     case SepiaToneCommand:
8363     {
8364       double
8365         threshold;
8366
8367       Image
8368         *sepia_image;
8369
8370       static char
8371         factor[MaxTextExtent] = "80%";
8372
8373       /*
8374         Query user for sepia-tone factor.
8375       */
8376       (void) XDialogWidget(display,windows,"Sepia Tone",
8377         "Enter the sepia tone factor (0 - 99.9%):",factor);
8378       if (*factor == '\0')
8379         break;
8380       /*
8381         Sepia tone image pixels.
8382       */
8383       XSetCursorState(display,windows,MagickTrue);
8384       XCheckRefreshWindows(display,windows);
8385       threshold=SiPrefixToDouble(factor,QuantumRange);
8386       sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8387       if (sepia_image != (Image *) NULL)
8388         {
8389           *image=DestroyImage(*image);
8390           *image=sepia_image;
8391         }
8392       CatchException(&(*image)->exception);
8393       XSetCursorState(display,windows,MagickFalse);
8394       if (windows->image.orphan != MagickFalse)
8395         break;
8396       XConfigureImageColormap(display,resource_info,windows,*image);
8397       (void) XConfigureImage(display,resource_info,windows,*image);
8398       break;
8399     }
8400     case SolarizeCommand:
8401     {
8402       double
8403         threshold;
8404
8405       static char
8406         factor[MaxTextExtent] = "60%";
8407
8408       /*
8409         Query user for solarize factor.
8410       */
8411       (void) XDialogWidget(display,windows,"Solarize",
8412         "Enter the solarize factor (0 - 99.9%):",factor);
8413       if (*factor == '\0')
8414         break;
8415       /*
8416         Solarize image pixels.
8417       */
8418       XSetCursorState(display,windows,MagickTrue);
8419       XCheckRefreshWindows(display,windows);
8420       threshold=SiPrefixToDouble(factor,QuantumRange);
8421       (void) SolarizeImage(*image,threshold);
8422       XSetCursorState(display,windows,MagickFalse);
8423       if (windows->image.orphan != MagickFalse)
8424         break;
8425       XConfigureImageColormap(display,resource_info,windows,*image);
8426       (void) XConfigureImage(display,resource_info,windows,*image);
8427       break;
8428     }
8429     case SwirlCommand:
8430     {
8431       Image
8432         *swirl_image;
8433
8434       static char
8435         degrees[MaxTextExtent] = "60";
8436
8437       /*
8438         Query user for swirl angle.
8439       */
8440       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8441         degrees);
8442       if (*degrees == '\0')
8443         break;
8444       /*
8445         Swirl image pixels about the center.
8446       */
8447       XSetCursorState(display,windows,MagickTrue);
8448       XCheckRefreshWindows(display,windows);
8449       flags=ParseGeometry(degrees,&geometry_info);
8450       swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8451       if (swirl_image != (Image *) NULL)
8452         {
8453           *image=DestroyImage(*image);
8454           *image=swirl_image;
8455         }
8456       CatchException(&(*image)->exception);
8457       XSetCursorState(display,windows,MagickFalse);
8458       if (windows->image.orphan != MagickFalse)
8459         break;
8460       XConfigureImageColormap(display,resource_info,windows,*image);
8461       (void) XConfigureImage(display,resource_info,windows,*image);
8462       break;
8463     }
8464     case ImplodeCommand:
8465     {
8466       Image
8467         *implode_image;
8468
8469       static char
8470         factor[MaxTextExtent] = "0.3";
8471
8472       /*
8473         Query user for implode factor.
8474       */
8475       (void) XDialogWidget(display,windows,"Implode",
8476         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8477       if (*factor == '\0')
8478         break;
8479       /*
8480         Implode image pixels about the center.
8481       */
8482       XSetCursorState(display,windows,MagickTrue);
8483       XCheckRefreshWindows(display,windows);
8484       flags=ParseGeometry(factor,&geometry_info);
8485       implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8486       if (implode_image != (Image *) NULL)
8487         {
8488           *image=DestroyImage(*image);
8489           *image=implode_image;
8490         }
8491       CatchException(&(*image)->exception);
8492       XSetCursorState(display,windows,MagickFalse);
8493       if (windows->image.orphan != MagickFalse)
8494         break;
8495       XConfigureImageColormap(display,resource_info,windows,*image);
8496       (void) XConfigureImage(display,resource_info,windows,*image);
8497       break;
8498     }
8499     case VignetteCommand:
8500     {
8501       Image
8502         *vignette_image;
8503
8504       static char
8505         geometry[MaxTextExtent] = "0x20";
8506
8507       /*
8508         Query user for the vignette geometry.
8509       */
8510       (void) XDialogWidget(display,windows,"Vignette",
8511         "Enter the radius, sigma, and x and y offsets:",geometry);
8512       if (*geometry == '\0')
8513         break;
8514       /*
8515         Soften the edges of the image in vignette style
8516       */
8517       XSetCursorState(display,windows,MagickTrue);
8518       XCheckRefreshWindows(display,windows);
8519       flags=ParseGeometry(geometry,&geometry_info);
8520       if ((flags & SigmaValue) == 0)
8521         geometry_info.sigma=1.0;
8522       if ((flags & XiValue) == 0)
8523         geometry_info.xi=0.1*(*image)->columns;
8524       if ((flags & PsiValue) == 0)
8525         geometry_info.psi=0.1*(*image)->rows;
8526       vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8527         (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8528         0.5),&(*image)->exception);
8529       if (vignette_image != (Image *) NULL)
8530         {
8531           *image=DestroyImage(*image);
8532           *image=vignette_image;
8533         }
8534       CatchException(&(*image)->exception);
8535       XSetCursorState(display,windows,MagickFalse);
8536       if (windows->image.orphan != MagickFalse)
8537         break;
8538       XConfigureImageColormap(display,resource_info,windows,*image);
8539       (void) XConfigureImage(display,resource_info,windows,*image);
8540       break;
8541     }
8542     case WaveCommand:
8543     {
8544       Image
8545         *wave_image;
8546
8547       static char
8548         geometry[MaxTextExtent] = "25x150";
8549
8550       /*
8551         Query user for the wave geometry.
8552       */
8553       (void) XDialogWidget(display,windows,"Wave",
8554         "Enter the amplitude and length of the wave:",geometry);
8555       if (*geometry == '\0')
8556         break;
8557       /*
8558         Alter an image along a sine wave.
8559       */
8560       XSetCursorState(display,windows,MagickTrue);
8561       XCheckRefreshWindows(display,windows);
8562       flags=ParseGeometry(geometry,&geometry_info);
8563       if ((flags & SigmaValue) == 0)
8564         geometry_info.sigma=1.0;
8565       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8566         &(*image)->exception);
8567       if (wave_image != (Image *) NULL)
8568         {
8569           *image=DestroyImage(*image);
8570           *image=wave_image;
8571         }
8572       CatchException(&(*image)->exception);
8573       XSetCursorState(display,windows,MagickFalse);
8574       if (windows->image.orphan != MagickFalse)
8575         break;
8576       XConfigureImageColormap(display,resource_info,windows,*image);
8577       (void) XConfigureImage(display,resource_info,windows,*image);
8578       break;
8579     }
8580     case OilPaintCommand:
8581     {
8582       Image
8583         *paint_image;
8584
8585       static char
8586         radius[MaxTextExtent] = "0";
8587
8588       /*
8589         Query user for circular neighborhood radius.
8590       */
8591       (void) XDialogWidget(display,windows,"Oil Paint",
8592         "Enter the mask radius:",radius);
8593       if (*radius == '\0')
8594         break;
8595       /*
8596         OilPaint image scanlines.
8597       */
8598       XSetCursorState(display,windows,MagickTrue);
8599       XCheckRefreshWindows(display,windows);
8600       flags=ParseGeometry(radius,&geometry_info);
8601       paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8602       if (paint_image != (Image *) NULL)
8603         {
8604           *image=DestroyImage(*image);
8605           *image=paint_image;
8606         }
8607       CatchException(&(*image)->exception);
8608       XSetCursorState(display,windows,MagickFalse);
8609       if (windows->image.orphan != MagickFalse)
8610         break;
8611       XConfigureImageColormap(display,resource_info,windows,*image);
8612       (void) XConfigureImage(display,resource_info,windows,*image);
8613       break;
8614     }
8615     case CharcoalDrawCommand:
8616     {
8617       Image
8618         *charcoal_image;
8619
8620       static char
8621         radius[MaxTextExtent] = "0x1";
8622
8623       /*
8624         Query user for charcoal radius.
8625       */
8626       (void) XDialogWidget(display,windows,"Charcoal Draw",
8627         "Enter the charcoal radius and sigma:",radius);
8628       if (*radius == '\0')
8629         break;
8630       /*
8631         Charcoal the image.
8632       */
8633       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8634       XSetCursorState(display,windows,MagickTrue);
8635       XCheckRefreshWindows(display,windows);
8636       flags=ParseGeometry(radius,&geometry_info);
8637       if ((flags & SigmaValue) == 0)
8638         geometry_info.sigma=geometry_info.rho;
8639       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8640         &(*image)->exception);
8641       if (charcoal_image != (Image *) NULL)
8642         {
8643           *image=DestroyImage(*image);
8644           *image=charcoal_image;
8645         }
8646       CatchException(&(*image)->exception);
8647       XSetCursorState(display,windows,MagickFalse);
8648       if (windows->image.orphan != MagickFalse)
8649         break;
8650       XConfigureImageColormap(display,resource_info,windows,*image);
8651       (void) XConfigureImage(display,resource_info,windows,*image);
8652       break;
8653     }
8654     case AnnotateCommand:
8655     {
8656       /*
8657         Annotate the image with text.
8658       */
8659       status=XAnnotateEditImage(display,resource_info,windows,*image);
8660       if (status == MagickFalse)
8661         {
8662           XNoticeWidget(display,windows,"Unable to annotate X image",
8663             (*image)->filename);
8664           break;
8665         }
8666       break;
8667     }
8668     case DrawCommand:
8669     {
8670       /*
8671         Draw image.
8672       */
8673       status=XDrawEditImage(display,resource_info,windows,image);
8674       if (status == MagickFalse)
8675         {
8676           XNoticeWidget(display,windows,"Unable to draw on the X image",
8677             (*image)->filename);
8678           break;
8679         }
8680       break;
8681     }
8682     case ColorCommand:
8683     {
8684       /*
8685         Color edit.
8686       */
8687       status=XColorEditImage(display,resource_info,windows,image);
8688       if (status == MagickFalse)
8689         {
8690           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8691             (*image)->filename);
8692           break;
8693         }
8694       break;
8695     }
8696     case MatteCommand:
8697     {
8698       /*
8699         Matte edit.
8700       */
8701       status=XMatteEditImage(display,resource_info,windows,image);
8702       if (status == MagickFalse)
8703         {
8704           XNoticeWidget(display,windows,"Unable to matte edit X image",
8705             (*image)->filename);
8706           break;
8707         }
8708       break;
8709     }
8710     case CompositeCommand:
8711     {
8712       /*
8713         Composite image.
8714       */
8715       status=XCompositeImage(display,resource_info,windows,*image);
8716       if (status == MagickFalse)
8717         {
8718           XNoticeWidget(display,windows,"Unable to composite X image",
8719             (*image)->filename);
8720           break;
8721         }
8722       break;
8723     }
8724     case AddBorderCommand:
8725     {
8726       Image
8727         *border_image;
8728
8729       static char
8730         geometry[MaxTextExtent] = "6x6";
8731
8732       /*
8733         Query user for border color and geometry.
8734       */
8735       XColorBrowserWidget(display,windows,"Select",color);
8736       if (*color == '\0')
8737         break;
8738       (void) XDialogWidget(display,windows,"Add Border",
8739         "Enter border geometry:",geometry);
8740       if (*geometry == '\0')
8741         break;
8742       /*
8743         Add a border to the image.
8744       */
8745       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8746       XSetCursorState(display,windows,MagickTrue);
8747       XCheckRefreshWindows(display,windows);
8748       (void) QueryColorDatabase(color,&(*image)->border_color,
8749         &(*image)->exception);
8750       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8751         &(*image)->exception);
8752       border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8753       if (border_image != (Image *) NULL)
8754         {
8755           *image=DestroyImage(*image);
8756           *image=border_image;
8757         }
8758       CatchException(&(*image)->exception);
8759       XSetCursorState(display,windows,MagickFalse);
8760       if (windows->image.orphan != MagickFalse)
8761         break;
8762       windows->image.window_changes.width=(int) (*image)->columns;
8763       windows->image.window_changes.height=(int) (*image)->rows;
8764       XConfigureImageColormap(display,resource_info,windows,*image);
8765       (void) XConfigureImage(display,resource_info,windows,*image);
8766       break;
8767     }
8768     case AddFrameCommand:
8769     {
8770       FrameInfo
8771         frame_info;
8772
8773       Image
8774         *frame_image;
8775
8776       static char
8777         geometry[MaxTextExtent] = "6x6";
8778
8779       /*
8780         Query user for frame color and geometry.
8781       */
8782       XColorBrowserWidget(display,windows,"Select",color);
8783       if (*color == '\0')
8784         break;
8785       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8786         geometry);
8787       if (*geometry == '\0')
8788         break;
8789       /*
8790         Surround image with an ornamental border.
8791       */
8792       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8793       XSetCursorState(display,windows,MagickTrue);
8794       XCheckRefreshWindows(display,windows);
8795       (void) QueryColorDatabase(color,&(*image)->matte_color,
8796         &(*image)->exception);
8797       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8798         &(*image)->exception);
8799       frame_info.width=page_geometry.width;
8800       frame_info.height=page_geometry.height;
8801       frame_info.outer_bevel=page_geometry.x;
8802       frame_info.inner_bevel=page_geometry.y;
8803       frame_info.x=(ssize_t) frame_info.width;
8804       frame_info.y=(ssize_t) frame_info.height;
8805       frame_info.width=(*image)->columns+2*frame_info.width;
8806       frame_info.height=(*image)->rows+2*frame_info.height;
8807       frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8808       if (frame_image != (Image *) NULL)
8809         {
8810           *image=DestroyImage(*image);
8811           *image=frame_image;
8812         }
8813       CatchException(&(*image)->exception);
8814       XSetCursorState(display,windows,MagickFalse);
8815       if (windows->image.orphan != MagickFalse)
8816         break;
8817       windows->image.window_changes.width=(int) (*image)->columns;
8818       windows->image.window_changes.height=(int) (*image)->rows;
8819       XConfigureImageColormap(display,resource_info,windows,*image);
8820       (void) XConfigureImage(display,resource_info,windows,*image);
8821       break;
8822     }
8823     case CommentCommand:
8824     {
8825       const char
8826         *value;
8827
8828       FILE
8829         *file;
8830
8831       int
8832         unique_file;
8833
8834       /*
8835         Edit image comment.
8836       */
8837       unique_file=AcquireUniqueFileResource(image_info->filename);
8838       if (unique_file == -1)
8839         XNoticeWidget(display,windows,"Unable to edit image comment",
8840           image_info->filename);
8841       value=GetImageProperty(*image,"comment");
8842       if (value == (char *) NULL)
8843         unique_file=close(unique_file)-1;
8844       else
8845         {
8846           register const char
8847             *p;
8848
8849           file=fdopen(unique_file,"w");
8850           if (file == (FILE *) NULL)
8851             {
8852               XNoticeWidget(display,windows,"Unable to edit image comment",
8853                 image_info->filename);
8854               break;
8855             }
8856           for (p=value; *p != '\0'; p++)
8857             (void) fputc((int) *p,file);
8858           (void) fputc('\n',file);
8859           (void) fclose(file);
8860         }
8861       XSetCursorState(display,windows,MagickTrue);
8862       XCheckRefreshWindows(display,windows);
8863       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8864         &(*image)->exception);
8865       if (status == MagickFalse)
8866         XNoticeWidget(display,windows,"Unable to edit image comment",
8867           (char *) NULL);
8868       else
8869         {
8870           char
8871             *comment;
8872
8873           comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8874           if (comment != (char *) NULL)
8875             {
8876               (void) SetImageProperty(*image,"comment",comment);
8877               (*image)->taint=MagickTrue;
8878             }
8879         }
8880       (void) RelinquishUniqueFileResource(image_info->filename);
8881       XSetCursorState(display,windows,MagickFalse);
8882       break;
8883     }
8884     case LaunchCommand:
8885     {
8886       /*
8887         Launch program.
8888       */
8889       XSetCursorState(display,windows,MagickTrue);
8890       XCheckRefreshWindows(display,windows);
8891       (void) AcquireUniqueFilename(filename);
8892       (void) FormatMagickString((*image)->filename,MaxTextExtent,"launch:%s",
8893         filename);
8894       status=WriteImage(image_info,*image);
8895       if (status == MagickFalse)
8896         XNoticeWidget(display,windows,"Unable to launch image editor",
8897           (char *) NULL);
8898       else
8899         {
8900           nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8901           CatchException(&(*image)->exception);
8902           XClientMessage(display,windows->image.id,windows->im_protocols,
8903             windows->im_next_image,CurrentTime);
8904         }
8905       (void) RelinquishUniqueFileResource(filename);
8906       XSetCursorState(display,windows,MagickFalse);
8907       break;
8908     }
8909     case RegionofInterestCommand:
8910     {
8911       /*
8912         Apply an image processing technique to a region of interest.
8913       */
8914       (void) XROIImage(display,resource_info,windows,image);
8915       break;
8916     }
8917     case InfoCommand:
8918       break;
8919     case ZoomCommand:
8920     {
8921       /*
8922         Zoom image.
8923       */
8924       if (windows->magnify.mapped != MagickFalse)
8925         (void) XRaiseWindow(display,windows->magnify.id);
8926       else
8927         {
8928           /*
8929             Make magnify image.
8930           */
8931           XSetCursorState(display,windows,MagickTrue);
8932           (void) XMapRaised(display,windows->magnify.id);
8933           XSetCursorState(display,windows,MagickFalse);
8934         }
8935       break;
8936     }
8937     case ShowPreviewCommand:
8938     {
8939       char
8940         **previews;
8941
8942       Image
8943         *preview_image;
8944
8945       static char
8946         preview_type[MaxTextExtent] = "Gamma";
8947
8948       /*
8949         Select preview type from menu.
8950       */
8951       previews=GetMagickOptions(MagickPreviewOptions);
8952       if (previews == (char **) NULL)
8953         break;
8954       XListBrowserWidget(display,windows,&windows->widget,
8955         (const char **) previews,"Preview",
8956         "Select an enhancement, effect, or F/X:",preview_type);
8957       previews=DestroyStringList(previews);
8958       if (*preview_type == '\0')
8959         break;
8960       /*
8961         Show image preview.
8962       */
8963       XSetCursorState(display,windows,MagickTrue);
8964       XCheckRefreshWindows(display,windows);
8965       image_info->preview_type=(PreviewType)
8966         ParseMagickOption(MagickPreviewOptions,MagickFalse,preview_type);
8967       image_info->group=(ssize_t) windows->image.id;
8968       (void) DeleteImageProperty(*image,"label");
8969       (void) SetImageProperty(*image,"label","Preview");
8970       (void) AcquireUniqueFilename(filename);
8971       (void) FormatMagickString((*image)->filename,MaxTextExtent,"preview:%s",
8972         filename);
8973       status=WriteImage(image_info,*image);
8974       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
8975       preview_image=ReadImage(image_info,&(*image)->exception);
8976       (void) RelinquishUniqueFileResource(filename);
8977       if (preview_image == (Image *) NULL)
8978         break;
8979       (void) FormatMagickString(preview_image->filename,MaxTextExtent,"show:%s",
8980         filename);
8981       status=WriteImage(image_info,preview_image);
8982       preview_image=DestroyImage(preview_image);
8983       if (status == MagickFalse)
8984         XNoticeWidget(display,windows,"Unable to show image preview",
8985           (*image)->filename);
8986       XDelay(display,1500);
8987       XSetCursorState(display,windows,MagickFalse);
8988       break;
8989     }
8990     case ShowHistogramCommand:
8991     {
8992       Image
8993         *histogram_image;
8994
8995       /*
8996         Show image histogram.
8997       */
8998       XSetCursorState(display,windows,MagickTrue);
8999       XCheckRefreshWindows(display,windows);
9000       image_info->group=(ssize_t) windows->image.id;
9001       (void) DeleteImageProperty(*image,"label");
9002       (void) SetImageProperty(*image,"label","Histogram");
9003       (void) AcquireUniqueFilename(filename);
9004       (void) FormatMagickString((*image)->filename,MaxTextExtent,"histogram:%s",
9005         filename);
9006       status=WriteImage(image_info,*image);
9007       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9008       histogram_image=ReadImage(image_info,&(*image)->exception);
9009       (void) RelinquishUniqueFileResource(filename);
9010       if (histogram_image == (Image *) NULL)
9011         break;
9012       (void) FormatMagickString(histogram_image->filename,MaxTextExtent,
9013         "show:%s",filename);
9014       status=WriteImage(image_info,histogram_image);
9015       histogram_image=DestroyImage(histogram_image);
9016       if (status == MagickFalse)
9017         XNoticeWidget(display,windows,"Unable to show histogram",
9018           (*image)->filename);
9019       XDelay(display,1500);
9020       XSetCursorState(display,windows,MagickFalse);
9021       break;
9022     }
9023     case ShowMatteCommand:
9024     {
9025       Image
9026         *matte_image;
9027
9028       if ((*image)->matte == MagickFalse)
9029         {
9030           XNoticeWidget(display,windows,
9031             "Image does not have any matte information",(*image)->filename);
9032           break;
9033         }
9034       /*
9035         Show image matte.
9036       */
9037       XSetCursorState(display,windows,MagickTrue);
9038       XCheckRefreshWindows(display,windows);
9039       image_info->group=(ssize_t) windows->image.id;
9040       (void) DeleteImageProperty(*image,"label");
9041       (void) SetImageProperty(*image,"label","Matte");
9042       (void) AcquireUniqueFilename(filename);
9043       (void) FormatMagickString((*image)->filename,MaxTextExtent,"matte:%s",
9044         filename);
9045       status=WriteImage(image_info,*image);
9046       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9047       matte_image=ReadImage(image_info,&(*image)->exception);
9048       (void) RelinquishUniqueFileResource(filename);
9049       if (matte_image == (Image *) NULL)
9050         break;
9051       (void) FormatMagickString(matte_image->filename,MaxTextExtent,"show:%s",
9052         filename);
9053       status=WriteImage(image_info,matte_image);
9054       matte_image=DestroyImage(matte_image);
9055       if (status == MagickFalse)
9056         XNoticeWidget(display,windows,"Unable to show matte",
9057           (*image)->filename);
9058       XDelay(display,1500);
9059       XSetCursorState(display,windows,MagickFalse);
9060       break;
9061     }
9062     case BackgroundCommand:
9063     {
9064       /*
9065         Background image.
9066       */
9067       status=XBackgroundImage(display,resource_info,windows,image);
9068       if (status == MagickFalse)
9069         break;
9070       nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9071       if (nexus != (Image *) NULL)
9072         XClientMessage(display,windows->image.id,windows->im_protocols,
9073           windows->im_next_image,CurrentTime);
9074       break;
9075     }
9076     case SlideShowCommand:
9077     {
9078       static char
9079         delay[MaxTextExtent] = "5";
9080
9081       /*
9082         Display next image after pausing.
9083       */
9084       (void) XDialogWidget(display,windows,"Slide Show",
9085         "Pause how many 1/100ths of a second between images:",delay);
9086       if (*delay == '\0')
9087         break;
9088       resource_info->delay=StringToUnsignedLong(delay);
9089       XClientMessage(display,windows->image.id,windows->im_protocols,
9090         windows->im_next_image,CurrentTime);
9091       break;
9092     }
9093     case PreferencesCommand:
9094     {
9095       /*
9096         Set user preferences.
9097       */
9098       status=XPreferencesWidget(display,resource_info,windows);
9099       if (status == MagickFalse)
9100         break;
9101       nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9102       if (nexus != (Image *) NULL)
9103         XClientMessage(display,windows->image.id,windows->im_protocols,
9104           windows->im_next_image,CurrentTime);
9105       break;
9106     }
9107     case HelpCommand:
9108     {
9109       /*
9110         User requested help.
9111       */
9112       XTextViewWidget(display,resource_info,windows,MagickFalse,
9113         "Help Viewer - Display",DisplayHelp);
9114       break;
9115     }
9116     case BrowseDocumentationCommand:
9117     {
9118       Atom
9119         mozilla_atom;
9120
9121       Window
9122         mozilla_window,
9123         root_window;
9124
9125       /*
9126         Browse the ImageMagick documentation.
9127       */
9128       root_window=XRootWindow(display,XDefaultScreen(display));
9129       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9130       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9131       if (mozilla_window != (Window) NULL)
9132         {
9133           char
9134             command[MaxTextExtent],
9135             *url;
9136
9137           /*
9138             Display documentation using Netscape remote control.
9139           */
9140           url=GetMagickHomeURL();
9141           (void) FormatMagickString(command,MaxTextExtent,
9142             "openurl(%s,new-tab)",url);
9143           url=DestroyString(url);
9144           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9145           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9146             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9147           XSetCursorState(display,windows,MagickFalse);
9148           break;
9149         }
9150       XSetCursorState(display,windows,MagickTrue);
9151       XCheckRefreshWindows(display,windows);
9152       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9153         &(*image)->exception);
9154       if (status == MagickFalse)
9155         XNoticeWidget(display,windows,"Unable to browse documentation",
9156           (char *) NULL);
9157       XDelay(display,1500);
9158       XSetCursorState(display,windows,MagickFalse);
9159       break;
9160     }
9161     case VersionCommand:
9162     {
9163       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9164         GetMagickCopyright());
9165       break;
9166     }
9167     case SaveToUndoBufferCommand:
9168       break;
9169     default:
9170     {
9171       (void) XBell(display,0);
9172       break;
9173     }
9174   }
9175   image_info=DestroyImageInfo(image_info);
9176   return(nexus);
9177 }
9178 \f
9179 /*
9180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9181 %                                                                             %
9182 %                                                                             %
9183 %                                                                             %
9184 +   X M a g n i f y I m a g e                                                 %
9185 %                                                                             %
9186 %                                                                             %
9187 %                                                                             %
9188 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9189 %
9190 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9191 %  The magnified portion is displayed in a separate window.
9192 %
9193 %  The format of the XMagnifyImage method is:
9194 %
9195 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9196 %
9197 %  A description of each parameter follows:
9198 %
9199 %    o display: Specifies a connection to an X server;  returned from
9200 %      XOpenDisplay.
9201 %
9202 %    o windows: Specifies a pointer to a XWindows structure.
9203 %
9204 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9205 %      the entire image is refreshed.
9206 %
9207 */
9208 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9209 {
9210   char
9211     text[MaxTextExtent];
9212
9213   register int
9214     x,
9215     y;
9216
9217   size_t
9218     state;
9219
9220   /*
9221     Update magnified image until the mouse button is released.
9222   */
9223   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9224   state=DefaultState;
9225   x=event->xbutton.x;
9226   y=event->xbutton.y;
9227   windows->magnify.x=windows->image.x+x;
9228   windows->magnify.y=windows->image.y+y;
9229   do
9230   {
9231     /*
9232       Map and unmap Info widget as text cursor crosses its boundaries.
9233     */
9234     if (windows->info.mapped != MagickFalse)
9235       {
9236         if ((x < (int) (windows->info.x+windows->info.width)) &&
9237             (y < (int) (windows->info.y+windows->info.height)))
9238           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9239       }
9240     else
9241       if ((x > (int) (windows->info.x+windows->info.width)) ||
9242           (y > (int) (windows->info.y+windows->info.height)))
9243         (void) XMapWindow(display,windows->info.id);
9244     if (windows->info.mapped != MagickFalse)
9245       {
9246         /*
9247           Display pointer position.
9248         */
9249         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
9250           windows->magnify.x,windows->magnify.y);
9251         XInfoWidget(display,windows,text);
9252       }
9253     /*
9254       Wait for next event.
9255     */
9256     XScreenEvent(display,windows,event);
9257     switch (event->type)
9258     {
9259       case ButtonPress:
9260         break;
9261       case ButtonRelease:
9262       {
9263         /*
9264           User has finished magnifying image.
9265         */
9266         x=event->xbutton.x;
9267         y=event->xbutton.y;
9268         state|=ExitState;
9269         break;
9270       }
9271       case Expose:
9272         break;
9273       case MotionNotify:
9274       {
9275         x=event->xmotion.x;
9276         y=event->xmotion.y;
9277         break;
9278       }
9279       default:
9280         break;
9281     }
9282     /*
9283       Check boundary conditions.
9284     */
9285     if (x < 0)
9286       x=0;
9287     else
9288       if (x >= (int) windows->image.width)
9289         x=(int) windows->image.width-1;
9290     if (y < 0)
9291       y=0;
9292     else
9293      if (y >= (int) windows->image.height)
9294        y=(int) windows->image.height-1;
9295   } while ((state & ExitState) == 0);
9296   /*
9297     Display magnified image.
9298   */
9299   XSetCursorState(display,windows,MagickFalse);
9300 }
9301 \f
9302 /*
9303 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9304 %                                                                             %
9305 %                                                                             %
9306 %                                                                             %
9307 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9308 %                                                                             %
9309 %                                                                             %
9310 %                                                                             %
9311 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9312 %
9313 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9314 %  pixel as specified by the key symbol.
9315 %
9316 %  The format of the XMagnifyWindowCommand method is:
9317 %
9318 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9319 %        const MagickStatusType state,const KeySym key_symbol)
9320 %
9321 %  A description of each parameter follows:
9322 %
9323 %    o display: Specifies a connection to an X server; returned from
9324 %      XOpenDisplay.
9325 %
9326 %    o windows: Specifies a pointer to a XWindows structure.
9327 %
9328 %    o state: key mask.
9329 %
9330 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9331 %      to trim.
9332 %
9333 */
9334 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9335   const MagickStatusType state,const KeySym key_symbol)
9336 {
9337   unsigned int
9338     quantum;
9339
9340   /*
9341     User specified a magnify factor or position.
9342   */
9343   quantum=1;
9344   if ((state & Mod1Mask) != 0)
9345     quantum=10;
9346   switch ((int) key_symbol)
9347   {
9348     case QuitCommand:
9349     {
9350       (void) XWithdrawWindow(display,windows->magnify.id,
9351         windows->magnify.screen);
9352       break;
9353     }
9354     case XK_Home:
9355     case XK_KP_Home:
9356     {
9357       windows->magnify.x=(int) windows->image.width/2;
9358       windows->magnify.y=(int) windows->image.height/2;
9359       break;
9360     }
9361     case XK_Left:
9362     case XK_KP_Left:
9363     {
9364       if (windows->magnify.x > 0)
9365         windows->magnify.x-=quantum;
9366       break;
9367     }
9368     case XK_Up:
9369     case XK_KP_Up:
9370     {
9371       if (windows->magnify.y > 0)
9372         windows->magnify.y-=quantum;
9373       break;
9374     }
9375     case XK_Right:
9376     case XK_KP_Right:
9377     {
9378       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9379         windows->magnify.x+=quantum;
9380       break;
9381     }
9382     case XK_Down:
9383     case XK_KP_Down:
9384     {
9385       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9386         windows->magnify.y+=quantum;
9387       break;
9388     }
9389     case XK_0:
9390     case XK_1:
9391     case XK_2:
9392     case XK_3:
9393     case XK_4:
9394     case XK_5:
9395     case XK_6:
9396     case XK_7:
9397     case XK_8:
9398     case XK_9:
9399     {
9400       windows->magnify.data=(key_symbol-XK_0);
9401       break;
9402     }
9403     case XK_KP_0:
9404     case XK_KP_1:
9405     case XK_KP_2:
9406     case XK_KP_3:
9407     case XK_KP_4:
9408     case XK_KP_5:
9409     case XK_KP_6:
9410     case XK_KP_7:
9411     case XK_KP_8:
9412     case XK_KP_9:
9413     {
9414       windows->magnify.data=(key_symbol-XK_KP_0);
9415       break;
9416     }
9417     default:
9418       break;
9419   }
9420   XMakeMagnifyImage(display,windows);
9421 }
9422 \f
9423 /*
9424 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9425 %                                                                             %
9426 %                                                                             %
9427 %                                                                             %
9428 +   X M a k e P a n I m a g e                                                 %
9429 %                                                                             %
9430 %                                                                             %
9431 %                                                                             %
9432 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9433 %
9434 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9435 %  icon window.
9436 %
9437 %  The format of the XMakePanImage method is:
9438 %
9439 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9440 %          XWindows *windows,Image *image)
9441 %
9442 %  A description of each parameter follows:
9443 %
9444 %    o display: Specifies a connection to an X server;  returned from
9445 %      XOpenDisplay.
9446 %
9447 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9448 %
9449 %    o windows: Specifies a pointer to a XWindows structure.
9450 %
9451 %    o image: the image.
9452 %
9453 */
9454 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9455   XWindows *windows,Image *image)
9456 {
9457   MagickStatusType
9458     status;
9459
9460   /*
9461     Create and display image for panning icon.
9462   */
9463   XSetCursorState(display,windows,MagickTrue);
9464   XCheckRefreshWindows(display,windows);
9465   windows->pan.x=windows->image.x;
9466   windows->pan.y=windows->image.y;
9467   status=XMakeImage(display,resource_info,&windows->pan,image,
9468     windows->pan.width,windows->pan.height);
9469   if (status == MagickFalse)
9470     ThrowXWindowFatalException(XServerError,image->exception.reason,
9471       image->exception.description);
9472   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9473     windows->pan.pixmap);
9474   (void) XClearWindow(display,windows->pan.id);
9475   XDrawPanRectangle(display,windows);
9476   XSetCursorState(display,windows,MagickFalse);
9477 }
9478 \f
9479 /*
9480 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9481 %                                                                             %
9482 %                                                                             %
9483 %                                                                             %
9484 +   X M a t t a E d i t I m a g e                                             %
9485 %                                                                             %
9486 %                                                                             %
9487 %                                                                             %
9488 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9489 %
9490 %  XMatteEditImage() allows the user to interactively change the Matte channel
9491 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9492 %  before the matte information is stored.
9493 %
9494 %  The format of the XMatteEditImage method is:
9495 %
9496 %      MagickBooleanType XMatteEditImage(Display *display,
9497 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
9498 %
9499 %  A description of each parameter follows:
9500 %
9501 %    o display: Specifies a connection to an X server;  returned from
9502 %      XOpenDisplay.
9503 %
9504 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9505 %
9506 %    o windows: Specifies a pointer to a XWindows structure.
9507 %
9508 %    o image: the image; returned from ReadImage.
9509 %
9510 */
9511 static MagickBooleanType XMatteEditImage(Display *display,
9512   XResourceInfo *resource_info,XWindows *windows,Image **image)
9513 {
9514   static char
9515     matte[MaxTextExtent] = "0";
9516
9517   static const char
9518     *MatteEditMenu[] =
9519     {
9520       "Method",
9521       "Border Color",
9522       "Fuzz",
9523       "Matte Value",
9524       "Undo",
9525       "Help",
9526       "Dismiss",
9527       (char *) NULL
9528     };
9529
9530   static const ModeType
9531     MatteEditCommands[] =
9532     {
9533       MatteEditMethod,
9534       MatteEditBorderCommand,
9535       MatteEditFuzzCommand,
9536       MatteEditValueCommand,
9537       MatteEditUndoCommand,
9538       MatteEditHelpCommand,
9539       MatteEditDismissCommand
9540     };
9541
9542   static PaintMethod
9543     method = PointMethod;
9544
9545   static XColor
9546     border_color = { 0, 0, 0, 0, 0, 0 };
9547
9548   char
9549     command[MaxTextExtent],
9550     text[MaxTextExtent];
9551
9552   Cursor
9553     cursor;
9554
9555   int
9556     entry,
9557     id,
9558     x,
9559     x_offset,
9560     y,
9561     y_offset;
9562
9563   register int
9564     i;
9565
9566   register PixelPacket
9567     *q;
9568
9569   unsigned int
9570     height,
9571     width;
9572
9573   size_t
9574     state;
9575
9576   XEvent
9577     event;
9578
9579   /*
9580     Map Command widget.
9581   */
9582   (void) CloneString(&windows->command.name,"Matte Edit");
9583   windows->command.data=4;
9584   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9585   (void) XMapRaised(display,windows->command.id);
9586   XClientMessage(display,windows->image.id,windows->im_protocols,
9587     windows->im_update_widget,CurrentTime);
9588   /*
9589     Make cursor.
9590   */
9591   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9592     resource_info->background_color,resource_info->foreground_color);
9593   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9594   /*
9595     Track pointer until button 1 is pressed.
9596   */
9597   XQueryPosition(display,windows->image.id,&x,&y);
9598   (void) XSelectInput(display,windows->image.id,
9599     windows->image.attributes.event_mask | PointerMotionMask);
9600   state=DefaultState;
9601   do
9602   {
9603     if (windows->info.mapped != MagickFalse)
9604       {
9605         /*
9606           Display pointer position.
9607         */
9608         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
9609           x+windows->image.x,y+windows->image.y);
9610         XInfoWidget(display,windows,text);
9611       }
9612     /*
9613       Wait for next event.
9614     */
9615     XScreenEvent(display,windows,&event);
9616     if (event.xany.window == windows->command.id)
9617       {
9618         /*
9619           Select a command from the Command widget.
9620         */
9621         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9622         if (id < 0)
9623           {
9624             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9625             continue;
9626           }
9627         switch (MatteEditCommands[id])
9628         {
9629           case MatteEditMethod:
9630           {
9631             char
9632               **methods;
9633
9634             /*
9635               Select a method from the pop-up menu.
9636             */
9637             methods=GetMagickOptions(MagickMethodOptions);
9638             if (methods == (char **) NULL)
9639               break;
9640             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9641               (const char **) methods,command);
9642             if (entry >= 0)
9643               method=(PaintMethod) ParseMagickOption(MagickMethodOptions,
9644                 MagickFalse,methods[entry]);
9645             methods=DestroyStringList(methods);
9646             break;
9647           }
9648           case MatteEditBorderCommand:
9649           {
9650             const char
9651               *ColorMenu[MaxNumberPens];
9652
9653             int
9654               pen_number;
9655
9656             /*
9657               Initialize menu selections.
9658             */
9659             for (i=0; i < (int) (MaxNumberPens-2); i++)
9660               ColorMenu[i]=resource_info->pen_colors[i];
9661             ColorMenu[MaxNumberPens-2]="Browser...";
9662             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9663             /*
9664               Select a pen color from the pop-up menu.
9665             */
9666             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9667               (const char **) ColorMenu,command);
9668             if (pen_number < 0)
9669               break;
9670             if (pen_number == (MaxNumberPens-2))
9671               {
9672                 static char
9673                   color_name[MaxTextExtent] = "gray";
9674
9675                 /*
9676                   Select a pen color from a dialog.
9677                 */
9678                 resource_info->pen_colors[pen_number]=color_name;
9679                 XColorBrowserWidget(display,windows,"Select",color_name);
9680                 if (*color_name == '\0')
9681                   break;
9682               }
9683             /*
9684               Set border color.
9685             */
9686             (void) XParseColor(display,windows->map_info->colormap,
9687               resource_info->pen_colors[pen_number],&border_color);
9688             break;
9689           }
9690           case MatteEditFuzzCommand:
9691           {
9692             static char
9693               fuzz[MaxTextExtent];
9694
9695             static const char
9696               *FuzzMenu[] =
9697               {
9698                 "0%",
9699                 "2%",
9700                 "5%",
9701                 "10%",
9702                 "15%",
9703                 "Dialog...",
9704                 (char *) NULL,
9705               };
9706
9707             /*
9708               Select a command from the pop-up menu.
9709             */
9710             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9711               command);
9712             if (entry < 0)
9713               break;
9714             if (entry != 5)
9715               {
9716                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*QuantumRange+
9717                   1.0);
9718                 break;
9719               }
9720             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9721             (void) XDialogWidget(display,windows,"Ok",
9722               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9723             if (*fuzz == '\0')
9724               break;
9725             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9726             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9727             break;
9728           }
9729           case MatteEditValueCommand:
9730           {
9731             static char
9732               message[MaxTextExtent];
9733
9734             static const char
9735               *MatteMenu[] =
9736               {
9737                 "Opaque",
9738                 "Transparent",
9739                 "Dialog...",
9740                 (char *) NULL,
9741               };
9742
9743             /*
9744               Select a command from the pop-up menu.
9745             */
9746             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9747               command);
9748             if (entry < 0)
9749               break;
9750             if (entry != 2)
9751               {
9752                 (void) FormatMagickString(matte,MaxTextExtent,QuantumFormat,
9753                   OpaqueOpacity);
9754                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9755                   (void) FormatMagickString(matte,MaxTextExtent,QuantumFormat,
9756                     (Quantum) TransparentOpacity);
9757                 break;
9758               }
9759             (void) FormatMagickString(message,MaxTextExtent,
9760               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9761               QuantumRange);
9762             (void) XDialogWidget(display,windows,"Matte",message,matte);
9763             if (*matte == '\0')
9764               break;
9765             break;
9766           }
9767           case MatteEditUndoCommand:
9768           {
9769             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9770               image);
9771             break;
9772           }
9773           case MatteEditHelpCommand:
9774           {
9775             XTextViewWidget(display,resource_info,windows,MagickFalse,
9776               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9777             break;
9778           }
9779           case MatteEditDismissCommand:
9780           {
9781             /*
9782               Prematurely exit.
9783             */
9784             state|=EscapeState;
9785             state|=ExitState;
9786             break;
9787           }
9788           default:
9789             break;
9790         }
9791         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9792         continue;
9793       }
9794     switch (event.type)
9795     {
9796       case ButtonPress:
9797       {
9798         if (event.xbutton.button != Button1)
9799           break;
9800         if ((event.xbutton.window != windows->image.id) &&
9801             (event.xbutton.window != windows->magnify.id))
9802           break;
9803         /*
9804           Update matte data.
9805         */
9806         x=event.xbutton.x;
9807         y=event.xbutton.y;
9808         (void) XMagickCommand(display,resource_info,windows,
9809           SaveToUndoBufferCommand,image);
9810         state|=UpdateConfigurationState;
9811         break;
9812       }
9813       case ButtonRelease:
9814       {
9815         if (event.xbutton.button != Button1)
9816           break;
9817         if ((event.xbutton.window != windows->image.id) &&
9818             (event.xbutton.window != windows->magnify.id))
9819           break;
9820         /*
9821           Update colormap information.
9822         */
9823         x=event.xbutton.x;
9824         y=event.xbutton.y;
9825         XConfigureImageColormap(display,resource_info,windows,*image);
9826         (void) XConfigureImage(display,resource_info,windows,*image);
9827         XInfoWidget(display,windows,text);
9828         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9829         state&=(~UpdateConfigurationState);
9830         break;
9831       }
9832       case Expose:
9833         break;
9834       case KeyPress:
9835       {
9836         char
9837           command[MaxTextExtent];
9838
9839         KeySym
9840           key_symbol;
9841
9842         if (event.xkey.window == windows->magnify.id)
9843           {
9844             Window
9845               window;
9846
9847             window=windows->magnify.id;
9848             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9849           }
9850         if (event.xkey.window != windows->image.id)
9851           break;
9852         /*
9853           Respond to a user key press.
9854         */
9855         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9856           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9857         switch ((int) key_symbol)
9858         {
9859           case XK_Escape:
9860           case XK_F20:
9861           {
9862             /*
9863               Prematurely exit.
9864             */
9865             state|=ExitState;
9866             break;
9867           }
9868           case XK_F1:
9869           case XK_Help:
9870           {
9871             XTextViewWidget(display,resource_info,windows,MagickFalse,
9872               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9873             break;
9874           }
9875           default:
9876           {
9877             (void) XBell(display,0);
9878             break;
9879           }
9880         }
9881         break;
9882       }
9883       case MotionNotify:
9884       {
9885         /*
9886           Map and unmap Info widget as cursor crosses its boundaries.
9887         */
9888         x=event.xmotion.x;
9889         y=event.xmotion.y;
9890         if (windows->info.mapped != MagickFalse)
9891           {
9892             if ((x < (int) (windows->info.x+windows->info.width)) &&
9893                 (y < (int) (windows->info.y+windows->info.height)))
9894               (void) XWithdrawWindow(display,windows->info.id,
9895                 windows->info.screen);
9896           }
9897         else
9898           if ((x > (int) (windows->info.x+windows->info.width)) ||
9899               (y > (int) (windows->info.y+windows->info.height)))
9900             (void) XMapWindow(display,windows->info.id);
9901         break;
9902       }
9903       default:
9904         break;
9905     }
9906     if (event.xany.window == windows->magnify.id)
9907       {
9908         x=windows->magnify.x-windows->image.x;
9909         y=windows->magnify.y-windows->image.y;
9910       }
9911     x_offset=x;
9912     y_offset=y;
9913     if ((state & UpdateConfigurationState) != 0)
9914       {
9915         ExceptionInfo
9916           *exception;
9917
9918         int
9919           x,
9920           y;
9921
9922         /*
9923           Matte edit is relative to image configuration.
9924         */
9925         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9926           MagickTrue);
9927         XPutPixel(windows->image.ximage,x_offset,y_offset,
9928           windows->pixel_info->background_color.pixel);
9929         width=(unsigned int) (*image)->columns;
9930         height=(unsigned int) (*image)->rows;
9931         x=0;
9932         y=0;
9933         if (windows->image.crop_geometry != (char *) NULL)
9934           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
9935             &width,&height);
9936         x_offset=(int)
9937           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
9938         y_offset=(int)
9939           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
9940         if ((x_offset < 0) || (y_offset < 0))
9941           continue;
9942         if ((x_offset >= (int) (*image)->columns) ||
9943             (y_offset >= (int) (*image)->rows))
9944           continue;
9945         if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9946           return(MagickFalse);
9947         (*image)->matte=MagickTrue;
9948         exception=(&(*image)->exception);
9949         switch (method)
9950         {
9951           case PointMethod:
9952           default:
9953           {
9954             /*
9955               Update matte information using point algorithm.
9956             */
9957             q=GetAuthenticPixels(*image,x_offset,y_offset,1,1,exception);
9958             if (q == (PixelPacket *) NULL)
9959               break;
9960             q->opacity=(Quantum) StringToLong(matte);
9961             (void) SyncAuthenticPixels(*image,exception);
9962             break;
9963           }
9964           case ReplaceMethod:
9965           {
9966             PixelPacket
9967               target;
9968
9969             /*
9970               Update matte information using replace algorithm.
9971             */
9972             (void) GetOneVirtualPixel(*image,x_offset,y_offset,&target,
9973               exception);
9974             for (y=0; y < (ssize_t) (*image)->rows; y++)
9975             {
9976               q=GetAuthenticPixels(*image,0,y,(*image)->columns,1,
9977                 &(*image)->exception);
9978               if (q == (PixelPacket *) NULL)
9979                 break;
9980               for (x=0; x < (int) (*image)->columns; x++)
9981               {
9982                 if (IsColorSimilar(*image,q,&target))
9983                   q->opacity=(Quantum) StringToLong(matte);
9984                 q++;
9985               }
9986               if (SyncAuthenticPixels(*image,exception) == MagickFalse)
9987                 break;
9988             }
9989             break;
9990           }
9991           case FloodfillMethod:
9992           case FillToBorderMethod:
9993           {
9994             DrawInfo
9995               *draw_info;
9996
9997             MagickPixelPacket
9998               target;
9999
10000             /*
10001               Update matte information using floodfill algorithm.
10002             */
10003             (void) GetOneVirtualMagickPixel(*image,x_offset,y_offset,&target,
10004               exception);
10005             if (method == FillToBorderMethod)
10006               {
10007                 target.red=(MagickRealType)
10008                   ScaleShortToQuantum(border_color.red);
10009                 target.green=(MagickRealType)
10010                   ScaleShortToQuantum(border_color.green);
10011                 target.blue=(MagickRealType)
10012                   ScaleShortToQuantum(border_color.blue);
10013               }
10014             draw_info=CloneDrawInfo(resource_info->image_info,
10015               (DrawInfo *) NULL);
10016             draw_info->fill.opacity=ClampToQuantum(StringToDouble(matte));
10017             (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
10018               x_offset,y_offset,method == FloodfillMethod ? MagickFalse :
10019               MagickTrue);
10020             draw_info=DestroyDrawInfo(draw_info);
10021             break;
10022           }
10023           case ResetMethod:
10024           {
10025             /*
10026               Update matte information using reset algorithm.
10027             */
10028             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10029               return(MagickFalse);
10030             for (y=0; y < (ssize_t) (*image)->rows; y++)
10031             {
10032               q=QueueAuthenticPixels(*image,0,y,(*image)->columns,1,exception);
10033               if (q == (PixelPacket *) NULL)
10034                 break;
10035               for (x=0; x < (int) (*image)->columns; x++)
10036               {
10037                 q->opacity=(Quantum) StringToLong(matte);
10038                 q++;
10039               }
10040               if (SyncAuthenticPixels(*image,exception) == MagickFalse)
10041                 break;
10042             }
10043             if (StringToLong(matte) == OpaqueOpacity)
10044               (*image)->matte=MagickFalse;
10045             break;
10046           }
10047         }
10048         state&=(~UpdateConfigurationState);
10049       }
10050   } while ((state & ExitState) == 0);
10051   (void) XSelectInput(display,windows->image.id,
10052     windows->image.attributes.event_mask);
10053   XSetCursorState(display,windows,MagickFalse);
10054   (void) XFreeCursor(display,cursor);
10055   return(MagickTrue);
10056 }
10057 \f
10058 /*
10059 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10060 %                                                                             %
10061 %                                                                             %
10062 %                                                                             %
10063 +   X O p e n I m a g e                                                       %
10064 %                                                                             %
10065 %                                                                             %
10066 %                                                                             %
10067 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10068 %
10069 %  XOpenImage() loads an image from a file.
10070 %
10071 %  The format of the XOpenImage method is:
10072 %
10073 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10074 %       XWindows *windows,const unsigned int command)
10075 %
10076 %  A description of each parameter follows:
10077 %
10078 %    o display: Specifies a connection to an X server; returned from
10079 %      XOpenDisplay.
10080 %
10081 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10082 %
10083 %    o windows: Specifies a pointer to a XWindows structure.
10084 %
10085 %    o command: A value other than zero indicates that the file is selected
10086 %      from the command line argument list.
10087 %
10088 */
10089 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10090   XWindows *windows,const MagickBooleanType command)
10091 {
10092   const MagickInfo
10093     *magick_info;
10094
10095   ExceptionInfo
10096     *exception;
10097
10098   Image
10099     *nexus;
10100
10101   ImageInfo
10102     *image_info;
10103
10104   static char
10105     filename[MaxTextExtent] = "\0";
10106
10107   /*
10108     Request file name from user.
10109   */
10110   if (command == MagickFalse)
10111     XFileBrowserWidget(display,windows,"Open",filename);
10112   else
10113     {
10114       char
10115         **filelist,
10116         **files;
10117
10118       int
10119         count,
10120         status;
10121
10122       register int
10123         i,
10124         j;
10125
10126       /*
10127         Select next image from the command line.
10128       */
10129       status=XGetCommand(display,windows->image.id,&files,&count);
10130       if (status == 0)
10131         {
10132           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10133           return((Image *) NULL);
10134         }
10135       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10136       if (filelist == (char **) NULL)
10137         {
10138           ThrowXWindowFatalException(ResourceLimitError,
10139             "MemoryAllocationFailed","...");
10140           (void) XFreeStringList(files);
10141           return((Image *) NULL);
10142         }
10143       j=0;
10144       for (i=1; i < count; i++)
10145         if (*files[i] != '-')
10146           filelist[j++]=files[i];
10147       filelist[j]=(char *) NULL;
10148       XListBrowserWidget(display,windows,&windows->widget,
10149         (const char **) filelist,"Load","Select Image to Load:",filename);
10150       filelist=(char **) RelinquishMagickMemory(filelist);
10151       (void) XFreeStringList(files);
10152     }
10153   if (*filename == '\0')
10154     return((Image *) NULL);
10155   image_info=CloneImageInfo(resource_info->image_info);
10156   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10157     (void *) NULL);
10158   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10159   exception=AcquireExceptionInfo();
10160   (void) SetImageInfo(image_info,0,exception);
10161   if (LocaleCompare(image_info->magick,"X") == 0)
10162     {
10163       char
10164         seconds[MaxTextExtent];
10165
10166       /*
10167         User may want to delay the X server screen grab.
10168       */
10169       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10170       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10171         seconds);
10172       if (*seconds == '\0')
10173         return((Image *) NULL);
10174       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10175     }
10176   magick_info=GetMagickInfo(image_info->magick,exception);
10177   if ((magick_info != (const MagickInfo *) NULL) &&
10178       (magick_info->raw != MagickFalse))
10179     {
10180       char
10181         geometry[MaxTextExtent];
10182
10183       /*
10184         Request image size from the user.
10185       */
10186       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10187       if (image_info->size != (char *) NULL)
10188         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10189       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10190         geometry);
10191       (void) CloneString(&image_info->size,geometry);
10192     }
10193   /*
10194     Load the image.
10195   */
10196   XSetCursorState(display,windows,MagickTrue);
10197   XCheckRefreshWindows(display,windows);
10198   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10199   nexus=ReadImage(image_info,exception);
10200   CatchException(exception);
10201   XSetCursorState(display,windows,MagickFalse);
10202   if (nexus != (Image *) NULL)
10203     XClientMessage(display,windows->image.id,windows->im_protocols,
10204       windows->im_next_image,CurrentTime);
10205   else
10206     {
10207       char
10208         *text,
10209         **textlist;
10210
10211       /*
10212         Unknown image format.
10213       */
10214       text=FileToString(filename,~0,exception);
10215       if (text == (char *) NULL)
10216         return((Image *) NULL);
10217       textlist=StringToList(text);
10218       if (textlist != (char **) NULL)
10219         {
10220           char
10221             title[MaxTextExtent];
10222
10223           register int
10224             i;
10225
10226           (void) FormatMagickString(title,MaxTextExtent,
10227             "Unknown format: %s",filename);
10228           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10229             (const char **) textlist);
10230           for (i=0; textlist[i] != (char *) NULL; i++)
10231             textlist[i]=DestroyString(textlist[i]);
10232           textlist=(char **) RelinquishMagickMemory(textlist);
10233         }
10234       text=DestroyString(text);
10235     }
10236   exception=DestroyExceptionInfo(exception);
10237   image_info=DestroyImageInfo(image_info);
10238   return(nexus);
10239 }
10240 \f
10241 /*
10242 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10243 %                                                                             %
10244 %                                                                             %
10245 %                                                                             %
10246 +   X P a n I m a g e                                                         %
10247 %                                                                             %
10248 %                                                                             %
10249 %                                                                             %
10250 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10251 %
10252 %  XPanImage() pans the image until the mouse button is released.
10253 %
10254 %  The format of the XPanImage method is:
10255 %
10256 %      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10257 %
10258 %  A description of each parameter follows:
10259 %
10260 %    o display: Specifies a connection to an X server;  returned from
10261 %      XOpenDisplay.
10262 %
10263 %    o windows: Specifies a pointer to a XWindows structure.
10264 %
10265 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10266 %      the entire image is refreshed.
10267 %
10268 */
10269 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10270 {
10271   char
10272     text[MaxTextExtent];
10273
10274   Cursor
10275     cursor;
10276
10277   MagickRealType
10278     x_factor,
10279     y_factor;
10280
10281   RectangleInfo
10282     pan_info;
10283
10284   size_t
10285     state;
10286
10287   /*
10288     Define cursor.
10289   */
10290   if ((windows->image.ximage->width > (int) windows->image.width) &&
10291       (windows->image.ximage->height > (int) windows->image.height))
10292     cursor=XCreateFontCursor(display,XC_fleur);
10293   else
10294     if (windows->image.ximage->width > (int) windows->image.width)
10295       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10296     else
10297       if (windows->image.ximage->height > (int) windows->image.height)
10298         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10299       else
10300         cursor=XCreateFontCursor(display,XC_arrow);
10301   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10302   /*
10303     Pan image as pointer moves until the mouse button is released.
10304   */
10305   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10306   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10307   pan_info.width=windows->pan.width*windows->image.width/
10308     windows->image.ximage->width;
10309   pan_info.height=windows->pan.height*windows->image.height/
10310     windows->image.ximage->height;
10311   pan_info.x=0;
10312   pan_info.y=0;
10313   state=UpdateConfigurationState;
10314   do
10315   {
10316     switch (event->type)
10317     {
10318       case ButtonPress:
10319       {
10320         /*
10321           User choose an initial pan location.
10322         */
10323         pan_info.x=event->xbutton.x;
10324         pan_info.y=event->xbutton.y;
10325         state|=UpdateConfigurationState;
10326         break;
10327       }
10328       case ButtonRelease:
10329       {
10330         /*
10331           User has finished panning the image.
10332         */
10333         pan_info.x=event->xbutton.x;
10334         pan_info.y=event->xbutton.y;
10335         state|=UpdateConfigurationState | ExitState;
10336         break;
10337       }
10338       case MotionNotify:
10339       {
10340         pan_info.x=event->xmotion.x;
10341         pan_info.y=event->xmotion.y;
10342         state|=UpdateConfigurationState;
10343       }
10344       default:
10345         break;
10346     }
10347     if ((state & UpdateConfigurationState) != 0)
10348       {
10349         /*
10350           Check boundary conditions.
10351         */
10352         if (pan_info.x < (int) (pan_info.width/2))
10353           pan_info.x=0;
10354         else
10355           pan_info.x=(int) (x_factor*(pan_info.x-(pan_info.width/2)));
10356         if (pan_info.x < 0)
10357           pan_info.x=0;
10358         else
10359           if ((int) (pan_info.x+windows->image.width) >
10360               windows->image.ximage->width)
10361             pan_info.x=(ssize_t)
10362               (windows->image.ximage->width-windows->image.width);
10363         if (pan_info.y < (ssize_t) (pan_info.height/2))
10364           pan_info.y=0;
10365         else
10366           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10367         if (pan_info.y < 0)
10368           pan_info.y=0;
10369         else
10370           if ((int) (pan_info.y+windows->image.height) >
10371               windows->image.ximage->height)
10372             pan_info.y=(ssize_t)
10373               (windows->image.ximage->height-windows->image.height);
10374         if ((windows->image.x != (int) pan_info.x) ||
10375             (windows->image.y != (int) pan_info.y))
10376           {
10377             /*
10378               Display image pan offset.
10379             */
10380             windows->image.x=(int) pan_info.x;
10381             windows->image.y=(int) pan_info.y;
10382             (void) FormatMagickString(text,MaxTextExtent," %ux%u%+d%+d ",
10383               windows->image.width,windows->image.height,windows->image.x,
10384               windows->image.y);
10385             XInfoWidget(display,windows,text);
10386             /*
10387               Refresh Image window.
10388             */
10389             XDrawPanRectangle(display,windows);
10390             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10391           }
10392         state&=(~UpdateConfigurationState);
10393       }
10394     /*
10395       Wait for next event.
10396     */
10397     if ((state & ExitState) == 0)
10398       XScreenEvent(display,windows,event);
10399   } while ((state & ExitState) == 0);
10400   /*
10401     Restore cursor.
10402   */
10403   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10404   (void) XFreeCursor(display,cursor);
10405   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10406 }
10407 \f
10408 /*
10409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10410 %                                                                             %
10411 %                                                                             %
10412 %                                                                             %
10413 +   X P a s t e I m a g e                                                     %
10414 %                                                                             %
10415 %                                                                             %
10416 %                                                                             %
10417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10418 %
10419 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10420 %  window image at a location the user chooses with the pointer.
10421 %
10422 %  The format of the XPasteImage method is:
10423 %
10424 %      MagickBooleanType XPasteImage(Display *display,
10425 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
10426 %
10427 %  A description of each parameter follows:
10428 %
10429 %    o display: Specifies a connection to an X server;  returned from
10430 %      XOpenDisplay.
10431 %
10432 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10433 %
10434 %    o windows: Specifies a pointer to a XWindows structure.
10435 %
10436 %    o image: the image; returned from ReadImage.
10437 %
10438 */
10439 static MagickBooleanType XPasteImage(Display *display,
10440   XResourceInfo *resource_info,XWindows *windows,Image *image)
10441 {
10442   static const char
10443     *PasteMenu[] =
10444     {
10445       "Operator",
10446       "Help",
10447       "Dismiss",
10448       (char *) NULL
10449     };
10450
10451   static const ModeType
10452     PasteCommands[] =
10453     {
10454       PasteOperatorsCommand,
10455       PasteHelpCommand,
10456       PasteDismissCommand
10457     };
10458
10459   static CompositeOperator
10460     compose = CopyCompositeOp;
10461
10462   char
10463     text[MaxTextExtent];
10464
10465   Cursor
10466     cursor;
10467
10468   Image
10469     *paste_image;
10470
10471   int
10472     entry,
10473     id,
10474     x,
10475     y;
10476
10477   MagickRealType
10478     scale_factor;
10479
10480   RectangleInfo
10481     highlight_info,
10482     paste_info;
10483
10484   unsigned int
10485     height,
10486     width;
10487
10488   size_t
10489     state;
10490
10491   XEvent
10492     event;
10493
10494   /*
10495     Copy image.
10496   */
10497   if (resource_info->copy_image == (Image *) NULL)
10498     return(MagickFalse);
10499   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10500     &image->exception);
10501   /*
10502     Map Command widget.
10503   */
10504   (void) CloneString(&windows->command.name,"Paste");
10505   windows->command.data=1;
10506   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10507   (void) XMapRaised(display,windows->command.id);
10508   XClientMessage(display,windows->image.id,windows->im_protocols,
10509     windows->im_update_widget,CurrentTime);
10510   /*
10511     Track pointer until button 1 is pressed.
10512   */
10513   XSetCursorState(display,windows,MagickFalse);
10514   XQueryPosition(display,windows->image.id,&x,&y);
10515   (void) XSelectInput(display,windows->image.id,
10516     windows->image.attributes.event_mask | PointerMotionMask);
10517   paste_info.x=windows->image.x+x;
10518   paste_info.y=windows->image.y+y;
10519   paste_info.width=0;
10520   paste_info.height=0;
10521   cursor=XCreateFontCursor(display,XC_ul_angle);
10522   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10523   state=DefaultState;
10524   do
10525   {
10526     if (windows->info.mapped != MagickFalse)
10527       {
10528         /*
10529           Display pointer position.
10530         */
10531         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
10532           (long) paste_info.x,(long) paste_info.y);
10533         XInfoWidget(display,windows,text);
10534       }
10535     highlight_info=paste_info;
10536     highlight_info.x=paste_info.x-windows->image.x;
10537     highlight_info.y=paste_info.y-windows->image.y;
10538     XHighlightRectangle(display,windows->image.id,
10539       windows->image.highlight_context,&highlight_info);
10540     /*
10541       Wait for next event.
10542     */
10543     XScreenEvent(display,windows,&event);
10544     XHighlightRectangle(display,windows->image.id,
10545       windows->image.highlight_context,&highlight_info);
10546     if (event.xany.window == windows->command.id)
10547       {
10548         /*
10549           Select a command from the Command widget.
10550         */
10551         id=XCommandWidget(display,windows,PasteMenu,&event);
10552         if (id < 0)
10553           continue;
10554         switch (PasteCommands[id])
10555         {
10556           case PasteOperatorsCommand:
10557           {
10558             char
10559               command[MaxTextExtent],
10560               **operators;
10561
10562             /*
10563               Select a command from the pop-up menu.
10564             */
10565             operators=GetMagickOptions(MagickComposeOptions);
10566             if (operators == (char **) NULL)
10567               break;
10568             entry=XMenuWidget(display,windows,PasteMenu[id],
10569               (const char **) operators,command);
10570             if (entry >= 0)
10571               compose=(CompositeOperator) ParseMagickOption(
10572                 MagickComposeOptions,MagickFalse,operators[entry]);
10573             operators=DestroyStringList(operators);
10574             break;
10575           }
10576           case PasteHelpCommand:
10577           {
10578             XTextViewWidget(display,resource_info,windows,MagickFalse,
10579               "Help Viewer - Image Composite",ImagePasteHelp);
10580             break;
10581           }
10582           case PasteDismissCommand:
10583           {
10584             /*
10585               Prematurely exit.
10586             */
10587             state|=EscapeState;
10588             state|=ExitState;
10589             break;
10590           }
10591           default:
10592             break;
10593         }
10594         continue;
10595       }
10596     switch (event.type)
10597     {
10598       case ButtonPress:
10599       {
10600         if (image->debug != MagickFalse)
10601           (void) LogMagickEvent(X11Event,GetMagickModule(),
10602             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10603             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10604         if (event.xbutton.button != Button1)
10605           break;
10606         if (event.xbutton.window != windows->image.id)
10607           break;
10608         /*
10609           Paste rectangle is relative to image configuration.
10610         */
10611         width=(unsigned int) image->columns;
10612         height=(unsigned int) image->rows;
10613         x=0;
10614         y=0;
10615         if (windows->image.crop_geometry != (char *) NULL)
10616           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10617             &width,&height);
10618         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10619         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10620         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10621         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10622         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10623         paste_info.x=windows->image.x+event.xbutton.x;
10624         paste_info.y=windows->image.y+event.xbutton.y;
10625         break;
10626       }
10627       case ButtonRelease:
10628       {
10629         if (image->debug != MagickFalse)
10630           (void) LogMagickEvent(X11Event,GetMagickModule(),
10631             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10632             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10633         if (event.xbutton.button != Button1)
10634           break;
10635         if (event.xbutton.window != windows->image.id)
10636           break;
10637         if ((paste_info.width != 0) && (paste_info.height != 0))
10638           {
10639             /*
10640               User has selected the location of the paste image.
10641             */
10642             paste_info.x=windows->image.x+event.xbutton.x;
10643             paste_info.y=windows->image.y+event.xbutton.y;
10644             state|=ExitState;
10645           }
10646         break;
10647       }
10648       case Expose:
10649         break;
10650       case KeyPress:
10651       {
10652         char
10653           command[MaxTextExtent];
10654
10655         KeySym
10656           key_symbol;
10657
10658         int
10659           length;
10660
10661         if (event.xkey.window != windows->image.id)
10662           break;
10663         /*
10664           Respond to a user key press.
10665         */
10666         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10667           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10668         *(command+length)='\0';
10669         if (image->debug != MagickFalse)
10670           (void) LogMagickEvent(X11Event,GetMagickModule(),
10671             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10672         switch ((int) key_symbol)
10673         {
10674           case XK_Escape:
10675           case XK_F20:
10676           {
10677             /*
10678               Prematurely exit.
10679             */
10680             paste_image=DestroyImage(paste_image);
10681             state|=EscapeState;
10682             state|=ExitState;
10683             break;
10684           }
10685           case XK_F1:
10686           case XK_Help:
10687           {
10688             (void) XSetFunction(display,windows->image.highlight_context,
10689               GXcopy);
10690             XTextViewWidget(display,resource_info,windows,MagickFalse,
10691               "Help Viewer - Image Composite",ImagePasteHelp);
10692             (void) XSetFunction(display,windows->image.highlight_context,
10693               GXinvert);
10694             break;
10695           }
10696           default:
10697           {
10698             (void) XBell(display,0);
10699             break;
10700           }
10701         }
10702         break;
10703       }
10704       case MotionNotify:
10705       {
10706         /*
10707           Map and unmap Info widget as text cursor crosses its boundaries.
10708         */
10709         x=event.xmotion.x;
10710         y=event.xmotion.y;
10711         if (windows->info.mapped != MagickFalse)
10712           {
10713             if ((x < (int) (windows->info.x+windows->info.width)) &&
10714                 (y < (int) (windows->info.y+windows->info.height)))
10715               (void) XWithdrawWindow(display,windows->info.id,
10716                 windows->info.screen);
10717           }
10718         else
10719           if ((x > (int) (windows->info.x+windows->info.width)) ||
10720               (y > (int) (windows->info.y+windows->info.height)))
10721             (void) XMapWindow(display,windows->info.id);
10722         paste_info.x=windows->image.x+x;
10723         paste_info.y=windows->image.y+y;
10724         break;
10725       }
10726       default:
10727       {
10728         if (image->debug != MagickFalse)
10729           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10730             event.type);
10731         break;
10732       }
10733     }
10734   } while ((state & ExitState) == 0);
10735   (void) XSelectInput(display,windows->image.id,
10736     windows->image.attributes.event_mask);
10737   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10738   XSetCursorState(display,windows,MagickFalse);
10739   (void) XFreeCursor(display,cursor);
10740   if ((state & EscapeState) != 0)
10741     return(MagickTrue);
10742   /*
10743     Image pasting is relative to image configuration.
10744   */
10745   XSetCursorState(display,windows,MagickTrue);
10746   XCheckRefreshWindows(display,windows);
10747   width=(unsigned int) image->columns;
10748   height=(unsigned int) image->rows;
10749   x=0;
10750   y=0;
10751   if (windows->image.crop_geometry != (char *) NULL)
10752     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10753   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10754   paste_info.x+=x;
10755   paste_info.x=(int) (scale_factor*paste_info.x+0.5);
10756   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10757   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10758   paste_info.y+=y;
10759   paste_info.y=(int) (scale_factor*paste_info.y*scale_factor+0.5);
10760   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10761   /*
10762     Paste image with X Image window.
10763   */
10764   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10765   paste_image=DestroyImage(paste_image);
10766   XSetCursorState(display,windows,MagickFalse);
10767   /*
10768     Update image colormap.
10769   */
10770   XConfigureImageColormap(display,resource_info,windows,image);
10771   (void) XConfigureImage(display,resource_info,windows,image);
10772   return(MagickTrue);
10773 }
10774 \f
10775 /*
10776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10777 %                                                                             %
10778 %                                                                             %
10779 %                                                                             %
10780 +   X P r i n t I m a g e                                                     %
10781 %                                                                             %
10782 %                                                                             %
10783 %                                                                             %
10784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10785 %
10786 %  XPrintImage() prints an image to a Postscript printer.
10787 %
10788 %  The format of the XPrintImage method is:
10789 %
10790 %      MagickBooleanType XPrintImage(Display *display,
10791 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
10792 %
10793 %  A description of each parameter follows:
10794 %
10795 %    o display: Specifies a connection to an X server; returned from
10796 %      XOpenDisplay.
10797 %
10798 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10799 %
10800 %    o windows: Specifies a pointer to a XWindows structure.
10801 %
10802 %    o image: the image.
10803 %
10804 */
10805 static MagickBooleanType XPrintImage(Display *display,
10806   XResourceInfo *resource_info,XWindows *windows,Image *image)
10807 {
10808   char
10809     filename[MaxTextExtent],
10810     geometry[MaxTextExtent];
10811
10812   Image
10813     *print_image;
10814
10815   ImageInfo
10816     *image_info;
10817
10818   MagickStatusType
10819     status;
10820
10821   /*
10822     Request Postscript page geometry from user.
10823   */
10824   image_info=CloneImageInfo(resource_info->image_info);
10825   (void) FormatMagickString(geometry,MaxTextExtent,"Letter");
10826   if (image_info->page != (char *) NULL)
10827     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10828   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10829     "Select Postscript Page Geometry:",geometry);
10830   if (*geometry == '\0')
10831     return(MagickTrue);
10832   image_info->page=GetPageGeometry(geometry);
10833   /*
10834     Apply image transforms.
10835   */
10836   XSetCursorState(display,windows,MagickTrue);
10837   XCheckRefreshWindows(display,windows);
10838   print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10839   if (print_image == (Image *) NULL)
10840     return(MagickFalse);
10841   (void) FormatMagickString(geometry,MaxTextExtent,"%dx%d!",
10842     windows->image.ximage->width,windows->image.ximage->height);
10843   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10844   /*
10845     Print image.
10846   */
10847   (void) AcquireUniqueFilename(filename);
10848   (void) FormatMagickString(print_image->filename,MaxTextExtent,"print:%s",
10849     filename);
10850   status=WriteImage(image_info,print_image);
10851   (void) RelinquishUniqueFileResource(filename);
10852   print_image=DestroyImage(print_image);
10853   image_info=DestroyImageInfo(image_info);
10854   XSetCursorState(display,windows,MagickFalse);
10855   return(status != 0 ? MagickTrue : MagickFalse);
10856 }
10857 \f
10858 /*
10859 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10860 %                                                                             %
10861 %                                                                             %
10862 %                                                                             %
10863 +   X R O I I m a g e                                                         %
10864 %                                                                             %
10865 %                                                                             %
10866 %                                                                             %
10867 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10868 %
10869 %  XROIImage() applies an image processing technique to a region of interest.
10870 %
10871 %  The format of the XROIImage method is:
10872 %
10873 %      MagickBooleanType XROIImage(Display *display,
10874 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
10875 %
10876 %  A description of each parameter follows:
10877 %
10878 %    o display: Specifies a connection to an X server; returned from
10879 %      XOpenDisplay.
10880 %
10881 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10882 %
10883 %    o windows: Specifies a pointer to a XWindows structure.
10884 %
10885 %    o image: the image; returned from ReadImage.
10886 %
10887 */
10888 static MagickBooleanType XROIImage(Display *display,
10889   XResourceInfo *resource_info,XWindows *windows,Image **image)
10890 {
10891 #define ApplyMenus  7
10892
10893   static const char
10894     *ROIMenu[] =
10895     {
10896       "Help",
10897       "Dismiss",
10898       (char *) NULL
10899     },
10900     *ApplyMenu[] =
10901     {
10902       "File",
10903       "Edit",
10904       "Transform",
10905       "Enhance",
10906       "Effects",
10907       "F/X",
10908       "Miscellany",
10909       "Help",
10910       "Dismiss",
10911       (char *) NULL
10912     },
10913     *FileMenu[] =
10914     {
10915       "Save...",
10916       "Print...",
10917       (char *) NULL
10918     },
10919     *EditMenu[] =
10920     {
10921       "Undo",
10922       "Redo",
10923       (char *) NULL
10924     },
10925     *TransformMenu[] =
10926     {
10927       "Flop",
10928       "Flip",
10929       "Rotate Right",
10930       "Rotate Left",
10931       (char *) NULL
10932     },
10933     *EnhanceMenu[] =
10934     {
10935       "Hue...",
10936       "Saturation...",
10937       "Brightness...",
10938       "Gamma...",
10939       "Spiff",
10940       "Dull",
10941       "Contrast Stretch...",
10942       "Sigmoidal Contrast...",
10943       "Normalize",
10944       "Equalize",
10945       "Negate",
10946       "Grayscale",
10947       "Map...",
10948       "Quantize...",
10949       (char *) NULL
10950     },
10951     *EffectsMenu[] =
10952     {
10953       "Despeckle",
10954       "Emboss",
10955       "Reduce Noise",
10956       "Add Noise",
10957       "Sharpen...",
10958       "Blur...",
10959       "Threshold...",
10960       "Edge Detect...",
10961       "Spread...",
10962       "Shade...",
10963       "Raise...",
10964       "Segment...",
10965       (char *) NULL
10966     },
10967     *FXMenu[] =
10968     {
10969       "Solarize...",
10970       "Sepia Tone...",
10971       "Swirl...",
10972       "Implode...",
10973       "Vignette...",
10974       "Wave...",
10975       "Oil Paint...",
10976       "Charcoal Draw...",
10977       (char *) NULL
10978     },
10979     *MiscellanyMenu[] =
10980     {
10981       "Image Info",
10982       "Zoom Image",
10983       "Show Preview...",
10984       "Show Histogram",
10985       "Show Matte",
10986       (char *) NULL
10987     };
10988
10989   static const char
10990     **Menus[ApplyMenus] =
10991     {
10992       FileMenu,
10993       EditMenu,
10994       TransformMenu,
10995       EnhanceMenu,
10996       EffectsMenu,
10997       FXMenu,
10998       MiscellanyMenu
10999     };
11000
11001   static const CommandType
11002     ApplyCommands[] =
11003     {
11004       NullCommand,
11005       NullCommand,
11006       NullCommand,
11007       NullCommand,
11008       NullCommand,
11009       NullCommand,
11010       NullCommand,
11011       HelpCommand,
11012       QuitCommand
11013     },
11014     FileCommands[] =
11015     {
11016       SaveCommand,
11017       PrintCommand
11018     },
11019     EditCommands[] =
11020     {
11021       UndoCommand,
11022       RedoCommand
11023     },
11024     TransformCommands[] =
11025     {
11026       FlopCommand,
11027       FlipCommand,
11028       RotateRightCommand,
11029       RotateLeftCommand
11030     },
11031     EnhanceCommands[] =
11032     {
11033       HueCommand,
11034       SaturationCommand,
11035       BrightnessCommand,
11036       GammaCommand,
11037       SpiffCommand,
11038       DullCommand,
11039       ContrastStretchCommand,
11040       SigmoidalContrastCommand,
11041       NormalizeCommand,
11042       EqualizeCommand,
11043       NegateCommand,
11044       GrayscaleCommand,
11045       MapCommand,
11046       QuantizeCommand
11047     },
11048     EffectsCommands[] =
11049     {
11050       DespeckleCommand,
11051       EmbossCommand,
11052       ReduceNoiseCommand,
11053       AddNoiseCommand,
11054       SharpenCommand,
11055       BlurCommand,
11056       EdgeDetectCommand,
11057       SpreadCommand,
11058       ShadeCommand,
11059       RaiseCommand,
11060       SegmentCommand
11061     },
11062     FXCommands[] =
11063     {
11064       SolarizeCommand,
11065       SepiaToneCommand,
11066       SwirlCommand,
11067       ImplodeCommand,
11068       VignetteCommand,
11069       WaveCommand,
11070       OilPaintCommand,
11071       CharcoalDrawCommand
11072     },
11073     MiscellanyCommands[] =
11074     {
11075       InfoCommand,
11076       ZoomCommand,
11077       ShowPreviewCommand,
11078       ShowHistogramCommand,
11079       ShowMatteCommand
11080     },
11081     ROICommands[] =
11082     {
11083       ROIHelpCommand,
11084       ROIDismissCommand
11085     };
11086
11087   static const CommandType
11088     *Commands[ApplyMenus] =
11089     {
11090       FileCommands,
11091       EditCommands,
11092       TransformCommands,
11093       EnhanceCommands,
11094       EffectsCommands,
11095       FXCommands,
11096       MiscellanyCommands
11097     };
11098
11099   char
11100     command[MaxTextExtent],
11101     text[MaxTextExtent];
11102
11103   CommandType
11104     command_type;
11105
11106   Cursor
11107     cursor;
11108
11109   Image
11110     *roi_image;
11111
11112   int
11113     entry,
11114     id,
11115     x,
11116     y;
11117
11118   MagickRealType
11119     scale_factor;
11120
11121   MagickProgressMonitor
11122     progress_monitor;
11123
11124   RectangleInfo
11125     crop_info,
11126     highlight_info,
11127     roi_info;
11128
11129   unsigned int
11130     height,
11131     width;
11132
11133   size_t
11134     state;
11135
11136   XEvent
11137     event;
11138
11139   /*
11140     Map Command widget.
11141   */
11142   (void) CloneString(&windows->command.name,"ROI");
11143   windows->command.data=0;
11144   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11145   (void) XMapRaised(display,windows->command.id);
11146   XClientMessage(display,windows->image.id,windows->im_protocols,
11147     windows->im_update_widget,CurrentTime);
11148   /*
11149     Track pointer until button 1 is pressed.
11150   */
11151   XQueryPosition(display,windows->image.id,&x,&y);
11152   (void) XSelectInput(display,windows->image.id,
11153     windows->image.attributes.event_mask | PointerMotionMask);
11154   roi_info.x=windows->image.x+x;
11155   roi_info.y=windows->image.y+y;
11156   roi_info.width=0;
11157   roi_info.height=0;
11158   cursor=XCreateFontCursor(display,XC_fleur);
11159   state=DefaultState;
11160   do
11161   {
11162     if (windows->info.mapped != MagickFalse)
11163       {
11164         /*
11165           Display pointer position.
11166         */
11167         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
11168           (long) roi_info.x,(long) roi_info.y);
11169         XInfoWidget(display,windows,text);
11170       }
11171     /*
11172       Wait for next event.
11173     */
11174     XScreenEvent(display,windows,&event);
11175     if (event.xany.window == windows->command.id)
11176       {
11177         /*
11178           Select a command from the Command widget.
11179         */
11180         id=XCommandWidget(display,windows,ROIMenu,&event);
11181         if (id < 0)
11182           continue;
11183         switch (ROICommands[id])
11184         {
11185           case ROIHelpCommand:
11186           {
11187             XTextViewWidget(display,resource_info,windows,MagickFalse,
11188               "Help Viewer - Region of Interest",ImageROIHelp);
11189             break;
11190           }
11191           case ROIDismissCommand:
11192           {
11193             /*
11194               Prematurely exit.
11195             */
11196             state|=EscapeState;
11197             state|=ExitState;
11198             break;
11199           }
11200           default:
11201             break;
11202         }
11203         continue;
11204       }
11205     switch (event.type)
11206     {
11207       case ButtonPress:
11208       {
11209         if (event.xbutton.button != Button1)
11210           break;
11211         if (event.xbutton.window != windows->image.id)
11212           break;
11213         /*
11214           Note first corner of region of interest rectangle-- exit loop.
11215         */
11216         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11217         roi_info.x=windows->image.x+event.xbutton.x;
11218         roi_info.y=windows->image.y+event.xbutton.y;
11219         state|=ExitState;
11220         break;
11221       }
11222       case ButtonRelease:
11223         break;
11224       case Expose:
11225         break;
11226       case KeyPress:
11227       {
11228         KeySym
11229           key_symbol;
11230
11231         if (event.xkey.window != windows->image.id)
11232           break;
11233         /*
11234           Respond to a user key press.
11235         */
11236         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11237           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11238         switch ((int) key_symbol)
11239         {
11240           case XK_Escape:
11241           case XK_F20:
11242           {
11243             /*
11244               Prematurely exit.
11245             */
11246             state|=EscapeState;
11247             state|=ExitState;
11248             break;
11249           }
11250           case XK_F1:
11251           case XK_Help:
11252           {
11253             XTextViewWidget(display,resource_info,windows,MagickFalse,
11254               "Help Viewer - Region of Interest",ImageROIHelp);
11255             break;
11256           }
11257           default:
11258           {
11259             (void) XBell(display,0);
11260             break;
11261           }
11262         }
11263         break;
11264       }
11265       case MotionNotify:
11266       {
11267         /*
11268           Map and unmap Info widget as text cursor crosses its boundaries.
11269         */
11270         x=event.xmotion.x;
11271         y=event.xmotion.y;
11272         if (windows->info.mapped != MagickFalse)
11273           {
11274             if ((x < (int) (windows->info.x+windows->info.width)) &&
11275                 (y < (int) (windows->info.y+windows->info.height)))
11276               (void) XWithdrawWindow(display,windows->info.id,
11277                 windows->info.screen);
11278           }
11279         else
11280           if ((x > (int) (windows->info.x+windows->info.width)) ||
11281               (y > (int) (windows->info.y+windows->info.height)))
11282             (void) XMapWindow(display,windows->info.id);
11283         roi_info.x=windows->image.x+x;
11284         roi_info.y=windows->image.y+y;
11285         break;
11286       }
11287       default:
11288         break;
11289     }
11290   } while ((state & ExitState) == 0);
11291   (void) XSelectInput(display,windows->image.id,
11292     windows->image.attributes.event_mask);
11293   if ((state & EscapeState) != 0)
11294     {
11295       /*
11296         User want to exit without region of interest.
11297       */
11298       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11299       (void) XFreeCursor(display,cursor);
11300       return(MagickTrue);
11301     }
11302   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11303   do
11304   {
11305     /*
11306       Size rectangle as pointer moves until the mouse button is released.
11307     */
11308     x=(int) roi_info.x;
11309     y=(int) roi_info.y;
11310     roi_info.width=0;
11311     roi_info.height=0;
11312     state=DefaultState;
11313     do
11314     {
11315       highlight_info=roi_info;
11316       highlight_info.x=roi_info.x-windows->image.x;
11317       highlight_info.y=roi_info.y-windows->image.y;
11318       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11319         {
11320           /*
11321             Display info and draw region of interest rectangle.
11322           */
11323           if (windows->info.mapped == MagickFalse)
11324             (void) XMapWindow(display,windows->info.id);
11325           (void) FormatMagickString(text,MaxTextExtent,
11326             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11327             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11328           XInfoWidget(display,windows,text);
11329           XHighlightRectangle(display,windows->image.id,
11330             windows->image.highlight_context,&highlight_info);
11331         }
11332       else
11333         if (windows->info.mapped != MagickFalse)
11334           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11335       /*
11336         Wait for next event.
11337       */
11338       XScreenEvent(display,windows,&event);
11339       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11340         XHighlightRectangle(display,windows->image.id,
11341           windows->image.highlight_context,&highlight_info);
11342       switch (event.type)
11343       {
11344         case ButtonPress:
11345         {
11346           roi_info.x=windows->image.x+event.xbutton.x;
11347           roi_info.y=windows->image.y+event.xbutton.y;
11348           break;
11349         }
11350         case ButtonRelease:
11351         {
11352           /*
11353             User has committed to region of interest rectangle.
11354           */
11355           roi_info.x=windows->image.x+event.xbutton.x;
11356           roi_info.y=windows->image.y+event.xbutton.y;
11357           XSetCursorState(display,windows,MagickFalse);
11358           state|=ExitState;
11359           if (LocaleCompare(windows->command.name,"Apply") == 0)
11360             break;
11361           (void) CloneString(&windows->command.name,"Apply");
11362           windows->command.data=ApplyMenus;
11363           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11364           break;
11365         }
11366         case Expose:
11367           break;
11368         case MotionNotify:
11369         {
11370           roi_info.x=windows->image.x+event.xmotion.x;
11371           roi_info.y=windows->image.y+event.xmotion.y;
11372         }
11373         default:
11374           break;
11375       }
11376       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11377           ((state & ExitState) != 0))
11378         {
11379           /*
11380             Check boundary conditions.
11381           */
11382           if (roi_info.x < 0)
11383             roi_info.x=0;
11384           else
11385             if (roi_info.x > (int) windows->image.ximage->width)
11386               roi_info.x=windows->image.ximage->width;
11387           if ((int) roi_info.x < x)
11388             roi_info.width=(unsigned int) (x-roi_info.x);
11389           else
11390             {
11391               roi_info.width=(unsigned int) (roi_info.x-x);
11392               roi_info.x=x;
11393             }
11394           if (roi_info.y < 0)
11395             roi_info.y=0;
11396           else
11397             if (roi_info.y > (int) windows->image.ximage->height)
11398               roi_info.y=windows->image.ximage->height;
11399           if ((int) roi_info.y < y)
11400             roi_info.height=(unsigned int) (y-roi_info.y);
11401           else
11402             {
11403               roi_info.height=(unsigned int) (roi_info.y-y);
11404               roi_info.y=y;
11405             }
11406         }
11407     } while ((state & ExitState) == 0);
11408     /*
11409       Wait for user to grab a corner of the rectangle or press return.
11410     */
11411     state=DefaultState;
11412     command_type=NullCommand;
11413     (void) XMapWindow(display,windows->info.id);
11414     do
11415     {
11416       if (windows->info.mapped != MagickFalse)
11417         {
11418           /*
11419             Display pointer position.
11420           */
11421           (void) FormatMagickString(text,MaxTextExtent,
11422             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11423             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11424           XInfoWidget(display,windows,text);
11425         }
11426       highlight_info=roi_info;
11427       highlight_info.x=roi_info.x-windows->image.x;
11428       highlight_info.y=roi_info.y-windows->image.y;
11429       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11430         {
11431           state|=EscapeState;
11432           state|=ExitState;
11433           break;
11434         }
11435       if ((state & UpdateRegionState) != 0)
11436         {
11437           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11438           switch (command_type)
11439           {
11440             case UndoCommand:
11441             case RedoCommand:
11442             {
11443               (void) XMagickCommand(display,resource_info,windows,command_type,
11444                 image);
11445               break;
11446             }
11447             default:
11448             {
11449               /*
11450                 Region of interest is relative to image configuration.
11451               */
11452               progress_monitor=SetImageProgressMonitor(*image,
11453                 (MagickProgressMonitor) NULL,(*image)->client_data);
11454               crop_info=roi_info;
11455               width=(unsigned int) (*image)->columns;
11456               height=(unsigned int) (*image)->rows;
11457               x=0;
11458               y=0;
11459               if (windows->image.crop_geometry != (char *) NULL)
11460                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11461                   &width,&height);
11462               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11463               crop_info.x+=x;
11464               crop_info.x=(int) (scale_factor*crop_info.x+0.5);
11465               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11466               scale_factor=(MagickRealType)
11467                 height/windows->image.ximage->height;
11468               crop_info.y+=y;
11469               crop_info.y=(int) (scale_factor*crop_info.y+0.5);
11470               crop_info.height=(unsigned int)
11471                 (scale_factor*crop_info.height+0.5);
11472               roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11473               (void) SetImageProgressMonitor(*image,progress_monitor,
11474                 (*image)->client_data);
11475               if (roi_image == (Image *) NULL)
11476                 continue;
11477               /*
11478                 Apply image processing technique to the region of interest.
11479               */
11480               windows->image.orphan=MagickTrue;
11481               (void) XMagickCommand(display,resource_info,windows,command_type,
11482                 &roi_image);
11483               progress_monitor=SetImageProgressMonitor(*image,
11484                 (MagickProgressMonitor) NULL,(*image)->client_data);
11485               (void) XMagickCommand(display,resource_info,windows,
11486                 SaveToUndoBufferCommand,image);
11487               windows->image.orphan=MagickFalse;
11488               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11489                 crop_info.x,crop_info.y);
11490               roi_image=DestroyImage(roi_image);
11491               (void) SetImageProgressMonitor(*image,progress_monitor,
11492                 (*image)->client_data);
11493               break;
11494             }
11495           }
11496           if (command_type != InfoCommand)
11497             {
11498               XConfigureImageColormap(display,resource_info,windows,*image);
11499               (void) XConfigureImage(display,resource_info,windows,*image);
11500             }
11501           XCheckRefreshWindows(display,windows);
11502           XInfoWidget(display,windows,text);
11503           (void) XSetFunction(display,windows->image.highlight_context,
11504             GXinvert);
11505           state&=(~UpdateRegionState);
11506         }
11507       XHighlightRectangle(display,windows->image.id,
11508         windows->image.highlight_context,&highlight_info);
11509       XScreenEvent(display,windows,&event);
11510       if (event.xany.window == windows->command.id)
11511         {
11512           /*
11513             Select a command from the Command widget.
11514           */
11515           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11516           command_type=NullCommand;
11517           id=XCommandWidget(display,windows,ApplyMenu,&event);
11518           if (id >= 0)
11519             {
11520               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11521               command_type=ApplyCommands[id];
11522               if (id < ApplyMenus)
11523                 {
11524                   /*
11525                     Select a command from a pop-up menu.
11526                   */
11527                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11528                     (const char **) Menus[id],command);
11529                   if (entry >= 0)
11530                     {
11531                       (void) CopyMagickString(command,Menus[id][entry],
11532                         MaxTextExtent);
11533                       command_type=Commands[id][entry];
11534                     }
11535                 }
11536             }
11537           (void) XSetFunction(display,windows->image.highlight_context,
11538             GXinvert);
11539           XHighlightRectangle(display,windows->image.id,
11540             windows->image.highlight_context,&highlight_info);
11541           if (command_type == HelpCommand)
11542             {
11543               (void) XSetFunction(display,windows->image.highlight_context,
11544                 GXcopy);
11545               XTextViewWidget(display,resource_info,windows,MagickFalse,
11546                 "Help Viewer - Region of Interest",ImageROIHelp);
11547               (void) XSetFunction(display,windows->image.highlight_context,
11548                 GXinvert);
11549               continue;
11550             }
11551           if (command_type == QuitCommand)
11552             {
11553               /*
11554                 exit.
11555               */
11556               state|=EscapeState;
11557               state|=ExitState;
11558               continue;
11559             }
11560           if (command_type != NullCommand)
11561             state|=UpdateRegionState;
11562           continue;
11563         }
11564       XHighlightRectangle(display,windows->image.id,
11565         windows->image.highlight_context,&highlight_info);
11566       switch (event.type)
11567       {
11568         case ButtonPress:
11569         {
11570           x=windows->image.x;
11571           y=windows->image.y;
11572           if (event.xbutton.button != Button1)
11573             break;
11574           if (event.xbutton.window != windows->image.id)
11575             break;
11576           x=windows->image.x+event.xbutton.x;
11577           y=windows->image.y+event.xbutton.y;
11578           if ((x < (int) (roi_info.x+RoiDelta)) &&
11579               (x > (int) (roi_info.x-RoiDelta)) &&
11580               (y < (int) (roi_info.y+RoiDelta)) &&
11581               (y > (int) (roi_info.y-RoiDelta)))
11582             {
11583               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11584               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11585               state|=UpdateConfigurationState;
11586               break;
11587             }
11588           if ((x < (int) (roi_info.x+RoiDelta)) &&
11589               (x > (int) (roi_info.x-RoiDelta)) &&
11590               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11591               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11592             {
11593               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11594               state|=UpdateConfigurationState;
11595               break;
11596             }
11597           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11598               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11599               (y < (int) (roi_info.y+RoiDelta)) &&
11600               (y > (int) (roi_info.y-RoiDelta)))
11601             {
11602               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11603               state|=UpdateConfigurationState;
11604               break;
11605             }
11606           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11607               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11608               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11609               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11610             {
11611               state|=UpdateConfigurationState;
11612               break;
11613             }
11614         }
11615         case ButtonRelease:
11616         {
11617           if (event.xbutton.window == windows->pan.id)
11618             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11619                 (highlight_info.y != crop_info.y-windows->image.y))
11620               XHighlightRectangle(display,windows->image.id,
11621                 windows->image.highlight_context,&highlight_info);
11622           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11623             event.xbutton.time);
11624           break;
11625         }
11626         case Expose:
11627         {
11628           if (event.xexpose.window == windows->image.id)
11629             if (event.xexpose.count == 0)
11630               {
11631                 event.xexpose.x=(int) highlight_info.x;
11632                 event.xexpose.y=(int) highlight_info.y;
11633                 event.xexpose.width=(int) highlight_info.width;
11634                 event.xexpose.height=(int) highlight_info.height;
11635                 XRefreshWindow(display,&windows->image,&event);
11636               }
11637           if (event.xexpose.window == windows->info.id)
11638             if (event.xexpose.count == 0)
11639               XInfoWidget(display,windows,text);
11640           break;
11641         }
11642         case KeyPress:
11643         {
11644           KeySym
11645             key_symbol;
11646
11647           if (event.xkey.window != windows->image.id)
11648             break;
11649           /*
11650             Respond to a user key press.
11651           */
11652           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11653             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11654           switch ((int) key_symbol)
11655           {
11656             case XK_Shift_L:
11657             case XK_Shift_R:
11658               break;
11659             case XK_Escape:
11660             case XK_F20:
11661               state|=EscapeState;
11662             case XK_Return:
11663             {
11664               state|=ExitState;
11665               break;
11666             }
11667             case XK_Home:
11668             case XK_KP_Home:
11669             {
11670               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11671               roi_info.y=(ssize_t) (windows->image.height/2L-roi_info.height/2L);
11672               break;
11673             }
11674             case XK_Left:
11675             case XK_KP_Left:
11676             {
11677               roi_info.x--;
11678               break;
11679             }
11680             case XK_Up:
11681             case XK_KP_Up:
11682             case XK_Next:
11683             {
11684               roi_info.y--;
11685               break;
11686             }
11687             case XK_Right:
11688             case XK_KP_Right:
11689             {
11690               roi_info.x++;
11691               break;
11692             }
11693             case XK_Prior:
11694             case XK_Down:
11695             case XK_KP_Down:
11696             {
11697               roi_info.y++;
11698               break;
11699             }
11700             case XK_F1:
11701             case XK_Help:
11702             {
11703               (void) XSetFunction(display,windows->image.highlight_context,
11704                 GXcopy);
11705               XTextViewWidget(display,resource_info,windows,MagickFalse,
11706                 "Help Viewer - Region of Interest",ImageROIHelp);
11707               (void) XSetFunction(display,windows->image.highlight_context,
11708                 GXinvert);
11709               break;
11710             }
11711             default:
11712             {
11713               command_type=XImageWindowCommand(display,resource_info,windows,
11714                 event.xkey.state,key_symbol,image);
11715               if (command_type != NullCommand)
11716                 state|=UpdateRegionState;
11717               break;
11718             }
11719           }
11720           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11721             event.xkey.time);
11722           break;
11723         }
11724         case KeyRelease:
11725           break;
11726         case MotionNotify:
11727         {
11728           if (event.xbutton.window != windows->image.id)
11729             break;
11730           /*
11731             Map and unmap Info widget as text cursor crosses its boundaries.
11732           */
11733           x=event.xmotion.x;
11734           y=event.xmotion.y;
11735           if (windows->info.mapped != MagickFalse)
11736             {
11737               if ((x < (int) (windows->info.x+windows->info.width)) &&
11738                   (y < (int) (windows->info.y+windows->info.height)))
11739                 (void) XWithdrawWindow(display,windows->info.id,
11740                   windows->info.screen);
11741             }
11742           else
11743             if ((x > (int) (windows->info.x+windows->info.width)) ||
11744                 (y > (int) (windows->info.y+windows->info.height)))
11745               (void) XMapWindow(display,windows->info.id);
11746           roi_info.x=windows->image.x+event.xmotion.x;
11747           roi_info.y=windows->image.y+event.xmotion.y;
11748           break;
11749         }
11750         case SelectionRequest:
11751         {
11752           XSelectionEvent
11753             notify;
11754
11755           XSelectionRequestEvent
11756             *request;
11757
11758           /*
11759             Set primary selection.
11760           */
11761           (void) FormatMagickString(text,MaxTextExtent,
11762             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11763             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11764           request=(&(event.xselectionrequest));
11765           (void) XChangeProperty(request->display,request->requestor,
11766             request->property,request->target,8,PropModeReplace,
11767             (unsigned char *) text,(int) strlen(text));
11768           notify.type=SelectionNotify;
11769           notify.display=request->display;
11770           notify.requestor=request->requestor;
11771           notify.selection=request->selection;
11772           notify.target=request->target;
11773           notify.time=request->time;
11774           if (request->property == None)
11775             notify.property=request->target;
11776           else
11777             notify.property=request->property;
11778           (void) XSendEvent(request->display,request->requestor,False,0,
11779             (XEvent *) &notify);
11780         }
11781         default:
11782           break;
11783       }
11784       if ((state & UpdateConfigurationState) != 0)
11785         {
11786           (void) XPutBackEvent(display,&event);
11787           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11788           break;
11789         }
11790     } while ((state & ExitState) == 0);
11791   } while ((state & ExitState) == 0);
11792   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11793   XSetCursorState(display,windows,MagickFalse);
11794   if ((state & EscapeState) != 0)
11795     return(MagickTrue);
11796   return(MagickTrue);
11797 }
11798 \f
11799 /*
11800 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11801 %                                                                             %
11802 %                                                                             %
11803 %                                                                             %
11804 +   X R o t a t e I m a g e                                                   %
11805 %                                                                             %
11806 %                                                                             %
11807 %                                                                             %
11808 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11809 %
11810 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11811 %  rotation angle is computed from the slope of a line drawn by the user.
11812 %
11813 %  The format of the XRotateImage method is:
11814 %
11815 %      MagickBooleanType XRotateImage(Display *display,
11816 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11817 %        Image **image)
11818 %
11819 %  A description of each parameter follows:
11820 %
11821 %    o display: Specifies a connection to an X server; returned from
11822 %      XOpenDisplay.
11823 %
11824 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11825 %
11826 %    o windows: Specifies a pointer to a XWindows structure.
11827 %
11828 %    o degrees: Specifies the number of degrees to rotate the image.
11829 %
11830 %    o image: the image.
11831 %
11832 */
11833 static MagickBooleanType XRotateImage(Display *display,
11834   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11835 {
11836   static const char
11837     *RotateMenu[] =
11838     {
11839       "Pixel Color",
11840       "Direction",
11841       "Help",
11842       "Dismiss",
11843       (char *) NULL
11844     };
11845
11846   static ModeType
11847     direction = HorizontalRotateCommand;
11848
11849   static const ModeType
11850     DirectionCommands[] =
11851     {
11852       HorizontalRotateCommand,
11853       VerticalRotateCommand
11854     },
11855     RotateCommands[] =
11856     {
11857       RotateColorCommand,
11858       RotateDirectionCommand,
11859       RotateHelpCommand,
11860       RotateDismissCommand
11861     };
11862
11863   static unsigned int
11864     pen_id = 0;
11865
11866   char
11867     command[MaxTextExtent],
11868     text[MaxTextExtent];
11869
11870   Image
11871     *rotate_image;
11872
11873   int
11874     id,
11875     x,
11876     y;
11877
11878   MagickRealType
11879     normalized_degrees;
11880
11881   register int
11882     i;
11883
11884   unsigned int
11885     height,
11886     rotations,
11887     width;
11888
11889   if (degrees == 0.0)
11890     {
11891       unsigned int
11892         distance;
11893
11894       size_t
11895         state;
11896
11897       XEvent
11898         event;
11899
11900       XSegment
11901         rotate_info;
11902
11903       /*
11904         Map Command widget.
11905       */
11906       (void) CloneString(&windows->command.name,"Rotate");
11907       windows->command.data=2;
11908       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11909       (void) XMapRaised(display,windows->command.id);
11910       XClientMessage(display,windows->image.id,windows->im_protocols,
11911         windows->im_update_widget,CurrentTime);
11912       /*
11913         Wait for first button press.
11914       */
11915       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11916       XQueryPosition(display,windows->image.id,&x,&y);
11917       rotate_info.x1=x;
11918       rotate_info.y1=y;
11919       rotate_info.x2=x;
11920       rotate_info.y2=y;
11921       state=DefaultState;
11922       do
11923       {
11924         XHighlightLine(display,windows->image.id,
11925           windows->image.highlight_context,&rotate_info);
11926         /*
11927           Wait for next event.
11928         */
11929         XScreenEvent(display,windows,&event);
11930         XHighlightLine(display,windows->image.id,
11931           windows->image.highlight_context,&rotate_info);
11932         if (event.xany.window == windows->command.id)
11933           {
11934             /*
11935               Select a command from the Command widget.
11936             */
11937             id=XCommandWidget(display,windows,RotateMenu,&event);
11938             if (id < 0)
11939               continue;
11940             (void) XSetFunction(display,windows->image.highlight_context,
11941               GXcopy);
11942             switch (RotateCommands[id])
11943             {
11944               case RotateColorCommand:
11945               {
11946                 const char
11947                   *ColorMenu[MaxNumberPens];
11948
11949                 int
11950                   pen_number;
11951
11952                 XColor
11953                   color;
11954
11955                 /*
11956                   Initialize menu selections.
11957                 */
11958                 for (i=0; i < (int) (MaxNumberPens-2); i++)
11959                   ColorMenu[i]=resource_info->pen_colors[i];
11960                 ColorMenu[MaxNumberPens-2]="Browser...";
11961                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
11962                 /*
11963                   Select a pen color from the pop-up menu.
11964                 */
11965                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
11966                   (const char **) ColorMenu,command);
11967                 if (pen_number < 0)
11968                   break;
11969                 if (pen_number == (MaxNumberPens-2))
11970                   {
11971                     static char
11972                       color_name[MaxTextExtent] = "gray";
11973
11974                     /*
11975                       Select a pen color from a dialog.
11976                     */
11977                     resource_info->pen_colors[pen_number]=color_name;
11978                     XColorBrowserWidget(display,windows,"Select",color_name);
11979                     if (*color_name == '\0')
11980                       break;
11981                   }
11982                 /*
11983                   Set pen color.
11984                 */
11985                 (void) XParseColor(display,windows->map_info->colormap,
11986                   resource_info->pen_colors[pen_number],&color);
11987                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
11988                   (unsigned int) MaxColors,&color);
11989                 windows->pixel_info->pen_colors[pen_number]=color;
11990                 pen_id=(unsigned int) pen_number;
11991                 break;
11992               }
11993               case RotateDirectionCommand:
11994               {
11995                 static const char
11996                   *Directions[] =
11997                   {
11998                     "horizontal",
11999                     "vertical",
12000                     (char *) NULL,
12001                   };
12002
12003                 /*
12004                   Select a command from the pop-up menu.
12005                 */
12006                 id=XMenuWidget(display,windows,RotateMenu[id],
12007                   Directions,command);
12008                 if (id >= 0)
12009                   direction=DirectionCommands[id];
12010                 break;
12011               }
12012               case RotateHelpCommand:
12013               {
12014                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12015                   "Help Viewer - Image Rotation",ImageRotateHelp);
12016                 break;
12017               }
12018               case RotateDismissCommand:
12019               {
12020                 /*
12021                   Prematurely exit.
12022                 */
12023                 state|=EscapeState;
12024                 state|=ExitState;
12025                 break;
12026               }
12027               default:
12028                 break;
12029             }
12030             (void) XSetFunction(display,windows->image.highlight_context,
12031               GXinvert);
12032             continue;
12033           }
12034         switch (event.type)
12035         {
12036           case ButtonPress:
12037           {
12038             if (event.xbutton.button != Button1)
12039               break;
12040             if (event.xbutton.window != windows->image.id)
12041               break;
12042             /*
12043               exit loop.
12044             */
12045             (void) XSetFunction(display,windows->image.highlight_context,
12046               GXcopy);
12047             rotate_info.x1=event.xbutton.x;
12048             rotate_info.y1=event.xbutton.y;
12049             state|=ExitState;
12050             break;
12051           }
12052           case ButtonRelease:
12053             break;
12054           case Expose:
12055             break;
12056           case KeyPress:
12057           {
12058             char
12059               command[MaxTextExtent];
12060
12061             KeySym
12062               key_symbol;
12063
12064             if (event.xkey.window != windows->image.id)
12065               break;
12066             /*
12067               Respond to a user key press.
12068             */
12069             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12070               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12071             switch ((int) key_symbol)
12072             {
12073               case XK_Escape:
12074               case XK_F20:
12075               {
12076                 /*
12077                   Prematurely exit.
12078                 */
12079                 state|=EscapeState;
12080                 state|=ExitState;
12081                 break;
12082               }
12083               case XK_F1:
12084               case XK_Help:
12085               {
12086                 (void) XSetFunction(display,windows->image.highlight_context,
12087                   GXcopy);
12088                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12089                   "Help Viewer - Image Rotation",ImageRotateHelp);
12090                 (void) XSetFunction(display,windows->image.highlight_context,
12091                   GXinvert);
12092                 break;
12093               }
12094               default:
12095               {
12096                 (void) XBell(display,0);
12097                 break;
12098               }
12099             }
12100             break;
12101           }
12102           case MotionNotify:
12103           {
12104             rotate_info.x1=event.xmotion.x;
12105             rotate_info.y1=event.xmotion.y;
12106           }
12107         }
12108         rotate_info.x2=rotate_info.x1;
12109         rotate_info.y2=rotate_info.y1;
12110         if (direction == HorizontalRotateCommand)
12111           rotate_info.x2+=32;
12112         else
12113           rotate_info.y2-=32;
12114       } while ((state & ExitState) == 0);
12115       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12116       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12117       if ((state & EscapeState) != 0)
12118         return(MagickTrue);
12119       /*
12120         Draw line as pointer moves until the mouse button is released.
12121       */
12122       distance=0;
12123       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12124       state=DefaultState;
12125       do
12126       {
12127         if (distance > 9)
12128           {
12129             /*
12130               Display info and draw rotation line.
12131             */
12132             if (windows->info.mapped == MagickFalse)
12133               (void) XMapWindow(display,windows->info.id);
12134             (void) FormatMagickString(text,MaxTextExtent," %g",
12135               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12136             XInfoWidget(display,windows,text);
12137             XHighlightLine(display,windows->image.id,
12138               windows->image.highlight_context,&rotate_info);
12139           }
12140         else
12141           if (windows->info.mapped != MagickFalse)
12142             (void) XWithdrawWindow(display,windows->info.id,
12143               windows->info.screen);
12144         /*
12145           Wait for next event.
12146         */
12147         XScreenEvent(display,windows,&event);
12148         if (distance > 9)
12149           XHighlightLine(display,windows->image.id,
12150             windows->image.highlight_context,&rotate_info);
12151         switch (event.type)
12152         {
12153           case ButtonPress:
12154             break;
12155           case ButtonRelease:
12156           {
12157             /*
12158               User has committed to rotation line.
12159             */
12160             rotate_info.x2=event.xbutton.x;
12161             rotate_info.y2=event.xbutton.y;
12162             state|=ExitState;
12163             break;
12164           }
12165           case Expose:
12166             break;
12167           case MotionNotify:
12168           {
12169             rotate_info.x2=event.xmotion.x;
12170             rotate_info.y2=event.xmotion.y;
12171           }
12172           default:
12173             break;
12174         }
12175         /*
12176           Check boundary conditions.
12177         */
12178         if (rotate_info.x2 < 0)
12179           rotate_info.x2=0;
12180         else
12181           if (rotate_info.x2 > (int) windows->image.width)
12182             rotate_info.x2=(short) windows->image.width;
12183         if (rotate_info.y2 < 0)
12184           rotate_info.y2=0;
12185         else
12186           if (rotate_info.y2 > (int) windows->image.height)
12187             rotate_info.y2=(short) windows->image.height;
12188         /*
12189           Compute rotation angle from the slope of the line.
12190         */
12191         degrees=0.0;
12192         distance=(unsigned int)
12193           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12194           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12195         if (distance > 9)
12196           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12197             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12198       } while ((state & ExitState) == 0);
12199       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12200       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12201       if (distance <= 9)
12202         return(MagickTrue);
12203     }
12204   if (direction == VerticalRotateCommand)
12205     degrees-=90.0;
12206   if (degrees == 0.0)
12207     return(MagickTrue);
12208   /*
12209     Rotate image.
12210   */
12211   normalized_degrees=degrees;
12212   while (normalized_degrees < -45.0)
12213     normalized_degrees+=360.0;
12214   for (rotations=0; normalized_degrees > 45.0; rotations++)
12215     normalized_degrees-=90.0;
12216   if (normalized_degrees != 0.0)
12217     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12218   XSetCursorState(display,windows,MagickTrue);
12219   XCheckRefreshWindows(display,windows);
12220   (*image)->background_color.red=ScaleShortToQuantum(
12221     windows->pixel_info->pen_colors[pen_id].red);
12222   (*image)->background_color.green=ScaleShortToQuantum(
12223     windows->pixel_info->pen_colors[pen_id].green);
12224   (*image)->background_color.blue=ScaleShortToQuantum(
12225     windows->pixel_info->pen_colors[pen_id].blue);
12226   rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12227   XSetCursorState(display,windows,MagickFalse);
12228   if (rotate_image == (Image *) NULL)
12229     return(MagickFalse);
12230   *image=DestroyImage(*image);
12231   *image=rotate_image;
12232   if (windows->image.crop_geometry != (char *) NULL)
12233     {
12234       /*
12235         Rotate crop geometry.
12236       */
12237       width=(unsigned int) (*image)->columns;
12238       height=(unsigned int) (*image)->rows;
12239       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12240       switch (rotations % 4)
12241       {
12242         default:
12243         case 0:
12244           break;
12245         case 1:
12246         {
12247           /*
12248             Rotate 90 degrees.
12249           */
12250           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12251             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12252             (int) height-y,x);
12253           break;
12254         }
12255         case 2:
12256         {
12257           /*
12258             Rotate 180 degrees.
12259           */
12260           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12261             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12262           break;
12263         }
12264         case 3:
12265         {
12266           /*
12267             Rotate 270 degrees.
12268           */
12269           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12270             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12271           break;
12272         }
12273       }
12274     }
12275   if (windows->image.orphan != MagickFalse)
12276     return(MagickTrue);
12277   if (normalized_degrees != 0.0)
12278     {
12279       /*
12280         Update image colormap.
12281       */
12282       windows->image.window_changes.width=(int) (*image)->columns;
12283       windows->image.window_changes.height=(int) (*image)->rows;
12284       if (windows->image.crop_geometry != (char *) NULL)
12285         {
12286           /*
12287             Obtain dimensions of image from crop geometry.
12288           */
12289           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12290             &width,&height);
12291           windows->image.window_changes.width=(int) width;
12292           windows->image.window_changes.height=(int) height;
12293         }
12294       XConfigureImageColormap(display,resource_info,windows,*image);
12295     }
12296   else
12297     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12298       {
12299         windows->image.window_changes.width=windows->image.ximage->height;
12300         windows->image.window_changes.height=windows->image.ximage->width;
12301       }
12302   /*
12303     Update image configuration.
12304   */
12305   (void) XConfigureImage(display,resource_info,windows,*image);
12306   return(MagickTrue);
12307 }
12308 \f
12309 /*
12310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12311 %                                                                             %
12312 %                                                                             %
12313 %                                                                             %
12314 +   X S a v e I m a g e                                                       %
12315 %                                                                             %
12316 %                                                                             %
12317 %                                                                             %
12318 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12319 %
12320 %  XSaveImage() saves an image to a file.
12321 %
12322 %  The format of the XSaveImage method is:
12323 %
12324 %      MagickBooleanType XSaveImage(Display *display,
12325 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
12326 %
12327 %  A description of each parameter follows:
12328 %
12329 %    o display: Specifies a connection to an X server; returned from
12330 %      XOpenDisplay.
12331 %
12332 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12333 %
12334 %    o windows: Specifies a pointer to a XWindows structure.
12335 %
12336 %    o image: the image.
12337 %
12338 */
12339 static MagickBooleanType XSaveImage(Display *display,
12340   XResourceInfo *resource_info,XWindows *windows,Image *image)
12341 {
12342   char
12343     filename[MaxTextExtent],
12344     geometry[MaxTextExtent];
12345
12346   Image
12347     *save_image;
12348
12349   ImageInfo
12350     *image_info;
12351
12352   MagickStatusType
12353     status;
12354
12355   /*
12356     Request file name from user.
12357   */
12358   if (resource_info->write_filename != (char *) NULL)
12359     (void) CopyMagickString(filename,resource_info->write_filename,
12360       MaxTextExtent);
12361   else
12362     {
12363       char
12364         path[MaxTextExtent];
12365
12366       int
12367         status;
12368
12369       GetPathComponent(image->filename,HeadPath,path);
12370       GetPathComponent(image->filename,TailPath,filename);
12371       status=chdir(path);
12372       if (status == -1)
12373         (void) ThrowMagickException(&image->exception,GetMagickModule(),
12374           FileOpenError,"UnableToOpenFile","%s",path);
12375     }
12376   XFileBrowserWidget(display,windows,"Save",filename);
12377   if (*filename == '\0')
12378     return(MagickTrue);
12379   if (IsPathAccessible(filename) != MagickFalse)
12380     {
12381       int
12382         status;
12383
12384       /*
12385         File exists-- seek user's permission before overwriting.
12386       */
12387       status=XConfirmWidget(display,windows,"Overwrite",filename);
12388       if (status <= 0)
12389         return(MagickTrue);
12390     }
12391   image_info=CloneImageInfo(resource_info->image_info);
12392   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12393   (void) SetImageInfo(image_info,1,&image->exception);
12394   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12395       (LocaleCompare(image_info->magick,"JPG") == 0))
12396     {
12397       char
12398         quality[MaxTextExtent];
12399
12400       int
12401         status;
12402
12403       /*
12404         Request JPEG quality from user.
12405       */
12406       (void) FormatMagickString(quality,MaxTextExtent,"%.20g",(double)
12407         image->quality);
12408       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12409         quality);
12410       if (*quality == '\0')
12411         return(MagickTrue);
12412       image->quality=StringToUnsignedLong(quality);
12413       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12414     }
12415   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12416       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12417       (LocaleCompare(image_info->magick,"PS") == 0) ||
12418       (LocaleCompare(image_info->magick,"PS2") == 0))
12419     {
12420       char
12421         geometry[MaxTextExtent];
12422
12423       /*
12424         Request page geometry from user.
12425       */
12426       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12427       if (LocaleCompare(image_info->magick,"PDF") == 0)
12428         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12429       if (image_info->page != (char *) NULL)
12430         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12431       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12432         "Select page geometry:",geometry);
12433       if (*geometry != '\0')
12434         image_info->page=GetPageGeometry(geometry);
12435     }
12436   /*
12437     Apply image transforms.
12438   */
12439   XSetCursorState(display,windows,MagickTrue);
12440   XCheckRefreshWindows(display,windows);
12441   save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12442   if (save_image == (Image *) NULL)
12443     return(MagickFalse);
12444   (void) FormatMagickString(geometry,MaxTextExtent,"%dx%d!",
12445     windows->image.ximage->width,windows->image.ximage->height);
12446   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12447   /*
12448     Write image.
12449   */
12450   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12451   status=WriteImage(image_info,save_image);
12452   if (status != MagickFalse)
12453     image->taint=MagickFalse;
12454   save_image=DestroyImage(save_image);
12455   image_info=DestroyImageInfo(image_info);
12456   XSetCursorState(display,windows,MagickFalse);
12457   return(status != 0 ? MagickTrue : MagickFalse);
12458 }
12459 \f
12460 /*
12461 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12462 %                                                                             %
12463 %                                                                             %
12464 %                                                                             %
12465 +   X S c r e e n E v e n t                                                   %
12466 %                                                                             %
12467 %                                                                             %
12468 %                                                                             %
12469 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12470 %
12471 %  XScreenEvent() handles global events associated with the Pan and Magnify
12472 %  windows.
12473 %
12474 %  The format of the XScreenEvent function is:
12475 %
12476 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12477 %
12478 %  A description of each parameter follows:
12479 %
12480 %    o display: Specifies a pointer to the Display structure;  returned from
12481 %      XOpenDisplay.
12482 %
12483 %    o windows: Specifies a pointer to a XWindows structure.
12484 %
12485 %    o event: Specifies a pointer to a X11 XEvent structure.
12486 %
12487 %
12488 */
12489
12490 #if defined(__cplusplus) || defined(c_plusplus)
12491 extern "C" {
12492 #endif
12493
12494 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12495 {
12496   register XWindows
12497     *windows;
12498
12499   windows=(XWindows *) data;
12500   if ((event->type == ClientMessage) &&
12501       (event->xclient.window == windows->image.id))
12502     return(MagickFalse);
12503   return(MagickTrue);
12504 }
12505
12506 #if defined(__cplusplus) || defined(c_plusplus)
12507 }
12508 #endif
12509
12510 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12511 {
12512   register int
12513     x,
12514     y;
12515
12516   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12517   if (event->xany.window == windows->command.id)
12518     return;
12519   switch (event->type)
12520   {
12521     case ButtonPress:
12522     case ButtonRelease:
12523     {
12524       if ((event->xbutton.button == Button3) &&
12525           (event->xbutton.state & Mod1Mask))
12526         {
12527           /*
12528             Convert Alt-Button3 to Button2.
12529           */
12530           event->xbutton.button=Button2;
12531           event->xbutton.state&=(~Mod1Mask);
12532         }
12533       if (event->xbutton.window == windows->backdrop.id)
12534         {
12535           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12536             event->xbutton.time);
12537           break;
12538         }
12539       if (event->xbutton.window == windows->pan.id)
12540         {
12541           XPanImage(display,windows,event);
12542           break;
12543         }
12544       if (event->xbutton.window == windows->image.id)
12545         if (event->xbutton.button == Button2)
12546           {
12547             /*
12548               Update magnified image.
12549             */
12550             x=event->xbutton.x;
12551             y=event->xbutton.y;
12552             if (x < 0)
12553               x=0;
12554             else
12555               if (x >= (int) windows->image.width)
12556                 x=(int) (windows->image.width-1);
12557             windows->magnify.x=windows->image.x+x;
12558             if (y < 0)
12559               y=0;
12560             else
12561              if (y >= (int) windows->image.height)
12562                y=(int) (windows->image.height-1);
12563             windows->magnify.y=windows->image.y+y;
12564             if (windows->magnify.mapped == MagickFalse)
12565               (void) XMapRaised(display,windows->magnify.id);
12566             XMakeMagnifyImage(display,windows);
12567             if (event->type == ButtonRelease)
12568               (void) XWithdrawWindow(display,windows->info.id,
12569                 windows->info.screen);
12570             break;
12571           }
12572       break;
12573     }
12574     case ClientMessage:
12575     {
12576       /*
12577         If client window delete message, exit.
12578       */
12579       if (event->xclient.message_type != windows->wm_protocols)
12580         break;
12581       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12582         break;
12583       if (event->xclient.window == windows->magnify.id)
12584         {
12585           (void) XWithdrawWindow(display,windows->magnify.id,
12586             windows->magnify.screen);
12587           break;
12588         }
12589       break;
12590     }
12591     case ConfigureNotify:
12592     {
12593       if (event->xconfigure.window == windows->magnify.id)
12594         {
12595           unsigned int
12596             magnify;
12597
12598           /*
12599             Magnify window has a new configuration.
12600           */
12601           windows->magnify.width=(unsigned int) event->xconfigure.width;
12602           windows->magnify.height=(unsigned int) event->xconfigure.height;
12603           if (windows->magnify.mapped == MagickFalse)
12604             break;
12605           magnify=1;
12606           while ((int) magnify <= event->xconfigure.width)
12607             magnify<<=1;
12608           while ((int) magnify <= event->xconfigure.height)
12609             magnify<<=1;
12610           magnify>>=1;
12611           if (((int) magnify != event->xconfigure.width) ||
12612               ((int) magnify != event->xconfigure.height))
12613             {
12614               XWindowChanges
12615                 window_changes;
12616
12617               window_changes.width=(int) magnify;
12618               window_changes.height=(int) magnify;
12619               (void) XReconfigureWMWindow(display,windows->magnify.id,
12620                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12621                 &window_changes);
12622               break;
12623             }
12624           XMakeMagnifyImage(display,windows);
12625           break;
12626         }
12627       break;
12628     }
12629     case Expose:
12630     {
12631       if (event->xexpose.window == windows->image.id)
12632         {
12633           XRefreshWindow(display,&windows->image,event);
12634           break;
12635         }
12636       if (event->xexpose.window == windows->pan.id)
12637         if (event->xexpose.count == 0)
12638           {
12639             XDrawPanRectangle(display,windows);
12640             break;
12641           }
12642       if (event->xexpose.window == windows->magnify.id)
12643         if (event->xexpose.count == 0)
12644           {
12645             XMakeMagnifyImage(display,windows);
12646             break;
12647           }
12648       break;
12649     }
12650     case KeyPress:
12651     {
12652       char
12653         command[MaxTextExtent];
12654
12655       KeySym
12656         key_symbol;
12657
12658       if (event->xkey.window != windows->magnify.id)
12659         break;
12660       /*
12661         Respond to a user key press.
12662       */
12663       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12664         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12665       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12666       break;
12667     }
12668     case MapNotify:
12669     {
12670       if (event->xmap.window == windows->magnify.id)
12671         {
12672           windows->magnify.mapped=MagickTrue;
12673           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12674           break;
12675         }
12676       if (event->xmap.window == windows->info.id)
12677         {
12678           windows->info.mapped=MagickTrue;
12679           break;
12680         }
12681       break;
12682     }
12683     case MotionNotify:
12684     {
12685       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12686       if (event->xmotion.window == windows->image.id)
12687         if (windows->magnify.mapped != MagickFalse)
12688           {
12689             /*
12690               Update magnified image.
12691             */
12692             x=event->xmotion.x;
12693             y=event->xmotion.y;
12694             if (x < 0)
12695               x=0;
12696             else
12697               if (x >= (int) windows->image.width)
12698                 x=(int) (windows->image.width-1);
12699             windows->magnify.x=windows->image.x+x;
12700             if (y < 0)
12701               y=0;
12702             else
12703              if (y >= (int) windows->image.height)
12704                y=(int) (windows->image.height-1);
12705             windows->magnify.y=windows->image.y+y;
12706             XMakeMagnifyImage(display,windows);
12707           }
12708       break;
12709     }
12710     case UnmapNotify:
12711     {
12712       if (event->xunmap.window == windows->magnify.id)
12713         {
12714           windows->magnify.mapped=MagickFalse;
12715           break;
12716         }
12717       if (event->xunmap.window == windows->info.id)
12718         {
12719           windows->info.mapped=MagickFalse;
12720           break;
12721         }
12722       break;
12723     }
12724     default:
12725       break;
12726   }
12727 }
12728 \f
12729 /*
12730 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12731 %                                                                             %
12732 %                                                                             %
12733 %                                                                             %
12734 +   X S e t C r o p G e o m e t r y                                           %
12735 %                                                                             %
12736 %                                                                             %
12737 %                                                                             %
12738 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12739 %
12740 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12741 %  and translates it to a cropping geometry relative to the image.
12742 %
12743 %  The format of the XSetCropGeometry method is:
12744 %
12745 %      void XSetCropGeometry(Display *display,XWindows *windows,
12746 %        RectangleInfo *crop_info,Image *image)
12747 %
12748 %  A description of each parameter follows:
12749 %
12750 %    o display: Specifies a connection to an X server; returned from
12751 %      XOpenDisplay.
12752 %
12753 %    o windows: Specifies a pointer to a XWindows structure.
12754 %
12755 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12756 %      Image window to crop.
12757 %
12758 %    o image: the image.
12759 %
12760 */
12761 static void XSetCropGeometry(Display *display,XWindows *windows,
12762   RectangleInfo *crop_info,Image *image)
12763 {
12764   char
12765     text[MaxTextExtent];
12766
12767   int
12768     x,
12769     y;
12770
12771   MagickRealType
12772     scale_factor;
12773
12774   unsigned int
12775     height,
12776     width;
12777
12778   if (windows->info.mapped != MagickFalse)
12779     {
12780       /*
12781         Display info on cropping rectangle.
12782       */
12783       (void) FormatMagickString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12784         (double) crop_info->width,(double) crop_info->height,(double)
12785         crop_info->x,(double) crop_info->y);
12786       XInfoWidget(display,windows,text);
12787     }
12788   /*
12789     Cropping geometry is relative to any previous crop geometry.
12790   */
12791   x=0;
12792   y=0;
12793   width=(unsigned int) image->columns;
12794   height=(unsigned int) image->rows;
12795   if (windows->image.crop_geometry != (char *) NULL)
12796     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12797   else
12798     windows->image.crop_geometry=AcquireString((char *) NULL);
12799   /*
12800     Define the crop geometry string from the cropping rectangle.
12801   */
12802   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12803   if (crop_info->x > 0)
12804     x+=(int) (scale_factor*crop_info->x+0.5);
12805   width=(unsigned int) (scale_factor*crop_info->width+0.5);
12806   if (width == 0)
12807     width=1;
12808   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12809   if (crop_info->y > 0)
12810     y+=(int) (scale_factor*crop_info->y+0.5);
12811   height=(unsigned int) (scale_factor*crop_info->height+0.5);
12812   if (height == 0)
12813     height=1;
12814   (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12815     "%ux%u%+d%+d",width,height,x,y);
12816 }
12817 \f
12818 /*
12819 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12820 %                                                                             %
12821 %                                                                             %
12822 %                                                                             %
12823 +   X T i l e I m a g e                                                       %
12824 %                                                                             %
12825 %                                                                             %
12826 %                                                                             %
12827 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12828 %
12829 %  XTileImage() loads or deletes a selected tile from a visual image directory.
12830 %  The load or delete command is chosen from a menu.
12831 %
12832 %  The format of the XTileImage method is:
12833 %
12834 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
12835 %        XWindows *windows,Image *image,XEvent *event)
12836 %
12837 %  A description of each parameter follows:
12838 %
12839 %    o tile_image:  XTileImage reads or deletes the tile image
12840 %      and returns it.  A null image is returned if an error occurs.
12841 %
12842 %    o display: Specifies a connection to an X server;  returned from
12843 %      XOpenDisplay.
12844 %
12845 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12846 %
12847 %    o windows: Specifies a pointer to a XWindows structure.
12848 %
12849 %    o image: the image; returned from ReadImage.
12850 %
12851 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
12852 %      the entire image is refreshed.
12853 %
12854 */
12855 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12856   XWindows *windows,Image *image,XEvent *event)
12857 {
12858   static const char
12859     *VerbMenu[] =
12860     {
12861       "Load",
12862       "Next",
12863       "Former",
12864       "Delete",
12865       "Update",
12866       (char *) NULL,
12867     };
12868
12869   static const ModeType
12870     TileCommands[] =
12871     {
12872       TileLoadCommand,
12873       TileNextCommand,
12874       TileFormerCommand,
12875       TileDeleteCommand,
12876       TileUpdateCommand
12877     };
12878
12879   char
12880     command[MaxTextExtent],
12881     filename[MaxTextExtent];
12882
12883   Image
12884     *tile_image;
12885
12886   int
12887     id,
12888     status,
12889     tile,
12890     x,
12891     y;
12892
12893   MagickRealType
12894     scale_factor;
12895
12896   register char
12897     *p,
12898     *q;
12899
12900   register int
12901     i;
12902
12903   unsigned int
12904     height,
12905     width;
12906
12907   /*
12908     Tile image is relative to montage image configuration.
12909   */
12910   x=0;
12911   y=0;
12912   width=(unsigned int) image->columns;
12913   height=(unsigned int) image->rows;
12914   if (windows->image.crop_geometry != (char *) NULL)
12915     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12916   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12917   event->xbutton.x+=windows->image.x;
12918   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
12919   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12920   event->xbutton.y+=windows->image.y;
12921   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
12922   /*
12923     Determine size and location of each tile in the visual image directory.
12924   */
12925   width=(unsigned int) image->columns;
12926   height=(unsigned int) image->rows;
12927   x=0;
12928   y=0;
12929   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
12930   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
12931     (event->xbutton.x-x)/width;
12932   if (tile < 0)
12933     {
12934       /*
12935         Button press is outside any tile.
12936       */
12937       (void) XBell(display,0);
12938       return((Image *) NULL);
12939     }
12940   /*
12941     Determine file name from the tile directory.
12942   */
12943   p=image->directory;
12944   for (i=tile; (i != 0) && (*p != '\0'); )
12945   {
12946     if (*p == '\n')
12947       i--;
12948     p++;
12949   }
12950   if (*p == '\0')
12951     {
12952       /*
12953         Button press is outside any tile.
12954       */
12955       (void) XBell(display,0);
12956       return((Image *) NULL);
12957     }
12958   /*
12959     Select a command from the pop-up menu.
12960   */
12961   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
12962   if (id < 0)
12963     return((Image *) NULL);
12964   q=p;
12965   while ((*q != '\n') && (*q != '\0'))
12966     q++;
12967   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
12968   /*
12969     Perform command for the selected tile.
12970   */
12971   XSetCursorState(display,windows,MagickTrue);
12972   XCheckRefreshWindows(display,windows);
12973   tile_image=NewImageList();
12974   switch (TileCommands[id])
12975   {
12976     case TileLoadCommand:
12977     {
12978       /*
12979         Load tile image.
12980       */
12981       XCheckRefreshWindows(display,windows);
12982       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
12983         MaxTextExtent);
12984       (void) CopyMagickString(resource_info->image_info->filename,filename,
12985         MaxTextExtent);
12986       tile_image=ReadImage(resource_info->image_info,&image->exception);
12987       CatchException(&image->exception);
12988       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12989       break;
12990     }
12991     case TileNextCommand:
12992     {
12993       /*
12994         Display next image.
12995       */
12996       XClientMessage(display,windows->image.id,windows->im_protocols,
12997         windows->im_next_image,CurrentTime);
12998       break;
12999     }
13000     case TileFormerCommand:
13001     {
13002       /*
13003         Display former image.
13004       */
13005       XClientMessage(display,windows->image.id,windows->im_protocols,
13006         windows->im_former_image,CurrentTime);
13007       break;
13008     }
13009     case TileDeleteCommand:
13010     {
13011       /*
13012         Delete tile image.
13013       */
13014       if (IsPathAccessible(filename) == MagickFalse)
13015         {
13016           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13017           break;
13018         }
13019       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13020       if (status <= 0)
13021         break;
13022       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13023       if (status != MagickFalse)
13024         {
13025           XNoticeWidget(display,windows,"Unable to delete image file:",
13026             filename);
13027           break;
13028         }
13029     }
13030     case TileUpdateCommand:
13031     {
13032       ExceptionInfo
13033         *exception;
13034
13035       int
13036         x_offset,
13037         y_offset;
13038
13039       PixelPacket
13040         pixel;
13041
13042       register int
13043         j;
13044
13045       register PixelPacket
13046         *s;
13047
13048       /*
13049         Ensure all the images exist.
13050       */
13051       tile=0;
13052       for (p=image->directory; *p != '\0'; p++)
13053       {
13054         q=p;
13055         while ((*q != '\n') && (*q != '\0'))
13056           q++;
13057         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13058         p=q;
13059         if (IsPathAccessible(filename) != MagickFalse)
13060           {
13061             tile++;
13062             continue;
13063           }
13064         /*
13065           Overwrite tile with background color.
13066         */
13067         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13068         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13069         exception=(&image->exception);
13070         (void) GetOneVirtualPixel(image,0,0,&pixel,exception);
13071         for (i=0; i < (int) height; i++)
13072         {
13073           s=GetAuthenticPixels(image,x_offset,y_offset+i,width,1,exception);
13074           if (s == (PixelPacket *) NULL)
13075             break;
13076           for (j=0; j < (int) width; j++)
13077             *s++=pixel;
13078           if (SyncAuthenticPixels(image,exception) == MagickFalse)
13079             break;
13080         }
13081         tile++;
13082       }
13083       windows->image.window_changes.width=(int) image->columns;
13084       windows->image.window_changes.height=(int) image->rows;
13085       XConfigureImageColormap(display,resource_info,windows,image);
13086       (void) XConfigureImage(display,resource_info,windows,image);
13087       break;
13088     }
13089     default:
13090       break;
13091   }
13092   XSetCursorState(display,windows,MagickFalse);
13093   return(tile_image);
13094 }
13095 \f
13096 /*
13097 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13098 %                                                                             %
13099 %                                                                             %
13100 %                                                                             %
13101 +   X T r a n s l a t e I m a g e                                             %
13102 %                                                                             %
13103 %                                                                             %
13104 %                                                                             %
13105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13106 %
13107 %  XTranslateImage() translates the image within an Image window by one pixel
13108 %  as specified by the key symbol.  If the image has a `montage string the
13109 %  translation is respect to the width and height contained within the string.
13110 %
13111 %  The format of the XTranslateImage method is:
13112 %
13113 %      void XTranslateImage(Display *display,XWindows *windows,
13114 %        Image *image,const KeySym key_symbol)
13115 %
13116 %  A description of each parameter follows:
13117 %
13118 %    o display: Specifies a connection to an X server; returned from
13119 %      XOpenDisplay.
13120 %
13121 %    o windows: Specifies a pointer to a XWindows structure.
13122 %
13123 %    o image: the image.
13124 %
13125 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13126 %      to trim.
13127 %
13128 */
13129 static void XTranslateImage(Display *display,XWindows *windows,
13130   Image *image,const KeySym key_symbol)
13131 {
13132   char
13133     text[MaxTextExtent];
13134
13135   int
13136     x,
13137     y;
13138
13139   unsigned int
13140     x_offset,
13141     y_offset;
13142
13143   /*
13144     User specified a pan position offset.
13145   */
13146   x_offset=windows->image.width;
13147   y_offset=windows->image.height;
13148   if (image->montage != (char *) NULL)
13149     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13150   switch ((int) key_symbol)
13151   {
13152     case XK_Home:
13153     case XK_KP_Home:
13154     {
13155       windows->image.x=(int) windows->image.width/2;
13156       windows->image.y=(int) windows->image.height/2;
13157       break;
13158     }
13159     case XK_Left:
13160     case XK_KP_Left:
13161     {
13162       windows->image.x-=x_offset;
13163       break;
13164     }
13165     case XK_Next:
13166     case XK_Up:
13167     case XK_KP_Up:
13168     {
13169       windows->image.y-=y_offset;
13170       break;
13171     }
13172     case XK_Right:
13173     case XK_KP_Right:
13174     {
13175       windows->image.x+=x_offset;
13176       break;
13177     }
13178     case XK_Prior:
13179     case XK_Down:
13180     case XK_KP_Down:
13181     {
13182       windows->image.y+=y_offset;
13183       break;
13184     }
13185     default:
13186       return;
13187   }
13188   /*
13189     Check boundary conditions.
13190   */
13191   if (windows->image.x < 0)
13192     windows->image.x=0;
13193   else
13194     if ((int) (windows->image.x+windows->image.width) >
13195         windows->image.ximage->width)
13196       windows->image.x=windows->image.ximage->width-windows->image.width;
13197   if (windows->image.y < 0)
13198     windows->image.y=0;
13199   else
13200     if ((int) (windows->image.y+windows->image.height) >
13201         windows->image.ximage->height)
13202       windows->image.y=windows->image.ximage->height-windows->image.height;
13203   /*
13204     Refresh Image window.
13205   */
13206   (void) FormatMagickString(text,MaxTextExtent," %ux%u%+d%+d ",
13207     windows->image.width,windows->image.height,windows->image.x,
13208     windows->image.y);
13209   XInfoWidget(display,windows,text);
13210   XCheckRefreshWindows(display,windows);
13211   XDrawPanRectangle(display,windows);
13212   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13213   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13214 }
13215 \f
13216 /*
13217 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13218 %                                                                             %
13219 %                                                                             %
13220 %                                                                             %
13221 +   X T r i m I m a g e                                                       %
13222 %                                                                             %
13223 %                                                                             %
13224 %                                                                             %
13225 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13226 %
13227 %  XTrimImage() trims the edges from the Image window.
13228 %
13229 %  The format of the XTrimImage method is:
13230 %
13231 %      MagickBooleanType XTrimImage(Display *display,
13232 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
13233 %
13234 %  A description of each parameter follows:
13235 %
13236 %    o display: Specifies a connection to an X server; returned from
13237 %      XOpenDisplay.
13238 %
13239 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13240 %
13241 %    o windows: Specifies a pointer to a XWindows structure.
13242 %
13243 %    o image: the image.
13244 %
13245 */
13246 static MagickBooleanType XTrimImage(Display *display,
13247   XResourceInfo *resource_info,XWindows *windows,Image *image)
13248 {
13249   RectangleInfo
13250     trim_info;
13251
13252   register int
13253     x,
13254     y;
13255
13256   size_t
13257     background,
13258     pixel;
13259
13260   /*
13261     Trim edges from image.
13262   */
13263   XSetCursorState(display,windows,MagickTrue);
13264   XCheckRefreshWindows(display,windows);
13265   /*
13266     Crop the left edge.
13267   */
13268   background=XGetPixel(windows->image.ximage,0,0);
13269   trim_info.width=(size_t) windows->image.ximage->width;
13270   for (x=0; x < windows->image.ximage->width; x++)
13271   {
13272     for (y=0; y < windows->image.ximage->height; y++)
13273     {
13274       pixel=XGetPixel(windows->image.ximage,x,y);
13275       if (pixel != background)
13276         break;
13277     }
13278     if (y < windows->image.ximage->height)
13279       break;
13280   }
13281   trim_info.x=x;
13282   if (trim_info.x == (int) windows->image.ximage->width)
13283     {
13284       XSetCursorState(display,windows,MagickFalse);
13285       return(MagickFalse);
13286     }
13287   /*
13288     Crop the right edge.
13289   */
13290   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13291   for (x=windows->image.ximage->width-1; x != 0; x--)
13292   {
13293     for (y=0; y < windows->image.ximage->height; y++)
13294     {
13295       pixel=XGetPixel(windows->image.ximage,x,y);
13296       if (pixel != background)
13297         break;
13298     }
13299     if (y < windows->image.ximage->height)
13300       break;
13301   }
13302   trim_info.width=(size_t) (x-trim_info.x+1);
13303   /*
13304     Crop the top edge.
13305   */
13306   background=XGetPixel(windows->image.ximage,0,0);
13307   trim_info.height=(size_t) windows->image.ximage->height;
13308   for (y=0; y < windows->image.ximage->height; y++)
13309   {
13310     for (x=0; x < windows->image.ximage->width; x++)
13311     {
13312       pixel=XGetPixel(windows->image.ximage,x,y);
13313       if (pixel != background)
13314         break;
13315     }
13316     if (x < windows->image.ximage->width)
13317       break;
13318   }
13319   trim_info.y=y;
13320   /*
13321     Crop the bottom edge.
13322   */
13323   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13324   for (y=windows->image.ximage->height-1; y != 0; y--)
13325   {
13326     for (x=0; x < windows->image.ximage->width; x++)
13327     {
13328       pixel=XGetPixel(windows->image.ximage,x,y);
13329       if (pixel != background)
13330         break;
13331     }
13332     if (x < windows->image.ximage->width)
13333       break;
13334   }
13335   trim_info.height=(size_t) y-trim_info.y+1;
13336   if (((unsigned int) trim_info.width != windows->image.width) ||
13337       ((unsigned int) trim_info.height != windows->image.height))
13338     {
13339       /*
13340         Reconfigure Image window as defined by the trimming rectangle.
13341       */
13342       XSetCropGeometry(display,windows,&trim_info,image);
13343       windows->image.window_changes.width=(int) trim_info.width;
13344       windows->image.window_changes.height=(int) trim_info.height;
13345       (void) XConfigureImage(display,resource_info,windows,image);
13346     }
13347   XSetCursorState(display,windows,MagickFalse);
13348   return(MagickTrue);
13349 }
13350 \f
13351 /*
13352 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13353 %                                                                             %
13354 %                                                                             %
13355 %                                                                             %
13356 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13357 %                                                                             %
13358 %                                                                             %
13359 %                                                                             %
13360 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13361 %
13362 %  XVisualDirectoryImage() creates a Visual Image Directory.
13363 %
13364 %  The format of the XVisualDirectoryImage method is:
13365 %
13366 %      Image *XVisualDirectoryImage(Display *display,
13367 %        XResourceInfo *resource_info,XWindows *windows)
13368 %
13369 %  A description of each parameter follows:
13370 %
13371 %    o nexus: Method XVisualDirectoryImage returns a visual image
13372 %      directory if it can be created successfully.  Otherwise a null image
13373 %      is returned.
13374 %
13375 %    o display: Specifies a connection to an X server; returned from
13376 %      XOpenDisplay.
13377 %
13378 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13379 %
13380 %    o windows: Specifies a pointer to a XWindows structure.
13381 %
13382 */
13383 static Image *XVisualDirectoryImage(Display *display,
13384   XResourceInfo *resource_info,XWindows *windows)
13385 {
13386 #define TileImageTag  "Scale/Image"
13387 #define XClientName  "montage"
13388
13389   char
13390     **filelist;
13391
13392   ExceptionInfo
13393     *exception;
13394
13395   Image
13396     *images,
13397     *montage_image,
13398     *next_image,
13399     *thumbnail_image;
13400
13401   ImageInfo
13402     *read_info;
13403
13404   int
13405     number_files;
13406
13407   MagickBooleanType
13408     backdrop;
13409
13410   MagickStatusType
13411     status;
13412
13413   MontageInfo
13414     *montage_info;
13415
13416   RectangleInfo
13417     geometry;
13418
13419   register int
13420     i;
13421
13422   static char
13423     filename[MaxTextExtent] = "\0",
13424     filenames[MaxTextExtent] = "*";
13425
13426   XResourceInfo
13427     background_resources;
13428
13429   /*
13430     Request file name from user.
13431   */
13432   XFileBrowserWidget(display,windows,"Directory",filenames);
13433   if (*filenames == '\0')
13434     return((Image *) NULL);
13435   /*
13436     Expand the filenames.
13437   */
13438   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13439   if (filelist == (char **) NULL)
13440     {
13441       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13442         filenames);
13443       return((Image *) NULL);
13444     }
13445   number_files=1;
13446   filelist[0]=filenames;
13447   status=ExpandFilenames(&number_files,&filelist);
13448   if ((status == MagickFalse) || (number_files == 0))
13449     {
13450       if (number_files == 0)
13451         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13452       else
13453         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13454           filenames);
13455       return((Image *) NULL);
13456     }
13457   /*
13458     Set image background resources.
13459   */
13460   background_resources=(*resource_info);
13461   background_resources.window_id=AcquireString("");
13462   (void) FormatMagickString(background_resources.window_id,MaxTextExtent,
13463     "0x%lx",windows->image.id);
13464   background_resources.backdrop=MagickTrue;
13465   /*
13466     Read each image and convert them to a tile.
13467   */
13468   backdrop=(windows->visual_info->klass == TrueColor) ||
13469     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13470   read_info=CloneImageInfo(resource_info->image_info);
13471   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13472     (void *) NULL);
13473   images=NewImageList();
13474   exception=AcquireExceptionInfo();
13475   XSetCursorState(display,windows,MagickTrue);
13476   XCheckRefreshWindows(display,windows);
13477   for (i=0; i < (ssize_t) number_files; i++)
13478   {
13479     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13480     filelist[i]=DestroyString(filelist[i]);
13481     *read_info->magick='\0';
13482     (void) CloneString(&read_info->size,DefaultTileGeometry);
13483     next_image=ReadImage(read_info,exception);
13484     CatchException(exception);
13485     if (next_image != (Image *) NULL)
13486       {
13487         (void) DeleteImageProperty(next_image,"label");
13488         (void) SetImageProperty(next_image,"label",DefaultTileLabel);
13489         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13490           exception);
13491         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13492           geometry.height,exception);
13493         if (thumbnail_image != (Image *) NULL)
13494           {
13495             next_image=DestroyImage(next_image);
13496             next_image=thumbnail_image;
13497           }
13498         if (backdrop)
13499           {
13500             (void) XDisplayBackgroundImage(display,&background_resources,
13501               next_image);
13502             XSetCursorState(display,windows,MagickTrue);
13503           }
13504         AppendImageToList(&images,next_image);
13505         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13506           {
13507             MagickBooleanType
13508               proceed;
13509
13510             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13511               (MagickSizeType) number_files);
13512             if (proceed == MagickFalse)
13513               break;
13514           }
13515       }
13516   }
13517   exception=DestroyExceptionInfo(exception);
13518   filelist=(char **) RelinquishMagickMemory(filelist);
13519   read_info=DestroyImageInfo(read_info);
13520   if (images == (Image *) NULL)
13521     {
13522       XSetCursorState(display,windows,MagickFalse);
13523       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13524       return((Image *) NULL);
13525     }
13526   /*
13527     Create the Visual Image Directory.
13528   */
13529   montage_info=CloneMontageInfo(resource_info->image_info,(MontageInfo *) NULL);
13530   if (resource_info->font != (char *) NULL)
13531     (void) CloneString(&montage_info->font,resource_info->font);
13532   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13533   montage_image=MontageImageList(resource_info->image_info,montage_info,
13534     GetFirstImageInList(images),&images->exception);
13535   montage_info=DestroyMontageInfo(montage_info);
13536   images=DestroyImageList(images);
13537   XSetCursorState(display,windows,MagickFalse);
13538   if (montage_image == (Image *) NULL)
13539     return(montage_image);
13540   XClientMessage(display,windows->image.id,windows->im_protocols,
13541     windows->im_next_image,CurrentTime);
13542   return(montage_image);
13543 }
13544 \f
13545 /*
13546 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13547 %                                                                             %
13548 %                                                                             %
13549 %                                                                             %
13550 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13551 %                                                                             %
13552 %                                                                             %
13553 %                                                                             %
13554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13555 %
13556 %  XDisplayBackgroundImage() displays an image in the background of a window.
13557 %
13558 %  The format of the XDisplayBackgroundImage method is:
13559 %
13560 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13561 %        XResourceInfo *resource_info,Image *image)
13562 %
13563 %  A description of each parameter follows:
13564 %
13565 %    o display: Specifies a connection to an X server;  returned from
13566 %      XOpenDisplay.
13567 %
13568 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13569 %
13570 %    o image: the image.
13571 %
13572 */
13573 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13574   XResourceInfo *resource_info,Image *image)
13575 {
13576   char
13577     geometry[MaxTextExtent],
13578     visual_type[MaxTextExtent];
13579
13580   int
13581     height,
13582     status,
13583     width;
13584
13585   RectangleInfo
13586     geometry_info;
13587
13588   static XPixelInfo
13589     pixel;
13590
13591   static XStandardColormap
13592     *map_info;
13593
13594   static XVisualInfo
13595     *visual_info = (XVisualInfo *) NULL;
13596
13597   static XWindowInfo
13598     window_info;
13599
13600   size_t
13601     delay;
13602
13603   Window
13604     root_window;
13605
13606   XGCValues
13607     context_values;
13608
13609   XResourceInfo
13610     resources;
13611
13612   XWindowAttributes
13613     window_attributes;
13614
13615   /*
13616     Determine target window.
13617   */
13618   assert(image != (Image *) NULL);
13619   assert(image->signature == MagickSignature);
13620   if (image->debug != MagickFalse)
13621     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13622   resources=(*resource_info);
13623   window_info.id=(Window) NULL;
13624   root_window=XRootWindow(display,XDefaultScreen(display));
13625   if (LocaleCompare(resources.window_id,"root") == 0)
13626     window_info.id=root_window;
13627   else
13628     {
13629       if (isdigit((unsigned char) *resources.window_id) != 0)
13630         window_info.id=XWindowByID(display,root_window,
13631           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13632       if (window_info.id == (Window) NULL)
13633         window_info.id=XWindowByName(display,root_window,resources.window_id);
13634     }
13635   if (window_info.id == (Window) NULL)
13636     {
13637       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13638         resources.window_id);
13639       return(MagickFalse);
13640     }
13641   /*
13642     Determine window visual id.
13643   */
13644   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13645   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13646   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13647   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13648   if (status != 0)
13649     (void) FormatMagickString(visual_type,MaxTextExtent,"0x%lx",
13650       XVisualIDFromVisual(window_attributes.visual));
13651   if (visual_info == (XVisualInfo *) NULL)
13652     {
13653       /*
13654         Allocate standard colormap.
13655       */
13656       map_info=XAllocStandardColormap();
13657       if (map_info == (XStandardColormap *) NULL)
13658         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13659           image->filename);
13660       map_info->colormap=(Colormap) NULL;
13661       pixel.pixels=(unsigned long *) NULL;
13662       /*
13663         Initialize visual info.
13664       */
13665       resources.map_type=(char *) NULL;
13666       resources.visual_type=visual_type;
13667       visual_info=XBestVisualInfo(display,map_info,&resources);
13668       if (visual_info == (XVisualInfo *) NULL)
13669         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13670           resources.visual_type);
13671       /*
13672         Initialize window info.
13673       */
13674       window_info.ximage=(XImage *) NULL;
13675       window_info.matte_image=(XImage *) NULL;
13676       window_info.pixmap=(Pixmap) NULL;
13677       window_info.matte_pixmap=(Pixmap) NULL;
13678     }
13679   /*
13680     Free previous root colors.
13681   */
13682   if (window_info.id == root_window)
13683     (void) XDestroyWindowColors(display,root_window);
13684   /*
13685     Initialize Standard Colormap.
13686   */
13687   resources.colormap=SharedColormap;
13688   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13689   /*
13690     Graphic context superclass.
13691   */
13692   context_values.background=pixel.background_color.pixel;
13693   context_values.foreground=pixel.foreground_color.pixel;
13694   pixel.annotate_context=XCreateGC(display,window_info.id,
13695     (size_t) (GCBackground | GCForeground),&context_values);
13696   if (pixel.annotate_context == (GC) NULL)
13697     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13698       image->filename);
13699   /*
13700     Initialize Image window attributes.
13701   */
13702   window_info.name=AcquireString("\0");
13703   window_info.icon_name=AcquireString("\0");
13704   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13705     &resources,&window_info);
13706   /*
13707     Create the X image.
13708   */
13709   window_info.width=(unsigned int) image->columns;
13710   window_info.height=(unsigned int) image->rows;
13711   if ((image->columns != window_info.width) ||
13712       (image->rows != window_info.height))
13713     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13714       image->filename);
13715   (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>",
13716     window_attributes.width,window_attributes.height);
13717   geometry_info.width=window_info.width;
13718   geometry_info.height=window_info.height;
13719   geometry_info.x=(ssize_t) window_info.x;
13720   geometry_info.y=(ssize_t) window_info.y;
13721   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13722     &geometry_info.width,&geometry_info.height);
13723   window_info.width=(unsigned int) geometry_info.width;
13724   window_info.height=(unsigned int) geometry_info.height;
13725   window_info.x=(int) geometry_info.x;
13726   window_info.y=(int) geometry_info.y;
13727   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13728     window_info.height);
13729   if (status == MagickFalse)
13730     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13731       image->filename);
13732   window_info.x=0;
13733   window_info.y=0;
13734   if (image->debug != MagickFalse)
13735     {
13736       (void) LogMagickEvent(X11Event,GetMagickModule(),
13737         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13738         (double) image->columns,(double) image->rows);
13739       if (image->colors != 0)
13740         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13741           image->colors);
13742       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13743     }
13744   /*
13745     Adjust image dimensions as specified by backdrop or geometry options.
13746   */
13747   width=(int) window_info.width;
13748   height=(int) window_info.height;
13749   if (resources.backdrop != MagickFalse)
13750     {
13751       /*
13752         Center image on window.
13753       */
13754       window_info.x=(window_attributes.width/2)-
13755         (window_info.ximage->width/2);
13756       window_info.y=(window_attributes.height/2)-
13757         (window_info.ximage->height/2);
13758       width=window_attributes.width;
13759       height=window_attributes.height;
13760     }
13761   if ((resources.image_geometry != (char *) NULL) &&
13762       (*resources.image_geometry != '\0'))
13763     {
13764       char
13765         default_geometry[MaxTextExtent];
13766
13767       int
13768         flags,
13769         gravity;
13770
13771       XSizeHints
13772         *size_hints;
13773
13774       /*
13775         User specified geometry.
13776       */
13777       size_hints=XAllocSizeHints();
13778       if (size_hints == (XSizeHints *) NULL)
13779         ThrowXWindowFatalException(ResourceLimitFatalError,
13780           "MemoryAllocationFailed",image->filename);
13781       size_hints->flags=0L;
13782       (void) FormatMagickString(default_geometry,MaxTextExtent,"%dx%d",
13783         width,height);
13784       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13785         default_geometry,window_info.border_width,size_hints,&window_info.x,
13786         &window_info.y,&width,&height,&gravity);
13787       if (flags & (XValue | YValue))
13788         {
13789           width=window_attributes.width;
13790           height=window_attributes.height;
13791         }
13792       (void) XFree((void *) size_hints);
13793     }
13794   /*
13795     Create the X pixmap.
13796   */
13797   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13798     (unsigned int) height,window_info.depth);
13799   if (window_info.pixmap == (Pixmap) NULL)
13800     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13801       image->filename);
13802   /*
13803     Display pixmap on the window.
13804   */
13805   if (((unsigned int) width > window_info.width) ||
13806       ((unsigned int) height > window_info.height))
13807     (void) XFillRectangle(display,window_info.pixmap,
13808       window_info.annotate_context,0,0,(unsigned int) width,
13809       (unsigned int) height);
13810   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13811     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13812     window_info.width,(unsigned int) window_info.height);
13813   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13814   (void) XClearWindow(display,window_info.id);
13815   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13816   XDelay(display,delay == 0UL ? 10UL : delay);
13817   (void) XSync(display,MagickFalse);
13818   return(window_info.id == root_window ? MagickTrue : MagickFalse);
13819 }
13820 \f
13821 /*
13822 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13823 %                                                                             %
13824 %                                                                             %
13825 %                                                                             %
13826 +   X D i s p l a y I m a g e                                                 %
13827 %                                                                             %
13828 %                                                                             %
13829 %                                                                             %
13830 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13831 %
13832 %  XDisplayImage() displays an image via X11.  A new image is created and
13833 %  returned if the user interactively transforms the displayed image.
13834 %
13835 %  The format of the XDisplayImage method is:
13836 %
13837 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13838 %        char **argv,int argc,Image **image,size_t *state)
13839 %
13840 %  A description of each parameter follows:
13841 %
13842 %    o nexus:  Method XDisplayImage returns an image when the
13843 %      user chooses 'Open Image' from the command menu or picks a tile
13844 %      from the image directory.  Otherwise a null image is returned.
13845 %
13846 %    o display: Specifies a connection to an X server;  returned from
13847 %      XOpenDisplay.
13848 %
13849 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13850 %
13851 %    o argv: Specifies the application's argument list.
13852 %
13853 %    o argc: Specifies the number of arguments.
13854 %
13855 %    o image: Specifies an address to an address of an Image structure;
13856 %
13857 */
13858 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13859   char **argv,int argc,Image **image,size_t *state)
13860 {
13861 #define MagnifySize  256  /* must be a power of 2 */
13862 #define MagickMenus  10
13863 #define MagickTitle  "Commands"
13864
13865   static const char
13866     *CommandMenu[] =
13867     {
13868       "File",
13869       "Edit",
13870       "View",
13871       "Transform",
13872       "Enhance",
13873       "Effects",
13874       "F/X",
13875       "Image Edit",
13876       "Miscellany",
13877       "Help",
13878       (char *) NULL
13879     },
13880     *FileMenu[] =
13881     {
13882       "Open...",
13883       "Next",
13884       "Former",
13885       "Select...",
13886       "Save...",
13887       "Print...",
13888       "Delete...",
13889       "New...",
13890       "Visual Directory...",
13891       "Quit",
13892       (char *) NULL
13893     },
13894     *EditMenu[] =
13895     {
13896       "Undo",
13897       "Redo",
13898       "Cut",
13899       "Copy",
13900       "Paste",
13901       (char *) NULL
13902     },
13903     *ViewMenu[] =
13904     {
13905       "Half Size",
13906       "Original Size",
13907       "Double Size",
13908       "Resize...",
13909       "Apply",
13910       "Refresh",
13911       "Restore",
13912       (char *) NULL
13913     },
13914     *TransformMenu[] =
13915     {
13916       "Crop",
13917       "Chop",
13918       "Flop",
13919       "Flip",
13920       "Rotate Right",
13921       "Rotate Left",
13922       "Rotate...",
13923       "Shear...",
13924       "Roll...",
13925       "Trim Edges",
13926       (char *) NULL
13927     },
13928     *EnhanceMenu[] =
13929     {
13930       "Hue...",
13931       "Saturation...",
13932       "Brightness...",
13933       "Gamma...",
13934       "Spiff",
13935       "Dull",
13936       "Contrast Stretch...",
13937       "Sigmoidal Contrast...",
13938       "Normalize",
13939       "Equalize",
13940       "Negate",
13941       "Grayscale",
13942       "Map...",
13943       "Quantize...",
13944       (char *) NULL
13945     },
13946     *EffectsMenu[] =
13947     {
13948       "Despeckle",
13949       "Emboss",
13950       "Reduce Noise",
13951       "Add Noise...",
13952       "Sharpen...",
13953       "Blur...",
13954       "Threshold...",
13955       "Edge Detect...",
13956       "Spread...",
13957       "Shade...",
13958       "Raise...",
13959       "Segment...",
13960       (char *) NULL
13961     },
13962     *FXMenu[] =
13963     {
13964       "Solarize...",
13965       "Sepia Tone...",
13966       "Swirl...",
13967       "Implode...",
13968       "Vignette...",
13969       "Wave...",
13970       "Oil Paint...",
13971       "Charcoal Draw...",
13972       (char *) NULL
13973     },
13974     *ImageEditMenu[] =
13975     {
13976       "Annotate...",
13977       "Draw...",
13978       "Color...",
13979       "Matte...",
13980       "Composite...",
13981       "Add Border...",
13982       "Add Frame...",
13983       "Comment...",
13984       "Launch...",
13985       "Region of Interest...",
13986       (char *) NULL
13987     },
13988     *MiscellanyMenu[] =
13989     {
13990       "Image Info",
13991       "Zoom Image",
13992       "Show Preview...",
13993       "Show Histogram",
13994       "Show Matte",
13995       "Background...",
13996       "Slide Show...",
13997       "Preferences...",
13998       (char *) NULL
13999     },
14000     *HelpMenu[] =
14001     {
14002       "Overview",
14003       "Browse Documentation",
14004       "About Display",
14005       (char *) NULL
14006     },
14007     *ShortCutsMenu[] =
14008     {
14009       "Next",
14010       "Former",
14011       "Open...",
14012       "Save...",
14013       "Print...",
14014       "Undo",
14015       "Restore",
14016       "Image Info",
14017       "Quit",
14018       (char *) NULL
14019     },
14020     *VirtualMenu[] =
14021     {
14022       "Image Info",
14023       "Print",
14024       "Next",
14025       "Quit",
14026       (char *) NULL
14027     };
14028
14029   static const char
14030     **Menus[MagickMenus] =
14031     {
14032       FileMenu,
14033       EditMenu,
14034       ViewMenu,
14035       TransformMenu,
14036       EnhanceMenu,
14037       EffectsMenu,
14038       FXMenu,
14039       ImageEditMenu,
14040       MiscellanyMenu,
14041       HelpMenu
14042     };
14043
14044   static CommandType
14045     CommandMenus[] =
14046     {
14047       NullCommand,
14048       NullCommand,
14049       NullCommand,
14050       NullCommand,
14051       NullCommand,
14052       NullCommand,
14053       NullCommand,
14054       NullCommand,
14055       NullCommand,
14056       NullCommand,
14057     },
14058     FileCommands[] =
14059     {
14060       OpenCommand,
14061       NextCommand,
14062       FormerCommand,
14063       SelectCommand,
14064       SaveCommand,
14065       PrintCommand,
14066       DeleteCommand,
14067       NewCommand,
14068       VisualDirectoryCommand,
14069       QuitCommand
14070     },
14071     EditCommands[] =
14072     {
14073       UndoCommand,
14074       RedoCommand,
14075       CutCommand,
14076       CopyCommand,
14077       PasteCommand
14078     },
14079     ViewCommands[] =
14080     {
14081       HalfSizeCommand,
14082       OriginalSizeCommand,
14083       DoubleSizeCommand,
14084       ResizeCommand,
14085       ApplyCommand,
14086       RefreshCommand,
14087       RestoreCommand
14088     },
14089     TransformCommands[] =
14090     {
14091       CropCommand,
14092       ChopCommand,
14093       FlopCommand,
14094       FlipCommand,
14095       RotateRightCommand,
14096       RotateLeftCommand,
14097       RotateCommand,
14098       ShearCommand,
14099       RollCommand,
14100       TrimCommand
14101     },
14102     EnhanceCommands[] =
14103     {
14104       HueCommand,
14105       SaturationCommand,
14106       BrightnessCommand,
14107       GammaCommand,
14108       SpiffCommand,
14109       DullCommand,
14110       ContrastStretchCommand,
14111       SigmoidalContrastCommand,
14112       NormalizeCommand,
14113       EqualizeCommand,
14114       NegateCommand,
14115       GrayscaleCommand,
14116       MapCommand,
14117       QuantizeCommand
14118     },
14119     EffectsCommands[] =
14120     {
14121       DespeckleCommand,
14122       EmbossCommand,
14123       ReduceNoiseCommand,
14124       AddNoiseCommand,
14125       SharpenCommand,
14126       BlurCommand,
14127       ThresholdCommand,
14128       EdgeDetectCommand,
14129       SpreadCommand,
14130       ShadeCommand,
14131       RaiseCommand,
14132       SegmentCommand
14133     },
14134     FXCommands[] =
14135     {
14136       SolarizeCommand,
14137       SepiaToneCommand,
14138       SwirlCommand,
14139       ImplodeCommand,
14140       VignetteCommand,
14141       WaveCommand,
14142       OilPaintCommand,
14143       CharcoalDrawCommand
14144     },
14145     ImageEditCommands[] =
14146     {
14147       AnnotateCommand,
14148       DrawCommand,
14149       ColorCommand,
14150       MatteCommand,
14151       CompositeCommand,
14152       AddBorderCommand,
14153       AddFrameCommand,
14154       CommentCommand,
14155       LaunchCommand,
14156       RegionofInterestCommand
14157     },
14158     MiscellanyCommands[] =
14159     {
14160       InfoCommand,
14161       ZoomCommand,
14162       ShowPreviewCommand,
14163       ShowHistogramCommand,
14164       ShowMatteCommand,
14165       BackgroundCommand,
14166       SlideShowCommand,
14167       PreferencesCommand
14168     },
14169     HelpCommands[] =
14170     {
14171       HelpCommand,
14172       BrowseDocumentationCommand,
14173       VersionCommand
14174     },
14175     ShortCutsCommands[] =
14176     {
14177       NextCommand,
14178       FormerCommand,
14179       OpenCommand,
14180       SaveCommand,
14181       PrintCommand,
14182       UndoCommand,
14183       RestoreCommand,
14184       InfoCommand,
14185       QuitCommand
14186     },
14187     VirtualCommands[] =
14188     {
14189       InfoCommand,
14190       PrintCommand,
14191       NextCommand,
14192       QuitCommand
14193     };
14194
14195   static CommandType
14196     *Commands[MagickMenus] =
14197     {
14198       FileCommands,
14199       EditCommands,
14200       ViewCommands,
14201       TransformCommands,
14202       EnhanceCommands,
14203       EffectsCommands,
14204       FXCommands,
14205       ImageEditCommands,
14206       MiscellanyCommands,
14207       HelpCommands
14208     };
14209
14210   char
14211     command[MaxTextExtent],
14212     *cwd,
14213     geometry[MaxTextExtent],
14214     resource_name[MaxTextExtent];
14215
14216   CommandType
14217     command_type;
14218
14219   Image
14220     *display_image,
14221     *nexus;
14222
14223   int
14224     entry,
14225     id;
14226
14227   KeySym
14228     key_symbol;
14229
14230   MagickStatusType
14231     context_mask,
14232     status;
14233
14234   RectangleInfo
14235     geometry_info;
14236
14237   register int
14238     i;
14239
14240   static char
14241     working_directory[MaxTextExtent];
14242
14243   static XPoint
14244     vid_info;
14245
14246   static XWindowInfo
14247     *magick_windows[MaxXWindows];
14248
14249   static unsigned int
14250     number_windows;
14251
14252   struct stat
14253     attributes;
14254
14255   time_t
14256     timer,
14257     timestamp,
14258     update_time;
14259
14260   unsigned int
14261     height,
14262     width;
14263
14264   size_t
14265     delay;
14266
14267   WarningHandler
14268     warning_handler;
14269
14270   Window
14271     root_window;
14272
14273   XClassHint
14274     *class_hints;
14275
14276   XEvent
14277     event;
14278
14279   XFontStruct
14280     *font_info;
14281
14282   XGCValues
14283     context_values;
14284
14285   XPixelInfo
14286     *icon_pixel,
14287     *pixel;
14288
14289   XResourceInfo
14290     *icon_resources;
14291
14292   XStandardColormap
14293     *icon_map,
14294     *map_info;
14295
14296   XVisualInfo
14297     *icon_visual,
14298     *visual_info;
14299
14300   XWindowChanges
14301     window_changes;
14302
14303   XWindows
14304     *windows;
14305
14306   XWMHints
14307     *manager_hints;
14308
14309   assert(image != (Image **) NULL);
14310   assert((*image)->signature == MagickSignature);
14311   if ((*image)->debug != MagickFalse)
14312     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14313   display_image=(*image);
14314   warning_handler=(WarningHandler) NULL;
14315   windows=XSetWindows((XWindows *) ~0);
14316   if (windows != (XWindows *) NULL)
14317     {
14318       int
14319         status;
14320
14321       status=chdir(working_directory);
14322       if (status == -1)
14323         (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14324           FileOpenError,"UnableToOpenFile","%s",working_directory);
14325       warning_handler=resource_info->display_warnings ?
14326         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14327       warning_handler=resource_info->display_warnings ?
14328         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14329     }
14330   else
14331     {
14332       /*
14333         Allocate windows structure.
14334       */
14335       resource_info->colors=display_image->colors;
14336       windows=XSetWindows(XInitializeWindows(display,resource_info));
14337       if (windows == (XWindows *) NULL)
14338         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14339           (*image)->filename);
14340       /*
14341         Initialize window id's.
14342       */
14343       number_windows=0;
14344       magick_windows[number_windows++]=(&windows->icon);
14345       magick_windows[number_windows++]=(&windows->backdrop);
14346       magick_windows[number_windows++]=(&windows->image);
14347       magick_windows[number_windows++]=(&windows->info);
14348       magick_windows[number_windows++]=(&windows->command);
14349       magick_windows[number_windows++]=(&windows->widget);
14350       magick_windows[number_windows++]=(&windows->popup);
14351       magick_windows[number_windows++]=(&windows->magnify);
14352       magick_windows[number_windows++]=(&windows->pan);
14353       for (i=0; i < (int) number_windows; i++)
14354         magick_windows[i]->id=(Window) NULL;
14355       vid_info.x=0;
14356       vid_info.y=0;
14357     }
14358   /*
14359     Initialize font info.
14360   */
14361   if (windows->font_info != (XFontStruct *) NULL)
14362     (void) XFreeFont(display,windows->font_info);
14363   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14364   if (windows->font_info == (XFontStruct *) NULL)
14365     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14366       resource_info->font);
14367   /*
14368     Initialize Standard Colormap.
14369   */
14370   map_info=windows->map_info;
14371   icon_map=windows->icon_map;
14372   visual_info=windows->visual_info;
14373   icon_visual=windows->icon_visual;
14374   pixel=windows->pixel_info;
14375   icon_pixel=windows->icon_pixel;
14376   font_info=windows->font_info;
14377   icon_resources=windows->icon_resources;
14378   class_hints=windows->class_hints;
14379   manager_hints=windows->manager_hints;
14380   root_window=XRootWindow(display,visual_info->screen);
14381   nexus=NewImageList();
14382   if (display_image->debug != MagickFalse)
14383     {
14384       (void) LogMagickEvent(X11Event,GetMagickModule(),
14385         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14386         (double) display_image->scene,(double) display_image->columns,
14387         (double) display_image->rows);
14388       if (display_image->colors != 0)
14389         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14390           display_image->colors);
14391       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14392         display_image->magick);
14393     }
14394   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14395     map_info,pixel);
14396   display_image->taint=MagickFalse;
14397   /*
14398     Initialize graphic context.
14399   */
14400   windows->context.id=(Window) NULL;
14401   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14402     resource_info,&windows->context);
14403   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14404   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14405   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14406   manager_hints->flags=InputHint | StateHint;
14407   manager_hints->input=MagickFalse;
14408   manager_hints->initial_state=WithdrawnState;
14409   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14410     &windows->context);
14411   if (display_image->debug != MagickFalse)
14412     (void) LogMagickEvent(X11Event,GetMagickModule(),
14413       "Window id: 0x%lx (context)",windows->context.id);
14414   context_values.background=pixel->background_color.pixel;
14415   context_values.font=font_info->fid;
14416   context_values.foreground=pixel->foreground_color.pixel;
14417   context_values.graphics_exposures=MagickFalse;
14418   context_mask=(MagickStatusType)
14419     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14420   if (pixel->annotate_context != (GC) NULL)
14421     (void) XFreeGC(display,pixel->annotate_context);
14422   pixel->annotate_context=XCreateGC(display,windows->context.id,
14423     context_mask,&context_values);
14424   if (pixel->annotate_context == (GC) NULL)
14425     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14426       display_image->filename);
14427   context_values.background=pixel->depth_color.pixel;
14428   if (pixel->widget_context != (GC) NULL)
14429     (void) XFreeGC(display,pixel->widget_context);
14430   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14431     &context_values);
14432   if (pixel->widget_context == (GC) NULL)
14433     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14434       display_image->filename);
14435   context_values.background=pixel->foreground_color.pixel;
14436   context_values.foreground=pixel->background_color.pixel;
14437   context_values.plane_mask=context_values.background ^
14438     context_values.foreground;
14439   if (pixel->highlight_context != (GC) NULL)
14440     (void) XFreeGC(display,pixel->highlight_context);
14441   pixel->highlight_context=XCreateGC(display,windows->context.id,
14442     (size_t) (context_mask | GCPlaneMask),&context_values);
14443   if (pixel->highlight_context == (GC) NULL)
14444     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14445       display_image->filename);
14446   (void) XDestroyWindow(display,windows->context.id);
14447   /*
14448     Initialize icon window.
14449   */
14450   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14451     icon_resources,&windows->icon);
14452   windows->icon.geometry=resource_info->icon_geometry;
14453   XBestIconSize(display,&windows->icon,display_image);
14454   windows->icon.attributes.colormap=XDefaultColormap(display,
14455     icon_visual->screen);
14456   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14457   manager_hints->flags=InputHint | StateHint;
14458   manager_hints->input=MagickFalse;
14459   manager_hints->initial_state=IconicState;
14460   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14461     &windows->icon);
14462   if (display_image->debug != MagickFalse)
14463     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14464       windows->icon.id);
14465   /*
14466     Initialize graphic context for icon window.
14467   */
14468   if (icon_pixel->annotate_context != (GC) NULL)
14469     (void) XFreeGC(display,icon_pixel->annotate_context);
14470   context_values.background=icon_pixel->background_color.pixel;
14471   context_values.foreground=icon_pixel->foreground_color.pixel;
14472   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14473     (size_t) (GCBackground | GCForeground),&context_values);
14474   if (icon_pixel->annotate_context == (GC) NULL)
14475     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14476       display_image->filename);
14477   windows->icon.annotate_context=icon_pixel->annotate_context;
14478   /*
14479     Initialize Image window.
14480   */
14481   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14482     &windows->image);
14483   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14484   if (resource_info->use_shared_memory == MagickFalse)
14485     windows->image.shared_memory=MagickFalse;
14486   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14487     {
14488       char
14489         *title;
14490
14491       title=InterpretImageProperties(resource_info->image_info,display_image,
14492         resource_info->title);
14493       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14494       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14495       title=DestroyString(title);
14496     }
14497   else
14498     {
14499       char
14500         filename[MaxTextExtent];
14501
14502       /*
14503         Window name is the base of the filename.
14504       */
14505       GetPathComponent(display_image->magick_filename,TailPath,filename);
14506       if (GetImageListLength(display_image) == 1)
14507         (void) FormatMagickString(windows->image.name,MaxTextExtent,
14508           "%s: %s",MagickPackageName,filename);
14509       else
14510         (void) FormatMagickString(windows->image.name,MaxTextExtent,
14511           "%s: %s[%.20g of %.20g]",MagickPackageName,filename,(double)
14512           display_image->scene,(double) GetImageListLength(display_image));
14513       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14514     }
14515   if (resource_info->immutable)
14516     windows->image.immutable=MagickTrue;
14517   windows->image.use_pixmap=resource_info->use_pixmap;
14518   windows->image.geometry=resource_info->image_geometry;
14519   (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14520     XDisplayWidth(display,visual_info->screen),
14521     XDisplayHeight(display,visual_info->screen));
14522   geometry_info.width=display_image->columns;
14523   geometry_info.height=display_image->rows;
14524   geometry_info.x=0;
14525   geometry_info.y=0;
14526   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14527     &geometry_info.width,&geometry_info.height);
14528   windows->image.width=(unsigned int) geometry_info.width;
14529   windows->image.height=(unsigned int) geometry_info.height;
14530   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14531     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14532     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14533     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14534   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14535     resource_info,&windows->backdrop);
14536   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14537     {
14538       /*
14539         Initialize backdrop window.
14540       */
14541       windows->backdrop.x=0;
14542       windows->backdrop.y=0;
14543       (void) CloneString(&windows->backdrop.name,"Backdrop");
14544       windows->backdrop.flags=(size_t) (USSize | USPosition);
14545       windows->backdrop.width=(unsigned int)
14546         XDisplayWidth(display,visual_info->screen);
14547       windows->backdrop.height=(unsigned int)
14548         XDisplayHeight(display,visual_info->screen);
14549       windows->backdrop.border_width=0;
14550       windows->backdrop.immutable=MagickTrue;
14551       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14552         ButtonReleaseMask;
14553       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14554         StructureNotifyMask;
14555       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14556       manager_hints->icon_window=windows->icon.id;
14557       manager_hints->input=MagickTrue;
14558       manager_hints->initial_state=resource_info->iconic ? IconicState :
14559         NormalState;
14560       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14561         &windows->backdrop);
14562       if (display_image->debug != MagickFalse)
14563         (void) LogMagickEvent(X11Event,GetMagickModule(),
14564           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14565       (void) XMapWindow(display,windows->backdrop.id);
14566       (void) XClearWindow(display,windows->backdrop.id);
14567       if (windows->image.id != (Window) NULL)
14568         {
14569           (void) XDestroyWindow(display,windows->image.id);
14570           windows->image.id=(Window) NULL;
14571         }
14572       /*
14573         Position image in the center the backdrop.
14574       */
14575       windows->image.flags|=USPosition;
14576       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14577         (windows->image.width/2);
14578       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14579         (windows->image.height/2);
14580     }
14581   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14582   manager_hints->icon_window=windows->icon.id;
14583   manager_hints->input=MagickTrue;
14584   manager_hints->initial_state=resource_info->iconic ? IconicState :
14585     NormalState;
14586   if (windows->group_leader.id != (Window) NULL)
14587     {
14588       /*
14589         Follow the leader.
14590       */
14591       manager_hints->flags|=WindowGroupHint;
14592       manager_hints->window_group=windows->group_leader.id;
14593       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14594       if (display_image->debug != MagickFalse)
14595         (void) LogMagickEvent(X11Event,GetMagickModule(),
14596           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14597     }
14598   XMakeWindow(display,
14599     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14600     argv,argc,class_hints,manager_hints,&windows->image);
14601   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14602     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14603   if (windows->group_leader.id != (Window) NULL)
14604     (void) XSetTransientForHint(display,windows->image.id,
14605       windows->group_leader.id);
14606   if (display_image->debug != MagickFalse)
14607     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14608       windows->image.id);
14609   /*
14610     Initialize Info widget.
14611   */
14612   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14613     &windows->info);
14614   (void) CloneString(&windows->info.name,"Info");
14615   (void) CloneString(&windows->info.icon_name,"Info");
14616   windows->info.border_width=1;
14617   windows->info.x=2;
14618   windows->info.y=2;
14619   windows->info.flags|=PPosition;
14620   windows->info.attributes.win_gravity=UnmapGravity;
14621   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14622     StructureNotifyMask;
14623   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14624   manager_hints->input=MagickFalse;
14625   manager_hints->initial_state=NormalState;
14626   manager_hints->window_group=windows->image.id;
14627   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14628     &windows->info);
14629   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14630     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14631   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14632     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14633   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14634   if (windows->image.mapped != MagickFalse)
14635     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14636   if (display_image->debug != MagickFalse)
14637     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14638       windows->info.id);
14639   /*
14640     Initialize Command widget.
14641   */
14642   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14643     resource_info,&windows->command);
14644   windows->command.data=MagickMenus;
14645   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14646   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.command",
14647     resource_info->client_name);
14648   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14649     resource_name,"geometry",(char *) NULL);
14650   (void) CloneString(&windows->command.name,MagickTitle);
14651   windows->command.border_width=0;
14652   windows->command.flags|=PPosition;
14653   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14654     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14655     OwnerGrabButtonMask | StructureNotifyMask;
14656   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14657   manager_hints->input=MagickTrue;
14658   manager_hints->initial_state=NormalState;
14659   manager_hints->window_group=windows->image.id;
14660   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14661     &windows->command);
14662   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14663     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14664     HighlightHeight);
14665   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14666     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14667   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14668   if (windows->command.mapped != MagickFalse)
14669     (void) XMapRaised(display,windows->command.id);
14670   if (display_image->debug != MagickFalse)
14671     (void) LogMagickEvent(X11Event,GetMagickModule(),
14672       "Window id: 0x%lx (command)",windows->command.id);
14673   /*
14674     Initialize Widget window.
14675   */
14676   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14677     resource_info,&windows->widget);
14678   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.widget",
14679     resource_info->client_name);
14680   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14681     resource_name,"geometry",(char *) NULL);
14682   windows->widget.border_width=0;
14683   windows->widget.flags|=PPosition;
14684   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14685     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14686     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14687     StructureNotifyMask;
14688   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14689   manager_hints->input=MagickTrue;
14690   manager_hints->initial_state=NormalState;
14691   manager_hints->window_group=windows->image.id;
14692   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14693     &windows->widget);
14694   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14695     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14696   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14697     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14698   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14699   if (display_image->debug != MagickFalse)
14700     (void) LogMagickEvent(X11Event,GetMagickModule(),
14701       "Window id: 0x%lx (widget)",windows->widget.id);
14702   /*
14703     Initialize popup window.
14704   */
14705   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14706     resource_info,&windows->popup);
14707   windows->popup.border_width=0;
14708   windows->popup.flags|=PPosition;
14709   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14710     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14711     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14712   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14713   manager_hints->input=MagickTrue;
14714   manager_hints->initial_state=NormalState;
14715   manager_hints->window_group=windows->image.id;
14716   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14717     &windows->popup);
14718   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14719     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14720   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14721     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14722   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14723   if (display_image->debug != MagickFalse)
14724     (void) LogMagickEvent(X11Event,GetMagickModule(),
14725       "Window id: 0x%lx (pop up)",windows->popup.id);
14726   /*
14727     Initialize Magnify window and cursor.
14728   */
14729   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14730     resource_info,&windows->magnify);
14731   if (resource_info->use_shared_memory == MagickFalse)
14732     windows->magnify.shared_memory=MagickFalse;
14733   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.magnify",
14734     resource_info->client_name);
14735   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14736     resource_name,"geometry",(char *) NULL);
14737   (void) FormatMagickString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14738     resource_info->magnify);
14739   if (windows->magnify.cursor != (Cursor) NULL)
14740     (void) XFreeCursor(display,windows->magnify.cursor);
14741   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14742     map_info->colormap,resource_info->background_color,
14743     resource_info->foreground_color);
14744   if (windows->magnify.cursor == (Cursor) NULL)
14745     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14746       display_image->filename);
14747   windows->magnify.width=MagnifySize;
14748   windows->magnify.height=MagnifySize;
14749   windows->magnify.flags|=PPosition;
14750   windows->magnify.min_width=MagnifySize;
14751   windows->magnify.min_height=MagnifySize;
14752   windows->magnify.width_inc=MagnifySize;
14753   windows->magnify.height_inc=MagnifySize;
14754   windows->magnify.data=resource_info->magnify;
14755   windows->magnify.attributes.cursor=windows->magnify.cursor;
14756   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14757     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14758     StructureNotifyMask;
14759   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14760   manager_hints->input=MagickTrue;
14761   manager_hints->initial_state=NormalState;
14762   manager_hints->window_group=windows->image.id;
14763   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14764     &windows->magnify);
14765   if (display_image->debug != MagickFalse)
14766     (void) LogMagickEvent(X11Event,GetMagickModule(),
14767       "Window id: 0x%lx (magnify)",windows->magnify.id);
14768   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14769   /*
14770     Initialize panning window.
14771   */
14772   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14773     resource_info,&windows->pan);
14774   (void) CloneString(&windows->pan.name,"Pan Icon");
14775   windows->pan.width=windows->icon.width;
14776   windows->pan.height=windows->icon.height;
14777   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.pan",
14778     resource_info->client_name);
14779   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14780     resource_name,"geometry",(char *) NULL);
14781   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14782     &windows->pan.width,&windows->pan.height);
14783   windows->pan.flags|=PPosition;
14784   windows->pan.immutable=MagickTrue;
14785   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14786     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14787     StructureNotifyMask;
14788   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14789   manager_hints->input=MagickFalse;
14790   manager_hints->initial_state=NormalState;
14791   manager_hints->window_group=windows->image.id;
14792   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14793     &windows->pan);
14794   if (display_image->debug != MagickFalse)
14795     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14796       windows->pan.id);
14797   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14798   if (windows->info.mapped != MagickFalse)
14799     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14800   if ((windows->image.mapped == MagickFalse) ||
14801       (windows->backdrop.id != (Window) NULL))
14802     (void) XMapWindow(display,windows->image.id);
14803   /*
14804     Set our progress monitor and warning handlers.
14805   */
14806   if (warning_handler == (WarningHandler) NULL)
14807     {
14808       warning_handler=resource_info->display_warnings ?
14809         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14810       warning_handler=resource_info->display_warnings ?
14811         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14812     }
14813   /*
14814     Initialize Image and Magnify X images.
14815   */
14816   windows->image.x=0;
14817   windows->image.y=0;
14818   windows->magnify.shape=MagickFalse;
14819   width=(unsigned int) display_image->columns;
14820   height=(unsigned int) display_image->rows;
14821   if ((display_image->columns != width) || (display_image->rows != height))
14822     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14823       display_image->filename);
14824   status=XMakeImage(display,resource_info,&windows->image,display_image,
14825     width,height);
14826   if (status == MagickFalse)
14827     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14828       display_image->filename);
14829   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14830     windows->magnify.width,windows->magnify.height);
14831   if (status == MagickFalse)
14832     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14833       display_image->filename);
14834   if (windows->magnify.mapped != MagickFalse)
14835     (void) XMapRaised(display,windows->magnify.id);
14836   if (windows->pan.mapped != MagickFalse)
14837     (void) XMapRaised(display,windows->pan.id);
14838   windows->image.window_changes.width=(int) display_image->columns;
14839   windows->image.window_changes.height=(int) display_image->rows;
14840   (void) XConfigureImage(display,resource_info,windows,display_image);
14841   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14842   (void) XSync(display,MagickFalse);
14843   /*
14844     Respond to events.
14845   */
14846   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14847   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14848   update_time=0;
14849   if (resource_info->update != MagickFalse)
14850     {
14851       MagickBooleanType
14852         status;
14853
14854       /*
14855         Determine when file data was last modified.
14856       */
14857       status=GetPathAttributes(display_image->filename,&attributes);
14858       if (status != MagickFalse)
14859         update_time=attributes.st_mtime;
14860     }
14861   *state&=(~FormerImageState);
14862   *state&=(~MontageImageState);
14863   *state&=(~NextImageState);
14864   do
14865   {
14866     /*
14867       Handle a window event.
14868     */
14869     if (windows->image.mapped != MagickFalse)
14870       if ((display_image->delay != 0) || (resource_info->update != 0))
14871         {
14872           if (timer < time((time_t *) NULL))
14873             {
14874               if (resource_info->update == MagickFalse)
14875                 *state|=NextImageState | ExitState;
14876               else
14877                 {
14878                   MagickBooleanType
14879                     status;
14880
14881                   /*
14882                     Determine if image file was modified.
14883                   */
14884                   status=GetPathAttributes(display_image->filename,&attributes);
14885                   if (status != MagickFalse)
14886                     if (update_time != attributes.st_mtime)
14887                       {
14888                         /*
14889                           Redisplay image.
14890                         */
14891                         (void) FormatMagickString(
14892                           resource_info->image_info->filename,MaxTextExtent,
14893                           "%s:%s",display_image->magick,
14894                           display_image->filename);
14895                         nexus=ReadImage(resource_info->image_info,
14896                           &display_image->exception);
14897                         if (nexus != (Image *) NULL)
14898                           {
14899                             nexus=DestroyImage(nexus);
14900                             *state|=NextImageState | ExitState;
14901                           }
14902                       }
14903                   delay=display_image->delay/MagickMax(
14904                     display_image->ticks_per_second,1L);
14905                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14906                 }
14907             }
14908           if (XEventsQueued(display,QueuedAfterFlush) == 0)
14909             {
14910               /*
14911                 Do not block if delay > 0.
14912               */
14913               XDelay(display,SuspendTime << 2);
14914               continue;
14915             }
14916         }
14917     timestamp=time((time_t *) NULL);
14918     (void) XNextEvent(display,&event);
14919     if (windows->image.stasis == MagickFalse)
14920       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14921         MagickTrue : MagickFalse;
14922     if (windows->magnify.stasis == MagickFalse)
14923       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14924         MagickTrue : MagickFalse;
14925     if (event.xany.window == windows->command.id)
14926       {
14927         /*
14928           Select a command from the Command widget.
14929         */
14930         id=XCommandWidget(display,windows,CommandMenu,&event);
14931         if (id < 0)
14932           continue;
14933         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
14934         command_type=CommandMenus[id];
14935         if (id < MagickMenus)
14936           {
14937             /*
14938               Select a command from a pop-up menu.
14939             */
14940             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
14941               command);
14942             if (entry < 0)
14943               continue;
14944             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
14945             command_type=Commands[id][entry];
14946           }
14947         if (command_type != NullCommand)
14948           nexus=XMagickCommand(display,resource_info,windows,command_type,
14949             &display_image);
14950         continue;
14951       }
14952     switch (event.type)
14953     {
14954       case ButtonPress:
14955       {
14956         if (display_image->debug != MagickFalse)
14957           (void) LogMagickEvent(X11Event,GetMagickModule(),
14958             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
14959             event.xbutton.button,event.xbutton.x,event.xbutton.y);
14960         if ((event.xbutton.button == Button3) &&
14961             (event.xbutton.state & Mod1Mask))
14962           {
14963             /*
14964               Convert Alt-Button3 to Button2.
14965             */
14966             event.xbutton.button=Button2;
14967             event.xbutton.state&=(~Mod1Mask);
14968           }
14969         if (event.xbutton.window == windows->backdrop.id)
14970           {
14971             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
14972               event.xbutton.time);
14973             break;
14974           }
14975         if (event.xbutton.window == windows->image.id)
14976           {
14977             switch (event.xbutton.button)
14978             {
14979               case Button1:
14980               {
14981                 if (resource_info->immutable)
14982                   {
14983                     /*
14984                       Select a command from the Virtual menu.
14985                     */
14986                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
14987                       command);
14988                     if (entry >= 0)
14989                       nexus=XMagickCommand(display,resource_info,windows,
14990                         VirtualCommands[entry],&display_image);
14991                     break;
14992                   }
14993                 /*
14994                   Map/unmap Command widget.
14995                 */
14996                 if (windows->command.mapped != MagickFalse)
14997                   (void) XWithdrawWindow(display,windows->command.id,
14998                     windows->command.screen);
14999                 else
15000                   {
15001                     (void) XCommandWidget(display,windows,CommandMenu,
15002                       (XEvent *) NULL);
15003                     (void) XMapRaised(display,windows->command.id);
15004                   }
15005                 break;
15006               }
15007               case Button2:
15008               {
15009                 /*
15010                   User pressed the image magnify button.
15011                 */
15012                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15013                   &display_image);
15014                 XMagnifyImage(display,windows,&event);
15015                 break;
15016               }
15017               case Button3:
15018               {
15019                 if (resource_info->immutable)
15020                   {
15021                     /*
15022                       Select a command from the Virtual menu.
15023                     */
15024                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15025                       command);
15026                     if (entry >= 0)
15027                       nexus=XMagickCommand(display,resource_info,windows,
15028                         VirtualCommands[entry],&display_image);
15029                     break;
15030                   }
15031                 if (display_image->montage != (char *) NULL)
15032                   {
15033                     /*
15034                       Open or delete a tile from a visual image directory.
15035                     */
15036                     nexus=XTileImage(display,resource_info,windows,
15037                       display_image,&event);
15038                     if (nexus != (Image *) NULL)
15039                       *state|=MontageImageState | NextImageState | ExitState;
15040                     vid_info.x=windows->image.x;
15041                     vid_info.y=windows->image.y;
15042                     break;
15043                   }
15044                 /*
15045                   Select a command from the Short Cuts menu.
15046                 */
15047                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15048                   command);
15049                 if (entry >= 0)
15050                   nexus=XMagickCommand(display,resource_info,windows,
15051                     ShortCutsCommands[entry],&display_image);
15052                 break;
15053               }
15054               case Button4:
15055               {
15056                 /*
15057                   Wheel up.
15058                 */
15059                 XTranslateImage(display,windows,*image,XK_Up);
15060                 break;
15061               }
15062               case Button5:
15063               {
15064                 /*
15065                   Wheel down.
15066                 */
15067                 XTranslateImage(display,windows,*image,XK_Down);
15068                 break;
15069               }
15070               default:
15071                 break;
15072             }
15073             break;
15074           }
15075         if (event.xbutton.window == windows->magnify.id)
15076           {
15077             int
15078               factor;
15079
15080             static const char
15081               *MagnifyMenu[] =
15082               {
15083                 "2",
15084                 "4",
15085                 "5",
15086                 "6",
15087                 "7",
15088                 "8",
15089                 "9",
15090                 "3",
15091                 (char *) NULL,
15092               };
15093
15094             static KeySym
15095               MagnifyCommands[] =
15096               {
15097                 XK_2,
15098                 XK_4,
15099                 XK_5,
15100                 XK_6,
15101                 XK_7,
15102                 XK_8,
15103                 XK_9,
15104                 XK_3
15105               };
15106
15107             /*
15108               Select a magnify factor from the pop-up menu.
15109             */
15110             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15111             if (factor >= 0)
15112               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15113             break;
15114           }
15115         if (event.xbutton.window == windows->pan.id)
15116           {
15117             switch (event.xbutton.button)
15118             {
15119               case Button4:
15120               {
15121                 /*
15122                   Wheel up.
15123                 */
15124                 XTranslateImage(display,windows,*image,XK_Up);
15125                 break;
15126               }
15127               case Button5:
15128               {
15129                 /*
15130                   Wheel down.
15131                 */
15132                 XTranslateImage(display,windows,*image,XK_Down);
15133                 break;
15134               }
15135               default:
15136               {
15137                 XPanImage(display,windows,&event);
15138                 break;
15139               }
15140             }
15141             break;
15142           }
15143         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15144           1L);
15145         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15146         break;
15147       }
15148       case ButtonRelease:
15149       {
15150         if (display_image->debug != MagickFalse)
15151           (void) LogMagickEvent(X11Event,GetMagickModule(),
15152             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15153             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15154         break;
15155       }
15156       case ClientMessage:
15157       {
15158         if (display_image->debug != MagickFalse)
15159           (void) LogMagickEvent(X11Event,GetMagickModule(),
15160             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15161             event.xclient.message_type,event.xclient.format,(unsigned long)
15162             event.xclient.data.l[0]);
15163         if (event.xclient.message_type == windows->im_protocols)
15164           {
15165             if (*event.xclient.data.l == (long) windows->im_update_widget)
15166               {
15167                 (void) CloneString(&windows->command.name,MagickTitle);
15168                 windows->command.data=MagickMenus;
15169                 (void) XCommandWidget(display,windows,CommandMenu,
15170                   (XEvent *) NULL);
15171                 break;
15172               }
15173             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15174               {
15175                 /*
15176                   Update graphic context and window colormap.
15177                 */
15178                 for (i=0; i < (int) number_windows; i++)
15179                 {
15180                   if (magick_windows[i]->id == windows->icon.id)
15181                     continue;
15182                   context_values.background=pixel->background_color.pixel;
15183                   context_values.foreground=pixel->foreground_color.pixel;
15184                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15185                     context_mask,&context_values);
15186                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15187                     context_mask,&context_values);
15188                   context_values.background=pixel->foreground_color.pixel;
15189                   context_values.foreground=pixel->background_color.pixel;
15190                   context_values.plane_mask=context_values.background ^
15191                     context_values.foreground;
15192                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15193                     (size_t) (context_mask | GCPlaneMask),
15194                     &context_values);
15195                   magick_windows[i]->attributes.background_pixel=
15196                     pixel->background_color.pixel;
15197                   magick_windows[i]->attributes.border_pixel=
15198                     pixel->border_color.pixel;
15199                   magick_windows[i]->attributes.colormap=map_info->colormap;
15200                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15201                     magick_windows[i]->mask,&magick_windows[i]->attributes);
15202                 }
15203                 if (windows->pan.mapped != MagickFalse)
15204                   {
15205                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15206                       windows->pan.pixmap);
15207                     (void) XClearWindow(display,windows->pan.id);
15208                     XDrawPanRectangle(display,windows);
15209                   }
15210                 if (windows->backdrop.id != (Window) NULL)
15211                   (void) XInstallColormap(display,map_info->colormap);
15212                 break;
15213               }
15214             if (*event.xclient.data.l == (long) windows->im_former_image)
15215               {
15216                 *state|=FormerImageState | ExitState;
15217                 break;
15218               }
15219             if (*event.xclient.data.l == (long) windows->im_next_image)
15220               {
15221                 *state|=NextImageState | ExitState;
15222                 break;
15223               }
15224             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15225               {
15226                 *state|=RetainColorsState;
15227                 break;
15228               }
15229             if (*event.xclient.data.l == (long) windows->im_exit)
15230               {
15231                 *state|=ExitState;
15232                 break;
15233               }
15234             break;
15235           }
15236         if (event.xclient.message_type == windows->dnd_protocols)
15237           {
15238             Atom
15239               selection,
15240               type;
15241
15242             int
15243               format,
15244               status;
15245
15246             unsigned char
15247               *data;
15248
15249             unsigned long
15250               after,
15251               length;
15252
15253             /*
15254               Display image named by the Drag-and-Drop selection.
15255             */
15256             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15257               break;
15258             selection=XInternAtom(display,"DndSelection",MagickFalse);
15259             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15260               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15261               &length,&after,&data);
15262             if ((status != Success) || (length == 0))
15263               break;
15264             if (*event.xclient.data.l == 2)
15265               {
15266                 /*
15267                   Offix DND.
15268                 */
15269                 (void) CopyMagickString(resource_info->image_info->filename,
15270                   (char *) data,MaxTextExtent);
15271               }
15272             else
15273               {
15274                 /*
15275                   XDND.
15276                 */
15277                 if (strncmp((char *) data, "file:", 5) != 0)
15278                   {
15279                     (void) XFree((void *) data);
15280                     break;
15281                   }
15282                 (void) CopyMagickString(resource_info->image_info->filename,
15283                   ((char *) data)+5,MaxTextExtent);
15284               }
15285             nexus=ReadImage(resource_info->image_info,
15286               &display_image->exception);
15287             CatchException(&display_image->exception);
15288             if (nexus != (Image *) NULL)
15289               *state|=NextImageState | ExitState;
15290             (void) XFree((void *) data);
15291             break;
15292           }
15293         /*
15294           If client window delete message, exit.
15295         */
15296         if (event.xclient.message_type != windows->wm_protocols)
15297           break;
15298         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15299           break;
15300         (void) XWithdrawWindow(display,event.xclient.window,
15301           visual_info->screen);
15302         if (event.xclient.window == windows->image.id)
15303           {
15304             *state|=ExitState;
15305             break;
15306           }
15307         if (event.xclient.window == windows->pan.id)
15308           {
15309             /*
15310               Restore original image size when pan window is deleted.
15311             */
15312             windows->image.window_changes.width=windows->image.ximage->width;
15313             windows->image.window_changes.height=windows->image.ximage->height;
15314             (void) XConfigureImage(display,resource_info,windows,
15315               display_image);
15316           }
15317         break;
15318       }
15319       case ConfigureNotify:
15320       {
15321         if (display_image->debug != MagickFalse)
15322           (void) LogMagickEvent(X11Event,GetMagickModule(),
15323             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15324             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15325             event.xconfigure.y,event.xconfigure.send_event);
15326         if (event.xconfigure.window == windows->image.id)
15327           {
15328             /*
15329               Image window has a new configuration.
15330             */
15331             if (event.xconfigure.send_event != 0)
15332               {
15333                 XWindowChanges
15334                   window_changes;
15335
15336                 /*
15337                   Position the transient windows relative of the Image window.
15338                 */
15339                 if (windows->command.geometry == (char *) NULL)
15340                   if (windows->command.mapped == MagickFalse)
15341                     {
15342                       windows->command.x=event.xconfigure.x-
15343                         windows->command.width-25;
15344                       windows->command.y=event.xconfigure.y;
15345                       XConstrainWindowPosition(display,&windows->command);
15346                       window_changes.x=windows->command.x;
15347                       window_changes.y=windows->command.y;
15348                       (void) XReconfigureWMWindow(display,windows->command.id,
15349                         windows->command.screen,(unsigned int) (CWX | CWY),
15350                         &window_changes);
15351                     }
15352                 if (windows->widget.geometry == (char *) NULL)
15353                   if (windows->widget.mapped == MagickFalse)
15354                     {
15355                       windows->widget.x=event.xconfigure.x+
15356                         event.xconfigure.width/10;
15357                       windows->widget.y=event.xconfigure.y+
15358                         event.xconfigure.height/10;
15359                       XConstrainWindowPosition(display,&windows->widget);
15360                       window_changes.x=windows->widget.x;
15361                       window_changes.y=windows->widget.y;
15362                       (void) XReconfigureWMWindow(display,windows->widget.id,
15363                         windows->widget.screen,(unsigned int) (CWX | CWY),
15364                         &window_changes);
15365                     }
15366                 if (windows->magnify.geometry == (char *) NULL)
15367                   if (windows->magnify.mapped == MagickFalse)
15368                     {
15369                       windows->magnify.x=event.xconfigure.x+
15370                         event.xconfigure.width+25;
15371                       windows->magnify.y=event.xconfigure.y;
15372                       XConstrainWindowPosition(display,&windows->magnify);
15373                       window_changes.x=windows->magnify.x;
15374                       window_changes.y=windows->magnify.y;
15375                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15376                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15377                         &window_changes);
15378                     }
15379                 if (windows->pan.geometry == (char *) NULL)
15380                   if (windows->pan.mapped == MagickFalse)
15381                     {
15382                       windows->pan.x=event.xconfigure.x+
15383                         event.xconfigure.width+25;
15384                       windows->pan.y=event.xconfigure.y+
15385                         windows->magnify.height+50;
15386                       XConstrainWindowPosition(display,&windows->pan);
15387                       window_changes.x=windows->pan.x;
15388                       window_changes.y=windows->pan.y;
15389                       (void) XReconfigureWMWindow(display,windows->pan.id,
15390                         windows->pan.screen,(unsigned int) (CWX | CWY),
15391                         &window_changes);
15392                     }
15393               }
15394             if ((event.xconfigure.width == (int) windows->image.width) &&
15395                 (event.xconfigure.height == (int) windows->image.height))
15396               break;
15397             windows->image.width=(unsigned int) event.xconfigure.width;
15398             windows->image.height=(unsigned int) event.xconfigure.height;
15399             windows->image.x=0;
15400             windows->image.y=0;
15401             if (display_image->montage != (char *) NULL)
15402               {
15403                 windows->image.x=vid_info.x;
15404                 windows->image.y=vid_info.y;
15405               }
15406             if ((windows->image.mapped != MagickFalse) &&
15407                 (windows->image.stasis != MagickFalse))
15408               {
15409                 /*
15410                   Update image window configuration.
15411                 */
15412                 windows->image.window_changes.width=event.xconfigure.width;
15413                 windows->image.window_changes.height=event.xconfigure.height;
15414                 (void) XConfigureImage(display,resource_info,windows,
15415                   display_image);
15416               }
15417             /*
15418               Update pan window configuration.
15419             */
15420             if ((event.xconfigure.width < windows->image.ximage->width) ||
15421                 (event.xconfigure.height < windows->image.ximage->height))
15422               {
15423                 (void) XMapRaised(display,windows->pan.id);
15424                 XDrawPanRectangle(display,windows);
15425               }
15426             else
15427               if (windows->pan.mapped != MagickFalse)
15428                 (void) XWithdrawWindow(display,windows->pan.id,
15429                   windows->pan.screen);
15430             break;
15431           }
15432         if (event.xconfigure.window == windows->magnify.id)
15433           {
15434             unsigned int
15435               magnify;
15436
15437             /*
15438               Magnify window has a new configuration.
15439             */
15440             windows->magnify.width=(unsigned int) event.xconfigure.width;
15441             windows->magnify.height=(unsigned int) event.xconfigure.height;
15442             if (windows->magnify.mapped == MagickFalse)
15443               break;
15444             magnify=1;
15445             while ((int) magnify <= event.xconfigure.width)
15446               magnify<<=1;
15447             while ((int) magnify <= event.xconfigure.height)
15448               magnify<<=1;
15449             magnify>>=1;
15450             if (((int) magnify != event.xconfigure.width) ||
15451                 ((int) magnify != event.xconfigure.height))
15452               {
15453                 window_changes.width=(int) magnify;
15454                 window_changes.height=(int) magnify;
15455                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15456                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15457                   &window_changes);
15458                 break;
15459               }
15460             if ((windows->magnify.mapped != MagickFalse) &&
15461                 (windows->magnify.stasis != MagickFalse))
15462               {
15463                 status=XMakeImage(display,resource_info,&windows->magnify,
15464                   display_image,windows->magnify.width,windows->magnify.height);
15465                 XMakeMagnifyImage(display,windows);
15466               }
15467             break;
15468           }
15469         if ((windows->magnify.mapped != MagickFalse) &&
15470             (event.xconfigure.window == windows->pan.id))
15471           {
15472             /*
15473               Pan icon window has a new configuration.
15474             */
15475             if (event.xconfigure.send_event != 0)
15476               {
15477                 windows->pan.x=event.xconfigure.x;
15478                 windows->pan.y=event.xconfigure.y;
15479               }
15480             windows->pan.width=(unsigned int) event.xconfigure.width;
15481             windows->pan.height=(unsigned int) event.xconfigure.height;
15482             break;
15483           }
15484         if (event.xconfigure.window == windows->icon.id)
15485           {
15486             /*
15487               Icon window has a new configuration.
15488             */
15489             windows->icon.width=(unsigned int) event.xconfigure.width;
15490             windows->icon.height=(unsigned int) event.xconfigure.height;
15491             break;
15492           }
15493         break;
15494       }
15495       case DestroyNotify:
15496       {
15497         /*
15498           Group leader has exited.
15499         */
15500         if (display_image->debug != MagickFalse)
15501           (void) LogMagickEvent(X11Event,GetMagickModule(),
15502             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15503         if (event.xdestroywindow.window == windows->group_leader.id)
15504           {
15505             *state|=ExitState;
15506             break;
15507           }
15508         break;
15509       }
15510       case EnterNotify:
15511       {
15512         /*
15513           Selectively install colormap.
15514         */
15515         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15516           if (event.xcrossing.mode != NotifyUngrab)
15517             XInstallColormap(display,map_info->colormap);
15518         break;
15519       }
15520       case Expose:
15521       {
15522         if (display_image->debug != MagickFalse)
15523           (void) LogMagickEvent(X11Event,GetMagickModule(),
15524             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15525             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15526             event.xexpose.y);
15527         /*
15528           Refresh windows that are now exposed.
15529         */
15530         if ((event.xexpose.window == windows->image.id) &&
15531             (windows->image.mapped != MagickFalse))
15532           {
15533             XRefreshWindow(display,&windows->image,&event);
15534             delay=display_image->delay/MagickMax(
15535               display_image->ticks_per_second,1L);
15536             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15537             break;
15538           }
15539         if ((event.xexpose.window == windows->magnify.id) &&
15540             (windows->magnify.mapped != MagickFalse))
15541           {
15542             XMakeMagnifyImage(display,windows);
15543             break;
15544           }
15545         if (event.xexpose.window == windows->pan.id)
15546           {
15547             XDrawPanRectangle(display,windows);
15548             break;
15549           }
15550         if (event.xexpose.window == windows->icon.id)
15551           {
15552             XRefreshWindow(display,&windows->icon,&event);
15553             break;
15554           }
15555         break;
15556       }
15557       case KeyPress:
15558       {
15559         int
15560           length;
15561
15562         /*
15563           Respond to a user key press.
15564         */
15565         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15566           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15567         *(command+length)='\0';
15568         if (display_image->debug != MagickFalse)
15569           (void) LogMagickEvent(X11Event,GetMagickModule(),
15570             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15571             key_symbol,command);
15572         if (event.xkey.window == windows->image.id)
15573           {
15574             command_type=XImageWindowCommand(display,resource_info,windows,
15575               event.xkey.state,key_symbol,&display_image);
15576             if (command_type != NullCommand)
15577               nexus=XMagickCommand(display,resource_info,windows,command_type,
15578                 &display_image);
15579           }
15580         if (event.xkey.window == windows->magnify.id)
15581           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15582         if (event.xkey.window == windows->pan.id)
15583           {
15584             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15585               (void) XWithdrawWindow(display,windows->pan.id,
15586                 windows->pan.screen);
15587             else
15588               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15589                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15590                   "Help Viewer - Image Pan",ImagePanHelp);
15591               else
15592                 XTranslateImage(display,windows,*image,key_symbol);
15593           }
15594         delay=display_image->delay/MagickMax(
15595           display_image->ticks_per_second,1L);
15596         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15597         break;
15598       }
15599       case KeyRelease:
15600       {
15601         /*
15602           Respond to a user key release.
15603         */
15604         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15605           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15606         if (display_image->debug != MagickFalse)
15607           (void) LogMagickEvent(X11Event,GetMagickModule(),
15608             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15609         break;
15610       }
15611       case LeaveNotify:
15612       {
15613         /*
15614           Selectively uninstall colormap.
15615         */
15616         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15617           if (event.xcrossing.mode != NotifyUngrab)
15618             XUninstallColormap(display,map_info->colormap);
15619         break;
15620       }
15621       case MapNotify:
15622       {
15623         if (display_image->debug != MagickFalse)
15624           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15625             event.xmap.window);
15626         if (event.xmap.window == windows->backdrop.id)
15627           {
15628             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15629               CurrentTime);
15630             windows->backdrop.mapped=MagickTrue;
15631             break;
15632           }
15633         if (event.xmap.window == windows->image.id)
15634           {
15635             if (windows->backdrop.id != (Window) NULL)
15636               (void) XInstallColormap(display,map_info->colormap);
15637             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15638               {
15639                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15640                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15641               }
15642             if (((int) windows->image.width < windows->image.ximage->width) ||
15643                 ((int) windows->image.height < windows->image.ximage->height))
15644               (void) XMapRaised(display,windows->pan.id);
15645             windows->image.mapped=MagickTrue;
15646             break;
15647           }
15648         if (event.xmap.window == windows->magnify.id)
15649           {
15650             XMakeMagnifyImage(display,windows);
15651             windows->magnify.mapped=MagickTrue;
15652             (void) XWithdrawWindow(display,windows->info.id,
15653               windows->info.screen);
15654             break;
15655           }
15656         if (event.xmap.window == windows->pan.id)
15657           {
15658             XMakePanImage(display,resource_info,windows,display_image);
15659             windows->pan.mapped=MagickTrue;
15660             break;
15661           }
15662         if (event.xmap.window == windows->info.id)
15663           {
15664             windows->info.mapped=MagickTrue;
15665             break;
15666           }
15667         if (event.xmap.window == windows->icon.id)
15668           {
15669             MagickBooleanType
15670               taint;
15671
15672             /*
15673               Create an icon image.
15674             */
15675             taint=display_image->taint;
15676             XMakeStandardColormap(display,icon_visual,icon_resources,
15677               display_image,icon_map,icon_pixel);
15678             (void) XMakeImage(display,icon_resources,&windows->icon,
15679               display_image,windows->icon.width,windows->icon.height);
15680             display_image->taint=taint;
15681             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15682               windows->icon.pixmap);
15683             (void) XClearWindow(display,windows->icon.id);
15684             (void) XWithdrawWindow(display,windows->info.id,
15685               windows->info.screen);
15686             windows->icon.mapped=MagickTrue;
15687             break;
15688           }
15689         if (event.xmap.window == windows->command.id)
15690           {
15691             windows->command.mapped=MagickTrue;
15692             break;
15693           }
15694         if (event.xmap.window == windows->popup.id)
15695           {
15696             windows->popup.mapped=MagickTrue;
15697             break;
15698           }
15699         if (event.xmap.window == windows->widget.id)
15700           {
15701             windows->widget.mapped=MagickTrue;
15702             break;
15703           }
15704         break;
15705       }
15706       case MappingNotify:
15707       {
15708         (void) XRefreshKeyboardMapping(&event.xmapping);
15709         break;
15710       }
15711       case NoExpose:
15712         break;
15713       case PropertyNotify:
15714       {
15715         Atom
15716           type;
15717
15718         int
15719           format,
15720           status;
15721
15722         unsigned char
15723           *data;
15724
15725         unsigned long
15726           after,
15727           length;
15728
15729         if (display_image->debug != MagickFalse)
15730           (void) LogMagickEvent(X11Event,GetMagickModule(),
15731             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15732             event.xproperty.atom,event.xproperty.state);
15733         if (event.xproperty.atom != windows->im_remote_command)
15734           break;
15735         /*
15736           Display image named by the remote command protocol.
15737         */
15738         status=XGetWindowProperty(display,event.xproperty.window,
15739           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15740           AnyPropertyType,&type,&format,&length,&after,&data);
15741         if ((status != Success) || (length == 0))
15742           break;
15743         if (LocaleCompare((char *) data,"-quit") == 0)
15744           {
15745             XClientMessage(display,windows->image.id,windows->im_protocols,
15746               windows->im_exit,CurrentTime);
15747             (void) XFree((void *) data);
15748             break;
15749           }
15750         (void) CopyMagickString(resource_info->image_info->filename,
15751           (char *) data,MaxTextExtent);
15752         (void) XFree((void *) data);
15753         nexus=ReadImage(resource_info->image_info,&display_image->exception);
15754         CatchException(&display_image->exception);
15755         if (nexus != (Image *) NULL)
15756           *state|=NextImageState | ExitState;
15757         break;
15758       }
15759       case ReparentNotify:
15760       {
15761         if (display_image->debug != MagickFalse)
15762           (void) LogMagickEvent(X11Event,GetMagickModule(),
15763             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15764             event.xreparent.window);
15765         break;
15766       }
15767       case UnmapNotify:
15768       {
15769         if (display_image->debug != MagickFalse)
15770           (void) LogMagickEvent(X11Event,GetMagickModule(),
15771             "Unmap Notify: 0x%lx",event.xunmap.window);
15772         if (event.xunmap.window == windows->backdrop.id)
15773           {
15774             windows->backdrop.mapped=MagickFalse;
15775             break;
15776           }
15777         if (event.xunmap.window == windows->image.id)
15778           {
15779             windows->image.mapped=MagickFalse;
15780             break;
15781           }
15782         if (event.xunmap.window == windows->magnify.id)
15783           {
15784             windows->magnify.mapped=MagickFalse;
15785             break;
15786           }
15787         if (event.xunmap.window == windows->pan.id)
15788           {
15789             windows->pan.mapped=MagickFalse;
15790             break;
15791           }
15792         if (event.xunmap.window == windows->info.id)
15793           {
15794             windows->info.mapped=MagickFalse;
15795             break;
15796           }
15797         if (event.xunmap.window == windows->icon.id)
15798           {
15799             if (map_info->colormap == icon_map->colormap)
15800               XConfigureImageColormap(display,resource_info,windows,
15801                 display_image);
15802             (void) XFreeStandardColormap(display,icon_visual,icon_map,
15803               icon_pixel);
15804             windows->icon.mapped=MagickFalse;
15805             break;
15806           }
15807         if (event.xunmap.window == windows->command.id)
15808           {
15809             windows->command.mapped=MagickFalse;
15810             break;
15811           }
15812         if (event.xunmap.window == windows->popup.id)
15813           {
15814             if (windows->backdrop.id != (Window) NULL)
15815               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15816                 CurrentTime);
15817             windows->popup.mapped=MagickFalse;
15818             break;
15819           }
15820         if (event.xunmap.window == windows->widget.id)
15821           {
15822             if (windows->backdrop.id != (Window) NULL)
15823               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15824                 CurrentTime);
15825             windows->widget.mapped=MagickFalse;
15826             break;
15827           }
15828         break;
15829       }
15830       default:
15831       {
15832         if (display_image->debug != MagickFalse)
15833           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15834             event.type);
15835         break;
15836       }
15837     }
15838   } while (!(*state & ExitState));
15839   if ((*state & ExitState) == 0)
15840     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15841       &display_image);
15842   else
15843     if (resource_info->confirm_edit != MagickFalse)
15844       {
15845         /*
15846           Query user if image has changed.
15847         */
15848         if ((resource_info->immutable == MagickFalse) &&
15849             (display_image->taint != MagickFalse))
15850           {
15851             int
15852               status;
15853
15854             status=XConfirmWidget(display,windows,"Your image changed.",
15855               "Do you want to save it");
15856             if (status == 0)
15857               *state&=(~ExitState);
15858             else
15859               if (status > 0)
15860                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15861                   &display_image);
15862           }
15863       }
15864   if ((windows->visual_info->klass == GrayScale) ||
15865       (windows->visual_info->klass == PseudoColor) ||
15866       (windows->visual_info->klass == DirectColor))
15867     {
15868       /*
15869         Withdraw pan and Magnify window.
15870       */
15871       if (windows->info.mapped != MagickFalse)
15872         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15873       if (windows->magnify.mapped != MagickFalse)
15874         (void) XWithdrawWindow(display,windows->magnify.id,
15875           windows->magnify.screen);
15876       if (windows->command.mapped != MagickFalse)
15877         (void) XWithdrawWindow(display,windows->command.id,
15878           windows->command.screen);
15879     }
15880   if (windows->pan.mapped != MagickFalse)
15881     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15882   if (resource_info->backdrop == MagickFalse)
15883     if (windows->backdrop.mapped)
15884       {
15885         (void) XWithdrawWindow(display,windows->backdrop.id,
15886           windows->backdrop.screen);
15887         (void) XDestroyWindow(display,windows->backdrop.id);
15888         windows->backdrop.id=(Window) NULL;
15889         (void) XWithdrawWindow(display,windows->image.id,
15890           windows->image.screen);
15891         (void) XDestroyWindow(display,windows->image.id);
15892         windows->image.id=(Window) NULL;
15893       }
15894   XSetCursorState(display,windows,MagickTrue);
15895   XCheckRefreshWindows(display,windows);
15896   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15897     *state&=(~ExitState);
15898   if (*state & ExitState)
15899     {
15900       /*
15901         Free Standard Colormap.
15902       */
15903       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
15904       if (resource_info->map_type == (char *) NULL)
15905         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
15906       /*
15907         Free X resources.
15908       */
15909       if (resource_info->copy_image != (Image *) NULL)
15910         {
15911           resource_info->copy_image=DestroyImage(resource_info->copy_image);
15912           resource_info->copy_image=NewImageList();
15913         }
15914       DestroyXResources();
15915     }
15916   (void) XSync(display,MagickFalse);
15917   /*
15918     Restore our progress monitor and warning handlers.
15919   */
15920   (void) SetErrorHandler(warning_handler);
15921   (void) SetWarningHandler(warning_handler);
15922   /*
15923     Change to home directory.
15924   */
15925   cwd=getcwd(working_directory,MaxTextExtent);
15926   {
15927     int
15928       status;
15929
15930     status=chdir(resource_info->home_directory);
15931     if (status == -1)
15932       (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
15933         FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
15934   }
15935   *image=display_image;
15936   return(nexus);
15937 }
15938 #else
15939 \f
15940 /*
15941 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15942 %                                                                             %
15943 %                                                                             %
15944 %                                                                             %
15945 +   D i s p l a y I m a g e s                                                 %
15946 %                                                                             %
15947 %                                                                             %
15948 %                                                                             %
15949 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15950 %
15951 %  DisplayImages() displays an image sequence to any X window screen.  It
15952 %  returns a value other than 0 if successful.  Check the exception member
15953 %  of image to determine the reason for any failure.
15954 %
15955 %  The format of the DisplayImages method is:
15956 %
15957 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
15958 %        Image *images)
15959 %
15960 %  A description of each parameter follows:
15961 %
15962 %    o image_info: the image info.
15963 %
15964 %    o image: the image.
15965 %
15966 */
15967 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
15968   Image *image)
15969 {
15970   assert(image_info != (const ImageInfo *) NULL);
15971   assert(image_info->signature == MagickSignature);
15972   assert(image != (Image *) NULL);
15973   assert(image->signature == MagickSignature);
15974   if (image->debug != MagickFalse)
15975     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
15976   (void) ThrowMagickException(&image->exception,GetMagickModule(),
15977     MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
15978     image->filename);
15979   return(MagickFalse);
15980 }
15981 \f
15982 /*
15983 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15984 %                                                                             %
15985 %                                                                             %
15986 %                                                                             %
15987 +   R e m o t e D i s p l a y C o m m a n d                                   %
15988 %                                                                             %
15989 %                                                                             %
15990 %                                                                             %
15991 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15992 %
15993 %  RemoteDisplayCommand() encourages a remote display program to display the
15994 %  specified image filename.
15995 %
15996 %  The format of the RemoteDisplayCommand method is:
15997 %
15998 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
15999 %        const char *window,const char *filename,ExceptionInfo *exception)
16000 %
16001 %  A description of each parameter follows:
16002 %
16003 %    o image_info: the image info.
16004 %
16005 %    o window: Specifies the name or id of an X window.
16006 %
16007 %    o filename: the name of the image filename to display.
16008 %
16009 %    o exception: return any errors or warnings in this structure.
16010 %
16011 */
16012 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16013   const char *window,const char *filename,ExceptionInfo *exception)
16014 {
16015   assert(image_info != (const ImageInfo *) NULL);
16016   assert(image_info->signature == MagickSignature);
16017   assert(filename != (char *) NULL);
16018   (void) window;
16019   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16020   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16021     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16022   return(MagickFalse);
16023 }
16024 #endif