]> granicus.if.org Git - imagemagick/blob - magick/display.c
9d73ca7152dcb87ada23ab7c2e2e04f5b67a0c46
[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((ssize_t) 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   OpaqueBitmap[8] =
106   {
107     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
108   },
109   ShadowBitmap[8] =
110   {
111     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
112   };
113
114 static const char
115   *PageSizes[] =
116   {
117     "Letter",
118     "Tabloid",
119     "Ledger",
120     "Legal",
121     "Statement",
122     "Executive",
123     "A3",
124     "A4",
125     "A5",
126     "B4",
127     "B5",
128     "Folio",
129     "Quarto",
130     "10x14",
131     (char *) NULL
132   };
133 \f
134 /*
135   Help widget declarations.
136 */
137 static const char
138   *ImageAnnotateHelp[] =
139   {
140     "In annotate mode, the Command widget has these options:",
141     "",
142     "    Font Name",
143     "      fixed",
144     "      variable",
145     "      5x8",
146     "      6x10",
147     "      7x13bold",
148     "      8x13bold",
149     "      9x15bold",
150     "      10x20",
151     "      12x24",
152     "      Browser...",
153     "    Font Color",
154     "      black",
155     "      blue",
156     "      cyan",
157     "      green",
158     "      gray",
159     "      red",
160     "      magenta",
161     "      yellow",
162     "      white",
163     "      transparent",
164     "      Browser...",
165     "    Font Color",
166     "      black",
167     "      blue",
168     "      cyan",
169     "      green",
170     "      gray",
171     "      red",
172     "      magenta",
173     "      yellow",
174     "      white",
175     "      transparent",
176     "      Browser...",
177     "    Rotate Text",
178     "      -90",
179     "      -45",
180     "      -30",
181     "      0",
182     "      30",
183     "      45",
184     "      90",
185     "      180",
186     "      Dialog...",
187     "    Help",
188     "    Dismiss",
189     "",
190     "Choose a font name from the Font Name sub-menu.  Additional",
191     "font names can be specified with the font browser.  You can",
192     "change the menu names by setting the X resources font1",
193     "through font9.",
194     "",
195     "Choose a font color from the Font Color sub-menu.",
196     "Additional font colors can be specified with the color",
197     "browser.  You can change the menu colors by setting the X",
198     "resources pen1 through pen9.",
199     "",
200     "If you select the color browser and press Grab, you can",
201     "choose the font color by moving the pointer to the desired",
202     "color on the screen and press any button.",
203     "",
204     "If you choose to rotate the text, choose Rotate Text from the",
205     "menu and select an angle.  Typically you will only want to",
206     "rotate one line of text at a time.  Depending on the angle you",
207     "choose, subsequent lines may end up overwriting each other.",
208     "",
209     "Choosing a font and its color is optional.  The default font",
210     "is fixed and the default color is black.  However, you must",
211     "choose a location to begin entering text and press button 1.",
212     "An underscore character will appear at the location of the",
213     "pointer.  The cursor changes to a pencil to indicate you are",
214     "in text mode.  To exit immediately, press Dismiss.",
215     "",
216     "In text mode, any key presses will display the character at",
217     "the location of the underscore and advance the underscore",
218     "cursor.  Enter your text and once completed press Apply to",
219     "finish your image annotation.  To correct errors press BACK",
220     "SPACE.  To delete an entire line of text, press DELETE.  Any",
221     "text that exceeds the boundaries of the image window is",
222     "automagically continued onto the next line.",
223     "",
224     "The actual color you request for the font is saved in the",
225     "image.  However, the color that appears in your image window",
226     "may be different.  For example, on a monochrome screen the",
227     "text will appear black or white even if you choose the color",
228     "red as the font color.  However, the image saved to a file",
229     "with -write is written with red lettering.  To assure the",
230     "correct color text in the final image, any PseudoClass image",
231     "is promoted to DirectClass (see miff(5)).  To force a",
232     "PseudoClass image to remain PseudoClass, use -colors.",
233     (char *) NULL,
234   },
235   *ImageChopHelp[] =
236   {
237     "In chop mode, the Command widget has these options:",
238     "",
239     "    Direction",
240     "      horizontal",
241     "      vertical",
242     "    Help",
243     "    Dismiss",
244     "",
245     "If the you choose the horizontal direction (this the",
246     "default), the area of the image between the two horizontal",
247     "endpoints of the chop line is removed.  Otherwise, the area",
248     "of the image between the two vertical endpoints of the chop",
249     "line is removed.",
250     "",
251     "Select a location within the image window to begin your chop,",
252     "press and hold any button.  Next, move the pointer to",
253     "another location in the image.  As you move a line will",
254     "connect the initial location and the pointer.  When you",
255     "release the button, the area within the image to chop is",
256     "determined by which direction you choose from the Command",
257     "widget.",
258     "",
259     "To cancel the image chopping, move the pointer back to the",
260     "starting point of the line and release the button.",
261     (char *) NULL,
262   },
263   *ImageColorEditHelp[] =
264   {
265     "In color edit mode, the Command widget has these options:",
266     "",
267     "    Method",
268     "      point",
269     "      replace",
270     "      floodfill",
271     "      filltoborder",
272     "      reset",
273     "    Pixel Color",
274     "      black",
275     "      blue",
276     "      cyan",
277     "      green",
278     "      gray",
279     "      red",
280     "      magenta",
281     "      yellow",
282     "      white",
283     "      Browser...",
284     "    Border Color",
285     "      black",
286     "      blue",
287     "      cyan",
288     "      green",
289     "      gray",
290     "      red",
291     "      magenta",
292     "      yellow",
293     "      white",
294     "      Browser...",
295     "    Fuzz",
296     "      0%",
297     "      2%",
298     "      5%",
299     "      10%",
300     "      15%",
301     "      Dialog...",
302     "    Undo",
303     "    Help",
304     "    Dismiss",
305     "",
306     "Choose a color editing method from the Method sub-menu",
307     "of the Command widget.  The point method recolors any pixel",
308     "selected with the pointer until the button is released.  The",
309     "replace method recolors any pixel that matches the color of",
310     "the pixel you select with a button press.  Floodfill recolors",
311     "any pixel that matches the color of the pixel you select with",
312     "a button press and is a neighbor.  Whereas filltoborder recolors",
313     "any neighbor pixel that is not the border color.  Finally reset",
314     "changes the entire image to the designated color.",
315     "",
316     "Next, choose a pixel color from the Pixel Color sub-menu.",
317     "Additional pixel colors can be specified with the color",
318     "browser.  You can change the menu colors by setting the X",
319     "resources pen1 through pen9.",
320     "",
321     "Now press button 1 to select a pixel within the image window",
322     "to change its color.  Additional pixels may be recolored as",
323     "prescribed by the method you choose.",
324     "",
325     "If the Magnify widget is mapped, it can be helpful in positioning",
326     "your pointer within the image (refer to button 2).",
327     "",
328     "The actual color you request for the pixels is saved in the",
329     "image.  However, the color that appears in your image window",
330     "may be different.  For example, on a monochrome screen the",
331     "pixel will appear black or white even if you choose the",
332     "color red as the pixel color.  However, the image saved to a",
333     "file with -write is written with red pixels.  To assure the",
334     "correct color text in the final image, any PseudoClass image",
335     "is promoted to DirectClass (see miff(5)).  To force a",
336     "PseudoClass image to remain PseudoClass, use -colors.",
337     (char *) NULL,
338   },
339   *ImageCompositeHelp[] =
340   {
341     "First a widget window is displayed requesting you to enter an",
342     "image name. Press Composite, Grab or type a file name.",
343     "Press Cancel if you choose not to create a composite image.",
344     "When you choose Grab, move the pointer to the desired window",
345     "and press any button.",
346     "",
347     "If the Composite image does not have any matte information,",
348     "you are informed and the file browser is displayed again.",
349     "Enter the name of a mask image.  The image is typically",
350     "grayscale and the same size as the composite image.  If the",
351     "image is not grayscale, it is converted to grayscale and the",
352     "resulting intensities are used as matte information.",
353     "",
354     "A small window appears showing the location of the cursor in",
355     "the image window. You are now in composite mode.  To exit",
356     "immediately, press Dismiss.  In composite mode, the Command",
357     "widget has these options:",
358     "",
359     "    Operators",
360     "      Over",
361     "      In",
362     "      Out",
363     "      Atop",
364     "      Xor",
365     "      Plus",
366     "      Minus",
367     "      Add",
368     "      Subtract",
369     "      Difference",
370     "      Multiply",
371     "      Bumpmap",
372     "      Copy",
373     "      CopyRed",
374     "      CopyGreen",
375     "      CopyBlue",
376     "      CopyOpacity",
377     "      Clear",
378     "    Dissolve",
379     "    Displace",
380     "    Help",
381     "    Dismiss",
382     "",
383     "Choose a composite operation from the Operators sub-menu of",
384     "the Command widget.  How each operator behaves is described",
385     "below.  Image window is the image currently displayed on",
386     "your X server and image is the image obtained with the File",
387     "Browser widget.",
388     "",
389     "Over     The result is the union of the two image shapes,",
390     "         with image obscuring image window in the region of",
391     "         overlap.",
392     "",
393     "In       The result is simply image cut by the shape of",
394     "         image window.  None of the image data of image",
395     "         window is in the result.",
396     "",
397     "Out      The resulting image is image with the shape of",
398     "         image window cut out.",
399     "",
400     "Atop     The result is the same shape as image image window,",
401     "         with image obscuring image window where the image",
402     "         shapes overlap.  Note this differs from over",
403     "         because the portion of image outside image window's",
404     "         shape does not appear in the result.",
405     "",
406     "Xor      The result is the image data from both image and",
407     "         image window that is outside the overlap region.",
408     "         The overlap region is blank.",
409     "",
410     "Plus     The result is just the sum of the image data.",
411     "         Output values are cropped to QuantumRange (no overflow).",
412     "",
413     "Minus    The result of image - image window, with underflow",
414     "         cropped to zero.",
415     "",
416     "Add      The result of image + image window, with overflow",
417     "         wrapping around (mod 256).",
418     "",
419     "Subtract The result of image - image window, with underflow",
420     "         wrapping around (mod 256).  The add and subtract",
421     "         operators can be used to perform reversible",
422     "         transformations.",
423     "",
424     "Difference",
425     "         The result of abs(image - image window).  This",
426     "         useful for comparing two very similar images.",
427     "",
428     "Multiply",
429     "         The result of image * image window.  This",
430     "         useful for the creation of drop-shadows.",
431     "",
432     "Bumpmap  The result of surface normals from image * image",
433     "         window.",
434     "",
435     "Copy     The resulting image is image window replaced with",
436     "         image.  Here the matte information is ignored.",
437     "",
438     "CopyRed  The red layer of the image window is replace with",
439     "         the red layer of the image.  The other layers are",
440     "         untouched.",
441     "",
442     "CopyGreen",
443     "         The green layer of the image window is replace with",
444     "         the green layer of the image.  The other layers are",
445     "         untouched.",
446     "",
447     "CopyBlue The blue layer of the image window is replace with",
448     "         the blue layer of the image.  The other layers are",
449     "         untouched.",
450     "",
451     "CopyOpacity",
452     "         The matte layer of the image window is replace with",
453     "         the matte layer of the image.  The other layers are",
454     "         untouched.",
455     "",
456     "The image compositor requires a matte, or alpha channel in",
457     "the image for some operations.  This extra channel usually",
458     "defines a mask which represents a sort of a cookie-cutter",
459     "for the image.  This the case when matte is opaque (full",
460     "coverage) for pixels inside the shape, zero outside, and",
461     "between 0 and QuantumRange on the boundary.  If image does not",
462     "have a matte channel, it is initialized with 0 for any pixel",
463     "matching in color to pixel location (0,0), otherwise QuantumRange.",
464     "",
465     "If you choose Dissolve, the composite operator becomes Over.  The",
466     "image matte channel percent transparency is initialized to factor.",
467     "The image window is initialized to (100-factor). Where factor is the",
468     "value you specify in the Dialog widget.",
469     "",
470     "Displace shifts the image pixels as defined by a displacement",
471     "map.  With this option, image is used as a displacement map.",
472     "Black, within the displacement map, is a maximum positive",
473     "displacement.  White is a maximum negative displacement and",
474     "middle gray is neutral.  The displacement is scaled to determine",
475     "the pixel shift.  By default, the displacement applies in both the",
476     "horizontal and vertical directions.  However, if you specify a mask,",
477     "image is the horizontal X displacement and mask the vertical Y",
478     "displacement.",
479     "",
480     "Note that matte information for image window is not retained",
481     "for colormapped X server visuals (e.g. StaticColor,",
482     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
483     "behavior may require a TrueColor or DirectColor visual or a",
484     "Standard Colormap.",
485     "",
486     "Choosing a composite operator is optional.  The default",
487     "operator is replace.  However, you must choose a location to",
488     "composite your image and press button 1.  Press and hold the",
489     "button before releasing and an outline of the image will",
490     "appear to help you identify your location.",
491     "",
492     "The actual colors of the composite image is saved.  However,",
493     "the color that appears in image window may be different.",
494     "For example, on a monochrome screen image window will appear",
495     "black or white even though your composited image may have",
496     "many colors.  If the image is saved to a file it is written",
497     "with the correct colors.  To assure the correct colors are",
498     "saved in the final image, any PseudoClass image is promoted",
499     "to DirectClass (see miff(5)).  To force a PseudoClass image",
500     "to remain PseudoClass, use -colors.",
501     (char *) NULL,
502   },
503   *ImageCutHelp[] =
504   {
505     "In cut mode, the Command widget has these options:",
506     "",
507     "    Help",
508     "    Dismiss",
509     "",
510     "To define a cut region, press button 1 and drag.  The",
511     "cut region is defined by a highlighted rectangle that",
512     "expands or contracts as it follows the pointer.  Once you",
513     "are satisfied with the cut region, release the button.",
514     "You are now in rectify mode.  In rectify mode, the Command",
515     "widget has these options:",
516     "",
517     "    Cut",
518     "    Help",
519     "    Dismiss",
520     "",
521     "You can make adjustments by moving the pointer to one of the",
522     "cut rectangle corners, pressing a button, and dragging.",
523     "Finally, press Cut to commit your copy region.  To",
524     "exit without cutting the image, press Dismiss.",
525     (char *) NULL,
526   },
527   *ImageCopyHelp[] =
528   {
529     "In copy mode, the Command widget has these options:",
530     "",
531     "    Help",
532     "    Dismiss",
533     "",
534     "To define a copy region, press button 1 and drag.  The",
535     "copy region is defined by a highlighted rectangle that",
536     "expands or contracts as it follows the pointer.  Once you",
537     "are satisfied with the copy region, release the button.",
538     "You are now in rectify mode.  In rectify mode, the Command",
539     "widget has these options:",
540     "",
541     "    Copy",
542     "    Help",
543     "    Dismiss",
544     "",
545     "You can make adjustments by moving the pointer to one of the",
546     "copy rectangle corners, pressing a button, and dragging.",
547     "Finally, press Copy to commit your copy region.  To",
548     "exit without copying the image, press Dismiss.",
549     (char *) NULL,
550   },
551   *ImageCropHelp[] =
552   {
553     "In crop mode, the Command widget has these options:",
554     "",
555     "    Help",
556     "    Dismiss",
557     "",
558     "To define a cropping region, press button 1 and drag.  The",
559     "cropping region is defined by a highlighted rectangle that",
560     "expands or contracts as it follows the pointer.  Once you",
561     "are satisfied with the cropping region, release the button.",
562     "You are now in rectify mode.  In rectify mode, the Command",
563     "widget has these options:",
564     "",
565     "    Crop",
566     "    Help",
567     "    Dismiss",
568     "",
569     "You can make adjustments by moving the pointer to one of the",
570     "cropping rectangle corners, pressing a button, and dragging.",
571     "Finally, press Crop to commit your cropping region.  To",
572     "exit without cropping the image, press Dismiss.",
573     (char *) NULL,
574   },
575   *ImageDrawHelp[] =
576   {
577     "The cursor changes to a crosshair to indicate you are in",
578     "draw mode.  To exit immediately, press Dismiss.  In draw mode,",
579     "the Command widget has these options:",
580     "",
581     "    Element",
582     "      point",
583     "      line",
584     "      rectangle",
585     "      fill rectangle",
586     "      circle",
587     "      fill circle",
588     "      ellipse",
589     "      fill ellipse",
590     "      polygon",
591     "      fill polygon",
592     "    Color",
593     "      black",
594     "      blue",
595     "      cyan",
596     "      green",
597     "      gray",
598     "      red",
599     "      magenta",
600     "      yellow",
601     "      white",
602     "      transparent",
603     "      Browser...",
604     "    Stipple",
605     "      Brick",
606     "      Diagonal",
607     "      Scales",
608     "      Vertical",
609     "      Wavy",
610     "      Translucent",
611     "      Opaque",
612     "      Open...",
613     "    Width",
614     "      1",
615     "      2",
616     "      4",
617     "      8",
618     "      16",
619     "      Dialog...",
620     "    Undo",
621     "    Help",
622     "    Dismiss",
623     "",
624     "Choose a drawing primitive from the Element sub-menu.",
625     "",
626     "Choose a color from the Color sub-menu.  Additional",
627     "colors can be specified with the color browser.",
628     "",
629     "If you choose the color browser and press Grab, you can",
630     "select the color by moving the pointer to the desired",
631     "color on the screen and press any button.  The transparent",
632     "color updates the image matte channel and is useful for",
633     "image compositing.",
634     "",
635     "Choose a stipple, if appropriate, from the Stipple sub-menu.",
636     "Additional stipples can be specified with the file browser.",
637     "Stipples obtained from the file browser must be on disk in the",
638     "X11 bitmap format.",
639     "",
640     "Choose a width, if appropriate, from the Width sub-menu.  To",
641     "choose a specific width select the Dialog widget.",
642     "",
643     "Choose a point in the Image window and press button 1 and",
644     "hold.  Next, move the pointer to another location in the",
645     "image.  As you move, a line connects the initial location and",
646     "the pointer.  When you release the button, the image is",
647     "updated with the primitive you just drew.  For polygons, the",
648     "image is updated when you press and release the button without",
649     "moving the pointer.",
650     "",
651     "To cancel image drawing, move the pointer back to the",
652     "starting point of the line and release the button.",
653     (char *) NULL,
654   },
655   *DisplayHelp[] =
656   {
657     "BUTTONS",
658     "  The effects of each button press is described below.  Three",
659     "  buttons are required.  If you have a two button mouse,",
660     "  button 1 and 3 are returned.  Press ALT and button 3 to",
661     "  simulate button 2.",
662     "",
663     "  1    Press this button to map or unmap the Command widget.",
664     "",
665     "  2    Press and drag to define a region of the image to",
666     "       magnify.",
667     "",
668     "  3    Press and drag to choose from a select set of commands.",
669     "       This button behaves differently if the image being",
670     "       displayed is a visual image directory.  Here, choose a",
671     "       particular tile of the directory and press this button and",
672     "       drag to select a command from a pop-up menu.  Choose from",
673     "       these menu items:",
674     "",
675     "           Open",
676     "           Next",
677     "           Former",
678     "           Delete",
679     "           Update",
680     "",
681     "       If you choose Open, the image represented by the tile is",
682     "       displayed.  To return to the visual image directory, choose",
683     "       Next from the Command widget.  Next and Former moves to the",
684     "       next or former image respectively.  Choose Delete to delete",
685     "       a particular image tile.  Finally, choose Update to",
686     "       synchronize all the image tiles with their respective",
687     "       images.",
688     "",
689     "COMMAND WIDGET",
690     "  The Command widget lists a number of sub-menus and commands.",
691     "  They are",
692     "",
693     "      File",
694     "        Open...",
695     "        Next",
696     "        Former",
697     "        Select...",
698     "        Save...",
699     "        Print...",
700     "        Delete...",
701     "        New...",
702     "        Visual Directory...",
703     "        Quit",
704     "      Edit",
705     "        Undo",
706     "        Redo",
707     "        Cut",
708     "        Copy",
709     "        Paste",
710     "      View",
711     "        Half Size",
712     "        Original Size",
713     "        Double Size",
714     "        Resize...",
715     "        Apply",
716     "        Refresh",
717     "        Restore",
718     "      Transform",
719     "        Crop",
720     "        Chop",
721     "        Flop",
722     "        Flip",
723     "        Rotate Right",
724     "        Rotate Left",
725     "        Rotate...",
726     "        Shear...",
727     "        Roll...",
728     "        Trim Edges",
729     "      Enhance",
730     "        Brightness...",
731     "        Saturation...",
732     "        Hue...",
733     "        Gamma...",
734     "        Sharpen...",
735     "        Dull",
736     "        Contrast Stretch...",
737     "        Sigmoidal Contrast...",
738     "        Normalize",
739     "        Equalize",
740     "        Negate",
741     "        Grayscale",
742     "        Map...",
743     "        Quantize...",
744     "      Effects",
745     "        Despeckle",
746     "        Emboss",
747     "        Reduce Noise",
748     "        Add Noise",
749     "        Sharpen...",
750     "        Blur...",
751     "        Threshold...",
752     "        Edge Detect...",
753     "        Spread...",
754     "        Shade...",
755     "        Painting...",
756     "        Segment...",
757     "      F/X",
758     "        Solarize...",
759     "        Sepia Tone...",
760     "        Swirl...",
761     "        Implode...",
762     "        Vignette...",
763     "        Wave...",
764     "        Oil Painting...",
765     "        Charcoal Drawing...",
766     "      Image Edit",
767     "        Annotate...",
768     "        Draw...",
769     "        Color...",
770     "        Matte...",
771     "        Composite...",
772     "        Add Border...",
773     "        Add Frame...",
774     "        Comment...",
775     "        Launch...",
776     "        Region of Interest...",
777     "      Miscellany",
778     "        Image Info",
779     "        Zoom Image",
780     "        Show Preview...",
781     "        Show Histogram",
782     "        Show Matte",
783     "        Background...",
784     "        Slide Show",
785     "        Preferences...",
786     "      Help",
787     "        Overview",
788     "        Browse Documentation",
789     "        About Display",
790     "",
791     "  Menu items with a indented triangle have a sub-menu.  They",
792     "  are represented above as the indented items.  To access a",
793     "  sub-menu item, move the pointer to the appropriate menu and",
794     "  press a button and drag.  When you find the desired sub-menu",
795     "  item, release the button and the command is executed.  Move",
796     "  the pointer away from the sub-menu if you decide not to",
797     "  execute a particular command.",
798     "",
799     "KEYBOARD ACCELERATORS",
800     "  Accelerators are one or two key presses that effect a",
801     "  particular command.  The keyboard accelerators that",
802     "  display(1) understands is:",
803     "",
804     "  Ctl+O     Press to open an image from a file.",
805     "",
806     "  space     Press to display the next image.",
807     "",
808     "            If the image is a multi-paged document such as a Postscript",
809     "            document, you can skip ahead several pages by preceding",
810     "            this command with a number.  For example to display the",
811     "            third page beyond the current page, press 3<space>.",
812     "",
813     "  backspace Press to display the former image.",
814     "",
815     "            If the image is a multi-paged document such as a Postscript",
816     "            document, you can skip behind several pages by preceding",
817     "            this command with a number.  For example to display the",
818     "            third page preceding the current page, press 3<backspace>.",
819     "",
820     "  Ctl+S     Press to write the image to a file.",
821     "",
822     "  Ctl+P     Press to print the image to a Postscript printer.",
823     "",
824     "  Ctl+D     Press to delete an image file.",
825     "",
826     "  Ctl+N     Press to create a blank canvas.",
827     "",
828     "  Ctl+Q     Press to discard all images and exit program.",
829     "",
830     "  Ctl+Z     Press to undo last image transformation.",
831     "",
832     "  Ctl+R     Press to redo last image transformation.",
833     "",
834     "  Ctl+X     Press to cut a region of the image.",
835     "",
836     "  Ctl+C     Press to copy a region of the image.",
837     "",
838     "  Ctl+V     Press to paste a region to the image.",
839     "",
840     "  <         Press to half the image size.",
841     "",
842     "  -         Press to return to the original image size.",
843     "",
844     "  >         Press to double the image size.",
845     "",
846     "  %         Press to resize the image to a width and height you",
847     "            specify.",
848     "",
849     "Cmd-A       Press to make any image transformations permanent."
850     "",
851     "            By default, any image size transformations are applied",
852     "            to the original image to create the image displayed on",
853     "            the X server.  However, the transformations are not",
854     "            permanent (i.e. the original image does not change",
855     "            size only the X image does).  For example, if you",
856     "            press > the X image will appear to double in size,",
857     "            but the original image will in fact remain the same size.",
858     "            To force the original image to double in size, press >",
859     "            followed by Cmd-A.",
860     "",
861     "  @         Press to refresh the image window.",
862     "",
863     "  C         Press to cut out a rectangular region of the image.",
864     "",
865     "  [         Press to chop the image.",
866     "",
867     "  H         Press to flop image in the horizontal direction.",
868     "",
869     "  V         Press to flip image in the vertical direction.",
870     "",
871     "  /         Press to rotate the image 90 degrees clockwise.",
872     "",
873     " \\         Press to rotate the image 90 degrees counter-clockwise.",
874     "",
875     "  *         Press to rotate the image the number of degrees you",
876     "            specify.",
877     "",
878     "  S         Press to shear the image the number of degrees you",
879     "            specify.",
880     "",
881     "  R         Press to roll the image.",
882     "",
883     "  T         Press to trim the image edges.",
884     "",
885     "  Shft-H    Press to vary the image hue.",
886     "",
887     "  Shft-S    Press to vary the color saturation.",
888     "",
889     "  Shft-L    Press to vary the color brightness.",
890     "",
891     "  Shft-G    Press to gamma correct the image.",
892     "",
893     "  Shft-C    Press to sharpen the image contrast.",
894     "",
895     "  Shft-Z    Press to dull the image contrast.",
896     "",
897     "  =         Press to perform histogram equalization on the image.",
898     "",
899     "  Shft-N    Press to perform histogram normalization on the image.",
900     "",
901     "  Shft-~    Press to negate the colors of the image.",
902     "",
903     "  .         Press to convert the image colors to gray.",
904     "",
905     "  Shft-#    Press to set the maximum number of unique colors in the",
906     "            image.",
907     "",
908     "  F2        Press to reduce the speckles in an image.",
909     "",
910     "  F3        Press to eliminate peak noise from an image.",
911     "",
912     "  F4        Press to add noise to an image.",
913     "",
914     "  F5        Press to sharpen an image.",
915     "",
916     "  F6        Press to delete an image file.",
917     "",
918     "  F7        Press to threshold the image.",
919     "",
920     "  F8        Press to detect edges within an image.",
921     "",
922     "  F9        Press to emboss an image.",
923     "",
924     "  F10       Press to displace pixels by a random amount.",
925     "",
926     "  F11       Press to negate all pixels above the threshold level.",
927     "",
928     "  F12       Press to shade the image using a distant light source.",
929     "",
930     "  F13       Press to lighten or darken image edges to create a 3-D effect.",
931     "",
932     "  F14       Press to segment the image by color.",
933     "",
934     "  Meta-S    Press to swirl image pixels about the center.",
935     "",
936     "  Meta-I    Press to implode image pixels about the center.",
937     "",
938     "  Meta-W    Press to alter an image along a sine wave.",
939     "",
940     "  Meta-P    Press to simulate an oil painting.",
941     "",
942     "  Meta-C    Press to simulate a charcoal drawing.",
943     "",
944     "  Alt-A     Press to annotate the image with text.",
945     "",
946     "  Alt-D     Press to draw on an image.",
947     "",
948     "  Alt-P     Press to edit an image pixel color.",
949     "",
950     "  Alt-M     Press to edit the image matte information.",
951     "",
952     "  Alt-V     Press to composite the image with another.",
953     "",
954     "  Alt-B     Press to add a border to the image.",
955     "",
956     "  Alt-F     Press to add an ornamental border to the image.",
957     "",
958     "  Alt-Shft-!",
959     "            Press to add an image comment.",
960     "",
961     "  Ctl-A     Press to apply image processing techniques to a region",
962     "            of interest.",
963     "",
964     "  Shft-?    Press to display information about the image.",
965     "",
966     "  Shft-+    Press to map the zoom image window.",
967     "",
968     "  Shft-P    Press to preview an image enhancement, effect, or f/x.",
969     "",
970     "  F1        Press to display helpful information about display(1).",
971     "",
972     "  Find      Press to browse documentation about ImageMagick.",
973     "",
974     "  1-9       Press to change the level of magnification.",
975     "",
976     "  Use the arrow keys to move the image one pixel up, down,",
977     "  left, or right within the magnify window.  Be sure to first",
978     "  map the magnify window by pressing button 2.",
979     "",
980     "  Press ALT and one of the arrow keys to trim off one pixel",
981     "  from any side of the image.",
982     (char *) NULL,
983   },
984   *ImageMatteEditHelp[] =
985   {
986     "Matte information within an image is useful for some",
987     "operations such as image compositing (See IMAGE",
988     "COMPOSITING).  This extra channel usually defines a mask",
989     "which represents a sort of a cookie-cutter for the image.",
990     "This the case when matte is opaque (full coverage) for",
991     "pixels inside the shape, zero outside, and between 0 and",
992     "QuantumRange on the boundary.",
993     "",
994     "A small window appears showing the location of the cursor in",
995     "the image window. You are now in matte edit mode.  To exit",
996     "immediately, press Dismiss.  In matte edit mode, the Command",
997     "widget has these options:",
998     "",
999     "    Method",
1000     "      point",
1001     "      replace",
1002     "      floodfill",
1003     "      filltoborder",
1004     "      reset",
1005     "    Border Color",
1006     "      black",
1007     "      blue",
1008     "      cyan",
1009     "      green",
1010     "      gray",
1011     "      red",
1012     "      magenta",
1013     "      yellow",
1014     "      white",
1015     "      Browser...",
1016     "    Fuzz",
1017     "      0%",
1018     "      2%",
1019     "      5%",
1020     "      10%",
1021     "      15%",
1022     "      Dialog...",
1023     "    Matte",
1024     "      Opaque",
1025     "      Transparent",
1026     "      Dialog...",
1027     "    Undo",
1028     "    Help",
1029     "    Dismiss",
1030     "",
1031     "Choose a matte editing method from the Method sub-menu of",
1032     "the Command widget.  The point method changes the matte value",
1033     "of any pixel selected with the pointer until the button is",
1034     "is released.  The replace method changes the matte value of",
1035     "any pixel that matches the color of the pixel you select with",
1036     "a button press.  Floodfill changes the matte value of any pixel",
1037     "that matches the color of the pixel you select with a button",
1038     "press and is a neighbor.  Whereas filltoborder changes the matte",
1039     "value any neighbor pixel that is not the border color.  Finally",
1040     "reset changes the entire image to the designated matte value.",
1041     "",
1042     "Choose Matte Value and pick Opaque or Transarent.  For other values",
1043     "select the Dialog entry.  Here a dialog appears requesting a matte",
1044     "value.  The value you select is assigned as the opacity value of the",
1045     "selected pixel or pixels.",
1046     "",
1047     "Now, press any button to select a pixel within the image",
1048     "window to change its matte value.",
1049     "",
1050     "If the Magnify widget is mapped, it can be helpful in positioning",
1051     "your pointer within the image (refer to button 2).",
1052     "",
1053     "Matte information is only valid in a DirectClass image.",
1054     "Therefore, any PseudoClass image is promoted to DirectClass",
1055     "(see miff(5)).  Note that matte information for PseudoClass",
1056     "is not retained for colormapped X server visuals (e.g.",
1057     "StaticColor, StaticColor, GrayScale, PseudoColor) unless you",
1058     "immediately save your image to a file (refer to Write).",
1059     "Correct matte editing behavior may require a TrueColor or",
1060     "DirectColor visual or a Standard Colormap.",
1061     (char *) NULL,
1062   },
1063   *ImagePanHelp[] =
1064   {
1065     "When an image exceeds the width or height of the X server",
1066     "screen, display maps a small panning icon.  The rectangle",
1067     "within the panning icon shows the area that is currently",
1068     "displayed in the image window.  To pan about the image,",
1069     "press any button and drag the pointer within the panning",
1070     "icon.  The pan rectangle moves with the pointer and the",
1071     "image window is updated to reflect the location of the",
1072     "rectangle within the panning icon.  When you have selected",
1073     "the area of the image you wish to view, release the button.",
1074     "",
1075     "Use the arrow keys to pan the image one pixel up, down,",
1076     "left, or right within the image window.",
1077     "",
1078     "The panning icon is withdrawn if the image becomes smaller",
1079     "than the dimensions of the X server screen.",
1080     (char *) NULL,
1081   },
1082   *ImagePasteHelp[] =
1083   {
1084     "A small window appears showing the location of the cursor in",
1085     "the image window. You are now in paste mode.  To exit",
1086     "immediately, press Dismiss.  In paste mode, the Command",
1087     "widget has these options:",
1088     "",
1089     "    Operators",
1090     "      over",
1091     "      in",
1092     "      out",
1093     "      atop",
1094     "      xor",
1095     "      plus",
1096     "      minus",
1097     "      add",
1098     "      subtract",
1099     "      difference",
1100     "      replace",
1101     "    Help",
1102     "    Dismiss",
1103     "",
1104     "Choose a composite operation from the Operators sub-menu of",
1105     "the Command widget.  How each operator behaves is described",
1106     "below.  Image window is the image currently displayed on",
1107     "your X server and image is the image obtained with the File",
1108     "Browser widget.",
1109     "",
1110     "Over     The result is the union of the two image shapes,",
1111     "         with image obscuring image window in the region of",
1112     "         overlap.",
1113     "",
1114     "In       The result is simply image cut by the shape of",
1115     "         image window.  None of the image data of image",
1116     "         window is in the result.",
1117     "",
1118     "Out      The resulting image is image with the shape of",
1119     "         image window cut out.",
1120     "",
1121     "Atop     The result is the same shape as image image window,",
1122     "         with image obscuring image window where the image",
1123     "         shapes overlap.  Note this differs from over",
1124     "         because the portion of image outside image window's",
1125     "         shape does not appear in the result.",
1126     "",
1127     "Xor      The result is the image data from both image and",
1128     "         image window that is outside the overlap region.",
1129     "         The overlap region is blank.",
1130     "",
1131     "Plus     The result is just the sum of the image data.",
1132     "         Output values are cropped to QuantumRange (no overflow).",
1133     "         This operation is independent of the matte",
1134     "         channels.",
1135     "",
1136     "Minus    The result of image - image window, with underflow",
1137     "         cropped to zero.",
1138     "",
1139     "Add      The result of image + image window, with overflow",
1140     "         wrapping around (mod 256).",
1141     "",
1142     "Subtract The result of image - image window, with underflow",
1143     "         wrapping around (mod 256).  The add and subtract",
1144     "         operators can be used to perform reversible",
1145     "         transformations.",
1146     "",
1147     "Difference",
1148     "         The result of abs(image - image window).  This",
1149     "         useful for comparing two very similar images.",
1150     "",
1151     "Copy     The resulting image is image window replaced with",
1152     "         image.  Here the matte information is ignored.",
1153     "",
1154     "CopyRed  The red layer of the image window is replace with",
1155     "         the red layer of the image.  The other layers are",
1156     "         untouched.",
1157     "",
1158     "CopyGreen",
1159     "         The green layer of the image window is replace with",
1160     "         the green layer of the image.  The other layers are",
1161     "         untouched.",
1162     "",
1163     "CopyBlue The blue layer of the image window is replace with",
1164     "         the blue layer of the image.  The other layers are",
1165     "         untouched.",
1166     "",
1167     "CopyOpacity",
1168     "         The matte layer of the image window is replace with",
1169     "         the matte layer of the image.  The other layers are",
1170     "         untouched.",
1171     "",
1172     "The image compositor requires a matte, or alpha channel in",
1173     "the image for some operations.  This extra channel usually",
1174     "defines a mask which represents a sort of a cookie-cutter",
1175     "for the image.  This the case when matte is opaque (full",
1176     "coverage) for pixels inside the shape, zero outside, and",
1177     "between 0 and QuantumRange on the boundary.  If image does not",
1178     "have a matte channel, it is initialized with 0 for any pixel",
1179     "matching in color to pixel location (0,0), otherwise QuantumRange.",
1180     "",
1181     "Note that matte information for image window is not retained",
1182     "for colormapped X server visuals (e.g. StaticColor,",
1183     "StaticColor, GrayScale, PseudoColor).  Correct compositing",
1184     "behavior may require a TrueColor or DirectColor visual or a",
1185     "Standard Colormap.",
1186     "",
1187     "Choosing a composite operator is optional.  The default",
1188     "operator is replace.  However, you must choose a location to",
1189     "paste your image and press button 1.  Press and hold the",
1190     "button before releasing and an outline of the image will",
1191     "appear to help you identify your location.",
1192     "",
1193     "The actual colors of the pasted image is saved.  However,",
1194     "the color that appears in image window may be different.",
1195     "For example, on a monochrome screen image window will appear",
1196     "black or white even though your pasted image may have",
1197     "many colors.  If the image is saved to a file it is written",
1198     "with the correct colors.  To assure the correct colors are",
1199     "saved in the final image, any PseudoClass image is promoted",
1200     "to DirectClass (see miff(5)).  To force a PseudoClass image",
1201     "to remain PseudoClass, use -colors.",
1202     (char *) NULL,
1203   },
1204   *ImageROIHelp[] =
1205   {
1206     "In region of interest mode, the Command widget has these",
1207     "options:",
1208     "",
1209     "    Help",
1210     "    Dismiss",
1211     "",
1212     "To define a region of interest, press button 1 and drag.",
1213     "The region of interest is defined by a highlighted rectangle",
1214     "that expands or contracts as it follows the pointer.  Once",
1215     "you are satisfied with the region of interest, release the",
1216     "button.  You are now in apply mode.  In apply mode the",
1217     "Command widget has these options:",
1218     "",
1219     "      File",
1220     "        Save...",
1221     "        Print...",
1222     "      Edit",
1223     "        Undo",
1224     "        Redo",
1225     "      Transform",
1226     "        Flop",
1227     "        Flip",
1228     "        Rotate Right",
1229     "        Rotate Left",
1230     "      Enhance",
1231     "        Hue...",
1232     "        Saturation...",
1233     "        Brightness...",
1234     "        Gamma...",
1235     "        Spiff",
1236     "        Dull",
1237     "        Contrast Stretch",
1238     "        Sigmoidal Contrast...",
1239     "        Normalize",
1240     "        Equalize",
1241     "        Negate",
1242     "        Grayscale",
1243     "        Map...",
1244     "        Quantize...",
1245     "      Effects",
1246     "        Despeckle",
1247     "        Emboss",
1248     "        Reduce Noise",
1249     "        Sharpen...",
1250     "        Blur...",
1251     "        Threshold...",
1252     "        Edge Detect...",
1253     "        Spread...",
1254     "        Shade...",
1255     "        Raise...",
1256     "        Segment...",
1257     "      F/X",
1258     "        Solarize...",
1259     "        Sepia Tone...",
1260     "        Swirl...",
1261     "        Implode...",
1262     "        Vignette...",
1263     "        Wave...",
1264     "        Oil Painting...",
1265     "        Charcoal Drawing...",
1266     "      Miscellany",
1267     "        Image Info",
1268     "        Zoom Image",
1269     "        Show Preview...",
1270     "        Show Histogram",
1271     "        Show Matte",
1272     "      Help",
1273     "      Dismiss",
1274     "",
1275     "You can make adjustments to the region of interest by moving",
1276     "the pointer to one of the rectangle corners, pressing a",
1277     "button, and dragging.  Finally, choose an image processing",
1278     "technique from the Command widget.  You can choose more than",
1279     "one image processing technique to apply to an area.",
1280     "Alternatively, you can move the region of interest before",
1281     "applying another image processing technique.  To exit, press",
1282     "Dismiss.",
1283     (char *) NULL,
1284   },
1285   *ImageRotateHelp[] =
1286   {
1287     "In rotate mode, the Command widget has these options:",
1288     "",
1289     "    Pixel Color",
1290     "      black",
1291     "      blue",
1292     "      cyan",
1293     "      green",
1294     "      gray",
1295     "      red",
1296     "      magenta",
1297     "      yellow",
1298     "      white",
1299     "      Browser...",
1300     "    Direction",
1301     "      horizontal",
1302     "      vertical",
1303     "    Help",
1304     "    Dismiss",
1305     "",
1306     "Choose a background color from the Pixel Color sub-menu.",
1307     "Additional background colors can be specified with the color",
1308     "browser.  You can change the menu colors by setting the X",
1309     "resources pen1 through pen9.",
1310     "",
1311     "If you choose the color browser and press Grab, you can",
1312     "select the background color by moving the pointer to the",
1313     "desired color on the screen and press any button.",
1314     "",
1315     "Choose a point in the image window and press this button and",
1316     "hold.  Next, move the pointer to another location in the",
1317     "image.  As you move a line connects the initial location and",
1318     "the pointer.  When you release the button, the degree of",
1319     "image rotation is determined by the slope of the line you",
1320     "just drew.  The slope is relative to the direction you",
1321     "choose from the Direction sub-menu of the Command widget.",
1322     "",
1323     "To cancel the image rotation, move the pointer back to the",
1324     "starting point of the line and release the button.",
1325     (char *) NULL,
1326   };
1327 \f
1328 /*
1329   Enumeration declarations.
1330 */
1331 typedef enum
1332 {
1333   CopyMode,
1334   CropMode,
1335   CutMode
1336 } ClipboardMode;
1337
1338 typedef enum
1339 {
1340   OpenCommand,
1341   NextCommand,
1342   FormerCommand,
1343   SelectCommand,
1344   SaveCommand,
1345   PrintCommand,
1346   DeleteCommand,
1347   NewCommand,
1348   VisualDirectoryCommand,
1349   QuitCommand,
1350   UndoCommand,
1351   RedoCommand,
1352   CutCommand,
1353   CopyCommand,
1354   PasteCommand,
1355   HalfSizeCommand,
1356   OriginalSizeCommand,
1357   DoubleSizeCommand,
1358   ResizeCommand,
1359   ApplyCommand,
1360   RefreshCommand,
1361   RestoreCommand,
1362   CropCommand,
1363   ChopCommand,
1364   FlopCommand,
1365   FlipCommand,
1366   RotateRightCommand,
1367   RotateLeftCommand,
1368   RotateCommand,
1369   ShearCommand,
1370   RollCommand,
1371   TrimCommand,
1372   HueCommand,
1373   SaturationCommand,
1374   BrightnessCommand,
1375   GammaCommand,
1376   SpiffCommand,
1377   DullCommand,
1378   ContrastStretchCommand,
1379   SigmoidalContrastCommand,
1380   NormalizeCommand,
1381   EqualizeCommand,
1382   NegateCommand,
1383   GrayscaleCommand,
1384   MapCommand,
1385   QuantizeCommand,
1386   DespeckleCommand,
1387   EmbossCommand,
1388   ReduceNoiseCommand,
1389   AddNoiseCommand,
1390   SharpenCommand,
1391   BlurCommand,
1392   ThresholdCommand,
1393   EdgeDetectCommand,
1394   SpreadCommand,
1395   ShadeCommand,
1396   RaiseCommand,
1397   SegmentCommand,
1398   SolarizeCommand,
1399   SepiaToneCommand,
1400   SwirlCommand,
1401   ImplodeCommand,
1402   VignetteCommand,
1403   WaveCommand,
1404   OilPaintCommand,
1405   CharcoalDrawCommand,
1406   AnnotateCommand,
1407   DrawCommand,
1408   ColorCommand,
1409   MatteCommand,
1410   CompositeCommand,
1411   AddBorderCommand,
1412   AddFrameCommand,
1413   CommentCommand,
1414   LaunchCommand,
1415   RegionofInterestCommand,
1416   ROIHelpCommand,
1417   ROIDismissCommand,
1418   InfoCommand,
1419   ZoomCommand,
1420   ShowPreviewCommand,
1421   ShowHistogramCommand,
1422   ShowMatteCommand,
1423   BackgroundCommand,
1424   SlideShowCommand,
1425   PreferencesCommand,
1426   HelpCommand,
1427   BrowseDocumentationCommand,
1428   VersionCommand,
1429   SaveToUndoBufferCommand,
1430   FreeBuffersCommand,
1431   NullCommand
1432 } CommandType;
1433
1434 typedef enum
1435 {
1436   AnnotateNameCommand,
1437   AnnotateFontColorCommand,
1438   AnnotateBackgroundColorCommand,
1439   AnnotateRotateCommand,
1440   AnnotateHelpCommand,
1441   AnnotateDismissCommand,
1442   TextHelpCommand,
1443   TextApplyCommand,
1444   ChopDirectionCommand,
1445   ChopHelpCommand,
1446   ChopDismissCommand,
1447   HorizontalChopCommand,
1448   VerticalChopCommand,
1449   ColorEditMethodCommand,
1450   ColorEditColorCommand,
1451   ColorEditBorderCommand,
1452   ColorEditFuzzCommand,
1453   ColorEditUndoCommand,
1454   ColorEditHelpCommand,
1455   ColorEditDismissCommand,
1456   CompositeOperatorsCommand,
1457   CompositeDissolveCommand,
1458   CompositeDisplaceCommand,
1459   CompositeHelpCommand,
1460   CompositeDismissCommand,
1461   CropHelpCommand,
1462   CropDismissCommand,
1463   RectifyCopyCommand,
1464   RectifyHelpCommand,
1465   RectifyDismissCommand,
1466   DrawElementCommand,
1467   DrawColorCommand,
1468   DrawStippleCommand,
1469   DrawWidthCommand,
1470   DrawUndoCommand,
1471   DrawHelpCommand,
1472   DrawDismissCommand,
1473   MatteEditMethod,
1474   MatteEditBorderCommand,
1475   MatteEditFuzzCommand,
1476   MatteEditValueCommand,
1477   MatteEditUndoCommand,
1478   MatteEditHelpCommand,
1479   MatteEditDismissCommand,
1480   PasteOperatorsCommand,
1481   PasteHelpCommand,
1482   PasteDismissCommand,
1483   RotateColorCommand,
1484   RotateDirectionCommand,
1485   RotateCropCommand,
1486   RotateSharpenCommand,
1487   RotateHelpCommand,
1488   RotateDismissCommand,
1489   HorizontalRotateCommand,
1490   VerticalRotateCommand,
1491   TileLoadCommand,
1492   TileNextCommand,
1493   TileFormerCommand,
1494   TileDeleteCommand,
1495   TileUpdateCommand
1496 } ModeType;
1497 \f
1498 /*
1499   Stipples.
1500 */
1501 #define BricksWidth  20
1502 #define BricksHeight  20
1503 #define DiagonalWidth  16
1504 #define DiagonalHeight  16
1505 #define HighlightWidth  8
1506 #define HighlightHeight  8
1507 #define OpaqueWidth  8
1508 #define OpaqueHeight  8
1509 #define ScalesWidth  16
1510 #define ScalesHeight  16
1511 #define ShadowWidth  8
1512 #define ShadowHeight  8
1513 #define VerticalWidth  16
1514 #define VerticalHeight  16
1515 #define WavyWidth  16
1516 #define WavyHeight  16
1517 \f
1518 /*
1519   Constant declaration.
1520 */
1521 static const int
1522   RoiDelta = 8;
1523
1524 static const unsigned char
1525   BricksBitmap[] =
1526   {
1527     0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00,
1528     0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01,
1529     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0xff, 0xff, 0x0f, 0x03, 0x0c, 0x00,
1530     0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0x03, 0x0c, 0x00, 0xff, 0xff, 0x0f,
1531     0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01, 0x60, 0x80, 0x01
1532   },
1533   DiagonalBitmap[] =
1534   {
1535     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88,
1536     0x11, 0x11, 0x22, 0x22, 0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22,
1537     0x44, 0x44, 0x88, 0x88, 0x11, 0x11, 0x22, 0x22
1538   },
1539   ScalesBitmap[] =
1540   {
1541     0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3, 0x80, 0x80, 0x80, 0x80,
1542     0x41, 0x41, 0x3e, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x14, 0x14, 0xe3, 0xe3,
1543     0x80, 0x80, 0x80, 0x80, 0x41, 0x41, 0x3e, 0x3e
1544   },
1545   VerticalBitmap[] =
1546   {
1547     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1548     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
1549     0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
1550   },
1551   WavyBitmap[] =
1552   {
1553     0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfd, 0xff, 0xfd, 0xff, 0xfb, 0xff,
1554     0xe7, 0xff, 0x1f, 0xff, 0xff, 0xf8, 0xff, 0xe7, 0xff, 0xdf, 0xff, 0xbf,
1555     0xff, 0xbf, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f
1556   };
1557 \f
1558 /*
1559   Function prototypes.
1560 */
1561 static CommandType
1562   XImageWindowCommand(Display *,XResourceInfo *,XWindows *,
1563     const MagickStatusType,KeySym,Image **);
1564
1565 static Image
1566   *XMagickCommand(Display *,XResourceInfo *,XWindows *,const CommandType,
1567     Image **),
1568   *XOpenImage(Display *,XResourceInfo *,XWindows *,const MagickBooleanType),
1569   *XTileImage(Display *,XResourceInfo *,XWindows *,Image *,XEvent *),
1570   *XVisualDirectoryImage(Display *,XResourceInfo *,XWindows *);
1571
1572 static MagickBooleanType
1573   XAnnotateEditImage(Display *,XResourceInfo *,XWindows *,Image *),
1574   XDrawEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1575   XChopImage(Display *,XResourceInfo *,XWindows *,Image **),
1576   XCropImage(Display *,XResourceInfo *,XWindows *,Image *,const ClipboardMode),
1577   XBackgroundImage(Display *,XResourceInfo *,XWindows *,Image **),
1578   XColorEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1579   XCompositeImage(Display *,XResourceInfo *,XWindows *,Image *),
1580   XConfigureImage(Display *,XResourceInfo *,XWindows *,Image *),
1581   XMatteEditImage(Display *,XResourceInfo *,XWindows *,Image **),
1582   XPasteImage(Display *,XResourceInfo *,XWindows *,Image *),
1583   XPrintImage(Display *,XResourceInfo *,XWindows *,Image *),
1584   XRotateImage(Display *,XResourceInfo *,XWindows *,double,Image **),
1585   XROIImage(Display *,XResourceInfo *,XWindows *,Image **),
1586   XSaveImage(Display *,XResourceInfo *,XWindows *,Image *),
1587   XTrimImage(Display *,XResourceInfo *,XWindows *,Image *);
1588
1589 static void
1590   XDrawPanRectangle(Display *,XWindows *),
1591   XImageCache(Display *,XResourceInfo *,XWindows *,const CommandType,Image **),
1592   XMagnifyImage(Display *,XWindows *,XEvent *),
1593   XMakePanImage(Display *,XResourceInfo *,XWindows *,Image *),
1594   XPanImage(Display *,XWindows *,XEvent *),
1595   XMagnifyWindowCommand(Display *,XWindows *,const MagickStatusType,
1596     const KeySym),
1597   XSetCropGeometry(Display *,XWindows *,RectangleInfo *,Image *),
1598   XScreenEvent(Display *,XWindows *,XEvent *),
1599   XTranslateImage(Display *,XWindows *,Image *,const KeySym);
1600 \f
1601 /*
1602 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1603 %                                                                             %
1604 %                                                                             %
1605 %                                                                             %
1606 %   D i s p l a y I m a g e s                                                 %
1607 %                                                                             %
1608 %                                                                             %
1609 %                                                                             %
1610 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1611 %
1612 %  DisplayImages() displays an image sequence to any X window screen.  It
1613 %  returns a value other than 0 if successful.  Check the exception member
1614 %  of image to determine the reason for any failure.
1615 %
1616 %  The format of the DisplayImages method is:
1617 %
1618 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
1619 %        Image *images)
1620 %
1621 %  A description of each parameter follows:
1622 %
1623 %    o image_info: the image info.
1624 %
1625 %    o image: the image.
1626 %
1627 */
1628 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
1629   Image *images)
1630 {
1631   char
1632     *argv[1];
1633
1634   Display
1635     *display;
1636
1637   Image
1638     *image;
1639
1640   register ssize_t
1641     i;
1642
1643   size_t
1644     state;
1645
1646   XrmDatabase
1647     resource_database;
1648
1649   XResourceInfo
1650     resource_info;
1651
1652   assert(image_info != (const ImageInfo *) NULL);
1653   assert(image_info->signature == MagickSignature);
1654   assert(images != (Image *) NULL);
1655   assert(images->signature == MagickSignature);
1656   if (images->debug != MagickFalse)
1657     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
1658   display=XOpenDisplay(image_info->server_name);
1659   if (display == (Display *) NULL)
1660     {
1661       (void) ThrowMagickException(&images->exception,GetMagickModule(),
1662         XServerError,"UnableToOpenXServer","`%s'",XDisplayName(
1663         image_info->server_name));
1664       return(MagickFalse);
1665     }
1666   if (images->exception.severity != UndefinedException)
1667     CatchException(&images->exception);
1668   (void) XSetErrorHandler(XError);
1669   resource_database=XGetResourceDatabase(display,GetClientName());
1670   (void) ResetMagickMemory(&resource_info,0,sizeof(resource_info));
1671   XGetResourceInfo(image_info,resource_database,GetClientName(),&resource_info);
1672   if (image_info->page != (char *) NULL)
1673     resource_info.image_geometry=AcquireString(image_info->page);
1674   resource_info.immutable=MagickTrue;
1675   argv[0]=AcquireString(GetClientName());
1676   state=DefaultState;
1677   for (i=0; (state & ExitState) == 0; i++)
1678   {
1679     if ((images->iterations != 0) && (i >= (ssize_t) images->iterations))
1680       break;
1681     image=GetImageFromList(images,i % GetImageListLength(images));
1682     (void) XDisplayImage(display,&resource_info,argv,1,&image,&state);
1683   }
1684   argv[0]=DestroyString(argv[0]);
1685   (void) XCloseDisplay(display);
1686   XDestroyResourceInfo(&resource_info);
1687   if (images->exception.severity != UndefinedException)
1688     return(MagickFalse);
1689   return(MagickTrue);
1690 }
1691 \f
1692 /*
1693 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1694 %                                                                             %
1695 %                                                                             %
1696 %                                                                             %
1697 %   R e m o t e D i s p l a y C o m m a n d                                   %
1698 %                                                                             %
1699 %                                                                             %
1700 %                                                                             %
1701 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1702 %
1703 %  RemoteDisplayCommand() encourages a remote display program to display the
1704 %  specified image filename.
1705 %
1706 %  The format of the RemoteDisplayCommand method is:
1707 %
1708 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1709 %        const char *window,const char *filename,ExceptionInfo *exception)
1710 %
1711 %  A description of each parameter follows:
1712 %
1713 %    o image_info: the image info.
1714 %
1715 %    o window: Specifies the name or id of an X window.
1716 %
1717 %    o filename: the name of the image filename to display.
1718 %
1719 %    o exception: return any errors or warnings in this structure.
1720 %
1721 */
1722 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
1723   const char *window,const char *filename,ExceptionInfo *exception)
1724 {
1725   Display
1726     *display;
1727
1728   MagickStatusType
1729     status;
1730
1731   assert(image_info != (const ImageInfo *) NULL);
1732   assert(image_info->signature == MagickSignature);
1733   assert(filename != (char *) NULL);
1734   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
1735   display=XOpenDisplay(image_info->server_name);
1736   if (display == (Display *) NULL)
1737     {
1738       (void) ThrowMagickException(exception,GetMagickModule(),XServerError,
1739         "UnableToOpenXServer","`%s'",XDisplayName(image_info->server_name));
1740       return(MagickFalse);
1741     }
1742   (void) XSetErrorHandler(XError);
1743   status=XRemoteCommand(display,window,filename);
1744   (void) XCloseDisplay(display);
1745   return(status != 0 ? MagickTrue : MagickFalse);
1746 }
1747 \f
1748 /*
1749 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1750 %                                                                             %
1751 %                                                                             %
1752 %                                                                             %
1753 +   X A n n o t a t e E d i t I m a g e                                       %
1754 %                                                                             %
1755 %                                                                             %
1756 %                                                                             %
1757 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1758 %
1759 %  XAnnotateEditImage() annotates the image with text.
1760 %
1761 %  The format of the XAnnotateEditImage method is:
1762 %
1763 %      MagickBooleanType XAnnotateEditImage(Display *display,
1764 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
1765 %
1766 %  A description of each parameter follows:
1767 %
1768 %    o display: Specifies a connection to an X server;  returned from
1769 %      XOpenDisplay.
1770 %
1771 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
1772 %
1773 %    o windows: Specifies a pointer to a XWindows structure.
1774 %
1775 %    o image: the image; returned from ReadImage.
1776 %
1777 */
1778
1779 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
1780 {
1781   if (x > y)
1782     return(x);
1783   return(y);
1784 }
1785
1786 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
1787 {
1788   if (x < y)
1789     return(x);
1790   return(y);
1791 }
1792
1793 static MagickBooleanType XAnnotateEditImage(Display *display,
1794   XResourceInfo *resource_info,XWindows *windows,Image *image)
1795 {
1796   static const char
1797     *AnnotateMenu[] =
1798     {
1799       "Font Name",
1800       "Font Color",
1801       "Box Color",
1802       "Rotate Text",
1803       "Help",
1804       "Dismiss",
1805       (char *) NULL
1806     },
1807     *TextMenu[] =
1808     {
1809       "Help",
1810       "Apply",
1811       (char *) NULL
1812     };
1813
1814   static const ModeType
1815     AnnotateCommands[] =
1816     {
1817       AnnotateNameCommand,
1818       AnnotateFontColorCommand,
1819       AnnotateBackgroundColorCommand,
1820       AnnotateRotateCommand,
1821       AnnotateHelpCommand,
1822       AnnotateDismissCommand
1823     },
1824     TextCommands[] =
1825     {
1826       TextHelpCommand,
1827       TextApplyCommand
1828     };
1829
1830   static MagickBooleanType
1831     transparent_box = MagickTrue,
1832     transparent_pen = MagickFalse;
1833
1834   static MagickRealType
1835     degrees = 0.0;
1836
1837   static unsigned int
1838     box_id = MaxNumberPens-2,
1839     font_id = 0,
1840     pen_id = 0;
1841
1842   char
1843     command[MaxTextExtent],
1844     text[MaxTextExtent];
1845
1846   const char
1847     *ColorMenu[MaxNumberPens+1];
1848
1849   Cursor
1850     cursor;
1851
1852   GC
1853     annotate_context;
1854
1855   int
1856     id,
1857     pen_number,
1858     status,
1859     x,
1860     y;
1861
1862   KeySym
1863     key_symbol;
1864
1865   register char
1866     *p;
1867
1868   register ssize_t
1869     i;
1870
1871   unsigned int
1872     height,
1873     width;
1874
1875   size_t
1876     state;
1877
1878   XAnnotateInfo
1879     *annotate_info,
1880     *previous_info;
1881
1882   XColor
1883     color;
1884
1885   XFontStruct
1886     *font_info;
1887
1888   XEvent
1889     event,
1890     text_event;
1891
1892   /*
1893     Map Command widget.
1894   */
1895   (void) CloneString(&windows->command.name,"Annotate");
1896   windows->command.data=4;
1897   (void) XCommandWidget(display,windows,AnnotateMenu,(XEvent *) NULL);
1898   (void) XMapRaised(display,windows->command.id);
1899   XClientMessage(display,windows->image.id,windows->im_protocols,
1900     windows->im_update_widget,CurrentTime);
1901   /*
1902     Track pointer until button 1 is pressed.
1903   */
1904   XQueryPosition(display,windows->image.id,&x,&y);
1905   (void) XSelectInput(display,windows->image.id,
1906     windows->image.attributes.event_mask | PointerMotionMask);
1907   cursor=XCreateFontCursor(display,XC_left_side);
1908   (void) XCheckDefineCursor(display,windows->image.id,cursor);
1909   state=DefaultState;
1910   do
1911   {
1912     if (windows->info.mapped != MagickFalse)
1913       {
1914         /*
1915           Display pointer position.
1916         */
1917         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
1918           x+windows->image.x,y+windows->image.y);
1919         XInfoWidget(display,windows,text);
1920       }
1921     /*
1922       Wait for next event.
1923     */
1924     XScreenEvent(display,windows,&event);
1925     if (event.xany.window == windows->command.id)
1926       {
1927         /*
1928           Select a command from the Command widget.
1929         */
1930         id=XCommandWidget(display,windows,AnnotateMenu,&event);
1931         (void) XCheckDefineCursor(display,windows->image.id,cursor);
1932         if (id < 0)
1933           continue;
1934         switch (AnnotateCommands[id])
1935         {
1936           case AnnotateNameCommand:
1937           {
1938             const char
1939               *FontMenu[MaxNumberFonts];
1940
1941             int
1942               font_number;
1943
1944             /*
1945               Initialize menu selections.
1946             */
1947             for (i=0; i < MaxNumberFonts; i++)
1948               FontMenu[i]=resource_info->font_name[i];
1949             FontMenu[MaxNumberFonts-2]="Browser...";
1950             FontMenu[MaxNumberFonts-1]=(const char *) NULL;
1951             /*
1952               Select a font name from the pop-up menu.
1953             */
1954             font_number=XMenuWidget(display,windows,AnnotateMenu[id],
1955               (const char **) FontMenu,command);
1956             if (font_number < 0)
1957               break;
1958             if (font_number == (MaxNumberFonts-2))
1959               {
1960                 static char
1961                   font_name[MaxTextExtent] = "fixed";
1962
1963                 /*
1964                   Select a font name from a browser.
1965                 */
1966                 resource_info->font_name[font_number]=font_name;
1967                 XFontBrowserWidget(display,windows,"Select",font_name);
1968                 if (*font_name == '\0')
1969                   break;
1970               }
1971             /*
1972               Initialize font info.
1973             */
1974             font_info=XLoadQueryFont(display,resource_info->font_name[
1975               font_number]);
1976             if (font_info == (XFontStruct *) NULL)
1977               {
1978                 XNoticeWidget(display,windows,"Unable to load font:",
1979                   resource_info->font_name[font_number]);
1980                 break;
1981               }
1982             font_id=(unsigned int) font_number;
1983             (void) XFreeFont(display,font_info);
1984             break;
1985           }
1986           case AnnotateFontColorCommand:
1987           {
1988             /*
1989               Initialize menu selections.
1990             */
1991             for (i=0; i < (int) (MaxNumberPens-2); i++)
1992               ColorMenu[i]=resource_info->pen_colors[i];
1993             ColorMenu[MaxNumberPens-2]="transparent";
1994             ColorMenu[MaxNumberPens-1]="Browser...";
1995             ColorMenu[MaxNumberPens]=(const char *) NULL;
1996             /*
1997               Select a pen color from the pop-up menu.
1998             */
1999             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2000               (const char **) ColorMenu,command);
2001             if (pen_number < 0)
2002               break;
2003             transparent_pen=pen_number == (MaxNumberPens-2) ? MagickTrue :
2004               MagickFalse;
2005             if (transparent_pen != MagickFalse)
2006               break;
2007             if (pen_number == (MaxNumberPens-1))
2008               {
2009                 static char
2010                   color_name[MaxTextExtent] = "gray";
2011
2012                 /*
2013                   Select a pen color from a dialog.
2014                 */
2015                 resource_info->pen_colors[pen_number]=color_name;
2016                 XColorBrowserWidget(display,windows,"Select",color_name);
2017                 if (*color_name == '\0')
2018                   break;
2019               }
2020             /*
2021               Set pen color.
2022             */
2023             (void) XParseColor(display,windows->map_info->colormap,
2024               resource_info->pen_colors[pen_number],&color);
2025             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2026               (unsigned int) MaxColors,&color);
2027             windows->pixel_info->pen_colors[pen_number]=color;
2028             pen_id=(unsigned int) pen_number;
2029             break;
2030           }
2031           case AnnotateBackgroundColorCommand:
2032           {
2033             /*
2034               Initialize menu selections.
2035             */
2036             for (i=0; i < (int) (MaxNumberPens-2); i++)
2037               ColorMenu[i]=resource_info->pen_colors[i];
2038             ColorMenu[MaxNumberPens-2]="transparent";
2039             ColorMenu[MaxNumberPens-1]="Browser...";
2040             ColorMenu[MaxNumberPens]=(const char *) NULL;
2041             /*
2042               Select a pen color from the pop-up menu.
2043             */
2044             pen_number=XMenuWidget(display,windows,AnnotateMenu[id],
2045               (const char **) ColorMenu,command);
2046             if (pen_number < 0)
2047               break;
2048             transparent_box=pen_number == (MaxNumberPens-2) ? MagickTrue :
2049               MagickFalse;
2050             if (transparent_box != MagickFalse)
2051               break;
2052             if (pen_number == (MaxNumberPens-1))
2053               {
2054                 static char
2055                   color_name[MaxTextExtent] = "gray";
2056
2057                 /*
2058                   Select a pen color from a dialog.
2059                 */
2060                 resource_info->pen_colors[pen_number]=color_name;
2061                 XColorBrowserWidget(display,windows,"Select",color_name);
2062                 if (*color_name == '\0')
2063                   break;
2064               }
2065             /*
2066               Set pen color.
2067             */
2068             (void) XParseColor(display,windows->map_info->colormap,
2069               resource_info->pen_colors[pen_number],&color);
2070             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
2071               (unsigned int) MaxColors,&color);
2072             windows->pixel_info->pen_colors[pen_number]=color;
2073             box_id=(unsigned int) pen_number;
2074             break;
2075           }
2076           case AnnotateRotateCommand:
2077           {
2078             int
2079               entry;
2080
2081             static char
2082               angle[MaxTextExtent] = "30.0";
2083
2084             static const char
2085               *RotateMenu[] =
2086               {
2087                 "-90",
2088                 "-45",
2089                 "-30",
2090                 "0",
2091                 "30",
2092                 "45",
2093                 "90",
2094                 "180",
2095                 "Dialog...",
2096                 (char *) NULL,
2097               };
2098
2099             /*
2100               Select a command from the pop-up menu.
2101             */
2102             entry=XMenuWidget(display,windows,AnnotateMenu[id],RotateMenu,
2103               command);
2104             if (entry < 0)
2105               break;
2106             if (entry != 8)
2107               {
2108                 degrees=StringToDouble(RotateMenu[entry]);
2109                 break;
2110               }
2111             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2112               angle);
2113             if (*angle == '\0')
2114               break;
2115             degrees=StringToDouble(angle);
2116             break;
2117           }
2118           case AnnotateHelpCommand:
2119           {
2120             XTextViewWidget(display,resource_info,windows,MagickFalse,
2121               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2122             break;
2123           }
2124           case AnnotateDismissCommand:
2125           {
2126             /*
2127               Prematurely exit.
2128             */
2129             state|=EscapeState;
2130             state|=ExitState;
2131             break;
2132           }
2133           default:
2134             break;
2135         }
2136         continue;
2137       }
2138     switch (event.type)
2139     {
2140       case ButtonPress:
2141       {
2142         if (event.xbutton.button != Button1)
2143           break;
2144         if (event.xbutton.window != windows->image.id)
2145           break;
2146         /*
2147           Change to text entering mode.
2148         */
2149         x=event.xbutton.x;
2150         y=event.xbutton.y;
2151         state|=ExitState;
2152         break;
2153       }
2154       case ButtonRelease:
2155         break;
2156       case Expose:
2157         break;
2158       case KeyPress:
2159       {
2160         if (event.xkey.window != windows->image.id)
2161           break;
2162         /*
2163           Respond to a user key press.
2164         */
2165         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2166           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2167         switch ((int) key_symbol)
2168         {
2169           case XK_Escape:
2170           case XK_F20:
2171           {
2172             /*
2173               Prematurely exit.
2174             */
2175             state|=EscapeState;
2176             state|=ExitState;
2177             break;
2178           }
2179           case XK_F1:
2180           case XK_Help:
2181           {
2182             XTextViewWidget(display,resource_info,windows,MagickFalse,
2183               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2184             break;
2185           }
2186           default:
2187           {
2188             (void) XBell(display,0);
2189             break;
2190           }
2191         }
2192         break;
2193       }
2194       case MotionNotify:
2195       {
2196         /*
2197           Map and unmap Info widget as cursor crosses its boundaries.
2198         */
2199         x=event.xmotion.x;
2200         y=event.xmotion.y;
2201         if (windows->info.mapped != MagickFalse)
2202           {
2203             if ((x < (int) (windows->info.x+windows->info.width)) &&
2204                 (y < (int) (windows->info.y+windows->info.height)))
2205               (void) XWithdrawWindow(display,windows->info.id,
2206                 windows->info.screen);
2207           }
2208         else
2209           if ((x > (int) (windows->info.x+windows->info.width)) ||
2210               (y > (int) (windows->info.y+windows->info.height)))
2211             (void) XMapWindow(display,windows->info.id);
2212         break;
2213       }
2214       default:
2215         break;
2216     }
2217   } while ((state & ExitState) == 0);
2218   (void) XSelectInput(display,windows->image.id,
2219     windows->image.attributes.event_mask);
2220   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
2221   if ((state & EscapeState) != 0)
2222     return(MagickTrue);
2223   /*
2224     Set font info and check boundary conditions.
2225   */
2226   font_info=XLoadQueryFont(display,resource_info->font_name[font_id]);
2227   if (font_info == (XFontStruct *) NULL)
2228     {
2229       XNoticeWidget(display,windows,"Unable to load font:",
2230         resource_info->font_name[font_id]);
2231       font_info=windows->font_info;
2232     }
2233   if ((x+font_info->max_bounds.width) >= (int) windows->image.width)
2234     x=(int) windows->image.width-font_info->max_bounds.width;
2235   if (y < (int) (font_info->ascent+font_info->descent))
2236     y=(int) font_info->ascent+font_info->descent;
2237   if (((int) font_info->max_bounds.width > (int) windows->image.width) ||
2238       ((font_info->ascent+font_info->descent) >= (int) windows->image.height))
2239     return(MagickFalse);
2240   /*
2241     Initialize annotate structure.
2242   */
2243   annotate_info=(XAnnotateInfo *) AcquireMagickMemory(sizeof(*annotate_info));
2244   if (annotate_info == (XAnnotateInfo *) NULL)
2245     return(MagickFalse);
2246   XGetAnnotateInfo(annotate_info);
2247   annotate_info->x=x;
2248   annotate_info->y=y;
2249   if ((transparent_box == MagickFalse) && (transparent_pen == MagickFalse))
2250     annotate_info->stencil=OpaqueStencil;
2251   else
2252     if (transparent_box == MagickFalse)
2253       annotate_info->stencil=BackgroundStencil;
2254     else
2255       annotate_info->stencil=ForegroundStencil;
2256   annotate_info->height=(unsigned int) font_info->ascent+font_info->descent;
2257   annotate_info->degrees=degrees;
2258   annotate_info->font_info=font_info;
2259   annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2260     windows->image.width/MagickMax((ssize_t) font_info->min_bounds.width,1)+2UL,
2261     sizeof(*annotate_info->text));
2262   if (annotate_info->text == (char *) NULL)
2263     return(MagickFalse);
2264   /*
2265     Create cursor and set graphic context.
2266   */
2267   cursor=XCreateFontCursor(display,XC_pencil);
2268   (void) XCheckDefineCursor(display,windows->image.id,cursor);
2269   annotate_context=windows->image.annotate_context;
2270   (void) XSetFont(display,annotate_context,font_info->fid);
2271   (void) XSetBackground(display,annotate_context,
2272     windows->pixel_info->pen_colors[box_id].pixel);
2273   (void) XSetForeground(display,annotate_context,
2274     windows->pixel_info->pen_colors[pen_id].pixel);
2275   /*
2276     Begin annotating the image with text.
2277   */
2278   (void) CloneString(&windows->command.name,"Text");
2279   windows->command.data=0;
2280   (void) XCommandWidget(display,windows,TextMenu,(XEvent *) NULL);
2281   state=DefaultState;
2282   (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2283   text_event.xexpose.width=(int) font_info->max_bounds.width;
2284   text_event.xexpose.height=font_info->max_bounds.ascent+
2285     font_info->max_bounds.descent;
2286   p=annotate_info->text;
2287   do
2288   {
2289     /*
2290       Display text cursor.
2291     */
2292     *p='\0';
2293     (void) XDrawString(display,windows->image.id,annotate_context,x,y,"_",1);
2294     /*
2295       Wait for next event.
2296     */
2297     XScreenEvent(display,windows,&event);
2298     if (event.xany.window == windows->command.id)
2299       {
2300         /*
2301           Select a command from the Command widget.
2302         */
2303         (void) XSetBackground(display,annotate_context,
2304           windows->pixel_info->background_color.pixel);
2305         (void) XSetForeground(display,annotate_context,
2306           windows->pixel_info->foreground_color.pixel);
2307         id=XCommandWidget(display,windows,AnnotateMenu,&event);
2308         (void) XSetBackground(display,annotate_context,
2309           windows->pixel_info->pen_colors[box_id].pixel);
2310         (void) XSetForeground(display,annotate_context,
2311           windows->pixel_info->pen_colors[pen_id].pixel);
2312         if (id < 0)
2313           continue;
2314         switch (TextCommands[id])
2315         {
2316           case TextHelpCommand:
2317           {
2318             XTextViewWidget(display,resource_info,windows,MagickFalse,
2319               "Help Viewer - Image Annotation",ImageAnnotateHelp);
2320             (void) XCheckDefineCursor(display,windows->image.id,cursor);
2321             break;
2322           }
2323           case TextApplyCommand:
2324           {
2325             /*
2326               Finished annotating.
2327             */
2328             annotate_info->width=(unsigned int) XTextWidth(font_info,
2329               annotate_info->text,(int) strlen(annotate_info->text));
2330             XRefreshWindow(display,&windows->image,&text_event);
2331             state|=ExitState;
2332             break;
2333           }
2334           default:
2335             break;
2336         }
2337         continue;
2338       }
2339     /*
2340       Erase text cursor.
2341     */
2342     text_event.xexpose.x=x;
2343     text_event.xexpose.y=y-font_info->max_bounds.ascent;
2344     (void) XClearArea(display,windows->image.id,x,text_event.xexpose.y,
2345       (unsigned int) text_event.xexpose.width,(unsigned int)
2346       text_event.xexpose.height,MagickFalse);
2347     XRefreshWindow(display,&windows->image,&text_event);
2348     switch (event.type)
2349     {
2350       case ButtonPress:
2351       {
2352         if (event.xbutton.window != windows->image.id)
2353           break;
2354         if (event.xbutton.button == Button2)
2355           {
2356             /*
2357               Request primary selection.
2358             */
2359             (void) XConvertSelection(display,XA_PRIMARY,XA_STRING,XA_STRING,
2360               windows->image.id,CurrentTime);
2361             break;
2362           }
2363         break;
2364       }
2365       case Expose:
2366       {
2367         if (event.xexpose.count == 0)
2368           {
2369             XAnnotateInfo
2370               *text_info;
2371
2372             /*
2373               Refresh Image window.
2374             */
2375             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
2376             text_info=annotate_info;
2377             while (text_info != (XAnnotateInfo *) NULL)
2378             {
2379               if (annotate_info->stencil == ForegroundStencil)
2380                 (void) XDrawString(display,windows->image.id,annotate_context,
2381                   text_info->x,text_info->y,text_info->text,
2382                   (int) strlen(text_info->text));
2383               else
2384                 (void) XDrawImageString(display,windows->image.id,
2385                   annotate_context,text_info->x,text_info->y,text_info->text,
2386                   (int) strlen(text_info->text));
2387               text_info=text_info->previous;
2388             }
2389             (void) XDrawString(display,windows->image.id,annotate_context,
2390               x,y,"_",1);
2391           }
2392         break;
2393       }
2394       case KeyPress:
2395       {
2396         int
2397           length;
2398
2399         if (event.xkey.window != windows->image.id)
2400           break;
2401         /*
2402           Respond to a user key press.
2403         */
2404         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
2405           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2406         *(command+length)='\0';
2407         if (((event.xkey.state & ControlMask) != 0) ||
2408             ((event.xkey.state & Mod1Mask) != 0))
2409           state|=ModifierState;
2410         if ((state & ModifierState) != 0)
2411           switch ((int) key_symbol)
2412           {
2413             case XK_u:
2414             case XK_U:
2415             {
2416               key_symbol=DeleteCommand;
2417               break;
2418             }
2419             default:
2420               break;
2421           }
2422         switch ((int) key_symbol)
2423         {
2424           case XK_BackSpace:
2425           {
2426             /*
2427               Erase one character.
2428             */
2429             if (p == annotate_info->text)
2430               {
2431                 if (annotate_info->previous == (XAnnotateInfo *) NULL)
2432                   break;
2433                 else
2434                   {
2435                     /*
2436                       Go to end of the previous line of text.
2437                     */
2438                     annotate_info=annotate_info->previous;
2439                     p=annotate_info->text;
2440                     x=annotate_info->x+annotate_info->width;
2441                     y=annotate_info->y;
2442                     if (annotate_info->width != 0)
2443                       p+=strlen(annotate_info->text);
2444                     break;
2445                   }
2446               }
2447             p--;
2448             x-=XTextWidth(font_info,p,1);
2449             text_event.xexpose.x=x;
2450             text_event.xexpose.y=y-font_info->max_bounds.ascent;
2451             XRefreshWindow(display,&windows->image,&text_event);
2452             break;
2453           }
2454           case XK_bracketleft:
2455           {
2456             key_symbol=XK_Escape;
2457             break;
2458           }
2459           case DeleteCommand:
2460           {
2461             /*
2462               Erase the entire line of text.
2463             */
2464             while (p != annotate_info->text)
2465             {
2466               p--;
2467               x-=XTextWidth(font_info,p,1);
2468               text_event.xexpose.x=x;
2469               XRefreshWindow(display,&windows->image,&text_event);
2470             }
2471             break;
2472           }
2473           case XK_Escape:
2474           case XK_F20:
2475           {
2476             /*
2477               Finished annotating.
2478             */
2479             annotate_info->width=(unsigned int) XTextWidth(font_info,
2480               annotate_info->text,(int) strlen(annotate_info->text));
2481             XRefreshWindow(display,&windows->image,&text_event);
2482             state|=ExitState;
2483             break;
2484           }
2485           default:
2486           {
2487             /*
2488               Draw a single character on the Image window.
2489             */
2490             if ((state & ModifierState) != 0)
2491               break;
2492             if (*command == '\0')
2493               break;
2494             *p=(*command);
2495             if (annotate_info->stencil == ForegroundStencil)
2496               (void) XDrawString(display,windows->image.id,annotate_context,
2497                 x,y,p,1);
2498             else
2499               (void) XDrawImageString(display,windows->image.id,
2500                 annotate_context,x,y,p,1);
2501             x+=XTextWidth(font_info,p,1);
2502             p++;
2503             if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2504               break;
2505           }
2506           case XK_Return:
2507           case XK_KP_Enter:
2508           {
2509             /*
2510               Advance to the next line of text.
2511             */
2512             *p='\0';
2513             annotate_info->width=(unsigned int) XTextWidth(font_info,
2514               annotate_info->text,(int) strlen(annotate_info->text));
2515             if (annotate_info->next != (XAnnotateInfo *) NULL)
2516               {
2517                 /*
2518                   Line of text already exists.
2519                 */
2520                 annotate_info=annotate_info->next;
2521                 x=annotate_info->x;
2522                 y=annotate_info->y;
2523                 p=annotate_info->text;
2524                 break;
2525               }
2526             annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2527               sizeof(*annotate_info->next));
2528             if (annotate_info->next == (XAnnotateInfo *) NULL)
2529               return(MagickFalse);
2530             *annotate_info->next=(*annotate_info);
2531             annotate_info->next->previous=annotate_info;
2532             annotate_info=annotate_info->next;
2533             annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2534               windows->image.width/MagickMax((ssize_t)
2535               font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2536             if (annotate_info->text == (char *) NULL)
2537               return(MagickFalse);
2538             annotate_info->y+=annotate_info->height;
2539             if (annotate_info->y > (int) windows->image.height)
2540               annotate_info->y=(int) annotate_info->height;
2541             annotate_info->next=(XAnnotateInfo *) NULL;
2542             x=annotate_info->x;
2543             y=annotate_info->y;
2544             p=annotate_info->text;
2545             break;
2546           }
2547         }
2548         break;
2549       }
2550       case KeyRelease:
2551       {
2552         /*
2553           Respond to a user key release.
2554         */
2555         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
2556           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
2557         state&=(~ModifierState);
2558         break;
2559       }
2560       case SelectionNotify:
2561       {
2562         Atom
2563           type;
2564
2565         int
2566           format;
2567
2568         unsigned char
2569           *data;
2570
2571         unsigned long
2572           after,
2573           length;
2574
2575         /*
2576           Obtain response from primary selection.
2577         */
2578         if (event.xselection.property == (Atom) None)
2579           break;
2580         status=XGetWindowProperty(display,event.xselection.requestor,
2581           event.xselection.property,0L,(long) MaxTextExtent,True,XA_STRING,
2582           &type,&format,&length,&after,&data);
2583         if ((status != Success) || (type != XA_STRING) || (format == 32) ||
2584             (length == 0))
2585           break;
2586         /*
2587           Annotate Image window with primary selection.
2588         */
2589         for (i=0; i < (ssize_t) length; i++)
2590         {
2591           if ((char) data[i] != '\n')
2592             {
2593               /*
2594                 Draw a single character on the Image window.
2595               */
2596               *p=(char) data[i];
2597               (void) XDrawString(display,windows->image.id,annotate_context,
2598                 x,y,p,1);
2599               x+=XTextWidth(font_info,p,1);
2600               p++;
2601               if ((x+font_info->max_bounds.width) < (int) windows->image.width)
2602                 continue;
2603             }
2604           /*
2605             Advance to the next line of text.
2606           */
2607           *p='\0';
2608           annotate_info->width=(unsigned int) XTextWidth(font_info,
2609             annotate_info->text,(int) strlen(annotate_info->text));
2610           if (annotate_info->next != (XAnnotateInfo *) NULL)
2611             {
2612               /*
2613                 Line of text already exists.
2614               */
2615               annotate_info=annotate_info->next;
2616               x=annotate_info->x;
2617               y=annotate_info->y;
2618               p=annotate_info->text;
2619               continue;
2620             }
2621           annotate_info->next=(XAnnotateInfo *) AcquireMagickMemory(
2622             sizeof(*annotate_info->next));
2623           if (annotate_info->next == (XAnnotateInfo *) NULL)
2624             return(MagickFalse);
2625           *annotate_info->next=(*annotate_info);
2626           annotate_info->next->previous=annotate_info;
2627           annotate_info=annotate_info->next;
2628           annotate_info->text=(char *) AcquireQuantumMemory((size_t)
2629             windows->image.width/MagickMax((ssize_t)
2630             font_info->min_bounds.width,1)+2UL,sizeof(*annotate_info->text));
2631           if (annotate_info->text == (char *) NULL)
2632             return(MagickFalse);
2633           annotate_info->y+=annotate_info->height;
2634           if (annotate_info->y > (int) windows->image.height)
2635             annotate_info->y=(int) annotate_info->height;
2636           annotate_info->next=(XAnnotateInfo *) NULL;
2637           x=annotate_info->x;
2638           y=annotate_info->y;
2639           p=annotate_info->text;
2640         }
2641         (void) XFree((void *) data);
2642         break;
2643       }
2644       default:
2645         break;
2646     }
2647   } while ((state & ExitState) == 0);
2648   (void) XFreeCursor(display,cursor);
2649   /*
2650     Annotation is relative to image configuration.
2651   */
2652   width=(unsigned int) image->columns;
2653   height=(unsigned int) image->rows;
2654   x=0;
2655   y=0;
2656   if (windows->image.crop_geometry != (char *) NULL)
2657     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
2658   /*
2659     Initialize annotated image.
2660   */
2661   XSetCursorState(display,windows,MagickTrue);
2662   XCheckRefreshWindows(display,windows);
2663   while (annotate_info != (XAnnotateInfo *) NULL)
2664   {
2665     if (annotate_info->width == 0)
2666       {
2667         /*
2668           No text on this line--  go to the next line of text.
2669         */
2670         previous_info=annotate_info->previous;
2671         annotate_info->text=(char *)
2672           RelinquishMagickMemory(annotate_info->text);
2673         annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2674         annotate_info=previous_info;
2675         continue;
2676       }
2677     /*
2678       Determine pixel index for box and pen color.
2679     */
2680     windows->pixel_info->box_color=windows->pixel_info->pen_colors[box_id];
2681     if (windows->pixel_info->colors != 0)
2682       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2683         if (windows->pixel_info->pixels[i] ==
2684             windows->pixel_info->pen_colors[box_id].pixel)
2685           {
2686             windows->pixel_info->box_index=(unsigned short) i;
2687             break;
2688           }
2689     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
2690     if (windows->pixel_info->colors != 0)
2691       for (i=0; i < (ssize_t) windows->pixel_info->colors; i++)
2692         if (windows->pixel_info->pixels[i] ==
2693             windows->pixel_info->pen_colors[pen_id].pixel)
2694           {
2695             windows->pixel_info->pen_index=(unsigned short) i;
2696             break;
2697           }
2698     /*
2699       Define the annotate geometry string.
2700     */
2701     annotate_info->x=(int)
2702       width*(annotate_info->x+windows->image.x)/windows->image.ximage->width;
2703     annotate_info->y=(int) height*(annotate_info->y-font_info->ascent+
2704       windows->image.y)/windows->image.ximage->height;
2705     (void) FormatMagickString(annotate_info->geometry,MaxTextExtent,
2706       "%ux%u%+d%+d",width*annotate_info->width/windows->image.ximage->width,
2707       height*annotate_info->height/windows->image.ximage->height,
2708       annotate_info->x+x,annotate_info->y+y);
2709     /*
2710       Annotate image with text.
2711     */
2712     status=XAnnotateImage(display,windows->pixel_info,annotate_info,image);
2713     if (status == 0)
2714       return(MagickFalse);
2715     /*
2716       Free up memory.
2717     */
2718     previous_info=annotate_info->previous;
2719     annotate_info->text=DestroyString(annotate_info->text);
2720     annotate_info=(XAnnotateInfo *) RelinquishMagickMemory(annotate_info);
2721     annotate_info=previous_info;
2722   }
2723   (void) XSetForeground(display,annotate_context,
2724     windows->pixel_info->foreground_color.pixel);
2725   (void) XSetBackground(display,annotate_context,
2726     windows->pixel_info->background_color.pixel);
2727   (void) XSetFont(display,annotate_context,windows->font_info->fid);
2728   XSetCursorState(display,windows,MagickFalse);
2729   (void) XFreeFont(display,font_info);
2730   /*
2731     Update image configuration.
2732   */
2733   XConfigureImageColormap(display,resource_info,windows,image);
2734   (void) XConfigureImage(display,resource_info,windows,image);
2735   return(MagickTrue);
2736 }
2737 \f
2738 /*
2739 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2740 %                                                                             %
2741 %                                                                             %
2742 %                                                                             %
2743 +   X B a c k g r o u n d I m a g e                                           %
2744 %                                                                             %
2745 %                                                                             %
2746 %                                                                             %
2747 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2748 %
2749 %  XBackgroundImage() displays the image in the background of a window.
2750 %
2751 %  The format of the XBackgroundImage method is:
2752 %
2753 %      MagickBooleanType XBackgroundImage(Display *display,
2754 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
2755 %
2756 %  A description of each parameter follows:
2757 %
2758 %    o display: Specifies a connection to an X server; returned from
2759 %      XOpenDisplay.
2760 %
2761 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2762 %
2763 %    o windows: Specifies a pointer to a XWindows structure.
2764 %
2765 %    o image: the image.
2766 %
2767 */
2768 static MagickBooleanType XBackgroundImage(Display *display,
2769   XResourceInfo *resource_info,XWindows *windows,Image **image)
2770 {
2771 #define BackgroundImageTag  "Background/Image"
2772
2773   int
2774     status;
2775
2776   static char
2777     window_id[MaxTextExtent] = "root";
2778
2779   XResourceInfo
2780     background_resources;
2781
2782   /*
2783     Put image in background.
2784   */
2785   status=XDialogWidget(display,windows,"Background",
2786     "Enter window id (id 0x00 selects window with pointer):",window_id);
2787   if (*window_id == '\0')
2788     return(MagickFalse);
2789   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
2790   XInfoWidget(display,windows,BackgroundImageTag);
2791   XSetCursorState(display,windows,MagickTrue);
2792   XCheckRefreshWindows(display,windows);
2793   background_resources=(*resource_info);
2794   background_resources.window_id=window_id;
2795   background_resources.backdrop=status != 0 ? MagickTrue : MagickFalse;
2796   status=XDisplayBackgroundImage(display,&background_resources,*image);
2797   if (status != MagickFalse)
2798     XClientMessage(display,windows->image.id,windows->im_protocols,
2799       windows->im_retain_colors,CurrentTime);
2800   XSetCursorState(display,windows,MagickFalse);
2801   (void) XMagickCommand(display,resource_info,windows,UndoCommand,image);
2802   return(MagickTrue);
2803 }
2804 \f
2805 /*
2806 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2807 %                                                                             %
2808 %                                                                             %
2809 %                                                                             %
2810 +   X C h o p I m a g e                                                       %
2811 %                                                                             %
2812 %                                                                             %
2813 %                                                                             %
2814 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2815 %
2816 %  XChopImage() chops the X image.
2817 %
2818 %  The format of the XChopImage method is:
2819 %
2820 %    MagickBooleanType XChopImage(Display *display,XResourceInfo *resource_info,
2821 %      XWindows *windows,Image **image)
2822 %
2823 %  A description of each parameter follows:
2824 %
2825 %    o display: Specifies a connection to an X server; returned from
2826 %      XOpenDisplay.
2827 %
2828 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
2829 %
2830 %    o windows: Specifies a pointer to a XWindows structure.
2831 %
2832 %    o image: the image.
2833 %
2834 */
2835 static MagickBooleanType XChopImage(Display *display,
2836   XResourceInfo *resource_info,XWindows *windows,Image **image)
2837 {
2838   static const char
2839     *ChopMenu[] =
2840     {
2841       "Direction",
2842       "Help",
2843       "Dismiss",
2844       (char *) NULL
2845     };
2846
2847   static ModeType
2848     direction = HorizontalChopCommand;
2849
2850   static const ModeType
2851     ChopCommands[] =
2852     {
2853       ChopDirectionCommand,
2854       ChopHelpCommand,
2855       ChopDismissCommand
2856     },
2857     DirectionCommands[] =
2858     {
2859       HorizontalChopCommand,
2860       VerticalChopCommand
2861     };
2862
2863   char
2864     text[MaxTextExtent];
2865
2866   Image
2867     *chop_image;
2868
2869   int
2870     id,
2871     x,
2872     y;
2873
2874   MagickRealType
2875     scale_factor;
2876
2877   RectangleInfo
2878     chop_info;
2879
2880   unsigned int
2881     distance,
2882     height,
2883     width;
2884
2885   size_t
2886     state;
2887
2888   XEvent
2889     event;
2890
2891   XSegment
2892     segment_info;
2893
2894   /*
2895     Map Command widget.
2896   */
2897   (void) CloneString(&windows->command.name,"Chop");
2898   windows->command.data=1;
2899   (void) XCommandWidget(display,windows,ChopMenu,(XEvent *) NULL);
2900   (void) XMapRaised(display,windows->command.id);
2901   XClientMessage(display,windows->image.id,windows->im_protocols,
2902     windows->im_update_widget,CurrentTime);
2903   /*
2904     Track pointer until button 1 is pressed.
2905   */
2906   XQueryPosition(display,windows->image.id,&x,&y);
2907   (void) XSelectInput(display,windows->image.id,
2908     windows->image.attributes.event_mask | PointerMotionMask);
2909   state=DefaultState;
2910   do
2911   {
2912     if (windows->info.mapped != MagickFalse)
2913       {
2914         /*
2915           Display pointer position.
2916         */
2917         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
2918           x+windows->image.x,y+windows->image.y);
2919         XInfoWidget(display,windows,text);
2920       }
2921     /*
2922       Wait for next event.
2923     */
2924     XScreenEvent(display,windows,&event);
2925     if (event.xany.window == windows->command.id)
2926       {
2927         /*
2928           Select a command from the Command widget.
2929         */
2930         id=XCommandWidget(display,windows,ChopMenu,&event);
2931         if (id < 0)
2932           continue;
2933         switch (ChopCommands[id])
2934         {
2935           case ChopDirectionCommand:
2936           {
2937             char
2938               command[MaxTextExtent];
2939
2940             static const char
2941               *Directions[] =
2942               {
2943                 "horizontal",
2944                 "vertical",
2945                 (char *) NULL,
2946               };
2947
2948             /*
2949               Select a command from the pop-up menu.
2950             */
2951             id=XMenuWidget(display,windows,ChopMenu[id],Directions,command);
2952             if (id >= 0)
2953               direction=DirectionCommands[id];
2954             break;
2955           }
2956           case ChopHelpCommand:
2957           {
2958             XTextViewWidget(display,resource_info,windows,MagickFalse,
2959               "Help Viewer - Image Chop",ImageChopHelp);
2960             break;
2961           }
2962           case ChopDismissCommand:
2963           {
2964             /*
2965               Prematurely exit.
2966             */
2967             state|=EscapeState;
2968             state|=ExitState;
2969             break;
2970           }
2971           default:
2972             break;
2973         }
2974         continue;
2975       }
2976     switch (event.type)
2977     {
2978       case ButtonPress:
2979       {
2980         if (event.xbutton.button != Button1)
2981           break;
2982         if (event.xbutton.window != windows->image.id)
2983           break;
2984         /*
2985           User has committed to start point of chopping line.
2986         */
2987         segment_info.x1=(short int) event.xbutton.x;
2988         segment_info.x2=(short int) event.xbutton.x;
2989         segment_info.y1=(short int) event.xbutton.y;
2990         segment_info.y2=(short int) event.xbutton.y;
2991         state|=ExitState;
2992         break;
2993       }
2994       case ButtonRelease:
2995         break;
2996       case Expose:
2997         break;
2998       case KeyPress:
2999       {
3000         char
3001           command[MaxTextExtent];
3002
3003         KeySym
3004           key_symbol;
3005
3006         if (event.xkey.window != windows->image.id)
3007           break;
3008         /*
3009           Respond to a user key press.
3010         */
3011         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3012           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3013         switch ((int) key_symbol)
3014         {
3015           case XK_Escape:
3016           case XK_F20:
3017           {
3018             /*
3019               Prematurely exit.
3020             */
3021             state|=EscapeState;
3022             state|=ExitState;
3023             break;
3024           }
3025           case XK_F1:
3026           case XK_Help:
3027           {
3028             (void) XSetFunction(display,windows->image.highlight_context,
3029               GXcopy);
3030             XTextViewWidget(display,resource_info,windows,MagickFalse,
3031               "Help Viewer - Image Chop",ImageChopHelp);
3032             (void) XSetFunction(display,windows->image.highlight_context,
3033               GXinvert);
3034             break;
3035           }
3036           default:
3037           {
3038             (void) XBell(display,0);
3039             break;
3040           }
3041         }
3042         break;
3043       }
3044       case MotionNotify:
3045       {
3046         /*
3047           Map and unmap Info widget as text cursor crosses its boundaries.
3048         */
3049         x=event.xmotion.x;
3050         y=event.xmotion.y;
3051         if (windows->info.mapped != MagickFalse)
3052           {
3053             if ((x < (int) (windows->info.x+windows->info.width)) &&
3054                 (y < (int) (windows->info.y+windows->info.height)))
3055               (void) XWithdrawWindow(display,windows->info.id,
3056                 windows->info.screen);
3057           }
3058         else
3059           if ((x > (int) (windows->info.x+windows->info.width)) ||
3060               (y > (int) (windows->info.y+windows->info.height)))
3061             (void) XMapWindow(display,windows->info.id);
3062       }
3063     }
3064   } while ((state & ExitState) == 0);
3065   (void) XSelectInput(display,windows->image.id,
3066     windows->image.attributes.event_mask);
3067   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3068   if ((state & EscapeState) != 0)
3069     return(MagickTrue);
3070   /*
3071     Draw line as pointer moves until the mouse button is released.
3072   */
3073   chop_info.width=0;
3074   chop_info.height=0;
3075   chop_info.x=0;
3076   chop_info.y=0;
3077   distance=0;
3078   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3079   state=DefaultState;
3080   do
3081   {
3082     if (distance > 9)
3083       {
3084         /*
3085           Display info and draw chopping line.
3086         */
3087         if (windows->info.mapped == MagickFalse)
3088           (void) XMapWindow(display,windows->info.id);
3089         (void) FormatMagickString(text,MaxTextExtent,
3090           " %.20gx%.20g%+.20g%+.20g",(double) chop_info.width,(double)
3091           chop_info.height,(double) chop_info.x,(double) chop_info.y);
3092         XInfoWidget(display,windows,text);
3093         XHighlightLine(display,windows->image.id,
3094           windows->image.highlight_context,&segment_info);
3095       }
3096     else
3097       if (windows->info.mapped != MagickFalse)
3098         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3099     /*
3100       Wait for next event.
3101     */
3102     XScreenEvent(display,windows,&event);
3103     if (distance > 9)
3104       XHighlightLine(display,windows->image.id,
3105         windows->image.highlight_context,&segment_info);
3106     switch (event.type)
3107     {
3108       case ButtonPress:
3109       {
3110         segment_info.x2=(short int) event.xmotion.x;
3111         segment_info.y2=(short int) event.xmotion.y;
3112         break;
3113       }
3114       case ButtonRelease:
3115       {
3116         /*
3117           User has committed to chopping line.
3118         */
3119         segment_info.x2=(short int) event.xbutton.x;
3120         segment_info.y2=(short int) event.xbutton.y;
3121         state|=ExitState;
3122         break;
3123       }
3124       case Expose:
3125         break;
3126       case MotionNotify:
3127       {
3128         segment_info.x2=(short int) event.xmotion.x;
3129         segment_info.y2=(short int) event.xmotion.y;
3130       }
3131       default:
3132         break;
3133     }
3134     /*
3135       Check boundary conditions.
3136     */
3137     if (segment_info.x2 < 0)
3138       segment_info.x2=0;
3139     else
3140       if (segment_info.x2 > windows->image.ximage->width)
3141         segment_info.x2=windows->image.ximage->width;
3142     if (segment_info.y2 < 0)
3143       segment_info.y2=0;
3144     else
3145       if (segment_info.y2 > windows->image.ximage->height)
3146         segment_info.y2=windows->image.ximage->height;
3147     distance=(unsigned int)
3148       (((segment_info.x2-segment_info.x1)*(segment_info.x2-segment_info.x1))+
3149        ((segment_info.y2-segment_info.y1)*(segment_info.y2-segment_info.y1)));
3150     /*
3151       Compute chopping geometry.
3152     */
3153     if (direction == HorizontalChopCommand)
3154       {
3155         chop_info.width=(size_t) (segment_info.x2-segment_info.x1+1);
3156         chop_info.x=(ssize_t) windows->image.x+segment_info.x1;
3157         chop_info.height=0;
3158         chop_info.y=0;
3159         if (segment_info.x1 > (int) segment_info.x2)
3160           {
3161             chop_info.width=(size_t) (segment_info.x1-segment_info.x2+1);
3162             chop_info.x=(ssize_t) windows->image.x+segment_info.x2;
3163           }
3164       }
3165     else
3166       {
3167         chop_info.width=0;
3168         chop_info.height=(size_t) (segment_info.y2-segment_info.y1+1);
3169         chop_info.x=0;
3170         chop_info.y=(ssize_t) windows->image.y+segment_info.y1;
3171         if (segment_info.y1 > segment_info.y2)
3172           {
3173             chop_info.height=(size_t) (segment_info.y1-segment_info.y2+1);
3174             chop_info.y=(ssize_t) windows->image.y+segment_info.y2;
3175           }
3176       }
3177   } while ((state & ExitState) == 0);
3178   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
3179   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
3180   if (distance <= 9)
3181     return(MagickTrue);
3182   /*
3183     Image chopping is relative to image configuration.
3184   */
3185   (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
3186   XSetCursorState(display,windows,MagickTrue);
3187   XCheckRefreshWindows(display,windows);
3188   windows->image.window_changes.width=windows->image.ximage->width-
3189     (unsigned int) chop_info.width;
3190   windows->image.window_changes.height=windows->image.ximage->height-
3191     (unsigned int) chop_info.height;
3192   width=(unsigned int) (*image)->columns;
3193   height=(unsigned int) (*image)->rows;
3194   x=0;
3195   y=0;
3196   if (windows->image.crop_geometry != (char *) NULL)
3197     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
3198   scale_factor=(MagickRealType) width/windows->image.ximage->width;
3199   chop_info.x+=x;
3200   chop_info.x=(ssize_t) (scale_factor*chop_info.x+0.5);
3201   chop_info.width=(unsigned int) (scale_factor*chop_info.width+0.5);
3202   scale_factor=(MagickRealType) height/windows->image.ximage->height;
3203   chop_info.y+=y;
3204   chop_info.y=(ssize_t) (scale_factor*chop_info.y+0.5);
3205   chop_info.height=(unsigned int) (scale_factor*chop_info.height+0.5);
3206   /*
3207     Chop image.
3208   */
3209   chop_image=ChopImage(*image,&chop_info,&(*image)->exception);
3210   XSetCursorState(display,windows,MagickFalse);
3211   if (chop_image == (Image *) NULL)
3212     return(MagickFalse);
3213   *image=DestroyImage(*image);
3214   *image=chop_image;
3215   /*
3216     Update image configuration.
3217   */
3218   XConfigureImageColormap(display,resource_info,windows,*image);
3219   (void) XConfigureImage(display,resource_info,windows,*image);
3220   return(MagickTrue);
3221 }
3222 \f
3223 /*
3224 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3225 %                                                                             %
3226 %                                                                             %
3227 %                                                                             %
3228 +   X C o l o r E d i t I m a g e                                             %
3229 %                                                                             %
3230 %                                                                             %
3231 %                                                                             %
3232 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3233 %
3234 %  XColorEditImage() allows the user to interactively change the color of one
3235 %  pixel for a DirectColor image or one colormap entry for a PseudoClass image.
3236 %
3237 %  The format of the XColorEditImage method is:
3238 %
3239 %      MagickBooleanType XColorEditImage(Display *display,
3240 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
3241 %
3242 %  A description of each parameter follows:
3243 %
3244 %    o display: Specifies a connection to an X server;  returned from
3245 %      XOpenDisplay.
3246 %
3247 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3248 %
3249 %    o windows: Specifies a pointer to a XWindows structure.
3250 %
3251 %    o image: the image; returned from ReadImage.
3252 %
3253 */
3254
3255
3256 static MagickBooleanType XColorEditImage(Display *display,
3257   XResourceInfo *resource_info,XWindows *windows,Image **image)
3258 {
3259   static const char
3260     *ColorEditMenu[] =
3261     {
3262       "Method",
3263       "Pixel Color",
3264       "Border Color",
3265       "Fuzz",
3266       "Undo",
3267       "Help",
3268       "Dismiss",
3269       (char *) NULL
3270     };
3271
3272   static const ModeType
3273     ColorEditCommands[] =
3274     {
3275       ColorEditMethodCommand,
3276       ColorEditColorCommand,
3277       ColorEditBorderCommand,
3278       ColorEditFuzzCommand,
3279       ColorEditUndoCommand,
3280       ColorEditHelpCommand,
3281       ColorEditDismissCommand
3282     };
3283
3284   static PaintMethod
3285     method = PointMethod;
3286
3287   static unsigned int
3288     pen_id = 0;
3289
3290   static XColor
3291     border_color = { 0, 0, 0, 0, 0, 0 };
3292
3293   char
3294     command[MaxTextExtent],
3295     text[MaxTextExtent];
3296
3297   Cursor
3298     cursor;
3299
3300   ExceptionInfo
3301     *exception;
3302
3303   int
3304     entry,
3305     id,
3306     x,
3307     x_offset,
3308     y,
3309     y_offset;
3310
3311   register PixelPacket
3312     *q;
3313
3314   register ssize_t
3315     i;
3316
3317   unsigned int
3318     height,
3319     width;
3320
3321   size_t
3322     state;
3323
3324   XColor
3325     color;
3326
3327   XEvent
3328     event;
3329
3330   /*
3331     Map Command widget.
3332   */
3333   (void) CloneString(&windows->command.name,"Color Edit");
3334   windows->command.data=4;
3335   (void) XCommandWidget(display,windows,ColorEditMenu,(XEvent *) NULL);
3336   (void) XMapRaised(display,windows->command.id);
3337   XClientMessage(display,windows->image.id,windows->im_protocols,
3338     windows->im_update_widget,CurrentTime);
3339   /*
3340     Make cursor.
3341   */
3342   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
3343     resource_info->background_color,resource_info->foreground_color);
3344   (void) XCheckDefineCursor(display,windows->image.id,cursor);
3345   /*
3346     Track pointer until button 1 is pressed.
3347   */
3348   XQueryPosition(display,windows->image.id,&x,&y);
3349   (void) XSelectInput(display,windows->image.id,
3350     windows->image.attributes.event_mask | PointerMotionMask);
3351   state=DefaultState;
3352   do
3353   {
3354     if (windows->info.mapped != MagickFalse)
3355       {
3356         /*
3357           Display pointer position.
3358         */
3359         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
3360           x+windows->image.x,y+windows->image.y);
3361         XInfoWidget(display,windows,text);
3362       }
3363     /*
3364       Wait for next event.
3365     */
3366     XScreenEvent(display,windows,&event);
3367     if (event.xany.window == windows->command.id)
3368       {
3369         /*
3370           Select a command from the Command widget.
3371         */
3372         id=XCommandWidget(display,windows,ColorEditMenu,&event);
3373         if (id < 0)
3374           {
3375             (void) XCheckDefineCursor(display,windows->image.id,cursor);
3376             continue;
3377           }
3378         switch (ColorEditCommands[id])
3379         {
3380           case ColorEditMethodCommand:
3381           {
3382             char
3383               **methods;
3384
3385             /*
3386               Select a method from the pop-up menu.
3387             */
3388             methods=(char **) GetMagickOptions(MagickMethodOptions);
3389             if (methods == (char **) NULL)
3390               break;
3391             entry=XMenuWidget(display,windows,ColorEditMenu[id],
3392               (const char **) methods,command);
3393             if (entry >= 0)
3394               method=(PaintMethod) ParseMagickOption(MagickMethodOptions,
3395                 MagickFalse,methods[entry]);
3396             methods=DestroyStringList(methods);
3397             break;
3398           }
3399           case ColorEditColorCommand:
3400           {
3401             const char
3402               *ColorMenu[MaxNumberPens];
3403
3404             int
3405               pen_number;
3406
3407             /*
3408               Initialize menu selections.
3409             */
3410             for (i=0; i < (int) (MaxNumberPens-2); i++)
3411               ColorMenu[i]=resource_info->pen_colors[i];
3412             ColorMenu[MaxNumberPens-2]="Browser...";
3413             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3414             /*
3415               Select a pen color from the pop-up menu.
3416             */
3417             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3418               (const char **) ColorMenu,command);
3419             if (pen_number < 0)
3420               break;
3421             if (pen_number == (MaxNumberPens-2))
3422               {
3423                 static char
3424                   color_name[MaxTextExtent] = "gray";
3425
3426                 /*
3427                   Select a pen color from a dialog.
3428                 */
3429                 resource_info->pen_colors[pen_number]=color_name;
3430                 XColorBrowserWidget(display,windows,"Select",color_name);
3431                 if (*color_name == '\0')
3432                   break;
3433               }
3434             /*
3435               Set pen color.
3436             */
3437             (void) XParseColor(display,windows->map_info->colormap,
3438               resource_info->pen_colors[pen_number],&color);
3439             XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
3440               (unsigned int) MaxColors,&color);
3441             windows->pixel_info->pen_colors[pen_number]=color;
3442             pen_id=(unsigned int) pen_number;
3443             break;
3444           }
3445           case ColorEditBorderCommand:
3446           {
3447             const char
3448               *ColorMenu[MaxNumberPens];
3449
3450             int
3451               pen_number;
3452
3453             /*
3454               Initialize menu selections.
3455             */
3456             for (i=0; i < (int) (MaxNumberPens-2); i++)
3457               ColorMenu[i]=resource_info->pen_colors[i];
3458             ColorMenu[MaxNumberPens-2]="Browser...";
3459             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
3460             /*
3461               Select a pen color from the pop-up menu.
3462             */
3463             pen_number=XMenuWidget(display,windows,ColorEditMenu[id],
3464               (const char **) ColorMenu,command);
3465             if (pen_number < 0)
3466               break;
3467             if (pen_number == (MaxNumberPens-2))
3468               {
3469                 static char
3470                   color_name[MaxTextExtent] = "gray";
3471
3472                 /*
3473                   Select a pen color from a dialog.
3474                 */
3475                 resource_info->pen_colors[pen_number]=color_name;
3476                 XColorBrowserWidget(display,windows,"Select",color_name);
3477                 if (*color_name == '\0')
3478                   break;
3479               }
3480             /*
3481               Set border color.
3482             */
3483             (void) XParseColor(display,windows->map_info->colormap,
3484               resource_info->pen_colors[pen_number],&border_color);
3485             break;
3486           }
3487           case ColorEditFuzzCommand:
3488           {
3489             static char
3490               fuzz[MaxTextExtent];
3491
3492             static const char
3493               *FuzzMenu[] =
3494               {
3495                 "0%",
3496                 "2%",
3497                 "5%",
3498                 "10%",
3499                 "15%",
3500                 "Dialog...",
3501                 (char *) NULL,
3502               };
3503
3504             /*
3505               Select a command from the pop-up menu.
3506             */
3507             entry=XMenuWidget(display,windows,ColorEditMenu[id],FuzzMenu,
3508               command);
3509             if (entry < 0)
3510               break;
3511             if (entry != 5)
3512               {
3513                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*
3514                   QuantumRange+1.0);
3515                 break;
3516               }
3517             (void) (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
3518             (void) XDialogWidget(display,windows,"Ok",
3519               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
3520             if (*fuzz == '\0')
3521               break;
3522             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
3523             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
3524             break;
3525           }
3526           case ColorEditUndoCommand:
3527           {
3528             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
3529               image);
3530             break;
3531           }
3532           case ColorEditHelpCommand:
3533           default:
3534           {
3535             XTextViewWidget(display,resource_info,windows,MagickFalse,
3536               "Help Viewer - Image Annotation",ImageColorEditHelp);
3537             break;
3538           }
3539           case ColorEditDismissCommand:
3540           {
3541             /*
3542               Prematurely exit.
3543             */
3544             state|=EscapeState;
3545             state|=ExitState;
3546             break;
3547           }
3548         }
3549         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3550         continue;
3551       }
3552     switch (event.type)
3553     {
3554       case ButtonPress:
3555       {
3556         if (event.xbutton.button != Button1)
3557           break;
3558         if ((event.xbutton.window != windows->image.id) &&
3559             (event.xbutton.window != windows->magnify.id))
3560           break;
3561         /*
3562           exit loop.
3563         */
3564         x=event.xbutton.x;
3565         y=event.xbutton.y;
3566         (void) XMagickCommand(display,resource_info,windows,
3567           SaveToUndoBufferCommand,image);
3568         state|=UpdateConfigurationState;
3569         break;
3570       }
3571       case ButtonRelease:
3572       {
3573         if (event.xbutton.button != Button1)
3574           break;
3575         if ((event.xbutton.window != windows->image.id) &&
3576             (event.xbutton.window != windows->magnify.id))
3577           break;
3578         /*
3579           Update colormap information.
3580         */
3581         x=event.xbutton.x;
3582         y=event.xbutton.y;
3583         XConfigureImageColormap(display,resource_info,windows,*image);
3584         (void) XConfigureImage(display,resource_info,windows,*image);
3585         XInfoWidget(display,windows,text);
3586         (void) XCheckDefineCursor(display,windows->image.id,cursor);
3587         state&=(~UpdateConfigurationState);
3588         break;
3589       }
3590       case Expose:
3591         break;
3592       case KeyPress:
3593       {
3594         KeySym
3595           key_symbol;
3596
3597         if (event.xkey.window == windows->magnify.id)
3598           {
3599             Window
3600               window;
3601
3602             window=windows->magnify.id;
3603             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
3604           }
3605         if (event.xkey.window != windows->image.id)
3606           break;
3607         /*
3608           Respond to a user key press.
3609         */
3610         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
3611           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
3612         switch ((int) key_symbol)
3613         {
3614           case XK_Escape:
3615           case XK_F20:
3616           {
3617             /*
3618               Prematurely exit.
3619             */
3620             state|=ExitState;
3621             break;
3622           }
3623           case XK_F1:
3624           case XK_Help:
3625           {
3626             XTextViewWidget(display,resource_info,windows,MagickFalse,
3627               "Help Viewer - Image Annotation",ImageColorEditHelp);
3628             break;
3629           }
3630           default:
3631           {
3632             (void) XBell(display,0);
3633             break;
3634           }
3635         }
3636         break;
3637       }
3638       case MotionNotify:
3639       {
3640         /*
3641           Map and unmap Info widget as cursor crosses its boundaries.
3642         */
3643         x=event.xmotion.x;
3644         y=event.xmotion.y;
3645         if (windows->info.mapped != MagickFalse)
3646           {
3647             if ((x < (int) (windows->info.x+windows->info.width)) &&
3648                 (y < (int) (windows->info.y+windows->info.height)))
3649               (void) XWithdrawWindow(display,windows->info.id,
3650                 windows->info.screen);
3651           }
3652         else
3653           if ((x > (int) (windows->info.x+windows->info.width)) ||
3654               (y > (int) (windows->info.y+windows->info.height)))
3655             (void) XMapWindow(display,windows->info.id);
3656         break;
3657       }
3658       default:
3659         break;
3660     }
3661     if (event.xany.window == windows->magnify.id)
3662       {
3663         x=windows->magnify.x-windows->image.x;
3664         y=windows->magnify.y-windows->image.y;
3665       }
3666     x_offset=x;
3667     y_offset=y;
3668     if ((state & UpdateConfigurationState) != 0)
3669       {
3670         CacheView
3671           *image_view;
3672
3673         int
3674           x,
3675           y;
3676
3677         /*
3678           Pixel edit is relative to image configuration.
3679         */
3680         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
3681           MagickTrue);
3682         color=windows->pixel_info->pen_colors[pen_id];
3683         XPutPixel(windows->image.ximage,x_offset,y_offset,color.pixel);
3684         width=(unsigned int) (*image)->columns;
3685         height=(unsigned int) (*image)->rows;
3686         x=0;
3687         y=0;
3688         if (windows->image.crop_geometry != (char *) NULL)
3689           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
3690             &width,&height);
3691         x_offset=(int)
3692           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
3693         y_offset=(int)
3694           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
3695         if ((x_offset < 0) || (y_offset < 0))
3696           continue;
3697         if ((x_offset >= (int) (*image)->columns) ||
3698             (y_offset >= (int) (*image)->rows))
3699           continue;
3700         exception=(&(*image)->exception);
3701         image_view=AcquireCacheView(*image);
3702         switch (method)
3703         {
3704           case PointMethod:
3705           default:
3706           {
3707             /*
3708               Update color information using point algorithm.
3709             */
3710             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3711               return(MagickFalse);
3712             q=GetCacheViewAuthenticPixels(image_view,(ssize_t)x_offset,
3713               (ssize_t)y_offset,1,1,exception);
3714             if (q == (PixelPacket *) NULL)
3715               break;
3716             q->red=ScaleShortToQuantum(color.red);
3717             q->green=ScaleShortToQuantum(color.green);
3718             q->blue=ScaleShortToQuantum(color.blue);
3719             (void) SyncCacheViewAuthenticPixels(image_view,
3720               &(*image)->exception);
3721             break;
3722           }
3723           case ReplaceMethod:
3724           {
3725             PixelPacket
3726               target;
3727
3728             /*
3729               Update color information using replace algorithm.
3730             */
3731             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
3732               (ssize_t) y_offset,&target,&(*image)->exception);
3733             if ((*image)->storage_class == DirectClass)
3734               {
3735                 for (y=0; y < (int) (*image)->rows; y++)
3736                 {
3737                   q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3738                     (*image)->columns,1,exception);
3739                   if (q == (PixelPacket *) NULL)
3740                     break;
3741                   for (x=0; x < (int) (*image)->columns; x++)
3742                   {
3743                     if (IsColorSimilar(*image,q,&target))
3744                       {
3745                         q->red=ScaleShortToQuantum(color.red);
3746                         q->green=ScaleShortToQuantum(color.green);
3747                         q->blue=ScaleShortToQuantum(color.blue);
3748                       }
3749                     q++;
3750                   }
3751                   if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3752                     break;
3753                 }
3754               }
3755             else
3756               {
3757                 for (i=0; i < (ssize_t) (*image)->colors; i++)
3758                   if (IsColorSimilar(*image,(*image)->colormap+i,&target))
3759                     {
3760                       (*image)->colormap[i].red=ScaleShortToQuantum(color.red);
3761                       (*image)->colormap[i].green=ScaleShortToQuantum(
3762                         color.green);
3763                       (*image)->colormap[i].blue=ScaleShortToQuantum(
3764                         color.blue);
3765                     }
3766                 (void) SyncImage(*image);
3767               }
3768             break;
3769           }
3770           case FloodfillMethod:
3771           case FillToBorderMethod:
3772           {
3773             DrawInfo
3774               *draw_info;
3775
3776             MagickPixelPacket
3777               target;
3778
3779             /*
3780               Update color information using floodfill algorithm.
3781             */
3782             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
3783               (ssize_t) y_offset,&target,exception);
3784             if (method == FillToBorderMethod)
3785               {
3786                 target.red=(MagickRealType)
3787                   ScaleShortToQuantum(border_color.red);
3788                 target.green=(MagickRealType)
3789                   ScaleShortToQuantum(border_color.green);
3790                 target.blue=(MagickRealType)
3791                   ScaleShortToQuantum(border_color.blue);
3792               }
3793             draw_info=CloneDrawInfo(resource_info->image_info,
3794               (DrawInfo *) NULL);
3795             (void) QueryColorDatabase(resource_info->pen_colors[pen_id],
3796               &draw_info->fill,exception);
3797             (void) FloodfillPaintImage(*image,DefaultChannels,draw_info,&target,
3798               (ssize_t) x_offset,(ssize_t) y_offset,
3799               method == FloodfillMethod ? MagickFalse : MagickTrue);
3800             draw_info=DestroyDrawInfo(draw_info);
3801             break;
3802           }
3803           case ResetMethod:
3804           {
3805             /*
3806               Update color information using reset algorithm.
3807             */
3808             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
3809               return(MagickFalse);
3810             for (y=0; y < (int) (*image)->rows; y++)
3811             {
3812               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
3813                 (*image)->columns,1,exception);
3814               if (q == (PixelPacket *) NULL)
3815                 break;
3816               for (x=0; x < (int) (*image)->columns; x++)
3817               {
3818                 q->red=ScaleShortToQuantum(color.red);
3819                 q->green=ScaleShortToQuantum(color.green);
3820                 q->blue=ScaleShortToQuantum(color.blue);
3821                 q++;
3822               }
3823               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3824                 break;
3825             }
3826             break;
3827           }
3828         }
3829         image_view=DestroyCacheView(image_view);
3830         state&=(~UpdateConfigurationState);
3831       }
3832   } while ((state & ExitState) == 0);
3833   (void) XSelectInput(display,windows->image.id,
3834     windows->image.attributes.event_mask);
3835   XSetCursorState(display,windows,MagickFalse);
3836   (void) XFreeCursor(display,cursor);
3837   return(MagickTrue);
3838 }
3839 \f
3840 /*
3841 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3842 %                                                                             %
3843 %                                                                             %
3844 %                                                                             %
3845 +   X C o m p o s i t e I m a g e                                             %
3846 %                                                                             %
3847 %                                                                             %
3848 %                                                                             %
3849 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3850 %
3851 %  XCompositeImage() requests an image name from the user, reads the image and
3852 %  composites it with the X window image at a location the user chooses with
3853 %  the pointer.
3854 %
3855 %  The format of the XCompositeImage method is:
3856 %
3857 %      MagickBooleanType XCompositeImage(Display *display,
3858 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
3859 %
3860 %  A description of each parameter follows:
3861 %
3862 %    o display: Specifies a connection to an X server;  returned from
3863 %      XOpenDisplay.
3864 %
3865 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
3866 %
3867 %    o windows: Specifies a pointer to a XWindows structure.
3868 %
3869 %    o image: the image; returned from ReadImage.
3870 %
3871 */
3872 static MagickBooleanType XCompositeImage(Display *display,
3873   XResourceInfo *resource_info,XWindows *windows,Image *image)
3874 {
3875   static char
3876     displacement_geometry[MaxTextExtent] = "30x30",
3877     filename[MaxTextExtent] = "\0";
3878
3879   static const char
3880     *CompositeMenu[] =
3881     {
3882       "Operators",
3883       "Dissolve",
3884       "Displace",
3885       "Help",
3886       "Dismiss",
3887       (char *) NULL
3888     };
3889
3890   static CompositeOperator
3891     compose = CopyCompositeOp;
3892
3893   static const ModeType
3894     CompositeCommands[] =
3895     {
3896       CompositeOperatorsCommand,
3897       CompositeDissolveCommand,
3898       CompositeDisplaceCommand,
3899       CompositeHelpCommand,
3900       CompositeDismissCommand
3901     };
3902
3903   char
3904     text[MaxTextExtent];
3905
3906   Cursor
3907     cursor;
3908
3909   Image
3910     *composite_image;
3911
3912   int
3913     entry,
3914     id,
3915     x,
3916     y;
3917
3918   MagickRealType
3919     blend,
3920     scale_factor;
3921
3922   RectangleInfo
3923     highlight_info,
3924     composite_info;
3925
3926   unsigned int
3927     height,
3928     width;
3929
3930   size_t
3931     state;
3932
3933   XEvent
3934     event;
3935
3936   /*
3937     Request image file name from user.
3938   */
3939   XFileBrowserWidget(display,windows,"Composite",filename);
3940   if (*filename == '\0')
3941     return(MagickTrue);
3942   /*
3943     Read image.
3944   */
3945   XSetCursorState(display,windows,MagickTrue);
3946   XCheckRefreshWindows(display,windows);
3947   (void) CopyMagickString(resource_info->image_info->filename,filename,
3948     MaxTextExtent);
3949   composite_image=ReadImage(resource_info->image_info,&image->exception);
3950   CatchException(&image->exception);
3951   XSetCursorState(display,windows,MagickFalse);
3952   if (composite_image == (Image *) NULL)
3953     return(MagickFalse);
3954   /*
3955     Map Command widget.
3956   */
3957   (void) CloneString(&windows->command.name,"Composite");
3958   windows->command.data=1;
3959   (void) XCommandWidget(display,windows,CompositeMenu,(XEvent *) NULL);
3960   (void) XMapRaised(display,windows->command.id);
3961   XClientMessage(display,windows->image.id,windows->im_protocols,
3962     windows->im_update_widget,CurrentTime);
3963   /*
3964     Track pointer until button 1 is pressed.
3965   */
3966   XQueryPosition(display,windows->image.id,&x,&y);
3967   (void) XSelectInput(display,windows->image.id,
3968     windows->image.attributes.event_mask | PointerMotionMask);
3969   composite_info.x=(ssize_t) windows->image.x+x;
3970   composite_info.y=(ssize_t) windows->image.y+y;
3971   composite_info.width=0;
3972   composite_info.height=0;
3973   cursor=XCreateFontCursor(display,XC_ul_angle);
3974   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
3975   blend=0.0;
3976   state=DefaultState;
3977   do
3978   {
3979     if (windows->info.mapped != MagickFalse)
3980       {
3981         /*
3982           Display pointer position.
3983         */
3984         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
3985           (long) composite_info.x,(long) composite_info.y);
3986         XInfoWidget(display,windows,text);
3987       }
3988     highlight_info=composite_info;
3989     highlight_info.x=composite_info.x-windows->image.x;
3990     highlight_info.y=composite_info.y-windows->image.y;
3991     XHighlightRectangle(display,windows->image.id,
3992       windows->image.highlight_context,&highlight_info);
3993     /*
3994       Wait for next event.
3995     */
3996     XScreenEvent(display,windows,&event);
3997     XHighlightRectangle(display,windows->image.id,
3998       windows->image.highlight_context,&highlight_info);
3999     if (event.xany.window == windows->command.id)
4000       {
4001         /*
4002           Select a command from the Command widget.
4003         */
4004         id=XCommandWidget(display,windows,CompositeMenu,&event);
4005         if (id < 0)
4006           continue;
4007         switch (CompositeCommands[id])
4008         {
4009           case CompositeOperatorsCommand:
4010           {
4011             char
4012               command[MaxTextExtent],
4013               **operators;
4014
4015             /*
4016               Select a command from the pop-up menu.
4017             */
4018             operators=GetMagickOptions(MagickComposeOptions);
4019             if (operators == (char **) NULL)
4020               break;
4021             entry=XMenuWidget(display,windows,CompositeMenu[id],
4022               (const char **) operators,command);
4023             if (entry >= 0)
4024               compose=(CompositeOperator) ParseMagickOption(
4025                 MagickComposeOptions,MagickFalse,operators[entry]);
4026             operators=DestroyStringList(operators);
4027             break;
4028           }
4029           case CompositeDissolveCommand:
4030           {
4031             static char
4032               factor[MaxTextExtent] = "20.0";
4033
4034             /*
4035               Dissolve the two images a given percent.
4036             */
4037             (void) XSetFunction(display,windows->image.highlight_context,
4038               GXcopy);
4039             (void) XDialogWidget(display,windows,"Dissolve",
4040               "Enter the blend factor (0.0 - 99.9%):",factor);
4041             (void) XSetFunction(display,windows->image.highlight_context,
4042               GXinvert);
4043             if (*factor == '\0')
4044               break;
4045             blend=StringToDouble(factor);
4046             compose=DissolveCompositeOp;
4047             break;
4048           }
4049           case CompositeDisplaceCommand:
4050           {
4051             /*
4052               Get horizontal and vertical scale displacement geometry.
4053             */
4054             (void) XSetFunction(display,windows->image.highlight_context,
4055               GXcopy);
4056             (void) XDialogWidget(display,windows,"Displace",
4057               "Enter the horizontal and vertical scale:",displacement_geometry);
4058             (void) XSetFunction(display,windows->image.highlight_context,
4059               GXinvert);
4060             if (*displacement_geometry == '\0')
4061               break;
4062             compose=DisplaceCompositeOp;
4063             break;
4064           }
4065           case CompositeHelpCommand:
4066           {
4067             (void) XSetFunction(display,windows->image.highlight_context,
4068               GXcopy);
4069             XTextViewWidget(display,resource_info,windows,MagickFalse,
4070               "Help Viewer - Image Composite",ImageCompositeHelp);
4071             (void) XSetFunction(display,windows->image.highlight_context,
4072               GXinvert);
4073             break;
4074           }
4075           case CompositeDismissCommand:
4076           {
4077             /*
4078               Prematurely exit.
4079             */
4080             state|=EscapeState;
4081             state|=ExitState;
4082             break;
4083           }
4084           default:
4085             break;
4086         }
4087         continue;
4088       }
4089     switch (event.type)
4090     {
4091       case ButtonPress:
4092       {
4093         if (image->debug != MagickFalse)
4094           (void) LogMagickEvent(X11Event,GetMagickModule(),
4095             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
4096             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4097         if (event.xbutton.button != Button1)
4098           break;
4099         if (event.xbutton.window != windows->image.id)
4100           break;
4101         /*
4102           Change cursor.
4103         */
4104         composite_info.width=composite_image->columns;
4105         composite_info.height=composite_image->rows;
4106         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4107         composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4108         composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4109         break;
4110       }
4111       case ButtonRelease:
4112       {
4113         if (image->debug != MagickFalse)
4114           (void) LogMagickEvent(X11Event,GetMagickModule(),
4115             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
4116             event.xbutton.button,event.xbutton.x,event.xbutton.y);
4117         if (event.xbutton.button != Button1)
4118           break;
4119         if (event.xbutton.window != windows->image.id)
4120           break;
4121         if ((composite_info.width != 0) && (composite_info.height != 0))
4122           {
4123             /*
4124               User has selected the location of the composite image.
4125             */
4126             composite_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4127             composite_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4128             state|=ExitState;
4129           }
4130         break;
4131       }
4132       case Expose:
4133         break;
4134       case KeyPress:
4135       {
4136         char
4137           command[MaxTextExtent];
4138
4139         KeySym
4140           key_symbol;
4141
4142         int
4143           length;
4144
4145         if (event.xkey.window != windows->image.id)
4146           break;
4147         /*
4148           Respond to a user key press.
4149         */
4150         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
4151           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4152         *(command+length)='\0';
4153         if (image->debug != MagickFalse)
4154           (void) LogMagickEvent(X11Event,GetMagickModule(),
4155             "Key press: 0x%lx (%s)",(unsigned long) key_symbol,command);
4156         switch ((int) key_symbol)
4157         {
4158           case XK_Escape:
4159           case XK_F20:
4160           {
4161             /*
4162               Prematurely exit.
4163             */
4164             composite_image=DestroyImage(composite_image);
4165             state|=EscapeState;
4166             state|=ExitState;
4167             break;
4168           }
4169           case XK_F1:
4170           case XK_Help:
4171           {
4172             (void) XSetFunction(display,windows->image.highlight_context,
4173               GXcopy);
4174             XTextViewWidget(display,resource_info,windows,MagickFalse,
4175               "Help Viewer - Image Composite",ImageCompositeHelp);
4176             (void) XSetFunction(display,windows->image.highlight_context,
4177               GXinvert);
4178             break;
4179           }
4180           default:
4181           {
4182             (void) XBell(display,0);
4183             break;
4184           }
4185         }
4186         break;
4187       }
4188       case MotionNotify:
4189       {
4190         /*
4191           Map and unmap Info widget as text cursor crosses its boundaries.
4192         */
4193         x=event.xmotion.x;
4194         y=event.xmotion.y;
4195         if (windows->info.mapped != MagickFalse)
4196           {
4197             if ((x < (int) (windows->info.x+windows->info.width)) &&
4198                 (y < (int) (windows->info.y+windows->info.height)))
4199               (void) XWithdrawWindow(display,windows->info.id,
4200                 windows->info.screen);
4201           }
4202         else
4203           if ((x > (int) (windows->info.x+windows->info.width)) ||
4204               (y > (int) (windows->info.y+windows->info.height)))
4205             (void) XMapWindow(display,windows->info.id);
4206         composite_info.x=(ssize_t) windows->image.x+x;
4207         composite_info.y=(ssize_t) windows->image.y+y;
4208         break;
4209       }
4210       default:
4211       {
4212         if (image->debug != MagickFalse)
4213           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
4214             event.type);
4215         break;
4216       }
4217     }
4218   } while ((state & ExitState) == 0);
4219   (void) XSelectInput(display,windows->image.id,
4220     windows->image.attributes.event_mask);
4221   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4222   XSetCursorState(display,windows,MagickFalse);
4223   (void) XFreeCursor(display,cursor);
4224   if ((state & EscapeState) != 0)
4225     return(MagickTrue);
4226   /*
4227     Image compositing is relative to image configuration.
4228   */
4229   XSetCursorState(display,windows,MagickTrue);
4230   XCheckRefreshWindows(display,windows);
4231   width=(unsigned int) image->columns;
4232   height=(unsigned int) image->rows;
4233   x=0;
4234   y=0;
4235   if (windows->image.crop_geometry != (char *) NULL)
4236     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
4237   scale_factor=(MagickRealType) width/windows->image.ximage->width;
4238   composite_info.x+=x;
4239   composite_info.x=(ssize_t) (scale_factor*composite_info.x+0.5);
4240   composite_info.width=(unsigned int) (scale_factor*composite_info.width+0.5);
4241   scale_factor=(MagickRealType) height/windows->image.ximage->height;
4242   composite_info.y+=y;
4243   composite_info.y=(ssize_t) (scale_factor*composite_info.y+0.5);
4244   composite_info.height=(unsigned int) (scale_factor*composite_info.height+0.5);
4245   if ((composite_info.width != composite_image->columns) ||
4246       (composite_info.height != composite_image->rows))
4247     {
4248       Image
4249         *resize_image;
4250
4251       /*
4252         Scale composite image.
4253       */
4254       resize_image=ResizeImage(composite_image,composite_info.width,
4255         composite_info.height,composite_image->filter,composite_image->blur,
4256         &image->exception);
4257       composite_image=DestroyImage(composite_image);
4258       if (resize_image == (Image *) NULL)
4259         {
4260           XSetCursorState(display,windows,MagickFalse);
4261           return(MagickFalse);
4262         }
4263       composite_image=resize_image;
4264     }
4265   if (compose == DisplaceCompositeOp)
4266     (void) SetImageArtifact(composite_image,"compose:args",
4267       displacement_geometry);
4268   if (blend != 0.0)
4269     {
4270       CacheView
4271         *image_view;
4272
4273       ExceptionInfo
4274         *exception;
4275
4276       int
4277         y;
4278
4279       Quantum
4280         opacity;
4281
4282       register int
4283         x;
4284
4285       register PixelPacket
4286         *q;
4287
4288       /*
4289         Create mattes for blending.
4290       */
4291       (void) SetImageAlphaChannel(composite_image,OpaqueAlphaChannel);
4292       opacity=(Quantum) (ScaleQuantumToChar((Quantum) QuantumRange)-
4293         ((ssize_t) ScaleQuantumToChar((Quantum) QuantumRange)*blend)/100);
4294       if (SetImageStorageClass(image,DirectClass) == MagickFalse)
4295         return(MagickFalse);
4296       image->matte=MagickTrue;
4297       exception=(&image->exception);
4298       image_view=AcquireCacheView(image);
4299       for (y=0; y < (int) image->rows; y++)
4300       {
4301         q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,image->columns,1,
4302           exception);
4303         if (q == (PixelPacket *) NULL)
4304           break;
4305         for (x=0; x < (int) image->columns; x++)
4306         {
4307           q->opacity=opacity;
4308           q++;
4309         }
4310         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
4311           break;
4312       }
4313       image_view=DestroyCacheView(image_view);
4314     }
4315   /*
4316     Composite image with X Image window.
4317   */
4318   (void) CompositeImage(image,compose,composite_image,composite_info.x,
4319     composite_info.y);
4320   composite_image=DestroyImage(composite_image);
4321   XSetCursorState(display,windows,MagickFalse);
4322   /*
4323     Update image configuration.
4324   */
4325   XConfigureImageColormap(display,resource_info,windows,image);
4326   (void) XConfigureImage(display,resource_info,windows,image);
4327   return(MagickTrue);
4328 }
4329 \f
4330 /*
4331 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4332 %                                                                             %
4333 %                                                                             %
4334 %                                                                             %
4335 +   X C o n f i g u r e I m a g e                                             %
4336 %                                                                             %
4337 %                                                                             %
4338 %                                                                             %
4339 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4340 %
4341 %  XConfigureImage() creates a new X image.  It also notifies the window
4342 %  manager of the new image size and configures the transient widows.
4343 %
4344 %  The format of the XConfigureImage method is:
4345 %
4346 %      MagickBooleanType XConfigureImage(Display *display,
4347 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
4348 %
4349 %  A description of each parameter follows:
4350 %
4351 %    o display: Specifies a connection to an X server; returned from
4352 %      XOpenDisplay.
4353 %
4354 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4355 %
4356 %    o windows: Specifies a pointer to a XWindows structure.
4357 %
4358 %    o image: the image.
4359 %
4360 %
4361 */
4362 static MagickBooleanType XConfigureImage(Display *display,
4363   XResourceInfo *resource_info,XWindows *windows,Image *image)
4364 {
4365   char
4366     geometry[MaxTextExtent];
4367
4368   ssize_t
4369     x,
4370     y;
4371
4372   MagickStatusType
4373     status;
4374
4375   size_t
4376     mask,
4377     height,
4378     width;
4379
4380   XSizeHints
4381     *size_hints;
4382
4383   XWindowChanges
4384     window_changes;
4385
4386   /*
4387     Dismiss if window dimensions are zero.
4388   */
4389   width=(unsigned int) windows->image.window_changes.width;
4390   height=(unsigned int) windows->image.window_changes.height;
4391   if (image->debug != MagickFalse)
4392     (void) LogMagickEvent(X11Event,GetMagickModule(),
4393       "Configure Image: %dx%d=>%.20gx%.20g",windows->image.ximage->width,
4394       windows->image.ximage->height,(double) width,(double) height);
4395   if ((width*height) == 0)
4396     return(MagickTrue);
4397   x=0;
4398   y=0;
4399   /*
4400     Resize image to fit Image window dimensions.
4401   */
4402   XSetCursorState(display,windows,MagickTrue);
4403   (void) XFlush(display);
4404   if (((int) width != windows->image.ximage->width) ||
4405       ((int) height != windows->image.ximage->height))
4406     image->taint=MagickTrue;
4407   windows->magnify.x=(int)
4408     width*windows->magnify.x/windows->image.ximage->width;
4409   windows->magnify.y=(int)
4410     height*windows->magnify.y/windows->image.ximage->height;
4411   windows->image.x=(int) (width*windows->image.x/windows->image.ximage->width);
4412   windows->image.y=(int)
4413     (height*windows->image.y/windows->image.ximage->height);
4414   status=XMakeImage(display,resource_info,&windows->image,image,
4415     (unsigned int) width,(unsigned int) height);
4416   if (status == MagickFalse)
4417     XNoticeWidget(display,windows,"Unable to configure X image:",
4418       windows->image.name);
4419   /*
4420     Notify window manager of the new configuration.
4421   */
4422   if (resource_info->image_geometry != (char *) NULL)
4423     (void) FormatMagickString(geometry,MaxTextExtent,"%s>!",
4424       resource_info->image_geometry);
4425   else
4426     (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
4427       XDisplayWidth(display,windows->image.screen),
4428       XDisplayHeight(display,windows->image.screen));
4429   (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
4430   window_changes.width=(int) width;
4431   if (window_changes.width > XDisplayWidth(display,windows->image.screen))
4432     window_changes.width=XDisplayWidth(display,windows->image.screen);
4433   window_changes.height=(int) height;
4434   if (window_changes.height > XDisplayHeight(display,windows->image.screen))
4435     window_changes.height=XDisplayHeight(display,windows->image.screen);
4436   mask=(size_t) (CWWidth | CWHeight);
4437   if (resource_info->backdrop)
4438     {
4439       mask|=CWX | CWY;
4440       window_changes.x=(int)
4441         ((XDisplayWidth(display,windows->image.screen)/2)-(width/2));
4442       window_changes.y=(int)
4443         ((XDisplayHeight(display,windows->image.screen)/2)-(height/2));
4444     }
4445   (void) XReconfigureWMWindow(display,windows->image.id,windows->image.screen,
4446     (unsigned int) mask,&window_changes);
4447   (void) XClearWindow(display,windows->image.id);
4448   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
4449   /*
4450     Update Magnify window configuration.
4451   */
4452   if (windows->magnify.mapped != MagickFalse)
4453     XMakeMagnifyImage(display,windows);
4454   windows->pan.crop_geometry=windows->image.crop_geometry;
4455   XBestIconSize(display,&windows->pan,image);
4456   while (((windows->pan.width << 1) < MaxIconSize) &&
4457          ((windows->pan.height << 1) < MaxIconSize))
4458   {
4459     windows->pan.width<<=1;
4460     windows->pan.height<<=1;
4461   }
4462   if (windows->pan.geometry != (char *) NULL)
4463     (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
4464       &windows->pan.width,&windows->pan.height);
4465   window_changes.width=(int) windows->pan.width;
4466   window_changes.height=(int) windows->pan.height;
4467   size_hints=XAllocSizeHints();
4468   if (size_hints != (XSizeHints *) NULL)
4469     {
4470       /*
4471         Set new size hints.
4472       */
4473       size_hints->flags=PSize | PMinSize | PMaxSize;
4474       size_hints->width=window_changes.width;
4475       size_hints->height=window_changes.height;
4476       size_hints->min_width=size_hints->width;
4477       size_hints->min_height=size_hints->height;
4478       size_hints->max_width=size_hints->width;
4479       size_hints->max_height=size_hints->height;
4480       (void) XSetNormalHints(display,windows->pan.id,size_hints);
4481       (void) XFree((void *) size_hints);
4482     }
4483   (void) XReconfigureWMWindow(display,windows->pan.id,windows->pan.screen,
4484     (unsigned int) (CWWidth | CWHeight),&window_changes);
4485   /*
4486     Update icon window configuration.
4487   */
4488   windows->icon.crop_geometry=windows->image.crop_geometry;
4489   XBestIconSize(display,&windows->icon,image);
4490   window_changes.width=(int) windows->icon.width;
4491   window_changes.height=(int) windows->icon.height;
4492   (void) XReconfigureWMWindow(display,windows->icon.id,windows->icon.screen,
4493     (unsigned int) (CWWidth | CWHeight),&window_changes);
4494   XSetCursorState(display,windows,MagickFalse);
4495   return(status != 0 ? MagickTrue : MagickFalse);
4496 }
4497 \f
4498 /*
4499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4500 %                                                                             %
4501 %                                                                             %
4502 %                                                                             %
4503 +   X C r o p I m a g e                                                       %
4504 %                                                                             %
4505 %                                                                             %
4506 %                                                                             %
4507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4508 %
4509 %  XCropImage() allows the user to select a region of the image and crop, copy,
4510 %  or cut it.  For copy or cut, the image can subsequently be composited onto
4511 %  the image with XPasteImage.
4512 %
4513 %  The format of the XCropImage method is:
4514 %
4515 %      MagickBooleanType XCropImage(Display *display,
4516 %        XResourceInfo *resource_info,XWindows *windows,Image *image,
4517 %        const ClipboardMode mode)
4518 %
4519 %  A description of each parameter follows:
4520 %
4521 %    o display: Specifies a connection to an X server; returned from
4522 %      XOpenDisplay.
4523 %
4524 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
4525 %
4526 %    o windows: Specifies a pointer to a XWindows structure.
4527 %
4528 %    o image: the image; returned from ReadImage.
4529 %
4530 %    o mode: This unsigned value specified whether the image should be
4531 %      cropped, copied, or cut.
4532 %
4533 */
4534 static MagickBooleanType XCropImage(Display *display,
4535   XResourceInfo *resource_info,XWindows *windows,Image *image,
4536   const ClipboardMode mode)
4537 {
4538   static const char
4539     *CropModeMenu[] =
4540     {
4541       "Help",
4542       "Dismiss",
4543       (char *) NULL
4544     },
4545     *RectifyModeMenu[] =
4546     {
4547       "Crop",
4548       "Help",
4549       "Dismiss",
4550       (char *) NULL
4551     };
4552
4553   static const ModeType
4554     CropCommands[] =
4555     {
4556       CropHelpCommand,
4557       CropDismissCommand
4558     },
4559     RectifyCommands[] =
4560     {
4561       RectifyCopyCommand,
4562       RectifyHelpCommand,
4563       RectifyDismissCommand
4564     };
4565
4566   CacheView
4567     *image_view;
4568
4569   char
4570     command[MaxTextExtent],
4571     text[MaxTextExtent];
4572
4573   Cursor
4574     cursor;
4575
4576   ExceptionInfo
4577     *exception;
4578
4579   int
4580     id,
4581     x,
4582     y;
4583
4584   KeySym
4585     key_symbol;
4586
4587   Image
4588     *crop_image;
4589
4590   MagickRealType
4591     scale_factor;
4592
4593   RectangleInfo
4594     crop_info,
4595     highlight_info;
4596
4597   register PixelPacket
4598     *q;
4599
4600   unsigned int
4601     height,
4602     width;
4603
4604   size_t
4605     state;
4606
4607   XEvent
4608     event;
4609
4610   /*
4611     Map Command widget.
4612   */
4613   switch (mode)
4614   {
4615     case CopyMode:
4616     {
4617       (void) CloneString(&windows->command.name,"Copy");
4618       break;
4619     }
4620     case CropMode:
4621     {
4622       (void) CloneString(&windows->command.name,"Crop");
4623       break;
4624     }
4625     case CutMode:
4626     {
4627       (void) CloneString(&windows->command.name,"Cut");
4628       break;
4629     }
4630   }
4631   RectifyModeMenu[0]=windows->command.name;
4632   windows->command.data=0;
4633   (void) XCommandWidget(display,windows,CropModeMenu,(XEvent *) NULL);
4634   (void) XMapRaised(display,windows->command.id);
4635   XClientMessage(display,windows->image.id,windows->im_protocols,
4636     windows->im_update_widget,CurrentTime);
4637   /*
4638     Track pointer until button 1 is pressed.
4639   */
4640   XQueryPosition(display,windows->image.id,&x,&y);
4641   (void) XSelectInput(display,windows->image.id,
4642     windows->image.attributes.event_mask | PointerMotionMask);
4643   crop_info.x=(ssize_t) windows->image.x+x;
4644   crop_info.y=(ssize_t) windows->image.y+y;
4645   crop_info.width=0;
4646   crop_info.height=0;
4647   cursor=XCreateFontCursor(display,XC_fleur);
4648   state=DefaultState;
4649   do
4650   {
4651     if (windows->info.mapped != MagickFalse)
4652       {
4653         /*
4654           Display pointer position.
4655         */
4656         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
4657           (long) crop_info.x,(long) crop_info.y);
4658         XInfoWidget(display,windows,text);
4659       }
4660     /*
4661       Wait for next event.
4662     */
4663     XScreenEvent(display,windows,&event);
4664     if (event.xany.window == windows->command.id)
4665       {
4666         /*
4667           Select a command from the Command widget.
4668         */
4669         id=XCommandWidget(display,windows,CropModeMenu,&event);
4670         if (id < 0)
4671           continue;
4672         switch (CropCommands[id])
4673         {
4674           case CropHelpCommand:
4675           {
4676             switch (mode)
4677             {
4678               case CopyMode:
4679               {
4680                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4681                   "Help Viewer - Image Copy",ImageCopyHelp);
4682                 break;
4683               }
4684               case CropMode:
4685               {
4686                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4687                   "Help Viewer - Image Crop",ImageCropHelp);
4688                 break;
4689               }
4690               case CutMode:
4691               {
4692                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4693                   "Help Viewer - Image Cut",ImageCutHelp);
4694                 break;
4695               }
4696             }
4697             break;
4698           }
4699           case CropDismissCommand:
4700           {
4701             /*
4702               Prematurely exit.
4703             */
4704             state|=EscapeState;
4705             state|=ExitState;
4706             break;
4707           }
4708           default:
4709             break;
4710         }
4711         continue;
4712       }
4713     switch (event.type)
4714     {
4715       case ButtonPress:
4716       {
4717         if (event.xbutton.button != Button1)
4718           break;
4719         if (event.xbutton.window != windows->image.id)
4720           break;
4721         /*
4722           Note first corner of cropping rectangle-- exit loop.
4723         */
4724         (void) XCheckDefineCursor(display,windows->image.id,cursor);
4725         crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4726         crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4727         state|=ExitState;
4728         break;
4729       }
4730       case ButtonRelease:
4731         break;
4732       case Expose:
4733         break;
4734       case KeyPress:
4735       {
4736         if (event.xkey.window != windows->image.id)
4737           break;
4738         /*
4739           Respond to a user key press.
4740         */
4741         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
4742           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
4743         switch ((int) key_symbol)
4744         {
4745           case XK_Escape:
4746           case XK_F20:
4747           {
4748             /*
4749               Prematurely exit.
4750             */
4751             state|=EscapeState;
4752             state|=ExitState;
4753             break;
4754           }
4755           case XK_F1:
4756           case XK_Help:
4757           {
4758             switch (mode)
4759             {
4760               case CopyMode:
4761               {
4762                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4763                   "Help Viewer - Image Copy",ImageCopyHelp);
4764                 break;
4765               }
4766               case CropMode:
4767               {
4768                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4769                   "Help Viewer - Image Crop",ImageCropHelp);
4770                 break;
4771               }
4772               case CutMode:
4773               {
4774                 XTextViewWidget(display,resource_info,windows,MagickFalse,
4775                   "Help Viewer - Image Cut",ImageCutHelp);
4776                 break;
4777               }
4778             }
4779             break;
4780           }
4781           default:
4782           {
4783             (void) XBell(display,0);
4784             break;
4785           }
4786         }
4787         break;
4788       }
4789       case MotionNotify:
4790       {
4791         if (event.xmotion.window != windows->image.id)
4792           break;
4793         /*
4794           Map and unmap Info widget as text cursor crosses its boundaries.
4795         */
4796         x=event.xmotion.x;
4797         y=event.xmotion.y;
4798         if (windows->info.mapped != MagickFalse)
4799           {
4800             if ((x < (int) (windows->info.x+windows->info.width)) &&
4801                 (y < (int) (windows->info.y+windows->info.height)))
4802               (void) XWithdrawWindow(display,windows->info.id,
4803                 windows->info.screen);
4804           }
4805         else
4806           if ((x > (int) (windows->info.x+windows->info.width)) ||
4807               (y > (int) (windows->info.y+windows->info.height)))
4808             (void) XMapWindow(display,windows->info.id);
4809         crop_info.x=(ssize_t) windows->image.x+x;
4810         crop_info.y=(ssize_t) windows->image.y+y;
4811         break;
4812       }
4813       default:
4814         break;
4815     }
4816   } while ((state & ExitState) == 0);
4817   (void) XSelectInput(display,windows->image.id,
4818     windows->image.attributes.event_mask);
4819   if ((state & EscapeState) != 0)
4820     {
4821       /*
4822         User want to exit without cropping.
4823       */
4824       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4825       (void) XFreeCursor(display,cursor);
4826       return(MagickTrue);
4827     }
4828   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
4829   do
4830   {
4831     /*
4832       Size rectangle as pointer moves until the mouse button is released.
4833     */
4834     x=(int) crop_info.x;
4835     y=(int) crop_info.y;
4836     crop_info.width=0;
4837     crop_info.height=0;
4838     state=DefaultState;
4839     do
4840     {
4841       highlight_info=crop_info;
4842       highlight_info.x=crop_info.x-windows->image.x;
4843       highlight_info.y=crop_info.y-windows->image.y;
4844       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4845         {
4846           /*
4847             Display info and draw cropping rectangle.
4848           */
4849           if (windows->info.mapped == MagickFalse)
4850             (void) XMapWindow(display,windows->info.id);
4851           (void) FormatMagickString(text,MaxTextExtent,
4852             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4853             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4854           XInfoWidget(display,windows,text);
4855           XHighlightRectangle(display,windows->image.id,
4856             windows->image.highlight_context,&highlight_info);
4857         }
4858       else
4859         if (windows->info.mapped != MagickFalse)
4860           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
4861       /*
4862         Wait for next event.
4863       */
4864       XScreenEvent(display,windows,&event);
4865       if ((highlight_info.width > 3) && (highlight_info.height > 3))
4866         XHighlightRectangle(display,windows->image.id,
4867           windows->image.highlight_context,&highlight_info);
4868       switch (event.type)
4869       {
4870         case ButtonPress:
4871         {
4872           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4873           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4874           break;
4875         }
4876         case ButtonRelease:
4877         {
4878           /*
4879             User has committed to cropping rectangle.
4880           */
4881           crop_info.x=(ssize_t) windows->image.x+event.xbutton.x;
4882           crop_info.y=(ssize_t) windows->image.y+event.xbutton.y;
4883           XSetCursorState(display,windows,MagickFalse);
4884           state|=ExitState;
4885           windows->command.data=0;
4886           (void) XCommandWidget(display,windows,RectifyModeMenu,
4887             (XEvent *) NULL);
4888           break;
4889         }
4890         case Expose:
4891           break;
4892         case MotionNotify:
4893         {
4894           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
4895           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
4896         }
4897         default:
4898           break;
4899       }
4900       if ((((int) crop_info.x != x) && ((int) crop_info.y != y)) ||
4901           ((state & ExitState) != 0))
4902         {
4903           /*
4904             Check boundary conditions.
4905           */
4906           if (crop_info.x < 0)
4907             crop_info.x=0;
4908           else
4909             if (crop_info.x > (ssize_t) windows->image.ximage->width)
4910               crop_info.x=(ssize_t) windows->image.ximage->width;
4911           if ((int) crop_info.x < x)
4912             crop_info.width=(unsigned int) (x-crop_info.x);
4913           else
4914             {
4915               crop_info.width=(unsigned int) (crop_info.x-x);
4916               crop_info.x=(ssize_t) x;
4917             }
4918           if (crop_info.y < 0)
4919             crop_info.y=0;
4920           else
4921             if (crop_info.y > (ssize_t) windows->image.ximage->height)
4922               crop_info.y=(ssize_t) windows->image.ximage->height;
4923           if ((int) crop_info.y < y)
4924             crop_info.height=(unsigned int) (y-crop_info.y);
4925           else
4926             {
4927               crop_info.height=(unsigned int) (crop_info.y-y);
4928               crop_info.y=(ssize_t) y;
4929             }
4930         }
4931     } while ((state & ExitState) == 0);
4932     /*
4933       Wait for user to grab a corner of the rectangle or press return.
4934     */
4935     state=DefaultState;
4936     (void) XMapWindow(display,windows->info.id);
4937     do
4938     {
4939       if (windows->info.mapped != MagickFalse)
4940         {
4941           /*
4942             Display pointer position.
4943           */
4944           (void) FormatMagickString(text,MaxTextExtent,
4945             " %.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
4946             crop_info.height,(double) crop_info.x,(double) crop_info.y);
4947           XInfoWidget(display,windows,text);
4948         }
4949       highlight_info=crop_info;
4950       highlight_info.x=crop_info.x-windows->image.x;
4951       highlight_info.y=crop_info.y-windows->image.y;
4952       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
4953         {
4954           state|=EscapeState;
4955           state|=ExitState;
4956           break;
4957         }
4958       XHighlightRectangle(display,windows->image.id,
4959         windows->image.highlight_context,&highlight_info);
4960       XScreenEvent(display,windows,&event);
4961       if (event.xany.window == windows->command.id)
4962         {
4963           /*
4964             Select a command from the Command widget.
4965           */
4966           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
4967           id=XCommandWidget(display,windows,RectifyModeMenu,&event);
4968           (void) XSetFunction(display,windows->image.highlight_context,
4969             GXinvert);
4970           XHighlightRectangle(display,windows->image.id,
4971             windows->image.highlight_context,&highlight_info);
4972           if (id >= 0)
4973             switch (RectifyCommands[id])
4974             {
4975               case RectifyCopyCommand:
4976               {
4977                 state|=ExitState;
4978                 break;
4979               }
4980               case RectifyHelpCommand:
4981               {
4982                 (void) XSetFunction(display,windows->image.highlight_context,
4983                   GXcopy);
4984                 switch (mode)
4985                 {
4986                   case CopyMode:
4987                   {
4988                     XTextViewWidget(display,resource_info,windows,MagickFalse,
4989                       "Help Viewer - Image Copy",ImageCopyHelp);
4990                     break;
4991                   }
4992                   case CropMode:
4993                   {
4994                     XTextViewWidget(display,resource_info,windows,MagickFalse,
4995                       "Help Viewer - Image Crop",ImageCropHelp);
4996                     break;
4997                   }
4998                   case CutMode:
4999                   {
5000                     XTextViewWidget(display,resource_info,windows,MagickFalse,
5001                       "Help Viewer - Image Cut",ImageCutHelp);
5002                     break;
5003                   }
5004                 }
5005                 (void) XSetFunction(display,windows->image.highlight_context,
5006                   GXinvert);
5007                 break;
5008               }
5009               case RectifyDismissCommand:
5010               {
5011                 /*
5012                   Prematurely exit.
5013                 */
5014                 state|=EscapeState;
5015                 state|=ExitState;
5016                 break;
5017               }
5018               default:
5019                 break;
5020             }
5021           continue;
5022         }
5023       XHighlightRectangle(display,windows->image.id,
5024         windows->image.highlight_context,&highlight_info);
5025       switch (event.type)
5026       {
5027         case ButtonPress:
5028         {
5029           if (event.xbutton.button != Button1)
5030             break;
5031           if (event.xbutton.window != windows->image.id)
5032             break;
5033           x=windows->image.x+event.xbutton.x;
5034           y=windows->image.y+event.xbutton.y;
5035           if ((x < (int) (crop_info.x+RoiDelta)) &&
5036               (x > (int) (crop_info.x-RoiDelta)) &&
5037               (y < (int) (crop_info.y+RoiDelta)) &&
5038               (y > (int) (crop_info.y-RoiDelta)))
5039             {
5040               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5041               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5042               state|=UpdateConfigurationState;
5043               break;
5044             }
5045           if ((x < (int) (crop_info.x+RoiDelta)) &&
5046               (x > (int) (crop_info.x-RoiDelta)) &&
5047               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5048               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5049             {
5050               crop_info.x=(ssize_t) (crop_info.x+crop_info.width);
5051               state|=UpdateConfigurationState;
5052               break;
5053             }
5054           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5055               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5056               (y < (int) (crop_info.y+RoiDelta)) &&
5057               (y > (int) (crop_info.y-RoiDelta)))
5058             {
5059               crop_info.y=(ssize_t) (crop_info.y+crop_info.height);
5060               state|=UpdateConfigurationState;
5061               break;
5062             }
5063           if ((x < (int) (crop_info.x+crop_info.width+RoiDelta)) &&
5064               (x > (int) (crop_info.x+crop_info.width-RoiDelta)) &&
5065               (y < (int) (crop_info.y+crop_info.height+RoiDelta)) &&
5066               (y > (int) (crop_info.y+crop_info.height-RoiDelta)))
5067             {
5068               state|=UpdateConfigurationState;
5069               break;
5070             }
5071         }
5072         case ButtonRelease:
5073         {
5074           if (event.xbutton.window == windows->pan.id)
5075             if ((highlight_info.x != crop_info.x-windows->image.x) ||
5076                 (highlight_info.y != crop_info.y-windows->image.y))
5077               XHighlightRectangle(display,windows->image.id,
5078                 windows->image.highlight_context,&highlight_info);
5079           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5080             event.xbutton.time);
5081           break;
5082         }
5083         case Expose:
5084         {
5085           if (event.xexpose.window == windows->image.id)
5086             if (event.xexpose.count == 0)
5087               {
5088                 event.xexpose.x=(int) highlight_info.x;
5089                 event.xexpose.y=(int) highlight_info.y;
5090                 event.xexpose.width=(int) highlight_info.width;
5091                 event.xexpose.height=(int) highlight_info.height;
5092                 XRefreshWindow(display,&windows->image,&event);
5093               }
5094           if (event.xexpose.window == windows->info.id)
5095             if (event.xexpose.count == 0)
5096               XInfoWidget(display,windows,text);
5097           break;
5098         }
5099         case KeyPress:
5100         {
5101           if (event.xkey.window != windows->image.id)
5102             break;
5103           /*
5104             Respond to a user key press.
5105           */
5106           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5107             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5108           switch ((int) key_symbol)
5109           {
5110             case XK_Escape:
5111             case XK_F20:
5112               state|=EscapeState;
5113             case XK_Return:
5114             {
5115               state|=ExitState;
5116               break;
5117             }
5118             case XK_Home:
5119             case XK_KP_Home:
5120             {
5121               crop_info.x=(ssize_t) (windows->image.width/2L-
5122                 crop_info.width/2L);
5123               crop_info.y=(ssize_t) (windows->image.height/2L-
5124                 crop_info.height/2L);
5125               break;
5126             }
5127             case XK_Left:
5128             case XK_KP_Left:
5129             {
5130               crop_info.x--;
5131               break;
5132             }
5133             case XK_Up:
5134             case XK_KP_Up:
5135             case XK_Next:
5136             {
5137               crop_info.y--;
5138               break;
5139             }
5140             case XK_Right:
5141             case XK_KP_Right:
5142             {
5143               crop_info.x++;
5144               break;
5145             }
5146             case XK_Prior:
5147             case XK_Down:
5148             case XK_KP_Down:
5149             {
5150               crop_info.y++;
5151               break;
5152             }
5153             case XK_F1:
5154             case XK_Help:
5155             {
5156               (void) XSetFunction(display,windows->image.highlight_context,
5157                 GXcopy);
5158               switch (mode)
5159               {
5160                 case CopyMode:
5161                 {
5162                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5163                     "Help Viewer - Image Copy",ImageCopyHelp);
5164                   break;
5165                 }
5166                 case CropMode:
5167                 {
5168                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5169                     "Help Viewer - Image Cropg",ImageCropHelp);
5170                   break;
5171                 }
5172                 case CutMode:
5173                 {
5174                   XTextViewWidget(display,resource_info,windows,MagickFalse,
5175                     "Help Viewer - Image Cutg",ImageCutHelp);
5176                   break;
5177                 }
5178               }
5179               (void) XSetFunction(display,windows->image.highlight_context,
5180                 GXinvert);
5181               break;
5182             }
5183             default:
5184             {
5185               (void) XBell(display,0);
5186               break;
5187             }
5188           }
5189           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
5190             event.xkey.time);
5191           break;
5192         }
5193         case KeyRelease:
5194           break;
5195         case MotionNotify:
5196         {
5197           if (event.xmotion.window != windows->image.id)
5198             break;
5199           /*
5200             Map and unmap Info widget as text cursor crosses its boundaries.
5201           */
5202           x=event.xmotion.x;
5203           y=event.xmotion.y;
5204           if (windows->info.mapped != MagickFalse)
5205             {
5206               if ((x < (int) (windows->info.x+windows->info.width)) &&
5207                   (y < (int) (windows->info.y+windows->info.height)))
5208                 (void) XWithdrawWindow(display,windows->info.id,
5209                   windows->info.screen);
5210             }
5211           else
5212             if ((x > (int) (windows->info.x+windows->info.width)) ||
5213                 (y > (int) (windows->info.y+windows->info.height)))
5214               (void) XMapWindow(display,windows->info.id);
5215           crop_info.x=(ssize_t) windows->image.x+event.xmotion.x;
5216           crop_info.y=(ssize_t) windows->image.y+event.xmotion.y;
5217           break;
5218         }
5219         case SelectionRequest:
5220         {
5221           XSelectionEvent
5222             notify;
5223
5224           XSelectionRequestEvent
5225             *request;
5226
5227           /*
5228             Set primary selection.
5229           */
5230           (void) FormatMagickString(text,MaxTextExtent,
5231             "%.20gx%.20g%+.20g%+.20g",(double) crop_info.width,(double)
5232             crop_info.height,(double) crop_info.x,(double) crop_info.y);
5233           request=(&(event.xselectionrequest));
5234           (void) XChangeProperty(request->display,request->requestor,
5235             request->property,request->target,8,PropModeReplace,
5236             (unsigned char *) text,(int) strlen(text));
5237           notify.type=SelectionNotify;
5238           notify.display=request->display;
5239           notify.requestor=request->requestor;
5240           notify.selection=request->selection;
5241           notify.target=request->target;
5242           notify.time=request->time;
5243           if (request->property == None)
5244             notify.property=request->target;
5245           else
5246             notify.property=request->property;
5247           (void) XSendEvent(request->display,request->requestor,False,0,
5248             (XEvent *) &notify);
5249         }
5250         default:
5251           break;
5252       }
5253       if ((state & UpdateConfigurationState) != 0)
5254         {
5255           (void) XPutBackEvent(display,&event);
5256           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5257           break;
5258         }
5259     } while ((state & ExitState) == 0);
5260   } while ((state & ExitState) == 0);
5261   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
5262   XSetCursorState(display,windows,MagickFalse);
5263   if ((state & EscapeState) != 0)
5264     return(MagickTrue);
5265   if (mode == CropMode)
5266     if (((int) crop_info.width != windows->image.ximage->width) ||
5267         ((int) crop_info.height != windows->image.ximage->height))
5268       {
5269         /*
5270           Reconfigure Image window as defined by cropping rectangle.
5271         */
5272         XSetCropGeometry(display,windows,&crop_info,image);
5273         windows->image.window_changes.width=(int) crop_info.width;
5274         windows->image.window_changes.height=(int) crop_info.height;
5275         (void) XConfigureImage(display,resource_info,windows,image);
5276         return(MagickTrue);
5277       }
5278   /*
5279     Copy image before applying image transforms.
5280   */
5281   XSetCursorState(display,windows,MagickTrue);
5282   XCheckRefreshWindows(display,windows);
5283   width=(unsigned int) image->columns;
5284   height=(unsigned int) image->rows;
5285   x=0;
5286   y=0;
5287   if (windows->image.crop_geometry != (char *) NULL)
5288     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
5289   scale_factor=(MagickRealType) width/windows->image.ximage->width;
5290   crop_info.x+=x;
5291   crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
5292   crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
5293   scale_factor=(MagickRealType) height/windows->image.ximage->height;
5294   crop_info.y+=y;
5295   crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
5296   crop_info.height=(unsigned int) (scale_factor*crop_info.height+0.5);
5297   crop_image=CropImage(image,&crop_info,&image->exception);
5298   XSetCursorState(display,windows,MagickFalse);
5299   if (crop_image == (Image *) NULL)
5300     return(MagickFalse);
5301   if (resource_info->copy_image != (Image *) NULL)
5302     resource_info->copy_image=DestroyImage(resource_info->copy_image);
5303   resource_info->copy_image=crop_image;
5304   if (mode == CopyMode)
5305     {
5306       (void) XConfigureImage(display,resource_info,windows,image);
5307       return(MagickTrue);
5308     }
5309   /*
5310     Cut image.
5311   */
5312   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
5313     return(MagickFalse);
5314   image->matte=MagickTrue;
5315   exception=(&image->exception);
5316   image_view=AcquireCacheView(image);
5317   for (y=0; y < (int) crop_info.height; y++)
5318   {
5319     q=GetCacheViewAuthenticPixels(image_view,crop_info.x,y+crop_info.y,
5320       crop_info.width,1,exception);
5321     if (q == (PixelPacket *) NULL)
5322       break;
5323     for (x=0; x < (int) crop_info.width; x++)
5324     {
5325       q->opacity=(Quantum) TransparentOpacity;
5326       q++;
5327     }
5328     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
5329       break;
5330   }
5331   image_view=DestroyCacheView(image_view);
5332   /*
5333     Update image configuration.
5334   */
5335   XConfigureImageColormap(display,resource_info,windows,image);
5336   (void) XConfigureImage(display,resource_info,windows,image);
5337   return(MagickTrue);
5338 }
5339 \f
5340 /*
5341 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5342 %                                                                             %
5343 %                                                                             %
5344 %                                                                             %
5345 +   X D r a w I m a g e                                                       %
5346 %                                                                             %
5347 %                                                                             %
5348 %                                                                             %
5349 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5350 %
5351 %  XDrawEditImage() draws a graphic element (point, line, rectangle, etc.) on
5352 %  the image.
5353 %
5354 %  The format of the XDrawEditImage method is:
5355 %
5356 %      MagickBooleanType XDrawEditImage(Display *display,
5357 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
5358 %
5359 %  A description of each parameter follows:
5360 %
5361 %    o display: Specifies a connection to an X server; returned from
5362 %      XOpenDisplay.
5363 %
5364 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
5365 %
5366 %    o windows: Specifies a pointer to a XWindows structure.
5367 %
5368 %    o image: the image.
5369 %
5370 */
5371 static MagickBooleanType XDrawEditImage(Display *display,
5372   XResourceInfo *resource_info,XWindows *windows,Image **image)
5373 {
5374   static const char
5375     *DrawMenu[] =
5376     {
5377       "Element",
5378       "Color",
5379       "Stipple",
5380       "Width",
5381       "Undo",
5382       "Help",
5383       "Dismiss",
5384       (char *) NULL
5385     };
5386
5387   static ElementType
5388     element = PointElement;
5389
5390   static const ModeType
5391     DrawCommands[] =
5392     {
5393       DrawElementCommand,
5394       DrawColorCommand,
5395       DrawStippleCommand,
5396       DrawWidthCommand,
5397       DrawUndoCommand,
5398       DrawHelpCommand,
5399       DrawDismissCommand
5400     };
5401
5402   static Pixmap
5403     stipple = (Pixmap) NULL;
5404
5405   static unsigned int
5406     pen_id = 0,
5407     line_width = 1;
5408
5409   char
5410     command[MaxTextExtent],
5411     text[MaxTextExtent];
5412
5413   Cursor
5414     cursor;
5415
5416   int
5417     entry,
5418     id,
5419     number_coordinates,
5420     x,
5421     y;
5422
5423   MagickRealType
5424     degrees;
5425
5426   MagickStatusType
5427     status;
5428
5429   RectangleInfo
5430     rectangle_info;
5431
5432   register int
5433     i;
5434
5435   unsigned int
5436     distance,
5437     height,
5438     max_coordinates,
5439     width;
5440
5441   size_t
5442     state;
5443
5444   Window
5445     root_window;
5446
5447   XDrawInfo
5448     draw_info;
5449
5450   XEvent
5451     event;
5452
5453   XPoint
5454     *coordinate_info;
5455
5456   XSegment
5457     line_info;
5458
5459   /*
5460     Allocate polygon info.
5461   */
5462   max_coordinates=2048;
5463   coordinate_info=(XPoint *) AcquireQuantumMemory((size_t) max_coordinates,
5464     sizeof(*coordinate_info));
5465   if (coordinate_info == (XPoint *) NULL)
5466     {
5467       (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
5468         ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
5469       return(MagickFalse);
5470     }
5471   /*
5472     Map Command widget.
5473   */
5474   (void) CloneString(&windows->command.name,"Draw");
5475   windows->command.data=4;
5476   (void) XCommandWidget(display,windows,DrawMenu,(XEvent *) NULL);
5477   (void) XMapRaised(display,windows->command.id);
5478   XClientMessage(display,windows->image.id,windows->im_protocols,
5479     windows->im_update_widget,CurrentTime);
5480   /*
5481     Wait for first button press.
5482   */
5483   root_window=XRootWindow(display,XDefaultScreen(display));
5484   draw_info.stencil=OpaqueStencil;
5485   status=MagickTrue;
5486   cursor=XCreateFontCursor(display,XC_tcross);
5487   for ( ; ; )
5488   {
5489     XQueryPosition(display,windows->image.id,&x,&y);
5490     (void) XSelectInput(display,windows->image.id,
5491       windows->image.attributes.event_mask | PointerMotionMask);
5492     (void) XCheckDefineCursor(display,windows->image.id,cursor);
5493     state=DefaultState;
5494     do
5495     {
5496       if (windows->info.mapped != MagickFalse)
5497         {
5498           /*
5499             Display pointer position.
5500           */
5501           (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
5502             x+windows->image.x,y+windows->image.y);
5503           XInfoWidget(display,windows,text);
5504         }
5505       /*
5506         Wait for next event.
5507       */
5508       XScreenEvent(display,windows,&event);
5509       if (event.xany.window == windows->command.id)
5510         {
5511           /*
5512             Select a command from the Command widget.
5513           */
5514           id=XCommandWidget(display,windows,DrawMenu,&event);
5515           if (id < 0)
5516             continue;
5517           switch (DrawCommands[id])
5518           {
5519             case DrawElementCommand:
5520             {
5521               static const char
5522                 *Elements[] =
5523                 {
5524                   "point",
5525                   "line",
5526                   "rectangle",
5527                   "fill rectangle",
5528                   "circle",
5529                   "fill circle",
5530                   "ellipse",
5531                   "fill ellipse",
5532                   "polygon",
5533                   "fill polygon",
5534                   (char *) NULL,
5535                 };
5536
5537               /*
5538                 Select a command from the pop-up menu.
5539               */
5540               element=(ElementType) (XMenuWidget(display,windows,
5541                 DrawMenu[id],Elements,command)+1);
5542               break;
5543             }
5544             case DrawColorCommand:
5545             {
5546               const char
5547                 *ColorMenu[MaxNumberPens+1];
5548
5549               int
5550                 pen_number;
5551
5552               MagickBooleanType
5553                 transparent;
5554
5555               XColor
5556                 color;
5557
5558               /*
5559                 Initialize menu selections.
5560               */
5561               for (i=0; i < (int) (MaxNumberPens-2); i++)
5562                 ColorMenu[i]=resource_info->pen_colors[i];
5563               ColorMenu[MaxNumberPens-2]="transparent";
5564               ColorMenu[MaxNumberPens-1]="Browser...";
5565               ColorMenu[MaxNumberPens]=(char *) NULL;
5566               /*
5567                 Select a pen color from the pop-up menu.
5568               */
5569               pen_number=XMenuWidget(display,windows,DrawMenu[id],
5570                 (const char **) ColorMenu,command);
5571               if (pen_number < 0)
5572                 break;
5573               transparent=pen_number == (MaxNumberPens-2) ? MagickTrue :
5574                 MagickFalse;
5575               if (transparent != MagickFalse)
5576                 {
5577                   draw_info.stencil=TransparentStencil;
5578                   break;
5579                 }
5580               if (pen_number == (MaxNumberPens-1))
5581                 {
5582                   static char
5583                     color_name[MaxTextExtent] = "gray";
5584
5585                   /*
5586                     Select a pen color from a dialog.
5587                   */
5588                   resource_info->pen_colors[pen_number]=color_name;
5589                   XColorBrowserWidget(display,windows,"Select",color_name);
5590                   if (*color_name == '\0')
5591                     break;
5592                 }
5593               /*
5594                 Set pen color.
5595               */
5596               (void) XParseColor(display,windows->map_info->colormap,
5597                 resource_info->pen_colors[pen_number],&color);
5598               XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
5599                 (unsigned int) MaxColors,&color);
5600               windows->pixel_info->pen_colors[pen_number]=color;
5601               pen_id=(unsigned int) pen_number;
5602               draw_info.stencil=OpaqueStencil;
5603               break;
5604             }
5605             case DrawStippleCommand:
5606             {
5607               Image
5608                 *stipple_image;
5609
5610               ImageInfo
5611                 *image_info;
5612
5613               int
5614                 status;
5615
5616               static char
5617                 filename[MaxTextExtent] = "\0";
5618
5619               static const char
5620                 *StipplesMenu[] =
5621                 {
5622                   "Brick",
5623                   "Diagonal",
5624                   "Scales",
5625                   "Vertical",
5626                   "Wavy",
5627                   "Translucent",
5628                   "Opaque",
5629                   (char *) NULL,
5630                   (char *) NULL,
5631                 };
5632
5633               /*
5634                 Select a command from the pop-up menu.
5635               */
5636               StipplesMenu[7]="Open...";
5637               entry=XMenuWidget(display,windows,DrawMenu[id],StipplesMenu,
5638                 command);
5639               if (entry < 0)
5640                 break;
5641               if (stipple != (Pixmap) NULL)
5642                 (void) XFreePixmap(display,stipple);
5643               stipple=(Pixmap) NULL;
5644               if (entry != 7)
5645                 {
5646                   switch (entry)
5647                   {
5648                     case 0:
5649                     {
5650                       stipple=XCreateBitmapFromData(display,root_window,
5651                         (char *) BricksBitmap,BricksWidth,BricksHeight);
5652                       break;
5653                     }
5654                     case 1:
5655                     {
5656                       stipple=XCreateBitmapFromData(display,root_window,
5657                         (char *) DiagonalBitmap,DiagonalWidth,DiagonalHeight);
5658                       break;
5659                     }
5660                     case 2:
5661                     {
5662                       stipple=XCreateBitmapFromData(display,root_window,
5663                         (char *) ScalesBitmap,ScalesWidth,ScalesHeight);
5664                       break;
5665                     }
5666                     case 3:
5667                     {
5668                       stipple=XCreateBitmapFromData(display,root_window,
5669                         (char *) VerticalBitmap,VerticalWidth,VerticalHeight);
5670                       break;
5671                     }
5672                     case 4:
5673                     {
5674                       stipple=XCreateBitmapFromData(display,root_window,
5675                         (char *) WavyBitmap,WavyWidth,WavyHeight);
5676                       break;
5677                     }
5678                     case 5:
5679                     {
5680                       stipple=XCreateBitmapFromData(display,root_window,
5681                         (char *) HighlightBitmap,HighlightWidth,
5682                         HighlightHeight);
5683                       break;
5684                     }
5685                     case 6:
5686                     default:
5687                     {
5688                       stipple=XCreateBitmapFromData(display,root_window,
5689                         (char *) OpaqueBitmap,OpaqueWidth,OpaqueHeight);
5690                       break;
5691                     }
5692                   }
5693                   break;
5694                 }
5695               XFileBrowserWidget(display,windows,"Stipple",filename);
5696               if (*filename == '\0')
5697                 break;
5698               /*
5699                 Read image.
5700               */
5701               XSetCursorState(display,windows,MagickTrue);
5702               XCheckRefreshWindows(display,windows);
5703               image_info=AcquireImageInfo();
5704               (void) CopyMagickString(image_info->filename,filename,
5705                 MaxTextExtent);
5706               stipple_image=ReadImage(image_info,&(*image)->exception);
5707               CatchException(&(*image)->exception);
5708               XSetCursorState(display,windows,MagickFalse);
5709               if (stipple_image == (Image *) NULL)
5710                 break;
5711               (void) AcquireUniqueFileResource(filename);
5712               (void) FormatMagickString(stipple_image->filename,MaxTextExtent,
5713                 "xbm:%s",filename);
5714               (void) WriteImage(image_info,stipple_image);
5715               stipple_image=DestroyImage(stipple_image);
5716               image_info=DestroyImageInfo(image_info);
5717               status=XReadBitmapFile(display,root_window,filename,&width,
5718                 &height,&stipple,&x,&y);
5719               (void) RelinquishUniqueFileResource(filename);
5720               if ((status != BitmapSuccess) != 0)
5721                 XNoticeWidget(display,windows,"Unable to read X bitmap image:",
5722                   filename);
5723               break;
5724             }
5725             case DrawWidthCommand:
5726             {
5727               static char
5728                 width[MaxTextExtent] = "0";
5729
5730               static const char
5731                 *WidthsMenu[] =
5732                 {
5733                   "1",
5734                   "2",
5735                   "4",
5736                   "8",
5737                   "16",
5738                   "Dialog...",
5739                   (char *) NULL,
5740                 };
5741
5742               /*
5743                 Select a command from the pop-up menu.
5744               */
5745               entry=XMenuWidget(display,windows,DrawMenu[id],WidthsMenu,
5746                 command);
5747               if (entry < 0)
5748                 break;
5749               if (entry != 5)
5750                 {
5751                   line_width=(unsigned int) StringToUnsignedLong(
5752                     WidthsMenu[entry]);
5753                   break;
5754                 }
5755               (void) XDialogWidget(display,windows,"Ok","Enter line width:",
5756                 width);
5757               if (*width == '\0')
5758                 break;
5759               line_width=(unsigned int) StringToUnsignedLong(width);
5760               break;
5761             }
5762             case DrawUndoCommand:
5763             {
5764               (void) XMagickCommand(display,resource_info,windows,UndoCommand,
5765                 image);
5766               break;
5767             }
5768             case DrawHelpCommand:
5769             {
5770               XTextViewWidget(display,resource_info,windows,MagickFalse,
5771                 "Help Viewer - Image Rotation",ImageDrawHelp);
5772               (void) XCheckDefineCursor(display,windows->image.id,cursor);
5773               break;
5774             }
5775             case DrawDismissCommand:
5776             {
5777               /*
5778                 Prematurely exit.
5779               */
5780               state|=EscapeState;
5781               state|=ExitState;
5782               break;
5783             }
5784             default:
5785               break;
5786           }
5787           (void) XCheckDefineCursor(display,windows->image.id,cursor);
5788           continue;
5789         }
5790       switch (event.type)
5791       {
5792         case ButtonPress:
5793         {
5794           if (event.xbutton.button != Button1)
5795             break;
5796           if (event.xbutton.window != windows->image.id)
5797             break;
5798           /*
5799             exit loop.
5800           */
5801           x=event.xbutton.x;
5802           y=event.xbutton.y;
5803           state|=ExitState;
5804           break;
5805         }
5806         case ButtonRelease:
5807           break;
5808         case Expose:
5809           break;
5810         case KeyPress:
5811         {
5812           KeySym
5813             key_symbol;
5814
5815           if (event.xkey.window != windows->image.id)
5816             break;
5817           /*
5818             Respond to a user key press.
5819           */
5820           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
5821             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
5822           switch ((int) key_symbol)
5823           {
5824             case XK_Escape:
5825             case XK_F20:
5826             {
5827               /*
5828                 Prematurely exit.
5829               */
5830               state|=EscapeState;
5831               state|=ExitState;
5832               break;
5833             }
5834             case XK_F1:
5835             case XK_Help:
5836             {
5837               XTextViewWidget(display,resource_info,windows,MagickFalse,
5838                 "Help Viewer - Image Rotation",ImageDrawHelp);
5839               break;
5840             }
5841             default:
5842             {
5843               (void) XBell(display,0);
5844               break;
5845             }
5846           }
5847           break;
5848         }
5849         case MotionNotify:
5850         {
5851           /*
5852             Map and unmap Info widget as text cursor crosses its boundaries.
5853           */
5854           x=event.xmotion.x;
5855           y=event.xmotion.y;
5856           if (windows->info.mapped != MagickFalse)
5857             {
5858               if ((x < (int) (windows->info.x+windows->info.width)) &&
5859                   (y < (int) (windows->info.y+windows->info.height)))
5860                 (void) XWithdrawWindow(display,windows->info.id,
5861                   windows->info.screen);
5862             }
5863           else
5864             if ((x > (int) (windows->info.x+windows->info.width)) ||
5865                 (y > (int) (windows->info.y+windows->info.height)))
5866               (void) XMapWindow(display,windows->info.id);
5867           break;
5868         }
5869       }
5870     } while ((state & ExitState) == 0);
5871     (void) XSelectInput(display,windows->image.id,
5872       windows->image.attributes.event_mask);
5873     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
5874     if ((state & EscapeState) != 0)
5875       break;
5876     /*
5877       Draw element as pointer moves until the button is released.
5878     */
5879     distance=0;
5880     degrees=0.0;
5881     line_info.x1=x;
5882     line_info.y1=y;
5883     line_info.x2=x;
5884     line_info.y2=y;
5885     rectangle_info.x=(ssize_t) x;
5886     rectangle_info.y=(ssize_t) y;
5887     rectangle_info.width=0;
5888     rectangle_info.height=0;
5889     number_coordinates=1;
5890     coordinate_info->x=x;
5891     coordinate_info->y=y;
5892     (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
5893     state=DefaultState;
5894     do
5895     {
5896       switch (element)
5897       {
5898         case PointElement:
5899         default:
5900         {
5901           if (number_coordinates > 1)
5902             {
5903               (void) XDrawLines(display,windows->image.id,
5904                 windows->image.highlight_context,coordinate_info,
5905                 number_coordinates,CoordModeOrigin);
5906               (void) FormatMagickString(text,MaxTextExtent," %+d%+d",
5907                 coordinate_info[number_coordinates-1].x,
5908                 coordinate_info[number_coordinates-1].y);
5909               XInfoWidget(display,windows,text);
5910             }
5911           break;
5912         }
5913         case LineElement:
5914         {
5915           if (distance > 9)
5916             {
5917               /*
5918                 Display angle of the line.
5919               */
5920               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5921                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5922               (void) FormatMagickString(text,MaxTextExtent," %g",
5923                 (double) degrees);
5924               XInfoWidget(display,windows,text);
5925               XHighlightLine(display,windows->image.id,
5926                 windows->image.highlight_context,&line_info);
5927             }
5928           else
5929             if (windows->info.mapped != MagickFalse)
5930               (void) XWithdrawWindow(display,windows->info.id,
5931                 windows->info.screen);
5932           break;
5933         }
5934         case RectangleElement:
5935         case FillRectangleElement:
5936         {
5937           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5938             {
5939               /*
5940                 Display info and draw drawing rectangle.
5941               */
5942               (void) FormatMagickString(text,MaxTextExtent,
5943                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5944                 (double) rectangle_info.height,(double) rectangle_info.x,
5945                 (double) rectangle_info.y);
5946               XInfoWidget(display,windows,text);
5947               XHighlightRectangle(display,windows->image.id,
5948                 windows->image.highlight_context,&rectangle_info);
5949             }
5950           else
5951             if (windows->info.mapped != MagickFalse)
5952               (void) XWithdrawWindow(display,windows->info.id,
5953                 windows->info.screen);
5954           break;
5955         }
5956         case CircleElement:
5957         case FillCircleElement:
5958         case EllipseElement:
5959         case FillEllipseElement:
5960         {
5961           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
5962             {
5963               /*
5964                 Display info and draw drawing rectangle.
5965               */
5966               (void) FormatMagickString(text,MaxTextExtent,
5967                 " %.20gx%.20g%+.20g%+.20g",(double) rectangle_info.width,
5968                 (double) rectangle_info.height,(double) rectangle_info.x,
5969                 (double) rectangle_info.y);
5970               XInfoWidget(display,windows,text);
5971               XHighlightEllipse(display,windows->image.id,
5972                 windows->image.highlight_context,&rectangle_info);
5973             }
5974           else
5975             if (windows->info.mapped != MagickFalse)
5976               (void) XWithdrawWindow(display,windows->info.id,
5977                 windows->info.screen);
5978           break;
5979         }
5980         case PolygonElement:
5981         case FillPolygonElement:
5982         {
5983           if (number_coordinates > 1)
5984             (void) XDrawLines(display,windows->image.id,
5985               windows->image.highlight_context,coordinate_info,
5986               number_coordinates,CoordModeOrigin);
5987           if (distance > 9)
5988             {
5989               /*
5990                 Display angle of the line.
5991               */
5992               degrees=RadiansToDegrees(-atan2((double) (line_info.y2-
5993                 line_info.y1),(double) (line_info.x2-line_info.x1)));
5994               (void) FormatMagickString(text,MaxTextExtent," %g",
5995                 (double) degrees);
5996               XInfoWidget(display,windows,text);
5997               XHighlightLine(display,windows->image.id,
5998                 windows->image.highlight_context,&line_info);
5999             }
6000           else
6001             if (windows->info.mapped != MagickFalse)
6002               (void) XWithdrawWindow(display,windows->info.id,
6003                 windows->info.screen);
6004           break;
6005         }
6006       }
6007       /*
6008         Wait for next event.
6009       */
6010       XScreenEvent(display,windows,&event);
6011       switch (element)
6012       {
6013         case PointElement:
6014         default:
6015         {
6016           if (number_coordinates > 1)
6017             (void) XDrawLines(display,windows->image.id,
6018               windows->image.highlight_context,coordinate_info,
6019               number_coordinates,CoordModeOrigin);
6020           break;
6021         }
6022         case LineElement:
6023         {
6024           if (distance > 9)
6025             XHighlightLine(display,windows->image.id,
6026               windows->image.highlight_context,&line_info);
6027           break;
6028         }
6029         case RectangleElement:
6030         case FillRectangleElement:
6031         {
6032           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6033             XHighlightRectangle(display,windows->image.id,
6034               windows->image.highlight_context,&rectangle_info);
6035           break;
6036         }
6037         case CircleElement:
6038         case FillCircleElement:
6039         case EllipseElement:
6040         case FillEllipseElement:
6041         {
6042           if ((rectangle_info.width > 3) && (rectangle_info.height > 3))
6043             XHighlightEllipse(display,windows->image.id,
6044               windows->image.highlight_context,&rectangle_info);
6045           break;
6046         }
6047         case PolygonElement:
6048         case FillPolygonElement:
6049         {
6050           if (number_coordinates > 1)
6051             (void) XDrawLines(display,windows->image.id,
6052               windows->image.highlight_context,coordinate_info,
6053               number_coordinates,CoordModeOrigin);
6054           if (distance > 9)
6055             XHighlightLine(display,windows->image.id,
6056               windows->image.highlight_context,&line_info);
6057           break;
6058         }
6059       }
6060       switch (event.type)
6061       {
6062         case ButtonPress:
6063           break;
6064         case ButtonRelease:
6065         {
6066           /*
6067             User has committed to element.
6068           */
6069           line_info.x2=event.xbutton.x;
6070           line_info.y2=event.xbutton.y;
6071           rectangle_info.x=(ssize_t) event.xbutton.x;
6072           rectangle_info.y=(ssize_t) event.xbutton.y;
6073           coordinate_info[number_coordinates].x=event.xbutton.x;
6074           coordinate_info[number_coordinates].y=event.xbutton.y;
6075           if (((element != PolygonElement) &&
6076                (element != FillPolygonElement)) || (distance <= 9))
6077             {
6078               state|=ExitState;
6079               break;
6080             }
6081           number_coordinates++;
6082           if (number_coordinates < (int) max_coordinates)
6083             {
6084               line_info.x1=event.xbutton.x;
6085               line_info.y1=event.xbutton.y;
6086               break;
6087             }
6088           max_coordinates<<=1;
6089           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6090             max_coordinates,sizeof(*coordinate_info));
6091           if (coordinate_info == (XPoint *) NULL)
6092             (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6093               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6094           break;
6095         }
6096         case Expose:
6097           break;
6098         case MotionNotify:
6099         {
6100           if (event.xmotion.window != windows->image.id)
6101             break;
6102           if (element != PointElement)
6103             {
6104               line_info.x2=event.xmotion.x;
6105               line_info.y2=event.xmotion.y;
6106               rectangle_info.x=(ssize_t) event.xmotion.x;
6107               rectangle_info.y=(ssize_t) event.xmotion.y;
6108               break;
6109             }
6110           coordinate_info[number_coordinates].x=event.xbutton.x;
6111           coordinate_info[number_coordinates].y=event.xbutton.y;
6112           number_coordinates++;
6113           if (number_coordinates < (int) max_coordinates)
6114             break;
6115           max_coordinates<<=1;
6116           coordinate_info=(XPoint *) ResizeQuantumMemory(coordinate_info,
6117             max_coordinates,sizeof(*coordinate_info));
6118           if (coordinate_info == (XPoint *) NULL)
6119             (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
6120               ResourceLimitError,"MemoryAllocationFailed","`%s'","...");
6121           break;
6122         }
6123         default:
6124           break;
6125       }
6126       /*
6127         Check boundary conditions.
6128       */
6129       if (line_info.x2 < 0)
6130         line_info.x2=0;
6131       else
6132         if (line_info.x2 > (int) windows->image.width)
6133           line_info.x2=(short) windows->image.width;
6134       if (line_info.y2 < 0)
6135         line_info.y2=0;
6136       else
6137         if (line_info.y2 > (int) windows->image.height)
6138           line_info.y2=(short) windows->image.height;
6139       distance=(unsigned int)
6140         (((line_info.x2-line_info.x1+1)*(line_info.x2-line_info.x1+1))+
6141          ((line_info.y2-line_info.y1+1)*(line_info.y2-line_info.y1+1)));
6142       if ((((int) rectangle_info.x != x) && ((int) rectangle_info.y != y)) ||
6143           ((state & ExitState) != 0))
6144         {
6145           if (rectangle_info.x < 0)
6146             rectangle_info.x=0;
6147           else
6148             if (rectangle_info.x > (ssize_t) windows->image.width)
6149               rectangle_info.x=(ssize_t) windows->image.width;
6150           if ((int) rectangle_info.x < x)
6151             rectangle_info.width=(unsigned int) (x-rectangle_info.x);
6152           else
6153             {
6154               rectangle_info.width=(unsigned int) (rectangle_info.x-x);
6155               rectangle_info.x=(ssize_t) x;
6156             }
6157           if (rectangle_info.y < 0)
6158             rectangle_info.y=0;
6159           else
6160             if (rectangle_info.y > (ssize_t) windows->image.height)
6161               rectangle_info.y=(ssize_t) windows->image.height;
6162           if ((int) rectangle_info.y < y)
6163             rectangle_info.height=(unsigned int) (y-rectangle_info.y);
6164           else
6165             {
6166               rectangle_info.height=(unsigned int) (rectangle_info.y-y);
6167               rectangle_info.y=(ssize_t) y;
6168             }
6169         }
6170     } while ((state & ExitState) == 0);
6171     (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
6172     if ((element == PointElement) || (element == PolygonElement) ||
6173         (element == FillPolygonElement))
6174       {
6175         /*
6176           Determine polygon bounding box.
6177         */
6178         rectangle_info.x=(ssize_t) coordinate_info->x;
6179         rectangle_info.y=(ssize_t) coordinate_info->y;
6180         x=coordinate_info->x;
6181         y=coordinate_info->y;
6182         for (i=1; i < number_coordinates; i++)
6183         {
6184           if (coordinate_info[i].x > x)
6185             x=coordinate_info[i].x;
6186           if (coordinate_info[i].y > y)
6187             y=coordinate_info[i].y;
6188           if ((ssize_t) coordinate_info[i].x < rectangle_info.x)
6189             rectangle_info.x=MagickMax((ssize_t) coordinate_info[i].x,0);
6190           if ((ssize_t) coordinate_info[i].y < rectangle_info.y)
6191             rectangle_info.y=MagickMax((ssize_t) coordinate_info[i].y,0);
6192         }
6193         rectangle_info.width=(size_t) (x-rectangle_info.x);
6194         rectangle_info.height=(size_t) (y-rectangle_info.y);
6195         for (i=0; i < number_coordinates; i++)
6196         {
6197           coordinate_info[i].x-=rectangle_info.x;
6198           coordinate_info[i].y-=rectangle_info.y;
6199         }
6200       }
6201     else
6202       if (distance <= 9)
6203         continue;
6204       else
6205         if ((element == RectangleElement) ||
6206             (element == CircleElement) || (element == EllipseElement))
6207           {
6208             rectangle_info.width--;
6209             rectangle_info.height--;
6210           }
6211     /*
6212       Drawing is relative to image configuration.
6213     */
6214     draw_info.x=(int) rectangle_info.x;
6215     draw_info.y=(int) rectangle_info.y;
6216     (void) XMagickCommand(display,resource_info,windows,SaveToUndoBufferCommand,
6217       image);
6218     width=(unsigned int) (*image)->columns;
6219     height=(unsigned int) (*image)->rows;
6220     x=0;
6221     y=0;
6222     if (windows->image.crop_geometry != (char *) NULL)
6223       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
6224     draw_info.x+=windows->image.x-(line_width/2);
6225     if (draw_info.x < 0)
6226       draw_info.x=0;
6227     draw_info.x=(int) (width*draw_info.x/windows->image.ximage->width);
6228     draw_info.y+=windows->image.y-(line_width/2);
6229     if (draw_info.y < 0)
6230       draw_info.y=0;
6231     draw_info.y=(int) height*draw_info.y/windows->image.ximage->height;
6232     draw_info.width=(unsigned int) rectangle_info.width+(line_width << 1);
6233     if (draw_info.width > (unsigned int) (*image)->columns)
6234       draw_info.width=(unsigned int) (*image)->columns;
6235     draw_info.height=(unsigned int) rectangle_info.height+(line_width << 1);
6236     if (draw_info.height > (unsigned int) (*image)->rows)
6237       draw_info.height=(unsigned int) (*image)->rows;
6238     (void) FormatMagickString(draw_info.geometry,MaxTextExtent,"%ux%u%+d%+d",
6239       width*draw_info.width/windows->image.ximage->width,
6240       height*draw_info.height/windows->image.ximage->height,
6241       draw_info.x+x,draw_info.y+y);
6242     /*
6243       Initialize drawing attributes.
6244     */
6245     draw_info.degrees=0.0;
6246     draw_info.element=element;
6247     draw_info.stipple=stipple;
6248     draw_info.line_width=line_width;
6249     draw_info.line_info=line_info;
6250     if (line_info.x1 > (int) (line_width/2))
6251       draw_info.line_info.x1=(short) line_width/2;
6252     if (line_info.y1 > (int) (line_width/2))
6253       draw_info.line_info.y1=(short) line_width/2;
6254     draw_info.line_info.x2=(short) (line_info.x2-line_info.x1+(line_width/2));
6255     draw_info.line_info.y2=(short) (line_info.y2-line_info.y1+(line_width/2));
6256     if ((draw_info.line_info.x2 < 0) && (draw_info.line_info.y2 < 0))
6257       {
6258         draw_info.line_info.x2=(-draw_info.line_info.x2);
6259         draw_info.line_info.y2=(-draw_info.line_info.y2);
6260       }
6261     if (draw_info.line_info.x2 < 0)
6262       {
6263         draw_info.line_info.x2=(-draw_info.line_info.x2);
6264         Swap(draw_info.line_info.x1,draw_info.line_info.x2);
6265       }
6266     if (draw_info.line_info.y2 < 0)
6267       {
6268         draw_info.line_info.y2=(-draw_info.line_info.y2);
6269         Swap(draw_info.line_info.y1,draw_info.line_info.y2);
6270       }
6271     draw_info.rectangle_info=rectangle_info;
6272     if (draw_info.rectangle_info.x > (ssize_t) (line_width/2))
6273       draw_info.rectangle_info.x=(ssize_t) line_width/2;
6274     if (draw_info.rectangle_info.y > (ssize_t) (line_width/2))
6275       draw_info.rectangle_info.y=(ssize_t) line_width/2;
6276     draw_info.number_coordinates=(unsigned int) number_coordinates;
6277     draw_info.coordinate_info=coordinate_info;
6278     windows->pixel_info->pen_color=windows->pixel_info->pen_colors[pen_id];
6279     /*
6280       Draw element on image.
6281     */
6282     XSetCursorState(display,windows,MagickTrue);
6283     XCheckRefreshWindows(display,windows);
6284     status=XDrawImage(display,windows->pixel_info,&draw_info,*image);
6285     XSetCursorState(display,windows,MagickFalse);
6286     /*
6287       Update image colormap and return to image drawing.
6288     */
6289     XConfigureImageColormap(display,resource_info,windows,*image);
6290     (void) XConfigureImage(display,resource_info,windows,*image);
6291   }
6292   XSetCursorState(display,windows,MagickFalse);
6293   coordinate_info=(XPoint *) RelinquishMagickMemory(coordinate_info);
6294   return(status != 0 ? MagickTrue : MagickFalse);
6295 }
6296 \f
6297 /*
6298 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6299 %                                                                             %
6300 %                                                                             %
6301 %                                                                             %
6302 +   X D r a w P a n R e c t a n g l e                                         %
6303 %                                                                             %
6304 %                                                                             %
6305 %                                                                             %
6306 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6307 %
6308 %  XDrawPanRectangle() draws a rectangle in the pan window.  The pan window
6309 %  displays a zoom image and the rectangle shows which portion of the image is
6310 %  displayed in the Image window.
6311 %
6312 %  The format of the XDrawPanRectangle method is:
6313 %
6314 %      XDrawPanRectangle(Display *display,XWindows *windows)
6315 %
6316 %  A description of each parameter follows:
6317 %
6318 %    o display: Specifies a connection to an X server;  returned from
6319 %      XOpenDisplay.
6320 %
6321 %    o windows: Specifies a pointer to a XWindows structure.
6322 %
6323 */
6324 static void XDrawPanRectangle(Display *display,XWindows *windows)
6325 {
6326   MagickRealType
6327     scale_factor;
6328
6329   RectangleInfo
6330     highlight_info;
6331
6332   /*
6333     Determine dimensions of the panning rectangle.
6334   */
6335   scale_factor=(MagickRealType) windows->pan.width/windows->image.ximage->width;
6336   highlight_info.x=(ssize_t) (scale_factor*windows->image.x+0.5);
6337   highlight_info.width=(unsigned int) (scale_factor*windows->image.width+0.5);
6338   scale_factor=(MagickRealType)
6339     windows->pan.height/windows->image.ximage->height;
6340   highlight_info.y=(ssize_t) (scale_factor*windows->image.y+0.5);
6341   highlight_info.height=(unsigned int) (scale_factor*windows->image.height+0.5);
6342   /*
6343     Display the panning rectangle.
6344   */
6345   (void) XClearWindow(display,windows->pan.id);
6346   XHighlightRectangle(display,windows->pan.id,windows->pan.annotate_context,
6347     &highlight_info);
6348 }
6349 \f
6350 /*
6351 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6352 %                                                                             %
6353 %                                                                             %
6354 %                                                                             %
6355 +   X I m a g e C a c h e                                                     %
6356 %                                                                             %
6357 %                                                                             %
6358 %                                                                             %
6359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6360 %
6361 %  XImageCache() handles the creation, manipulation, and destruction of the
6362 %  image cache (undo and redo buffers).
6363 %
6364 %  The format of the XImageCache method is:
6365 %
6366 %      void XImageCache(Display *display,XResourceInfo *resource_info,
6367 %        XWindows *windows,const CommandType command,Image **image)
6368 %
6369 %  A description of each parameter follows:
6370 %
6371 %    o display: Specifies a connection to an X server; returned from
6372 %      XOpenDisplay.
6373 %
6374 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6375 %
6376 %    o windows: Specifies a pointer to a XWindows structure.
6377 %
6378 %    o command: Specifies a command to perform.
6379 %
6380 %    o image: the image;  XImageCache
6381 %      may transform the image and return a new image pointer.
6382 %
6383 */
6384 static void XImageCache(Display *display,XResourceInfo *resource_info,
6385   XWindows *windows,const CommandType command,Image **image)
6386 {
6387   Image
6388     *cache_image;
6389
6390   static Image
6391     *redo_image = (Image *) NULL,
6392     *undo_image = (Image *) NULL;
6393
6394   switch (command)
6395   {
6396     case FreeBuffersCommand:
6397     {
6398       /*
6399         Free memory from the undo and redo cache.
6400       */
6401       while (undo_image != (Image *) NULL)
6402       {
6403         cache_image=undo_image;
6404         undo_image=GetPreviousImageInList(undo_image);
6405         cache_image->list=DestroyImage(cache_image->list);
6406         cache_image=DestroyImage(cache_image);
6407       }
6408       undo_image=NewImageList();
6409       if (redo_image != (Image *) NULL)
6410         redo_image=DestroyImage(redo_image);
6411       redo_image=NewImageList();
6412       return;
6413     }
6414     case UndoCommand:
6415     {
6416       /*
6417         Undo the last image transformation.
6418       */
6419       if (undo_image == (Image *) NULL)
6420         {
6421           (void) XBell(display,0);
6422           return;
6423         }
6424       cache_image=undo_image;
6425       undo_image=GetPreviousImageInList(undo_image);
6426       windows->image.window_changes.width=(int) cache_image->columns;
6427       windows->image.window_changes.height=(int) cache_image->rows;
6428       if (windows->image.crop_geometry != (char *) NULL)
6429         windows->image.crop_geometry=(char *)
6430           RelinquishMagickMemory(windows->image.crop_geometry);
6431       windows->image.crop_geometry=cache_image->geometry;
6432       if (redo_image != (Image *) NULL)
6433         redo_image=DestroyImage(redo_image);
6434       redo_image=(*image);
6435       *image=cache_image->list;
6436       cache_image=DestroyImage(cache_image);
6437       if (windows->image.orphan != MagickFalse)
6438         return;
6439       XConfigureImageColormap(display,resource_info,windows,*image);
6440       (void) XConfigureImage(display,resource_info,windows,*image);
6441       return;
6442     }
6443     case CutCommand:
6444     case PasteCommand:
6445     case ApplyCommand:
6446     case HalfSizeCommand:
6447     case OriginalSizeCommand:
6448     case DoubleSizeCommand:
6449     case ResizeCommand:
6450     case TrimCommand:
6451     case CropCommand:
6452     case ChopCommand:
6453     case FlipCommand:
6454     case FlopCommand:
6455     case RotateRightCommand:
6456     case RotateLeftCommand:
6457     case RotateCommand:
6458     case ShearCommand:
6459     case RollCommand:
6460     case NegateCommand:
6461     case ContrastStretchCommand:
6462     case SigmoidalContrastCommand:
6463     case NormalizeCommand:
6464     case EqualizeCommand:
6465     case HueCommand:
6466     case SaturationCommand:
6467     case BrightnessCommand:
6468     case GammaCommand:
6469     case SpiffCommand:
6470     case DullCommand:
6471     case GrayscaleCommand:
6472     case MapCommand:
6473     case QuantizeCommand:
6474     case DespeckleCommand:
6475     case EmbossCommand:
6476     case ReduceNoiseCommand:
6477     case AddNoiseCommand:
6478     case SharpenCommand:
6479     case BlurCommand:
6480     case ThresholdCommand:
6481     case EdgeDetectCommand:
6482     case SpreadCommand:
6483     case ShadeCommand:
6484     case RaiseCommand:
6485     case SegmentCommand:
6486     case SolarizeCommand:
6487     case SepiaToneCommand:
6488     case SwirlCommand:
6489     case ImplodeCommand:
6490     case VignetteCommand:
6491     case WaveCommand:
6492     case OilPaintCommand:
6493     case CharcoalDrawCommand:
6494     case AnnotateCommand:
6495     case AddBorderCommand:
6496     case AddFrameCommand:
6497     case CompositeCommand:
6498     case CommentCommand:
6499     case LaunchCommand:
6500     case RegionofInterestCommand:
6501     case SaveToUndoBufferCommand:
6502     case RedoCommand:
6503     {
6504       Image
6505         *previous_image;
6506
6507       ssize_t
6508         bytes;
6509
6510       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6511       if (undo_image != (Image *) NULL)
6512         {
6513           /*
6514             Ensure the undo stash.has enough memory available.
6515           */
6516           previous_image=undo_image;
6517           while (previous_image != (Image *) NULL)
6518           {
6519             bytes+=previous_image->list->columns*previous_image->list->rows*
6520               sizeof(PixelPacket);
6521             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6522               {
6523                 previous_image=GetPreviousImageInList(previous_image);
6524                 continue;
6525               }
6526             bytes-=previous_image->list->columns*previous_image->list->rows*
6527               sizeof(PixelPacket);
6528             if (previous_image == undo_image)
6529               undo_image=NewImageList();
6530             else
6531               previous_image->next->previous=NewImageList();
6532             break;
6533           }
6534           while (previous_image != (Image *) NULL)
6535           {
6536             /*
6537               Delete any excess memory from undo cache.
6538             */
6539             cache_image=previous_image;
6540             previous_image=GetPreviousImageInList(previous_image);
6541             cache_image->list=DestroyImage(cache_image->list);
6542             cache_image=DestroyImage(cache_image);
6543           }
6544         }
6545       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6546         break;
6547       /*
6548         Save image before transformations are applied.
6549       */
6550       cache_image=AcquireImage((ImageInfo *) NULL);
6551       if (cache_image == (Image *) NULL)
6552         break;
6553       XSetCursorState(display,windows,MagickTrue);
6554       XCheckRefreshWindows(display,windows);
6555       cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6556       XSetCursorState(display,windows,MagickFalse);
6557       if (cache_image->list == (Image *) NULL)
6558         {
6559           cache_image=DestroyImage(cache_image);
6560           break;
6561         }
6562       cache_image->columns=(size_t) windows->image.ximage->width;
6563       cache_image->rows=(size_t) windows->image.ximage->height;
6564       cache_image->geometry=windows->image.crop_geometry;
6565       if (windows->image.crop_geometry != (char *) NULL)
6566         {
6567           cache_image->geometry=AcquireString((char *) NULL);
6568           (void) CopyMagickString(cache_image->geometry,
6569             windows->image.crop_geometry,MaxTextExtent);
6570         }
6571       if (undo_image == (Image *) NULL)
6572         {
6573           undo_image=cache_image;
6574           break;
6575         }
6576       undo_image->next=cache_image;
6577       undo_image->next->previous=undo_image;
6578       undo_image=undo_image->next;
6579       break;
6580     }
6581     default:
6582       break;
6583   }
6584   if (command == RedoCommand)
6585     {
6586       /*
6587         Redo the last image transformation.
6588       */
6589       if (redo_image == (Image *) NULL)
6590         {
6591           (void) XBell(display,0);
6592           return;
6593         }
6594       windows->image.window_changes.width=(int) redo_image->columns;
6595       windows->image.window_changes.height=(int) redo_image->rows;
6596       if (windows->image.crop_geometry != (char *) NULL)
6597         windows->image.crop_geometry=(char *)
6598           RelinquishMagickMemory(windows->image.crop_geometry);
6599       windows->image.crop_geometry=redo_image->geometry;
6600       *image=DestroyImage(*image);
6601       *image=redo_image;
6602       redo_image=NewImageList();
6603       if (windows->image.orphan != MagickFalse)
6604         return;
6605       XConfigureImageColormap(display,resource_info,windows,*image);
6606       (void) XConfigureImage(display,resource_info,windows,*image);
6607       return;
6608     }
6609   if (command != InfoCommand)
6610     return;
6611   /*
6612     Display image info.
6613   */
6614   XSetCursorState(display,windows,MagickTrue);
6615   XCheckRefreshWindows(display,windows);
6616   XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6617   XSetCursorState(display,windows,MagickFalse);
6618 }
6619 \f
6620 /*
6621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6622 %                                                                             %
6623 %                                                                             %
6624 %                                                                             %
6625 +   X I m a g e W i n d o w C o m m a n d                                     %
6626 %                                                                             %
6627 %                                                                             %
6628 %                                                                             %
6629 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6630 %
6631 %  XImageWindowCommand() makes a transform to the image or Image window as
6632 %  specified by a user menu button or keyboard command.
6633 %
6634 %  The format of the XMagickCommand method is:
6635 %
6636 %      CommandType XImageWindowCommand(Display *display,
6637 %        XResourceInfo *resource_info,XWindows *windows,
6638 %        const MagickStatusType state,KeySym key_symbol,Image **image)
6639 %
6640 %  A description of each parameter follows:
6641 %
6642 %    o nexus:  Method XImageWindowCommand returns an image when the
6643 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6644 %      image is returned.
6645 %
6646 %    o display: Specifies a connection to an X server; returned from
6647 %      XOpenDisplay.
6648 %
6649 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6650 %
6651 %    o windows: Specifies a pointer to a XWindows structure.
6652 %
6653 %    o state: key mask.
6654 %
6655 %    o key_symbol: Specifies a command to perform.
6656 %
6657 %    o image: the image;  XImageWIndowCommand
6658 %      may transform the image and return a new image pointer.
6659 %
6660 */
6661 static CommandType XImageWindowCommand(Display *display,
6662   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6663   KeySym key_symbol,Image **image)
6664 {
6665   static char
6666     delta[MaxTextExtent] = "";
6667
6668   static const char
6669     Digits[] = "01234567890";
6670
6671   static KeySym
6672     last_symbol = XK_0;
6673
6674   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6675     {
6676       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6677         {
6678           *delta='\0';
6679           resource_info->quantum=1;
6680         }
6681       last_symbol=key_symbol;
6682       delta[strlen(delta)+1]='\0';
6683       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6684       resource_info->quantum=StringToLong(delta);
6685       return(NullCommand);
6686     }
6687   last_symbol=key_symbol;
6688   if (resource_info->immutable)
6689     {
6690       /*
6691         Virtual image window has a restricted command set.
6692       */
6693       switch (key_symbol)
6694       {
6695         case XK_question:
6696           return(InfoCommand);
6697         case XK_p:
6698         case XK_Print:
6699           return(PrintCommand);
6700         case XK_space:
6701           return(NextCommand);
6702         case XK_q:
6703         case XK_Escape:
6704           return(QuitCommand);
6705         default:
6706           break;
6707       }
6708       return(NullCommand);
6709     }
6710   switch ((int) key_symbol)
6711   {
6712     case XK_o:
6713     {
6714       if ((state & ControlMask) == 0)
6715         break;
6716       return(OpenCommand);
6717     }
6718     case XK_space:
6719       return(NextCommand);
6720     case XK_BackSpace:
6721       return(FormerCommand);
6722     case XK_s:
6723     {
6724       if ((state & Mod1Mask) != 0)
6725         return(SwirlCommand);
6726       if ((state & ControlMask) == 0)
6727         return(ShearCommand);
6728       return(SaveCommand);
6729     }
6730     case XK_p:
6731     case XK_Print:
6732     {
6733       if ((state & Mod1Mask) != 0)
6734         return(OilPaintCommand);
6735       if ((state & Mod4Mask) != 0)
6736         return(ColorCommand);
6737       if ((state & ControlMask) == 0)
6738         return(NullCommand);
6739       return(PrintCommand);
6740     }
6741     case XK_d:
6742     {
6743       if ((state & Mod4Mask) != 0)
6744         return(DrawCommand);
6745       if ((state & ControlMask) == 0)
6746         return(NullCommand);
6747       return(DeleteCommand);
6748     }
6749     case XK_Select:
6750     {
6751       if ((state & ControlMask) == 0)
6752         return(NullCommand);
6753       return(SelectCommand);
6754     }
6755     case XK_n:
6756     {
6757       if ((state & ControlMask) == 0)
6758         return(NullCommand);
6759       return(NewCommand);
6760     }
6761     case XK_q:
6762     case XK_Escape:
6763       return(QuitCommand);
6764     case XK_z:
6765     case XK_Undo:
6766     {
6767       if ((state & ControlMask) == 0)
6768         return(NullCommand);
6769       return(UndoCommand);
6770     }
6771     case XK_r:
6772     case XK_Redo:
6773     {
6774       if ((state & ControlMask) == 0)
6775         return(RollCommand);
6776       return(RedoCommand);
6777     }
6778     case XK_x:
6779     {
6780       if ((state & ControlMask) == 0)
6781         return(NullCommand);
6782       return(CutCommand);
6783     }
6784     case XK_c:
6785     {
6786       if ((state & Mod1Mask) != 0)
6787         return(CharcoalDrawCommand);
6788       if ((state & ControlMask) == 0)
6789         return(CropCommand);
6790       return(CopyCommand);
6791     }
6792     case XK_v:
6793     case XK_Insert:
6794     {
6795       if ((state & Mod4Mask) != 0)
6796         return(CompositeCommand);
6797       if ((state & ControlMask) == 0)
6798         return(FlipCommand);
6799       return(PasteCommand);
6800     }
6801     case XK_less:
6802       return(HalfSizeCommand);
6803     case XK_minus:
6804       return(OriginalSizeCommand);
6805     case XK_greater:
6806       return(DoubleSizeCommand);
6807     case XK_percent:
6808       return(ResizeCommand);
6809     case XK_at:
6810       return(RefreshCommand);
6811     case XK_bracketleft:
6812       return(ChopCommand);
6813     case XK_h:
6814       return(FlopCommand);
6815     case XK_slash:
6816       return(RotateRightCommand);
6817     case XK_backslash:
6818       return(RotateLeftCommand);
6819     case XK_asterisk:
6820       return(RotateCommand);
6821     case XK_t:
6822       return(TrimCommand);
6823     case XK_H:
6824       return(HueCommand);
6825     case XK_S:
6826       return(SaturationCommand);
6827     case XK_L:
6828       return(BrightnessCommand);
6829     case XK_G:
6830       return(GammaCommand);
6831     case XK_C:
6832       return(SpiffCommand);
6833     case XK_Z:
6834       return(DullCommand);
6835     case XK_N:
6836       return(NormalizeCommand);
6837     case XK_equal:
6838       return(EqualizeCommand);
6839     case XK_asciitilde:
6840       return(NegateCommand);
6841     case XK_period:
6842       return(GrayscaleCommand);
6843     case XK_numbersign:
6844       return(QuantizeCommand);
6845     case XK_F2:
6846       return(DespeckleCommand);
6847     case XK_F3:
6848       return(EmbossCommand);
6849     case XK_F4:
6850       return(ReduceNoiseCommand);
6851     case XK_F5:
6852       return(AddNoiseCommand);
6853     case XK_F6:
6854       return(SharpenCommand);
6855     case XK_F7:
6856       return(BlurCommand);
6857     case XK_F8:
6858       return(ThresholdCommand);
6859     case XK_F9:
6860       return(EdgeDetectCommand);
6861     case XK_F10:
6862       return(SpreadCommand);
6863     case XK_F11:
6864       return(ShadeCommand);
6865     case XK_F12:
6866       return(RaiseCommand);
6867     case XK_F13:
6868       return(SegmentCommand);
6869     case XK_i:
6870     {
6871       if ((state & Mod1Mask) == 0)
6872         return(NullCommand);
6873       return(ImplodeCommand);
6874     }
6875     case XK_w:
6876     {
6877       if ((state & Mod1Mask) == 0)
6878         return(NullCommand);
6879       return(WaveCommand);
6880     }
6881     case XK_m:
6882     {
6883       if ((state & Mod4Mask) == 0)
6884         return(NullCommand);
6885       return(MatteCommand);
6886     }
6887     case XK_b:
6888     {
6889       if ((state & Mod4Mask) == 0)
6890         return(NullCommand);
6891       return(AddBorderCommand);
6892     }
6893     case XK_f:
6894     {
6895       if ((state & Mod4Mask) == 0)
6896         return(NullCommand);
6897       return(AddFrameCommand);
6898     }
6899     case XK_exclam:
6900     {
6901       if ((state & Mod4Mask) == 0)
6902         return(NullCommand);
6903       return(CommentCommand);
6904     }
6905     case XK_a:
6906     {
6907       if ((state & Mod1Mask) != 0)
6908         return(ApplyCommand);
6909       if ((state & Mod4Mask) != 0)
6910         return(AnnotateCommand);
6911       if ((state & ControlMask) == 0)
6912         return(NullCommand);
6913       return(RegionofInterestCommand);
6914     }
6915     case XK_question:
6916       return(InfoCommand);
6917     case XK_plus:
6918       return(ZoomCommand);
6919     case XK_P:
6920     {
6921       if ((state & ShiftMask) == 0)
6922         return(NullCommand);
6923       return(ShowPreviewCommand);
6924     }
6925     case XK_Execute:
6926       return(LaunchCommand);
6927     case XK_F1:
6928       return(HelpCommand);
6929     case XK_Find:
6930       return(BrowseDocumentationCommand);
6931     case XK_Menu:
6932     {
6933       (void) XMapRaised(display,windows->command.id);
6934       return(NullCommand);
6935     }
6936     case XK_Next:
6937     case XK_Prior:
6938     case XK_Home:
6939     case XK_KP_Home:
6940     {
6941       XTranslateImage(display,windows,*image,key_symbol);
6942       return(NullCommand);
6943     }
6944     case XK_Up:
6945     case XK_KP_Up:
6946     case XK_Down:
6947     case XK_KP_Down:
6948     case XK_Left:
6949     case XK_KP_Left:
6950     case XK_Right:
6951     case XK_KP_Right:
6952     {
6953       if ((state & Mod1Mask) != 0)
6954         {
6955           RectangleInfo
6956             crop_info;
6957
6958           /*
6959             Trim one pixel from edge of image.
6960           */
6961           crop_info.x=0;
6962           crop_info.y=0;
6963           crop_info.width=(size_t) windows->image.ximage->width;
6964           crop_info.height=(size_t) windows->image.ximage->height;
6965           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6966             {
6967               if (resource_info->quantum >= (int) crop_info.height)
6968                 resource_info->quantum=(int) crop_info.height-1;
6969               crop_info.height-=resource_info->quantum;
6970             }
6971           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6972             {
6973               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6974                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6975               crop_info.y+=resource_info->quantum;
6976               crop_info.height-=resource_info->quantum;
6977             }
6978           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6979             {
6980               if (resource_info->quantum >= (int) crop_info.width)
6981                 resource_info->quantum=(int) crop_info.width-1;
6982               crop_info.width-=resource_info->quantum;
6983             }
6984           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6985             {
6986               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
6987                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
6988               crop_info.x+=resource_info->quantum;
6989               crop_info.width-=resource_info->quantum;
6990             }
6991           if ((int) (windows->image.x+windows->image.width) >
6992               (int) crop_info.width)
6993             windows->image.x=(int) (crop_info.width-windows->image.width);
6994           if ((int) (windows->image.y+windows->image.height) >
6995               (int) crop_info.height)
6996             windows->image.y=(int) (crop_info.height-windows->image.height);
6997           XSetCropGeometry(display,windows,&crop_info,*image);
6998           windows->image.window_changes.width=(int) crop_info.width;
6999           windows->image.window_changes.height=(int) crop_info.height;
7000           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7001           (void) XConfigureImage(display,resource_info,windows,*image);
7002           return(NullCommand);
7003         }
7004       XTranslateImage(display,windows,*image,key_symbol);
7005       return(NullCommand);
7006     }
7007     default:
7008       return(NullCommand);
7009   }
7010   return(NullCommand);
7011 }
7012 \f
7013 /*
7014 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7015 %                                                                             %
7016 %                                                                             %
7017 %                                                                             %
7018 +   X M a g i c k C o m m a n d                                               %
7019 %                                                                             %
7020 %                                                                             %
7021 %                                                                             %
7022 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7023 %
7024 %  XMagickCommand() makes a transform to the image or Image window as
7025 %  specified by a user menu button or keyboard command.
7026 %
7027 %  The format of the XMagickCommand method is:
7028 %
7029 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7030 %        XWindows *windows,const CommandType command,Image **image)
7031 %
7032 %  A description of each parameter follows:
7033 %
7034 %    o nexus:  Method XMagickCommand returns an image when the
7035 %      user chooses 'Load Image' from the command menu.  Otherwise a null
7036 %      image is returned.
7037 %
7038 %    o display: Specifies a connection to an X server; returned from
7039 %      XOpenDisplay.
7040 %
7041 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7042 %
7043 %    o windows: Specifies a pointer to a XWindows structure.
7044 %
7045 %    o command: Specifies a command to perform.
7046 %
7047 %    o image: the image;  XMagickCommand
7048 %      may transform the image and return a new image pointer.
7049 %
7050 */
7051 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7052   XWindows *windows,const CommandType command,Image **image)
7053 {
7054   char
7055     filename[MaxTextExtent],
7056     geometry[MaxTextExtent],
7057     modulate_factors[MaxTextExtent];
7058
7059   GeometryInfo
7060     geometry_info;
7061
7062   Image
7063     *nexus;
7064
7065   ImageInfo
7066     *image_info;
7067
7068   int
7069     x,
7070     y;
7071
7072   MagickStatusType
7073     flags,
7074     status;
7075
7076   QuantizeInfo
7077     quantize_info;
7078
7079   RectangleInfo
7080     page_geometry;
7081
7082   register int
7083     i;
7084
7085   static char
7086     color[MaxTextExtent] = "gray";
7087
7088   unsigned int
7089     height,
7090     width;
7091
7092   /*
7093     Process user command.
7094   */
7095   XCheckRefreshWindows(display,windows);
7096   XImageCache(display,resource_info,windows,command,image);
7097   nexus=NewImageList();
7098   windows->image.window_changes.width=windows->image.ximage->width;
7099   windows->image.window_changes.height=windows->image.ximage->height;
7100   image_info=CloneImageInfo(resource_info->image_info);
7101   SetGeometryInfo(&geometry_info);
7102   GetQuantizeInfo(&quantize_info);
7103   switch (command)
7104   {
7105     case OpenCommand:
7106     {
7107       /*
7108         Load image.
7109       */
7110       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7111       break;
7112     }
7113     case NextCommand:
7114     {
7115       /*
7116         Display next image.
7117       */
7118       for (i=0; i < resource_info->quantum; i++)
7119         XClientMessage(display,windows->image.id,windows->im_protocols,
7120           windows->im_next_image,CurrentTime);
7121       break;
7122     }
7123     case FormerCommand:
7124     {
7125       /*
7126         Display former image.
7127       */
7128       for (i=0; i < resource_info->quantum; i++)
7129         XClientMessage(display,windows->image.id,windows->im_protocols,
7130           windows->im_former_image,CurrentTime);
7131       break;
7132     }
7133     case SelectCommand:
7134     {
7135       int
7136         status;
7137
7138       /*
7139         Select image.
7140       */
7141       status=chdir(resource_info->home_directory);
7142       if (status == -1)
7143         (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7144           FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7145       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7146       break;
7147     }
7148     case SaveCommand:
7149     {
7150       /*
7151         Save image.
7152       */
7153       status=XSaveImage(display,resource_info,windows,*image);
7154       if (status == MagickFalse)
7155         {
7156           XNoticeWidget(display,windows,"Unable to write X image:",
7157             (*image)->filename);
7158           break;
7159         }
7160       break;
7161     }
7162     case PrintCommand:
7163     {
7164       /*
7165         Print image.
7166       */
7167       status=XPrintImage(display,resource_info,windows,*image);
7168       if (status == MagickFalse)
7169         {
7170           XNoticeWidget(display,windows,"Unable to print X image:",
7171             (*image)->filename);
7172           break;
7173         }
7174       break;
7175     }
7176     case DeleteCommand:
7177     {
7178       static char
7179         filename[MaxTextExtent] = "\0";
7180
7181       /*
7182         Delete image file.
7183       */
7184       XFileBrowserWidget(display,windows,"Delete",filename);
7185       if (*filename == '\0')
7186         break;
7187       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
7188       if (status != MagickFalse)
7189         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7190       break;
7191     }
7192     case NewCommand:
7193     {
7194       int
7195         status;
7196
7197       static char
7198         color[MaxTextExtent] = "gray",
7199         geometry[MaxTextExtent] = "640x480";
7200
7201       static const char
7202         *format = "gradient";
7203
7204       /*
7205         Query user for canvas geometry.
7206       */
7207       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7208         geometry);
7209       if (*geometry == '\0')
7210         break;
7211       if (status == 0)
7212         format="xc";
7213       XColorBrowserWidget(display,windows,"Select",color);
7214       if (*color == '\0')
7215         break;
7216       /*
7217         Create canvas.
7218       */
7219       (void) FormatMagickString(image_info->filename,MaxTextExtent,
7220         "%s:%s",format,color);
7221       (void) CloneString(&image_info->size,geometry);
7222       nexus=ReadImage(image_info,&(*image)->exception);
7223       CatchException(&(*image)->exception);
7224       XClientMessage(display,windows->image.id,windows->im_protocols,
7225         windows->im_next_image,CurrentTime);
7226       break;
7227     }
7228     case VisualDirectoryCommand:
7229     {
7230       /*
7231         Visual Image directory.
7232       */
7233       nexus=XVisualDirectoryImage(display,resource_info,windows);
7234       break;
7235     }
7236     case QuitCommand:
7237     {
7238       /*
7239         exit program.
7240       */
7241       if (resource_info->confirm_exit == MagickFalse)
7242         XClientMessage(display,windows->image.id,windows->im_protocols,
7243           windows->im_exit,CurrentTime);
7244       else
7245         {
7246           int
7247             status;
7248
7249           /*
7250             Confirm program exit.
7251           */
7252           status=XConfirmWidget(display,windows,"Do you really want to exit",
7253             resource_info->client_name);
7254           if (status > 0)
7255             XClientMessage(display,windows->image.id,windows->im_protocols,
7256               windows->im_exit,CurrentTime);
7257         }
7258       break;
7259     }
7260     case CutCommand:
7261     {
7262       /*
7263         Cut image.
7264       */
7265       (void) XCropImage(display,resource_info,windows,*image,CutMode);
7266       break;
7267     }
7268     case CopyCommand:
7269     {
7270       /*
7271         Copy image.
7272       */
7273       (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7274       break;
7275     }
7276     case PasteCommand:
7277     {
7278       /*
7279         Paste image.
7280       */
7281       status=XPasteImage(display,resource_info,windows,*image);
7282       if (status == MagickFalse)
7283         {
7284           XNoticeWidget(display,windows,"Unable to paste X image",
7285             (*image)->filename);
7286           break;
7287         }
7288       break;
7289     }
7290     case HalfSizeCommand:
7291     {
7292       /*
7293         Half image size.
7294       */
7295       windows->image.window_changes.width=windows->image.ximage->width/2;
7296       windows->image.window_changes.height=windows->image.ximage->height/2;
7297       (void) XConfigureImage(display,resource_info,windows,*image);
7298       break;
7299     }
7300     case OriginalSizeCommand:
7301     {
7302       /*
7303         Original image size.
7304       */
7305       windows->image.window_changes.width=(int) (*image)->columns;
7306       windows->image.window_changes.height=(int) (*image)->rows;
7307       (void) XConfigureImage(display,resource_info,windows,*image);
7308       break;
7309     }
7310     case DoubleSizeCommand:
7311     {
7312       /*
7313         Double the image size.
7314       */
7315       windows->image.window_changes.width=windows->image.ximage->width << 1;
7316       windows->image.window_changes.height=windows->image.ximage->height << 1;
7317       (void) XConfigureImage(display,resource_info,windows,*image);
7318       break;
7319     }
7320     case ResizeCommand:
7321     {
7322       int
7323         status;
7324
7325       ssize_t
7326         x,
7327         y;
7328
7329       size_t
7330         height,
7331         width;
7332
7333       /*
7334         Resize image.
7335       */
7336       width=(size_t) windows->image.ximage->width;
7337       height=(size_t) windows->image.ximage->height;
7338       x=0;
7339       y=0;
7340       (void) FormatMagickString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7341         (double) width,(double) height);
7342       status=XDialogWidget(display,windows,"Resize",
7343         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7344       if (*geometry == '\0')
7345         break;
7346       if (status == 0)
7347         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7348       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7349       windows->image.window_changes.width=(int) width;
7350       windows->image.window_changes.height=(int) height;
7351       (void) XConfigureImage(display,resource_info,windows,*image);
7352       break;
7353     }
7354     case ApplyCommand:
7355     {
7356       char
7357         image_geometry[MaxTextExtent];
7358
7359       if ((windows->image.crop_geometry == (char *) NULL) &&
7360           ((int) (*image)->columns == windows->image.ximage->width) &&
7361           ((int) (*image)->rows == windows->image.ximage->height))
7362         break;
7363       /*
7364         Apply size transforms to image.
7365       */
7366       XSetCursorState(display,windows,MagickTrue);
7367       XCheckRefreshWindows(display,windows);
7368       /*
7369         Crop and/or scale displayed image.
7370       */
7371       (void) FormatMagickString(image_geometry,MaxTextExtent,"%dx%d!",
7372         windows->image.ximage->width,windows->image.ximage->height);
7373       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7374       if (windows->image.crop_geometry != (char *) NULL)
7375         windows->image.crop_geometry=(char *)
7376           RelinquishMagickMemory(windows->image.crop_geometry);
7377       windows->image.x=0;
7378       windows->image.y=0;
7379       XConfigureImageColormap(display,resource_info,windows,*image);
7380       (void) XConfigureImage(display,resource_info,windows,*image);
7381       break;
7382     }
7383     case RefreshCommand:
7384     {
7385       (void) XConfigureImage(display,resource_info,windows,*image);
7386       break;
7387     }
7388     case RestoreCommand:
7389     {
7390       /*
7391         Restore Image window to its original size.
7392       */
7393       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7394           (windows->image.height == (unsigned int) (*image)->rows) &&
7395           (windows->image.crop_geometry == (char *) NULL))
7396         {
7397           (void) XBell(display,0);
7398           break;
7399         }
7400       windows->image.window_changes.width=(int) (*image)->columns;
7401       windows->image.window_changes.height=(int) (*image)->rows;
7402       if (windows->image.crop_geometry != (char *) NULL)
7403         {
7404           windows->image.crop_geometry=(char *)
7405             RelinquishMagickMemory(windows->image.crop_geometry);
7406           windows->image.crop_geometry=(char *) NULL;
7407           windows->image.x=0;
7408           windows->image.y=0;
7409         }
7410       XConfigureImageColormap(display,resource_info,windows,*image);
7411       (void) XConfigureImage(display,resource_info,windows,*image);
7412       break;
7413     }
7414     case CropCommand:
7415     {
7416       /*
7417         Crop image.
7418       */
7419       (void) XCropImage(display,resource_info,windows,*image,CropMode);
7420       break;
7421     }
7422     case ChopCommand:
7423     {
7424       /*
7425         Chop image.
7426       */
7427       status=XChopImage(display,resource_info,windows,image);
7428       if (status == MagickFalse)
7429         {
7430           XNoticeWidget(display,windows,"Unable to cut X image",
7431             (*image)->filename);
7432           break;
7433         }
7434       break;
7435     }
7436     case FlopCommand:
7437     {
7438       Image
7439         *flop_image;
7440
7441       /*
7442         Flop image scanlines.
7443       */
7444       XSetCursorState(display,windows,MagickTrue);
7445       XCheckRefreshWindows(display,windows);
7446       flop_image=FlopImage(*image,&(*image)->exception);
7447       if (flop_image != (Image *) NULL)
7448         {
7449           *image=DestroyImage(*image);
7450           *image=flop_image;
7451         }
7452       CatchException(&(*image)->exception);
7453       XSetCursorState(display,windows,MagickFalse);
7454       if (windows->image.crop_geometry != (char *) NULL)
7455         {
7456           /*
7457             Flop crop geometry.
7458           */
7459           width=(unsigned int) (*image)->columns;
7460           height=(unsigned int) (*image)->rows;
7461           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7462             &width,&height);
7463           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
7464             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7465         }
7466       if (windows->image.orphan != MagickFalse)
7467         break;
7468       (void) XConfigureImage(display,resource_info,windows,*image);
7469       break;
7470     }
7471     case FlipCommand:
7472     {
7473       Image
7474         *flip_image;
7475
7476       /*
7477         Flip image scanlines.
7478       */
7479       XSetCursorState(display,windows,MagickTrue);
7480       XCheckRefreshWindows(display,windows);
7481       flip_image=FlipImage(*image,&(*image)->exception);
7482       if (flip_image != (Image *) NULL)
7483         {
7484           *image=DestroyImage(*image);
7485           *image=flip_image;
7486         }
7487       CatchException(&(*image)->exception);
7488       XSetCursorState(display,windows,MagickFalse);
7489       if (windows->image.crop_geometry != (char *) NULL)
7490         {
7491           /*
7492             Flip crop geometry.
7493           */
7494           width=(unsigned int) (*image)->columns;
7495           height=(unsigned int) (*image)->rows;
7496           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7497             &width,&height);
7498           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
7499             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7500         }
7501       if (windows->image.orphan != MagickFalse)
7502         break;
7503       (void) XConfigureImage(display,resource_info,windows,*image);
7504       break;
7505     }
7506     case RotateRightCommand:
7507     {
7508       /*
7509         Rotate image 90 degrees clockwise.
7510       */
7511       status=XRotateImage(display,resource_info,windows,90.0,image);
7512       if (status == MagickFalse)
7513         {
7514           XNoticeWidget(display,windows,"Unable to rotate X image",
7515             (*image)->filename);
7516           break;
7517         }
7518       break;
7519     }
7520     case RotateLeftCommand:
7521     {
7522       /*
7523         Rotate image 90 degrees counter-clockwise.
7524       */
7525       status=XRotateImage(display,resource_info,windows,-90.0,image);
7526       if (status == MagickFalse)
7527         {
7528           XNoticeWidget(display,windows,"Unable to rotate X image",
7529             (*image)->filename);
7530           break;
7531         }
7532       break;
7533     }
7534     case RotateCommand:
7535     {
7536       /*
7537         Rotate image.
7538       */
7539       status=XRotateImage(display,resource_info,windows,0.0,image);
7540       if (status == MagickFalse)
7541         {
7542           XNoticeWidget(display,windows,"Unable to rotate X image",
7543             (*image)->filename);
7544           break;
7545         }
7546       break;
7547     }
7548     case ShearCommand:
7549     {
7550       Image
7551         *shear_image;
7552
7553       static char
7554         geometry[MaxTextExtent] = "45.0x45.0";
7555
7556       /*
7557         Query user for shear color and geometry.
7558       */
7559       XColorBrowserWidget(display,windows,"Select",color);
7560       if (*color == '\0')
7561         break;
7562       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7563         geometry);
7564       if (*geometry == '\0')
7565         break;
7566       /*
7567         Shear image.
7568       */
7569       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7570       XSetCursorState(display,windows,MagickTrue);
7571       XCheckRefreshWindows(display,windows);
7572       (void) QueryColorDatabase(color,&(*image)->background_color,
7573         &(*image)->exception);
7574       flags=ParseGeometry(geometry,&geometry_info);
7575       if ((flags & SigmaValue) == 0)
7576         geometry_info.sigma=geometry_info.rho;
7577       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7578         &(*image)->exception);
7579       if (shear_image != (Image *) NULL)
7580         {
7581           *image=DestroyImage(*image);
7582           *image=shear_image;
7583         }
7584       CatchException(&(*image)->exception);
7585       XSetCursorState(display,windows,MagickFalse);
7586       if (windows->image.orphan != MagickFalse)
7587         break;
7588       windows->image.window_changes.width=(int) (*image)->columns;
7589       windows->image.window_changes.height=(int) (*image)->rows;
7590       XConfigureImageColormap(display,resource_info,windows,*image);
7591       (void) XConfigureImage(display,resource_info,windows,*image);
7592       break;
7593     }
7594     case RollCommand:
7595     {
7596       Image
7597         *roll_image;
7598
7599       static char
7600         geometry[MaxTextExtent] = "+2+2";
7601
7602       /*
7603         Query user for the roll geometry.
7604       */
7605       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7606         geometry);
7607       if (*geometry == '\0')
7608         break;
7609       /*
7610         Roll image.
7611       */
7612       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7613       XSetCursorState(display,windows,MagickTrue);
7614       XCheckRefreshWindows(display,windows);
7615       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7616         &(*image)->exception);
7617       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7618         &(*image)->exception);
7619       if (roll_image != (Image *) NULL)
7620         {
7621           *image=DestroyImage(*image);
7622           *image=roll_image;
7623         }
7624       CatchException(&(*image)->exception);
7625       XSetCursorState(display,windows,MagickFalse);
7626       if (windows->image.orphan != MagickFalse)
7627         break;
7628       windows->image.window_changes.width=(int) (*image)->columns;
7629       windows->image.window_changes.height=(int) (*image)->rows;
7630       XConfigureImageColormap(display,resource_info,windows,*image);
7631       (void) XConfigureImage(display,resource_info,windows,*image);
7632       break;
7633     }
7634     case TrimCommand:
7635     {
7636       static char
7637         fuzz[MaxTextExtent];
7638
7639       /*
7640         Query user for the fuzz factor.
7641       */
7642       (void) FormatMagickString(fuzz,MaxTextExtent,"%g%%",100.0*
7643         (*image)->fuzz/(QuantumRange+1.0));
7644       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7645       if (*fuzz == '\0')
7646         break;
7647       (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7648       /*
7649         Trim image.
7650       */
7651       status=XTrimImage(display,resource_info,windows,*image);
7652       if (status == MagickFalse)
7653         {
7654           XNoticeWidget(display,windows,"Unable to trim X image",
7655             (*image)->filename);
7656           break;
7657         }
7658       break;
7659     }
7660     case HueCommand:
7661     {
7662       static char
7663         hue_percent[MaxTextExtent] = "110";
7664
7665       /*
7666         Query user for percent hue change.
7667       */
7668       (void) XDialogWidget(display,windows,"Apply",
7669         "Enter percent change in image hue (0-200):",hue_percent);
7670       if (*hue_percent == '\0')
7671         break;
7672       /*
7673         Vary the image hue.
7674       */
7675       XSetCursorState(display,windows,MagickTrue);
7676       XCheckRefreshWindows(display,windows);
7677       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7678       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7679         MaxTextExtent);
7680       (void) ModulateImage(*image,modulate_factors);
7681       XSetCursorState(display,windows,MagickFalse);
7682       if (windows->image.orphan != MagickFalse)
7683         break;
7684       XConfigureImageColormap(display,resource_info,windows,*image);
7685       (void) XConfigureImage(display,resource_info,windows,*image);
7686       break;
7687     }
7688     case SaturationCommand:
7689     {
7690       static char
7691         saturation_percent[MaxTextExtent] = "110";
7692
7693       /*
7694         Query user for percent saturation change.
7695       */
7696       (void) XDialogWidget(display,windows,"Apply",
7697         "Enter percent change in color saturation (0-200):",saturation_percent);
7698       if (*saturation_percent == '\0')
7699         break;
7700       /*
7701         Vary color saturation.
7702       */
7703       XSetCursorState(display,windows,MagickTrue);
7704       XCheckRefreshWindows(display,windows);
7705       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7706       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7707         MaxTextExtent);
7708       (void) ModulateImage(*image,modulate_factors);
7709       XSetCursorState(display,windows,MagickFalse);
7710       if (windows->image.orphan != MagickFalse)
7711         break;
7712       XConfigureImageColormap(display,resource_info,windows,*image);
7713       (void) XConfigureImage(display,resource_info,windows,*image);
7714       break;
7715     }
7716     case BrightnessCommand:
7717     {
7718       static char
7719         brightness_percent[MaxTextExtent] = "110";
7720
7721       /*
7722         Query user for percent brightness change.
7723       */
7724       (void) XDialogWidget(display,windows,"Apply",
7725         "Enter percent change in color brightness (0-200):",brightness_percent);
7726       if (*brightness_percent == '\0')
7727         break;
7728       /*
7729         Vary the color brightness.
7730       */
7731       XSetCursorState(display,windows,MagickTrue);
7732       XCheckRefreshWindows(display,windows);
7733       (void) CopyMagickString(modulate_factors,brightness_percent,
7734         MaxTextExtent);
7735       (void) ModulateImage(*image,modulate_factors);
7736       XSetCursorState(display,windows,MagickFalse);
7737       if (windows->image.orphan != MagickFalse)
7738         break;
7739       XConfigureImageColormap(display,resource_info,windows,*image);
7740       (void) XConfigureImage(display,resource_info,windows,*image);
7741       break;
7742     }
7743     case GammaCommand:
7744     {
7745       static char
7746         factor[MaxTextExtent] = "1.6";
7747
7748       /*
7749         Query user for gamma value.
7750       */
7751       (void) XDialogWidget(display,windows,"Gamma",
7752         "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
7753       if (*factor == '\0')
7754         break;
7755       /*
7756         Gamma correct image.
7757       */
7758       XSetCursorState(display,windows,MagickTrue);
7759       XCheckRefreshWindows(display,windows);
7760       (void) GammaImage(*image,factor);
7761       XSetCursorState(display,windows,MagickFalse);
7762       if (windows->image.orphan != MagickFalse)
7763         break;
7764       XConfigureImageColormap(display,resource_info,windows,*image);
7765       (void) XConfigureImage(display,resource_info,windows,*image);
7766       break;
7767     }
7768     case SpiffCommand:
7769     {
7770       /*
7771         Sharpen the image contrast.
7772       */
7773       XSetCursorState(display,windows,MagickTrue);
7774       XCheckRefreshWindows(display,windows);
7775       (void) ContrastImage(*image,MagickTrue);
7776       XSetCursorState(display,windows,MagickFalse);
7777       if (windows->image.orphan != MagickFalse)
7778         break;
7779       XConfigureImageColormap(display,resource_info,windows,*image);
7780       (void) XConfigureImage(display,resource_info,windows,*image);
7781       break;
7782     }
7783     case DullCommand:
7784     {
7785       /*
7786         Dull the image contrast.
7787       */
7788       XSetCursorState(display,windows,MagickTrue);
7789       XCheckRefreshWindows(display,windows);
7790       (void) ContrastImage(*image,MagickFalse);
7791       XSetCursorState(display,windows,MagickFalse);
7792       if (windows->image.orphan != MagickFalse)
7793         break;
7794       XConfigureImageColormap(display,resource_info,windows,*image);
7795       (void) XConfigureImage(display,resource_info,windows,*image);
7796       break;
7797     }
7798     case ContrastStretchCommand:
7799     {
7800       double
7801         black_point,
7802         white_point;
7803
7804       static char
7805         levels[MaxTextExtent] = "1%";
7806
7807       /*
7808         Query user for gamma value.
7809       */
7810       (void) XDialogWidget(display,windows,"Contrast Stretch",
7811         "Enter black and white points:",levels);
7812       if (*levels == '\0')
7813         break;
7814       /*
7815         Contrast stretch image.
7816       */
7817       XSetCursorState(display,windows,MagickTrue);
7818       XCheckRefreshWindows(display,windows);
7819       flags=ParseGeometry(levels,&geometry_info);
7820       black_point=geometry_info.rho;
7821       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7822       if ((flags & PercentValue) != 0)
7823         {
7824           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7825           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7826         }
7827       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7828       (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7829         white_point);
7830       XSetCursorState(display,windows,MagickFalse);
7831       if (windows->image.orphan != MagickFalse)
7832         break;
7833       XConfigureImageColormap(display,resource_info,windows,*image);
7834       (void) XConfigureImage(display,resource_info,windows,*image);
7835       break;
7836     }
7837     case SigmoidalContrastCommand:
7838     {
7839       static char
7840         levels[MaxTextExtent] = "3x50%";
7841
7842       /*
7843         Query user for gamma value.
7844       */
7845       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7846         "Enter contrast and midpoint:",levels);
7847       if (*levels == '\0')
7848         break;
7849       /*
7850         Contrast stretch image.
7851       */
7852       XSetCursorState(display,windows,MagickTrue);
7853       XCheckRefreshWindows(display,windows);
7854       (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7855       XSetCursorState(display,windows,MagickFalse);
7856       if (windows->image.orphan != MagickFalse)
7857         break;
7858       XConfigureImageColormap(display,resource_info,windows,*image);
7859       (void) XConfigureImage(display,resource_info,windows,*image);
7860       break;
7861     }
7862     case NormalizeCommand:
7863     {
7864       /*
7865         Perform histogram normalization on the image.
7866       */
7867       XSetCursorState(display,windows,MagickTrue);
7868       XCheckRefreshWindows(display,windows);
7869       (void) NormalizeImage(*image);
7870       XSetCursorState(display,windows,MagickFalse);
7871       if (windows->image.orphan != MagickFalse)
7872         break;
7873       XConfigureImageColormap(display,resource_info,windows,*image);
7874       (void) XConfigureImage(display,resource_info,windows,*image);
7875       break;
7876     }
7877     case EqualizeCommand:
7878     {
7879       /*
7880         Perform histogram equalization on the image.
7881       */
7882       XSetCursorState(display,windows,MagickTrue);
7883       XCheckRefreshWindows(display,windows);
7884       (void) EqualizeImage(*image);
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 NegateCommand:
7893     {
7894       /*
7895         Negate colors in image.
7896       */
7897       XSetCursorState(display,windows,MagickTrue);
7898       XCheckRefreshWindows(display,windows);
7899       (void) NegateImage(*image,MagickFalse);
7900       XSetCursorState(display,windows,MagickFalse);
7901       if (windows->image.orphan != MagickFalse)
7902         break;
7903       XConfigureImageColormap(display,resource_info,windows,*image);
7904       (void) XConfigureImage(display,resource_info,windows,*image);
7905       break;
7906     }
7907     case GrayscaleCommand:
7908     {
7909       /*
7910         Convert image to grayscale.
7911       */
7912       XSetCursorState(display,windows,MagickTrue);
7913       XCheckRefreshWindows(display,windows);
7914       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7915         GrayscaleType : GrayscaleMatteType);
7916       XSetCursorState(display,windows,MagickFalse);
7917       if (windows->image.orphan != MagickFalse)
7918         break;
7919       XConfigureImageColormap(display,resource_info,windows,*image);
7920       (void) XConfigureImage(display,resource_info,windows,*image);
7921       break;
7922     }
7923     case MapCommand:
7924     {
7925       Image
7926         *affinity_image;
7927
7928       static char
7929         filename[MaxTextExtent] = "\0";
7930
7931       /*
7932         Request image file name from user.
7933       */
7934       XFileBrowserWidget(display,windows,"Map",filename);
7935       if (*filename == '\0')
7936         break;
7937       /*
7938         Map image.
7939       */
7940       XSetCursorState(display,windows,MagickTrue);
7941       XCheckRefreshWindows(display,windows);
7942       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7943       affinity_image=ReadImage(image_info,&(*image)->exception);
7944       if (affinity_image != (Image *) NULL)
7945         {
7946           (void) RemapImage(&quantize_info,*image,affinity_image);
7947           affinity_image=DestroyImage(affinity_image);
7948         }
7949       CatchException(&(*image)->exception);
7950       XSetCursorState(display,windows,MagickFalse);
7951       if (windows->image.orphan != MagickFalse)
7952         break;
7953       XConfigureImageColormap(display,resource_info,windows,*image);
7954       (void) XConfigureImage(display,resource_info,windows,*image);
7955       break;
7956     }
7957     case QuantizeCommand:
7958     {
7959       int
7960         status;
7961
7962       static char
7963         colors[MaxTextExtent] = "256";
7964
7965       /*
7966         Query user for maximum number of colors.
7967       */
7968       status=XDialogWidget(display,windows,"Quantize",
7969         "Maximum number of colors:",colors);
7970       if (*colors == '\0')
7971         break;
7972       /*
7973         Color reduce the image.
7974       */
7975       XSetCursorState(display,windows,MagickTrue);
7976       XCheckRefreshWindows(display,windows);
7977       quantize_info.number_colors=StringToUnsignedLong(colors);
7978       quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7979       (void) QuantizeImage(&quantize_info,*image);
7980       XSetCursorState(display,windows,MagickFalse);
7981       if (windows->image.orphan != MagickFalse)
7982         break;
7983       XConfigureImageColormap(display,resource_info,windows,*image);
7984       (void) XConfigureImage(display,resource_info,windows,*image);
7985       break;
7986     }
7987     case DespeckleCommand:
7988     {
7989       Image
7990         *despeckle_image;
7991
7992       /*
7993         Despeckle image.
7994       */
7995       XSetCursorState(display,windows,MagickTrue);
7996       XCheckRefreshWindows(display,windows);
7997       despeckle_image=DespeckleImage(*image,&(*image)->exception);
7998       if (despeckle_image != (Image *) NULL)
7999         {
8000           *image=DestroyImage(*image);
8001           *image=despeckle_image;
8002         }
8003       CatchException(&(*image)->exception);
8004       XSetCursorState(display,windows,MagickFalse);
8005       if (windows->image.orphan != MagickFalse)
8006         break;
8007       XConfigureImageColormap(display,resource_info,windows,*image);
8008       (void) XConfigureImage(display,resource_info,windows,*image);
8009       break;
8010     }
8011     case EmbossCommand:
8012     {
8013       Image
8014         *emboss_image;
8015
8016       static char
8017         radius[MaxTextExtent] = "0.0x1.0";
8018
8019       /*
8020         Query user for emboss radius.
8021       */
8022       (void) XDialogWidget(display,windows,"Emboss",
8023         "Enter the emboss radius and standard deviation:",radius);
8024       if (*radius == '\0')
8025         break;
8026       /*
8027         Reduce noise in the image.
8028       */
8029       XSetCursorState(display,windows,MagickTrue);
8030       XCheckRefreshWindows(display,windows);
8031       flags=ParseGeometry(radius,&geometry_info);
8032       if ((flags & SigmaValue) == 0)
8033         geometry_info.sigma=1.0;
8034       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8035         &(*image)->exception);
8036       if (emboss_image != (Image *) NULL)
8037         {
8038           *image=DestroyImage(*image);
8039           *image=emboss_image;
8040         }
8041       CatchException(&(*image)->exception);
8042       XSetCursorState(display,windows,MagickFalse);
8043       if (windows->image.orphan != MagickFalse)
8044         break;
8045       XConfigureImageColormap(display,resource_info,windows,*image);
8046       (void) XConfigureImage(display,resource_info,windows,*image);
8047       break;
8048     }
8049     case ReduceNoiseCommand:
8050     {
8051       Image
8052         *noise_image;
8053
8054       static char
8055         radius[MaxTextExtent] = "0";
8056
8057       /*
8058         Query user for noise radius.
8059       */
8060       (void) XDialogWidget(display,windows,"Reduce Noise",
8061         "Enter the noise radius:",radius);
8062       if (*radius == '\0')
8063         break;
8064       /*
8065         Reduce noise in the image.
8066       */
8067       XSetCursorState(display,windows,MagickTrue);
8068       XCheckRefreshWindows(display,windows);
8069       flags=ParseGeometry(radius,&geometry_info);
8070       noise_image=ReduceNoiseImage(*image,geometry_info.rho,
8071         &(*image)->exception);
8072       if (noise_image != (Image *) NULL)
8073         {
8074           *image=DestroyImage(*image);
8075           *image=noise_image;
8076         }
8077       CatchException(&(*image)->exception);
8078       XSetCursorState(display,windows,MagickFalse);
8079       if (windows->image.orphan != MagickFalse)
8080         break;
8081       XConfigureImageColormap(display,resource_info,windows,*image);
8082       (void) XConfigureImage(display,resource_info,windows,*image);
8083       break;
8084     }
8085     case AddNoiseCommand:
8086     {
8087       char
8088         **noises;
8089
8090       Image
8091         *noise_image;
8092
8093       static char
8094         noise_type[MaxTextExtent] = "Gaussian";
8095
8096       /*
8097         Add noise to the image.
8098       */
8099       noises=GetMagickOptions(MagickNoiseOptions);
8100       if (noises == (char **) NULL)
8101         break;
8102       XListBrowserWidget(display,windows,&windows->widget,
8103         (const char **) noises,"Add Noise",
8104         "Select a type of noise to add to your image:",noise_type);
8105       noises=DestroyStringList(noises);
8106       if (*noise_type == '\0')
8107         break;
8108       XSetCursorState(display,windows,MagickTrue);
8109       XCheckRefreshWindows(display,windows);
8110       noise_image=AddNoiseImage(*image,(NoiseType) ParseMagickOption(
8111         MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8112       if (noise_image != (Image *) NULL)
8113         {
8114           *image=DestroyImage(*image);
8115           *image=noise_image;
8116         }
8117       CatchException(&(*image)->exception);
8118       XSetCursorState(display,windows,MagickFalse);
8119       if (windows->image.orphan != MagickFalse)
8120         break;
8121       XConfigureImageColormap(display,resource_info,windows,*image);
8122       (void) XConfigureImage(display,resource_info,windows,*image);
8123       break;
8124     }
8125     case SharpenCommand:
8126     {
8127       Image
8128         *sharp_image;
8129
8130       static char
8131         radius[MaxTextExtent] = "0.0x1.0";
8132
8133       /*
8134         Query user for sharpen radius.
8135       */
8136       (void) XDialogWidget(display,windows,"Sharpen",
8137         "Enter the sharpen radius and standard deviation:",radius);
8138       if (*radius == '\0')
8139         break;
8140       /*
8141         Sharpen image scanlines.
8142       */
8143       XSetCursorState(display,windows,MagickTrue);
8144       XCheckRefreshWindows(display,windows);
8145       flags=ParseGeometry(radius,&geometry_info);
8146       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8147         &(*image)->exception);
8148       if (sharp_image != (Image *) NULL)
8149         {
8150           *image=DestroyImage(*image);
8151           *image=sharp_image;
8152         }
8153       CatchException(&(*image)->exception);
8154       XSetCursorState(display,windows,MagickFalse);
8155       if (windows->image.orphan != MagickFalse)
8156         break;
8157       XConfigureImageColormap(display,resource_info,windows,*image);
8158       (void) XConfigureImage(display,resource_info,windows,*image);
8159       break;
8160     }
8161     case BlurCommand:
8162     {
8163       Image
8164         *blur_image;
8165
8166       static char
8167         radius[MaxTextExtent] = "0.0x1.0";
8168
8169       /*
8170         Query user for blur radius.
8171       */
8172       (void) XDialogWidget(display,windows,"Blur",
8173         "Enter the blur radius and standard deviation:",radius);
8174       if (*radius == '\0')
8175         break;
8176       /*
8177         Blur an image.
8178       */
8179       XSetCursorState(display,windows,MagickTrue);
8180       XCheckRefreshWindows(display,windows);
8181       flags=ParseGeometry(radius,&geometry_info);
8182       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8183         &(*image)->exception);
8184       if (blur_image != (Image *) NULL)
8185         {
8186           *image=DestroyImage(*image);
8187           *image=blur_image;
8188         }
8189       CatchException(&(*image)->exception);
8190       XSetCursorState(display,windows,MagickFalse);
8191       if (windows->image.orphan != MagickFalse)
8192         break;
8193       XConfigureImageColormap(display,resource_info,windows,*image);
8194       (void) XConfigureImage(display,resource_info,windows,*image);
8195       break;
8196     }
8197     case ThresholdCommand:
8198     {
8199       double
8200         threshold;
8201
8202       static char
8203         factor[MaxTextExtent] = "128";
8204
8205       /*
8206         Query user for threshold value.
8207       */
8208       (void) XDialogWidget(display,windows,"Threshold",
8209         "Enter threshold value:",factor);
8210       if (*factor == '\0')
8211         break;
8212       /*
8213         Gamma correct image.
8214       */
8215       XSetCursorState(display,windows,MagickTrue);
8216       XCheckRefreshWindows(display,windows);
8217       threshold=SiPrefixToDouble(factor,QuantumRange);
8218       (void) BilevelImage(*image,threshold);
8219       XSetCursorState(display,windows,MagickFalse);
8220       if (windows->image.orphan != MagickFalse)
8221         break;
8222       XConfigureImageColormap(display,resource_info,windows,*image);
8223       (void) XConfigureImage(display,resource_info,windows,*image);
8224       break;
8225     }
8226     case EdgeDetectCommand:
8227     {
8228       Image
8229         *edge_image;
8230
8231       static char
8232         radius[MaxTextExtent] = "0";
8233
8234       /*
8235         Query user for edge factor.
8236       */
8237       (void) XDialogWidget(display,windows,"Detect Edges",
8238         "Enter the edge detect radius:",radius);
8239       if (*radius == '\0')
8240         break;
8241       /*
8242         Detect edge in image.
8243       */
8244       XSetCursorState(display,windows,MagickTrue);
8245       XCheckRefreshWindows(display,windows);
8246       flags=ParseGeometry(radius,&geometry_info);
8247       edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8248       if (edge_image != (Image *) NULL)
8249         {
8250           *image=DestroyImage(*image);
8251           *image=edge_image;
8252         }
8253       CatchException(&(*image)->exception);
8254       XSetCursorState(display,windows,MagickFalse);
8255       if (windows->image.orphan != MagickFalse)
8256         break;
8257       XConfigureImageColormap(display,resource_info,windows,*image);
8258       (void) XConfigureImage(display,resource_info,windows,*image);
8259       break;
8260     }
8261     case SpreadCommand:
8262     {
8263       Image
8264         *spread_image;
8265
8266       static char
8267         amount[MaxTextExtent] = "2";
8268
8269       /*
8270         Query user for spread amount.
8271       */
8272       (void) XDialogWidget(display,windows,"Spread",
8273         "Enter the displacement amount:",amount);
8274       if (*amount == '\0')
8275         break;
8276       /*
8277         Displace image pixels by a random amount.
8278       */
8279       XSetCursorState(display,windows,MagickTrue);
8280       XCheckRefreshWindows(display,windows);
8281       flags=ParseGeometry(amount,&geometry_info);
8282       spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8283       if (spread_image != (Image *) NULL)
8284         {
8285           *image=DestroyImage(*image);
8286           *image=spread_image;
8287         }
8288       CatchException(&(*image)->exception);
8289       XSetCursorState(display,windows,MagickFalse);
8290       if (windows->image.orphan != MagickFalse)
8291         break;
8292       XConfigureImageColormap(display,resource_info,windows,*image);
8293       (void) XConfigureImage(display,resource_info,windows,*image);
8294       break;
8295     }
8296     case ShadeCommand:
8297     {
8298       Image
8299         *shade_image;
8300
8301       int
8302         status;
8303
8304       static char
8305         geometry[MaxTextExtent] = "30x30";
8306
8307       /*
8308         Query user for the shade geometry.
8309       */
8310       status=XDialogWidget(display,windows,"Shade",
8311         "Enter the azimuth and elevation of the light source:",geometry);
8312       if (*geometry == '\0')
8313         break;
8314       /*
8315         Shade image pixels.
8316       */
8317       XSetCursorState(display,windows,MagickTrue);
8318       XCheckRefreshWindows(display,windows);
8319       flags=ParseGeometry(geometry,&geometry_info);
8320       if ((flags & SigmaValue) == 0)
8321         geometry_info.sigma=1.0;
8322       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8323         geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8324       if (shade_image != (Image *) NULL)
8325         {
8326           *image=DestroyImage(*image);
8327           *image=shade_image;
8328         }
8329       CatchException(&(*image)->exception);
8330       XSetCursorState(display,windows,MagickFalse);
8331       if (windows->image.orphan != MagickFalse)
8332         break;
8333       XConfigureImageColormap(display,resource_info,windows,*image);
8334       (void) XConfigureImage(display,resource_info,windows,*image);
8335       break;
8336     }
8337     case RaiseCommand:
8338     {
8339       static char
8340         bevel_width[MaxTextExtent] = "10";
8341
8342       /*
8343         Query user for bevel width.
8344       */
8345       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8346       if (*bevel_width == '\0')
8347         break;
8348       /*
8349         Raise an image.
8350       */
8351       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8352       XSetCursorState(display,windows,MagickTrue);
8353       XCheckRefreshWindows(display,windows);
8354       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8355         &(*image)->exception);
8356       (void) RaiseImage(*image,&page_geometry,MagickTrue);
8357       XSetCursorState(display,windows,MagickFalse);
8358       if (windows->image.orphan != MagickFalse)
8359         break;
8360       XConfigureImageColormap(display,resource_info,windows,*image);
8361       (void) XConfigureImage(display,resource_info,windows,*image);
8362       break;
8363     }
8364     case SegmentCommand:
8365     {
8366       static char
8367         threshold[MaxTextExtent] = "1.0x1.5";
8368
8369       /*
8370         Query user for smoothing threshold.
8371       */
8372       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8373         threshold);
8374       if (*threshold == '\0')
8375         break;
8376       /*
8377         Segment an image.
8378       */
8379       XSetCursorState(display,windows,MagickTrue);
8380       XCheckRefreshWindows(display,windows);
8381       flags=ParseGeometry(threshold,&geometry_info);
8382       if ((flags & SigmaValue) == 0)
8383         geometry_info.sigma=1.0;
8384       (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8385         geometry_info.sigma);
8386       XSetCursorState(display,windows,MagickFalse);
8387       if (windows->image.orphan != MagickFalse)
8388         break;
8389       XConfigureImageColormap(display,resource_info,windows,*image);
8390       (void) XConfigureImage(display,resource_info,windows,*image);
8391       break;
8392     }
8393     case SepiaToneCommand:
8394     {
8395       double
8396         threshold;
8397
8398       Image
8399         *sepia_image;
8400
8401       static char
8402         factor[MaxTextExtent] = "80%";
8403
8404       /*
8405         Query user for sepia-tone factor.
8406       */
8407       (void) XDialogWidget(display,windows,"Sepia Tone",
8408         "Enter the sepia tone factor (0 - 99.9%):",factor);
8409       if (*factor == '\0')
8410         break;
8411       /*
8412         Sepia tone image pixels.
8413       */
8414       XSetCursorState(display,windows,MagickTrue);
8415       XCheckRefreshWindows(display,windows);
8416       threshold=SiPrefixToDouble(factor,QuantumRange);
8417       sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8418       if (sepia_image != (Image *) NULL)
8419         {
8420           *image=DestroyImage(*image);
8421           *image=sepia_image;
8422         }
8423       CatchException(&(*image)->exception);
8424       XSetCursorState(display,windows,MagickFalse);
8425       if (windows->image.orphan != MagickFalse)
8426         break;
8427       XConfigureImageColormap(display,resource_info,windows,*image);
8428       (void) XConfigureImage(display,resource_info,windows,*image);
8429       break;
8430     }
8431     case SolarizeCommand:
8432     {
8433       double
8434         threshold;
8435
8436       static char
8437         factor[MaxTextExtent] = "60%";
8438
8439       /*
8440         Query user for solarize factor.
8441       */
8442       (void) XDialogWidget(display,windows,"Solarize",
8443         "Enter the solarize factor (0 - 99.9%):",factor);
8444       if (*factor == '\0')
8445         break;
8446       /*
8447         Solarize image pixels.
8448       */
8449       XSetCursorState(display,windows,MagickTrue);
8450       XCheckRefreshWindows(display,windows);
8451       threshold=SiPrefixToDouble(factor,QuantumRange);
8452       (void) SolarizeImage(*image,threshold);
8453       XSetCursorState(display,windows,MagickFalse);
8454       if (windows->image.orphan != MagickFalse)
8455         break;
8456       XConfigureImageColormap(display,resource_info,windows,*image);
8457       (void) XConfigureImage(display,resource_info,windows,*image);
8458       break;
8459     }
8460     case SwirlCommand:
8461     {
8462       Image
8463         *swirl_image;
8464
8465       static char
8466         degrees[MaxTextExtent] = "60";
8467
8468       /*
8469         Query user for swirl angle.
8470       */
8471       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8472         degrees);
8473       if (*degrees == '\0')
8474         break;
8475       /*
8476         Swirl image pixels about the center.
8477       */
8478       XSetCursorState(display,windows,MagickTrue);
8479       XCheckRefreshWindows(display,windows);
8480       flags=ParseGeometry(degrees,&geometry_info);
8481       swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8482       if (swirl_image != (Image *) NULL)
8483         {
8484           *image=DestroyImage(*image);
8485           *image=swirl_image;
8486         }
8487       CatchException(&(*image)->exception);
8488       XSetCursorState(display,windows,MagickFalse);
8489       if (windows->image.orphan != MagickFalse)
8490         break;
8491       XConfigureImageColormap(display,resource_info,windows,*image);
8492       (void) XConfigureImage(display,resource_info,windows,*image);
8493       break;
8494     }
8495     case ImplodeCommand:
8496     {
8497       Image
8498         *implode_image;
8499
8500       static char
8501         factor[MaxTextExtent] = "0.3";
8502
8503       /*
8504         Query user for implode factor.
8505       */
8506       (void) XDialogWidget(display,windows,"Implode",
8507         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8508       if (*factor == '\0')
8509         break;
8510       /*
8511         Implode image pixels about the center.
8512       */
8513       XSetCursorState(display,windows,MagickTrue);
8514       XCheckRefreshWindows(display,windows);
8515       flags=ParseGeometry(factor,&geometry_info);
8516       implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8517       if (implode_image != (Image *) NULL)
8518         {
8519           *image=DestroyImage(*image);
8520           *image=implode_image;
8521         }
8522       CatchException(&(*image)->exception);
8523       XSetCursorState(display,windows,MagickFalse);
8524       if (windows->image.orphan != MagickFalse)
8525         break;
8526       XConfigureImageColormap(display,resource_info,windows,*image);
8527       (void) XConfigureImage(display,resource_info,windows,*image);
8528       break;
8529     }
8530     case VignetteCommand:
8531     {
8532       Image
8533         *vignette_image;
8534
8535       static char
8536         geometry[MaxTextExtent] = "0x20";
8537
8538       /*
8539         Query user for the vignette geometry.
8540       */
8541       (void) XDialogWidget(display,windows,"Vignette",
8542         "Enter the radius, sigma, and x and y offsets:",geometry);
8543       if (*geometry == '\0')
8544         break;
8545       /*
8546         Soften the edges of the image in vignette style
8547       */
8548       XSetCursorState(display,windows,MagickTrue);
8549       XCheckRefreshWindows(display,windows);
8550       flags=ParseGeometry(geometry,&geometry_info);
8551       if ((flags & SigmaValue) == 0)
8552         geometry_info.sigma=1.0;
8553       if ((flags & XiValue) == 0)
8554         geometry_info.xi=0.1*(*image)->columns;
8555       if ((flags & PsiValue) == 0)
8556         geometry_info.psi=0.1*(*image)->rows;
8557       vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8558         (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8559         0.5),&(*image)->exception);
8560       if (vignette_image != (Image *) NULL)
8561         {
8562           *image=DestroyImage(*image);
8563           *image=vignette_image;
8564         }
8565       CatchException(&(*image)->exception);
8566       XSetCursorState(display,windows,MagickFalse);
8567       if (windows->image.orphan != MagickFalse)
8568         break;
8569       XConfigureImageColormap(display,resource_info,windows,*image);
8570       (void) XConfigureImage(display,resource_info,windows,*image);
8571       break;
8572     }
8573     case WaveCommand:
8574     {
8575       Image
8576         *wave_image;
8577
8578       static char
8579         geometry[MaxTextExtent] = "25x150";
8580
8581       /*
8582         Query user for the wave geometry.
8583       */
8584       (void) XDialogWidget(display,windows,"Wave",
8585         "Enter the amplitude and length of the wave:",geometry);
8586       if (*geometry == '\0')
8587         break;
8588       /*
8589         Alter an image along a sine wave.
8590       */
8591       XSetCursorState(display,windows,MagickTrue);
8592       XCheckRefreshWindows(display,windows);
8593       flags=ParseGeometry(geometry,&geometry_info);
8594       if ((flags & SigmaValue) == 0)
8595         geometry_info.sigma=1.0;
8596       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8597         &(*image)->exception);
8598       if (wave_image != (Image *) NULL)
8599         {
8600           *image=DestroyImage(*image);
8601           *image=wave_image;
8602         }
8603       CatchException(&(*image)->exception);
8604       XSetCursorState(display,windows,MagickFalse);
8605       if (windows->image.orphan != MagickFalse)
8606         break;
8607       XConfigureImageColormap(display,resource_info,windows,*image);
8608       (void) XConfigureImage(display,resource_info,windows,*image);
8609       break;
8610     }
8611     case OilPaintCommand:
8612     {
8613       Image
8614         *paint_image;
8615
8616       static char
8617         radius[MaxTextExtent] = "0";
8618
8619       /*
8620         Query user for circular neighborhood radius.
8621       */
8622       (void) XDialogWidget(display,windows,"Oil Paint",
8623         "Enter the mask radius:",radius);
8624       if (*radius == '\0')
8625         break;
8626       /*
8627         OilPaint image scanlines.
8628       */
8629       XSetCursorState(display,windows,MagickTrue);
8630       XCheckRefreshWindows(display,windows);
8631       flags=ParseGeometry(radius,&geometry_info);
8632       paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8633       if (paint_image != (Image *) NULL)
8634         {
8635           *image=DestroyImage(*image);
8636           *image=paint_image;
8637         }
8638       CatchException(&(*image)->exception);
8639       XSetCursorState(display,windows,MagickFalse);
8640       if (windows->image.orphan != MagickFalse)
8641         break;
8642       XConfigureImageColormap(display,resource_info,windows,*image);
8643       (void) XConfigureImage(display,resource_info,windows,*image);
8644       break;
8645     }
8646     case CharcoalDrawCommand:
8647     {
8648       Image
8649         *charcoal_image;
8650
8651       static char
8652         radius[MaxTextExtent] = "0x1";
8653
8654       /*
8655         Query user for charcoal radius.
8656       */
8657       (void) XDialogWidget(display,windows,"Charcoal Draw",
8658         "Enter the charcoal radius and sigma:",radius);
8659       if (*radius == '\0')
8660         break;
8661       /*
8662         Charcoal the image.
8663       */
8664       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8665       XSetCursorState(display,windows,MagickTrue);
8666       XCheckRefreshWindows(display,windows);
8667       flags=ParseGeometry(radius,&geometry_info);
8668       if ((flags & SigmaValue) == 0)
8669         geometry_info.sigma=geometry_info.rho;
8670       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8671         &(*image)->exception);
8672       if (charcoal_image != (Image *) NULL)
8673         {
8674           *image=DestroyImage(*image);
8675           *image=charcoal_image;
8676         }
8677       CatchException(&(*image)->exception);
8678       XSetCursorState(display,windows,MagickFalse);
8679       if (windows->image.orphan != MagickFalse)
8680         break;
8681       XConfigureImageColormap(display,resource_info,windows,*image);
8682       (void) XConfigureImage(display,resource_info,windows,*image);
8683       break;
8684     }
8685     case AnnotateCommand:
8686     {
8687       /*
8688         Annotate the image with text.
8689       */
8690       status=XAnnotateEditImage(display,resource_info,windows,*image);
8691       if (status == MagickFalse)
8692         {
8693           XNoticeWidget(display,windows,"Unable to annotate X image",
8694             (*image)->filename);
8695           break;
8696         }
8697       break;
8698     }
8699     case DrawCommand:
8700     {
8701       /*
8702         Draw image.
8703       */
8704       status=XDrawEditImage(display,resource_info,windows,image);
8705       if (status == MagickFalse)
8706         {
8707           XNoticeWidget(display,windows,"Unable to draw on the X image",
8708             (*image)->filename);
8709           break;
8710         }
8711       break;
8712     }
8713     case ColorCommand:
8714     {
8715       /*
8716         Color edit.
8717       */
8718       status=XColorEditImage(display,resource_info,windows,image);
8719       if (status == MagickFalse)
8720         {
8721           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8722             (*image)->filename);
8723           break;
8724         }
8725       break;
8726     }
8727     case MatteCommand:
8728     {
8729       /*
8730         Matte edit.
8731       */
8732       status=XMatteEditImage(display,resource_info,windows,image);
8733       if (status == MagickFalse)
8734         {
8735           XNoticeWidget(display,windows,"Unable to matte edit X image",
8736             (*image)->filename);
8737           break;
8738         }
8739       break;
8740     }
8741     case CompositeCommand:
8742     {
8743       /*
8744         Composite image.
8745       */
8746       status=XCompositeImage(display,resource_info,windows,*image);
8747       if (status == MagickFalse)
8748         {
8749           XNoticeWidget(display,windows,"Unable to composite X image",
8750             (*image)->filename);
8751           break;
8752         }
8753       break;
8754     }
8755     case AddBorderCommand:
8756     {
8757       Image
8758         *border_image;
8759
8760       static char
8761         geometry[MaxTextExtent] = "6x6";
8762
8763       /*
8764         Query user for border color and geometry.
8765       */
8766       XColorBrowserWidget(display,windows,"Select",color);
8767       if (*color == '\0')
8768         break;
8769       (void) XDialogWidget(display,windows,"Add Border",
8770         "Enter border geometry:",geometry);
8771       if (*geometry == '\0')
8772         break;
8773       /*
8774         Add a border to the image.
8775       */
8776       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8777       XSetCursorState(display,windows,MagickTrue);
8778       XCheckRefreshWindows(display,windows);
8779       (void) QueryColorDatabase(color,&(*image)->border_color,
8780         &(*image)->exception);
8781       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8782         &(*image)->exception);
8783       border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8784       if (border_image != (Image *) NULL)
8785         {
8786           *image=DestroyImage(*image);
8787           *image=border_image;
8788         }
8789       CatchException(&(*image)->exception);
8790       XSetCursorState(display,windows,MagickFalse);
8791       if (windows->image.orphan != MagickFalse)
8792         break;
8793       windows->image.window_changes.width=(int) (*image)->columns;
8794       windows->image.window_changes.height=(int) (*image)->rows;
8795       XConfigureImageColormap(display,resource_info,windows,*image);
8796       (void) XConfigureImage(display,resource_info,windows,*image);
8797       break;
8798     }
8799     case AddFrameCommand:
8800     {
8801       FrameInfo
8802         frame_info;
8803
8804       Image
8805         *frame_image;
8806
8807       static char
8808         geometry[MaxTextExtent] = "6x6";
8809
8810       /*
8811         Query user for frame color and geometry.
8812       */
8813       XColorBrowserWidget(display,windows,"Select",color);
8814       if (*color == '\0')
8815         break;
8816       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8817         geometry);
8818       if (*geometry == '\0')
8819         break;
8820       /*
8821         Surround image with an ornamental border.
8822       */
8823       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8824       XSetCursorState(display,windows,MagickTrue);
8825       XCheckRefreshWindows(display,windows);
8826       (void) QueryColorDatabase(color,&(*image)->matte_color,
8827         &(*image)->exception);
8828       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8829         &(*image)->exception);
8830       frame_info.width=page_geometry.width;
8831       frame_info.height=page_geometry.height;
8832       frame_info.outer_bevel=page_geometry.x;
8833       frame_info.inner_bevel=page_geometry.y;
8834       frame_info.x=(ssize_t) frame_info.width;
8835       frame_info.y=(ssize_t) frame_info.height;
8836       frame_info.width=(*image)->columns+2*frame_info.width;
8837       frame_info.height=(*image)->rows+2*frame_info.height;
8838       frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8839       if (frame_image != (Image *) NULL)
8840         {
8841           *image=DestroyImage(*image);
8842           *image=frame_image;
8843         }
8844       CatchException(&(*image)->exception);
8845       XSetCursorState(display,windows,MagickFalse);
8846       if (windows->image.orphan != MagickFalse)
8847         break;
8848       windows->image.window_changes.width=(int) (*image)->columns;
8849       windows->image.window_changes.height=(int) (*image)->rows;
8850       XConfigureImageColormap(display,resource_info,windows,*image);
8851       (void) XConfigureImage(display,resource_info,windows,*image);
8852       break;
8853     }
8854     case CommentCommand:
8855     {
8856       const char
8857         *value;
8858
8859       FILE
8860         *file;
8861
8862       int
8863         unique_file;
8864
8865       /*
8866         Edit image comment.
8867       */
8868       unique_file=AcquireUniqueFileResource(image_info->filename);
8869       if (unique_file == -1)
8870         XNoticeWidget(display,windows,"Unable to edit image comment",
8871           image_info->filename);
8872       value=GetImageProperty(*image,"comment");
8873       if (value == (char *) NULL)
8874         unique_file=close(unique_file)-1;
8875       else
8876         {
8877           register const char
8878             *p;
8879
8880           file=fdopen(unique_file,"w");
8881           if (file == (FILE *) NULL)
8882             {
8883               XNoticeWidget(display,windows,"Unable to edit image comment",
8884                 image_info->filename);
8885               break;
8886             }
8887           for (p=value; *p != '\0'; p++)
8888             (void) fputc((int) *p,file);
8889           (void) fputc('\n',file);
8890           (void) fclose(file);
8891         }
8892       XSetCursorState(display,windows,MagickTrue);
8893       XCheckRefreshWindows(display,windows);
8894       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8895         &(*image)->exception);
8896       if (status == MagickFalse)
8897         XNoticeWidget(display,windows,"Unable to edit image comment",
8898           (char *) NULL);
8899       else
8900         {
8901           char
8902             *comment;
8903
8904           comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8905           if (comment != (char *) NULL)
8906             {
8907               (void) SetImageProperty(*image,"comment",comment);
8908               (*image)->taint=MagickTrue;
8909             }
8910         }
8911       (void) RelinquishUniqueFileResource(image_info->filename);
8912       XSetCursorState(display,windows,MagickFalse);
8913       break;
8914     }
8915     case LaunchCommand:
8916     {
8917       /*
8918         Launch program.
8919       */
8920       XSetCursorState(display,windows,MagickTrue);
8921       XCheckRefreshWindows(display,windows);
8922       (void) AcquireUniqueFilename(filename);
8923       (void) FormatMagickString((*image)->filename,MaxTextExtent,"launch:%s",
8924         filename);
8925       status=WriteImage(image_info,*image);
8926       if (status == MagickFalse)
8927         XNoticeWidget(display,windows,"Unable to launch image editor",
8928           (char *) NULL);
8929       else
8930         {
8931           nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8932           CatchException(&(*image)->exception);
8933           XClientMessage(display,windows->image.id,windows->im_protocols,
8934             windows->im_next_image,CurrentTime);
8935         }
8936       (void) RelinquishUniqueFileResource(filename);
8937       XSetCursorState(display,windows,MagickFalse);
8938       break;
8939     }
8940     case RegionofInterestCommand:
8941     {
8942       /*
8943         Apply an image processing technique to a region of interest.
8944       */
8945       (void) XROIImage(display,resource_info,windows,image);
8946       break;
8947     }
8948     case InfoCommand:
8949       break;
8950     case ZoomCommand:
8951     {
8952       /*
8953         Zoom image.
8954       */
8955       if (windows->magnify.mapped != MagickFalse)
8956         (void) XRaiseWindow(display,windows->magnify.id);
8957       else
8958         {
8959           /*
8960             Make magnify image.
8961           */
8962           XSetCursorState(display,windows,MagickTrue);
8963           (void) XMapRaised(display,windows->magnify.id);
8964           XSetCursorState(display,windows,MagickFalse);
8965         }
8966       break;
8967     }
8968     case ShowPreviewCommand:
8969     {
8970       char
8971         **previews;
8972
8973       Image
8974         *preview_image;
8975
8976       static char
8977         preview_type[MaxTextExtent] = "Gamma";
8978
8979       /*
8980         Select preview type from menu.
8981       */
8982       previews=GetMagickOptions(MagickPreviewOptions);
8983       if (previews == (char **) NULL)
8984         break;
8985       XListBrowserWidget(display,windows,&windows->widget,
8986         (const char **) previews,"Preview",
8987         "Select an enhancement, effect, or F/X:",preview_type);
8988       previews=DestroyStringList(previews);
8989       if (*preview_type == '\0')
8990         break;
8991       /*
8992         Show image preview.
8993       */
8994       XSetCursorState(display,windows,MagickTrue);
8995       XCheckRefreshWindows(display,windows);
8996       image_info->preview_type=(PreviewType)
8997         ParseMagickOption(MagickPreviewOptions,MagickFalse,preview_type);
8998       image_info->group=(ssize_t) windows->image.id;
8999       (void) DeleteImageProperty(*image,"label");
9000       (void) SetImageProperty(*image,"label","Preview");
9001       (void) AcquireUniqueFilename(filename);
9002       (void) FormatMagickString((*image)->filename,MaxTextExtent,"preview:%s",
9003         filename);
9004       status=WriteImage(image_info,*image);
9005       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9006       preview_image=ReadImage(image_info,&(*image)->exception);
9007       (void) RelinquishUniqueFileResource(filename);
9008       if (preview_image == (Image *) NULL)
9009         break;
9010       (void) FormatMagickString(preview_image->filename,MaxTextExtent,"show:%s",
9011         filename);
9012       status=WriteImage(image_info,preview_image);
9013       preview_image=DestroyImage(preview_image);
9014       if (status == MagickFalse)
9015         XNoticeWidget(display,windows,"Unable to show image preview",
9016           (*image)->filename);
9017       XDelay(display,1500);
9018       XSetCursorState(display,windows,MagickFalse);
9019       break;
9020     }
9021     case ShowHistogramCommand:
9022     {
9023       Image
9024         *histogram_image;
9025
9026       /*
9027         Show image histogram.
9028       */
9029       XSetCursorState(display,windows,MagickTrue);
9030       XCheckRefreshWindows(display,windows);
9031       image_info->group=(ssize_t) windows->image.id;
9032       (void) DeleteImageProperty(*image,"label");
9033       (void) SetImageProperty(*image,"label","Histogram");
9034       (void) AcquireUniqueFilename(filename);
9035       (void) FormatMagickString((*image)->filename,MaxTextExtent,"histogram:%s",
9036         filename);
9037       status=WriteImage(image_info,*image);
9038       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9039       histogram_image=ReadImage(image_info,&(*image)->exception);
9040       (void) RelinquishUniqueFileResource(filename);
9041       if (histogram_image == (Image *) NULL)
9042         break;
9043       (void) FormatMagickString(histogram_image->filename,MaxTextExtent,
9044         "show:%s",filename);
9045       status=WriteImage(image_info,histogram_image);
9046       histogram_image=DestroyImage(histogram_image);
9047       if (status == MagickFalse)
9048         XNoticeWidget(display,windows,"Unable to show histogram",
9049           (*image)->filename);
9050       XDelay(display,1500);
9051       XSetCursorState(display,windows,MagickFalse);
9052       break;
9053     }
9054     case ShowMatteCommand:
9055     {
9056       Image
9057         *matte_image;
9058
9059       if ((*image)->matte == MagickFalse)
9060         {
9061           XNoticeWidget(display,windows,
9062             "Image does not have any matte information",(*image)->filename);
9063           break;
9064         }
9065       /*
9066         Show image matte.
9067       */
9068       XSetCursorState(display,windows,MagickTrue);
9069       XCheckRefreshWindows(display,windows);
9070       image_info->group=(ssize_t) windows->image.id;
9071       (void) DeleteImageProperty(*image,"label");
9072       (void) SetImageProperty(*image,"label","Matte");
9073       (void) AcquireUniqueFilename(filename);
9074       (void) FormatMagickString((*image)->filename,MaxTextExtent,"matte:%s",
9075         filename);
9076       status=WriteImage(image_info,*image);
9077       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9078       matte_image=ReadImage(image_info,&(*image)->exception);
9079       (void) RelinquishUniqueFileResource(filename);
9080       if (matte_image == (Image *) NULL)
9081         break;
9082       (void) FormatMagickString(matte_image->filename,MaxTextExtent,"show:%s",
9083         filename);
9084       status=WriteImage(image_info,matte_image);
9085       matte_image=DestroyImage(matte_image);
9086       if (status == MagickFalse)
9087         XNoticeWidget(display,windows,"Unable to show matte",
9088           (*image)->filename);
9089       XDelay(display,1500);
9090       XSetCursorState(display,windows,MagickFalse);
9091       break;
9092     }
9093     case BackgroundCommand:
9094     {
9095       /*
9096         Background image.
9097       */
9098       status=XBackgroundImage(display,resource_info,windows,image);
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 SlideShowCommand:
9108     {
9109       static char
9110         delay[MaxTextExtent] = "5";
9111
9112       /*
9113         Display next image after pausing.
9114       */
9115       (void) XDialogWidget(display,windows,"Slide Show",
9116         "Pause how many 1/100ths of a second between images:",delay);
9117       if (*delay == '\0')
9118         break;
9119       resource_info->delay=StringToUnsignedLong(delay);
9120       XClientMessage(display,windows->image.id,windows->im_protocols,
9121         windows->im_next_image,CurrentTime);
9122       break;
9123     }
9124     case PreferencesCommand:
9125     {
9126       /*
9127         Set user preferences.
9128       */
9129       status=XPreferencesWidget(display,resource_info,windows);
9130       if (status == MagickFalse)
9131         break;
9132       nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9133       if (nexus != (Image *) NULL)
9134         XClientMessage(display,windows->image.id,windows->im_protocols,
9135           windows->im_next_image,CurrentTime);
9136       break;
9137     }
9138     case HelpCommand:
9139     {
9140       /*
9141         User requested help.
9142       */
9143       XTextViewWidget(display,resource_info,windows,MagickFalse,
9144         "Help Viewer - Display",DisplayHelp);
9145       break;
9146     }
9147     case BrowseDocumentationCommand:
9148     {
9149       Atom
9150         mozilla_atom;
9151
9152       Window
9153         mozilla_window,
9154         root_window;
9155
9156       /*
9157         Browse the ImageMagick documentation.
9158       */
9159       root_window=XRootWindow(display,XDefaultScreen(display));
9160       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9161       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9162       if (mozilla_window != (Window) NULL)
9163         {
9164           char
9165             command[MaxTextExtent],
9166             *url;
9167
9168           /*
9169             Display documentation using Netscape remote control.
9170           */
9171           url=GetMagickHomeURL();
9172           (void) FormatMagickString(command,MaxTextExtent,
9173             "openurl(%s,new-tab)",url);
9174           url=DestroyString(url);
9175           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9176           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9177             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9178           XSetCursorState(display,windows,MagickFalse);
9179           break;
9180         }
9181       XSetCursorState(display,windows,MagickTrue);
9182       XCheckRefreshWindows(display,windows);
9183       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9184         &(*image)->exception);
9185       if (status == MagickFalse)
9186         XNoticeWidget(display,windows,"Unable to browse documentation",
9187           (char *) NULL);
9188       XDelay(display,1500);
9189       XSetCursorState(display,windows,MagickFalse);
9190       break;
9191     }
9192     case VersionCommand:
9193     {
9194       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9195         GetMagickCopyright());
9196       break;
9197     }
9198     case SaveToUndoBufferCommand:
9199       break;
9200     default:
9201     {
9202       (void) XBell(display,0);
9203       break;
9204     }
9205   }
9206   image_info=DestroyImageInfo(image_info);
9207   return(nexus);
9208 }
9209 \f
9210 /*
9211 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9212 %                                                                             %
9213 %                                                                             %
9214 %                                                                             %
9215 +   X M a g n i f y I m a g e                                                 %
9216 %                                                                             %
9217 %                                                                             %
9218 %                                                                             %
9219 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9220 %
9221 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9222 %  The magnified portion is displayed in a separate window.
9223 %
9224 %  The format of the XMagnifyImage method is:
9225 %
9226 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9227 %
9228 %  A description of each parameter follows:
9229 %
9230 %    o display: Specifies a connection to an X server;  returned from
9231 %      XOpenDisplay.
9232 %
9233 %    o windows: Specifies a pointer to a XWindows structure.
9234 %
9235 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9236 %      the entire image is refreshed.
9237 %
9238 */
9239 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9240 {
9241   char
9242     text[MaxTextExtent];
9243
9244   register int
9245     x,
9246     y;
9247
9248   size_t
9249     state;
9250
9251   /*
9252     Update magnified image until the mouse button is released.
9253   */
9254   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9255   state=DefaultState;
9256   x=event->xbutton.x;
9257   y=event->xbutton.y;
9258   windows->magnify.x=(int) windows->image.x+x;
9259   windows->magnify.y=(int) windows->image.y+y;
9260   do
9261   {
9262     /*
9263       Map and unmap Info widget as text cursor crosses its boundaries.
9264     */
9265     if (windows->info.mapped != MagickFalse)
9266       {
9267         if ((x < (int) (windows->info.x+windows->info.width)) &&
9268             (y < (int) (windows->info.y+windows->info.height)))
9269           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9270       }
9271     else
9272       if ((x > (int) (windows->info.x+windows->info.width)) ||
9273           (y > (int) (windows->info.y+windows->info.height)))
9274         (void) XMapWindow(display,windows->info.id);
9275     if (windows->info.mapped != MagickFalse)
9276       {
9277         /*
9278           Display pointer position.
9279         */
9280         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
9281           windows->magnify.x,windows->magnify.y);
9282         XInfoWidget(display,windows,text);
9283       }
9284     /*
9285       Wait for next event.
9286     */
9287     XScreenEvent(display,windows,event);
9288     switch (event->type)
9289     {
9290       case ButtonPress:
9291         break;
9292       case ButtonRelease:
9293       {
9294         /*
9295           User has finished magnifying image.
9296         */
9297         x=event->xbutton.x;
9298         y=event->xbutton.y;
9299         state|=ExitState;
9300         break;
9301       }
9302       case Expose:
9303         break;
9304       case MotionNotify:
9305       {
9306         x=event->xmotion.x;
9307         y=event->xmotion.y;
9308         break;
9309       }
9310       default:
9311         break;
9312     }
9313     /*
9314       Check boundary conditions.
9315     */
9316     if (x < 0)
9317       x=0;
9318     else
9319       if (x >= (int) windows->image.width)
9320         x=(int) windows->image.width-1;
9321     if (y < 0)
9322       y=0;
9323     else
9324      if (y >= (int) windows->image.height)
9325        y=(int) windows->image.height-1;
9326   } while ((state & ExitState) == 0);
9327   /*
9328     Display magnified image.
9329   */
9330   XSetCursorState(display,windows,MagickFalse);
9331 }
9332 \f
9333 /*
9334 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9335 %                                                                             %
9336 %                                                                             %
9337 %                                                                             %
9338 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9339 %                                                                             %
9340 %                                                                             %
9341 %                                                                             %
9342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9343 %
9344 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9345 %  pixel as specified by the key symbol.
9346 %
9347 %  The format of the XMagnifyWindowCommand method is:
9348 %
9349 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9350 %        const MagickStatusType state,const KeySym key_symbol)
9351 %
9352 %  A description of each parameter follows:
9353 %
9354 %    o display: Specifies a connection to an X server; returned from
9355 %      XOpenDisplay.
9356 %
9357 %    o windows: Specifies a pointer to a XWindows structure.
9358 %
9359 %    o state: key mask.
9360 %
9361 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9362 %      to trim.
9363 %
9364 */
9365 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9366   const MagickStatusType state,const KeySym key_symbol)
9367 {
9368   unsigned int
9369     quantum;
9370
9371   /*
9372     User specified a magnify factor or position.
9373   */
9374   quantum=1;
9375   if ((state & Mod1Mask) != 0)
9376     quantum=10;
9377   switch ((int) key_symbol)
9378   {
9379     case QuitCommand:
9380     {
9381       (void) XWithdrawWindow(display,windows->magnify.id,
9382         windows->magnify.screen);
9383       break;
9384     }
9385     case XK_Home:
9386     case XK_KP_Home:
9387     {
9388       windows->magnify.x=(int) windows->image.width/2;
9389       windows->magnify.y=(int) windows->image.height/2;
9390       break;
9391     }
9392     case XK_Left:
9393     case XK_KP_Left:
9394     {
9395       if (windows->magnify.x > 0)
9396         windows->magnify.x-=quantum;
9397       break;
9398     }
9399     case XK_Up:
9400     case XK_KP_Up:
9401     {
9402       if (windows->magnify.y > 0)
9403         windows->magnify.y-=quantum;
9404       break;
9405     }
9406     case XK_Right:
9407     case XK_KP_Right:
9408     {
9409       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9410         windows->magnify.x+=quantum;
9411       break;
9412     }
9413     case XK_Down:
9414     case XK_KP_Down:
9415     {
9416       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9417         windows->magnify.y+=quantum;
9418       break;
9419     }
9420     case XK_0:
9421     case XK_1:
9422     case XK_2:
9423     case XK_3:
9424     case XK_4:
9425     case XK_5:
9426     case XK_6:
9427     case XK_7:
9428     case XK_8:
9429     case XK_9:
9430     {
9431       windows->magnify.data=(key_symbol-XK_0);
9432       break;
9433     }
9434     case XK_KP_0:
9435     case XK_KP_1:
9436     case XK_KP_2:
9437     case XK_KP_3:
9438     case XK_KP_4:
9439     case XK_KP_5:
9440     case XK_KP_6:
9441     case XK_KP_7:
9442     case XK_KP_8:
9443     case XK_KP_9:
9444     {
9445       windows->magnify.data=(key_symbol-XK_KP_0);
9446       break;
9447     }
9448     default:
9449       break;
9450   }
9451   XMakeMagnifyImage(display,windows);
9452 }
9453 \f
9454 /*
9455 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9456 %                                                                             %
9457 %                                                                             %
9458 %                                                                             %
9459 +   X M a k e P a n I m a g e                                                 %
9460 %                                                                             %
9461 %                                                                             %
9462 %                                                                             %
9463 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9464 %
9465 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9466 %  icon window.
9467 %
9468 %  The format of the XMakePanImage method is:
9469 %
9470 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9471 %          XWindows *windows,Image *image)
9472 %
9473 %  A description of each parameter follows:
9474 %
9475 %    o display: Specifies a connection to an X server;  returned from
9476 %      XOpenDisplay.
9477 %
9478 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9479 %
9480 %    o windows: Specifies a pointer to a XWindows structure.
9481 %
9482 %    o image: the image.
9483 %
9484 */
9485 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9486   XWindows *windows,Image *image)
9487 {
9488   MagickStatusType
9489     status;
9490
9491   /*
9492     Create and display image for panning icon.
9493   */
9494   XSetCursorState(display,windows,MagickTrue);
9495   XCheckRefreshWindows(display,windows);
9496   windows->pan.x=(int) windows->image.x;
9497   windows->pan.y=(int) windows->image.y;
9498   status=XMakeImage(display,resource_info,&windows->pan,image,
9499     windows->pan.width,windows->pan.height);
9500   if (status == MagickFalse)
9501     ThrowXWindowFatalException(XServerError,image->exception.reason,
9502       image->exception.description);
9503   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9504     windows->pan.pixmap);
9505   (void) XClearWindow(display,windows->pan.id);
9506   XDrawPanRectangle(display,windows);
9507   XSetCursorState(display,windows,MagickFalse);
9508 }
9509 \f
9510 /*
9511 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9512 %                                                                             %
9513 %                                                                             %
9514 %                                                                             %
9515 +   X M a t t a E d i t I m a g e                                             %
9516 %                                                                             %
9517 %                                                                             %
9518 %                                                                             %
9519 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9520 %
9521 %  XMatteEditImage() allows the user to interactively change the Matte channel
9522 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9523 %  before the matte information is stored.
9524 %
9525 %  The format of the XMatteEditImage method is:
9526 %
9527 %      MagickBooleanType XMatteEditImage(Display *display,
9528 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
9529 %
9530 %  A description of each parameter follows:
9531 %
9532 %    o display: Specifies a connection to an X server;  returned from
9533 %      XOpenDisplay.
9534 %
9535 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9536 %
9537 %    o windows: Specifies a pointer to a XWindows structure.
9538 %
9539 %    o image: the image; returned from ReadImage.
9540 %
9541 */
9542 static MagickBooleanType XMatteEditImage(Display *display,
9543   XResourceInfo *resource_info,XWindows *windows,Image **image)
9544 {
9545   static char
9546     matte[MaxTextExtent] = "0";
9547
9548   static const char
9549     *MatteEditMenu[] =
9550     {
9551       "Method",
9552       "Border Color",
9553       "Fuzz",
9554       "Matte Value",
9555       "Undo",
9556       "Help",
9557       "Dismiss",
9558       (char *) NULL
9559     };
9560
9561   static const ModeType
9562     MatteEditCommands[] =
9563     {
9564       MatteEditMethod,
9565       MatteEditBorderCommand,
9566       MatteEditFuzzCommand,
9567       MatteEditValueCommand,
9568       MatteEditUndoCommand,
9569       MatteEditHelpCommand,
9570       MatteEditDismissCommand
9571     };
9572
9573   static PaintMethod
9574     method = PointMethod;
9575
9576   static XColor
9577     border_color = { 0, 0, 0, 0, 0, 0 };
9578
9579   char
9580     command[MaxTextExtent],
9581     text[MaxTextExtent];
9582
9583   Cursor
9584     cursor;
9585
9586   int
9587     entry,
9588     id,
9589     x,
9590     x_offset,
9591     y,
9592     y_offset;
9593
9594   register int
9595     i;
9596
9597   register PixelPacket
9598     *q;
9599
9600   unsigned int
9601     height,
9602     width;
9603
9604   size_t
9605     state;
9606
9607   XEvent
9608     event;
9609
9610   /*
9611     Map Command widget.
9612   */
9613   (void) CloneString(&windows->command.name,"Matte Edit");
9614   windows->command.data=4;
9615   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9616   (void) XMapRaised(display,windows->command.id);
9617   XClientMessage(display,windows->image.id,windows->im_protocols,
9618     windows->im_update_widget,CurrentTime);
9619   /*
9620     Make cursor.
9621   */
9622   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9623     resource_info->background_color,resource_info->foreground_color);
9624   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9625   /*
9626     Track pointer until button 1 is pressed.
9627   */
9628   XQueryPosition(display,windows->image.id,&x,&y);
9629   (void) XSelectInput(display,windows->image.id,
9630     windows->image.attributes.event_mask | PointerMotionMask);
9631   state=DefaultState;
9632   do
9633   {
9634     if (windows->info.mapped != MagickFalse)
9635       {
9636         /*
9637           Display pointer position.
9638         */
9639         (void) FormatMagickString(text,MaxTextExtent," %+d%+d ",
9640           x+windows->image.x,y+windows->image.y);
9641         XInfoWidget(display,windows,text);
9642       }
9643     /*
9644       Wait for next event.
9645     */
9646     XScreenEvent(display,windows,&event);
9647     if (event.xany.window == windows->command.id)
9648       {
9649         /*
9650           Select a command from the Command widget.
9651         */
9652         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9653         if (id < 0)
9654           {
9655             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9656             continue;
9657           }
9658         switch (MatteEditCommands[id])
9659         {
9660           case MatteEditMethod:
9661           {
9662             char
9663               **methods;
9664
9665             /*
9666               Select a method from the pop-up menu.
9667             */
9668             methods=GetMagickOptions(MagickMethodOptions);
9669             if (methods == (char **) NULL)
9670               break;
9671             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9672               (const char **) methods,command);
9673             if (entry >= 0)
9674               method=(PaintMethod) ParseMagickOption(MagickMethodOptions,
9675                 MagickFalse,methods[entry]);
9676             methods=DestroyStringList(methods);
9677             break;
9678           }
9679           case MatteEditBorderCommand:
9680           {
9681             const char
9682               *ColorMenu[MaxNumberPens];
9683
9684             int
9685               pen_number;
9686
9687             /*
9688               Initialize menu selections.
9689             */
9690             for (i=0; i < (int) (MaxNumberPens-2); i++)
9691               ColorMenu[i]=resource_info->pen_colors[i];
9692             ColorMenu[MaxNumberPens-2]="Browser...";
9693             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9694             /*
9695               Select a pen color from the pop-up menu.
9696             */
9697             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9698               (const char **) ColorMenu,command);
9699             if (pen_number < 0)
9700               break;
9701             if (pen_number == (MaxNumberPens-2))
9702               {
9703                 static char
9704                   color_name[MaxTextExtent] = "gray";
9705
9706                 /*
9707                   Select a pen color from a dialog.
9708                 */
9709                 resource_info->pen_colors[pen_number]=color_name;
9710                 XColorBrowserWidget(display,windows,"Select",color_name);
9711                 if (*color_name == '\0')
9712                   break;
9713               }
9714             /*
9715               Set border color.
9716             */
9717             (void) XParseColor(display,windows->map_info->colormap,
9718               resource_info->pen_colors[pen_number],&border_color);
9719             break;
9720           }
9721           case MatteEditFuzzCommand:
9722           {
9723             static char
9724               fuzz[MaxTextExtent];
9725
9726             static const char
9727               *FuzzMenu[] =
9728               {
9729                 "0%",
9730                 "2%",
9731                 "5%",
9732                 "10%",
9733                 "15%",
9734                 "Dialog...",
9735                 (char *) NULL,
9736               };
9737
9738             /*
9739               Select a command from the pop-up menu.
9740             */
9741             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9742               command);
9743             if (entry < 0)
9744               break;
9745             if (entry != 5)
9746               {
9747                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*QuantumRange+
9748                   1.0);
9749                 break;
9750               }
9751             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9752             (void) XDialogWidget(display,windows,"Ok",
9753               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9754             if (*fuzz == '\0')
9755               break;
9756             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9757             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9758             break;
9759           }
9760           case MatteEditValueCommand:
9761           {
9762             static char
9763               message[MaxTextExtent];
9764
9765             static const char
9766               *MatteMenu[] =
9767               {
9768                 "Opaque",
9769                 "Transparent",
9770                 "Dialog...",
9771                 (char *) NULL,
9772               };
9773
9774             /*
9775               Select a command from the pop-up menu.
9776             */
9777             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9778               command);
9779             if (entry < 0)
9780               break;
9781             if (entry != 2)
9782               {
9783                 (void) FormatMagickString(matte,MaxTextExtent,QuantumFormat,
9784                   OpaqueOpacity);
9785                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9786                   (void) FormatMagickString(matte,MaxTextExtent,QuantumFormat,
9787                     (Quantum) TransparentOpacity);
9788                 break;
9789               }
9790             (void) FormatMagickString(message,MaxTextExtent,
9791               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9792               QuantumRange);
9793             (void) XDialogWidget(display,windows,"Matte",message,matte);
9794             if (*matte == '\0')
9795               break;
9796             break;
9797           }
9798           case MatteEditUndoCommand:
9799           {
9800             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9801               image);
9802             break;
9803           }
9804           case MatteEditHelpCommand:
9805           {
9806             XTextViewWidget(display,resource_info,windows,MagickFalse,
9807               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9808             break;
9809           }
9810           case MatteEditDismissCommand:
9811           {
9812             /*
9813               Prematurely exit.
9814             */
9815             state|=EscapeState;
9816             state|=ExitState;
9817             break;
9818           }
9819           default:
9820             break;
9821         }
9822         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9823         continue;
9824       }
9825     switch (event.type)
9826     {
9827       case ButtonPress:
9828       {
9829         if (event.xbutton.button != Button1)
9830           break;
9831         if ((event.xbutton.window != windows->image.id) &&
9832             (event.xbutton.window != windows->magnify.id))
9833           break;
9834         /*
9835           Update matte data.
9836         */
9837         x=event.xbutton.x;
9838         y=event.xbutton.y;
9839         (void) XMagickCommand(display,resource_info,windows,
9840           SaveToUndoBufferCommand,image);
9841         state|=UpdateConfigurationState;
9842         break;
9843       }
9844       case ButtonRelease:
9845       {
9846         if (event.xbutton.button != Button1)
9847           break;
9848         if ((event.xbutton.window != windows->image.id) &&
9849             (event.xbutton.window != windows->magnify.id))
9850           break;
9851         /*
9852           Update colormap information.
9853         */
9854         x=event.xbutton.x;
9855         y=event.xbutton.y;
9856         XConfigureImageColormap(display,resource_info,windows,*image);
9857         (void) XConfigureImage(display,resource_info,windows,*image);
9858         XInfoWidget(display,windows,text);
9859         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9860         state&=(~UpdateConfigurationState);
9861         break;
9862       }
9863       case Expose:
9864         break;
9865       case KeyPress:
9866       {
9867         char
9868           command[MaxTextExtent];
9869
9870         KeySym
9871           key_symbol;
9872
9873         if (event.xkey.window == windows->magnify.id)
9874           {
9875             Window
9876               window;
9877
9878             window=windows->magnify.id;
9879             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9880           }
9881         if (event.xkey.window != windows->image.id)
9882           break;
9883         /*
9884           Respond to a user key press.
9885         */
9886         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9887           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9888         switch ((int) key_symbol)
9889         {
9890           case XK_Escape:
9891           case XK_F20:
9892           {
9893             /*
9894               Prematurely exit.
9895             */
9896             state|=ExitState;
9897             break;
9898           }
9899           case XK_F1:
9900           case XK_Help:
9901           {
9902             XTextViewWidget(display,resource_info,windows,MagickFalse,
9903               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9904             break;
9905           }
9906           default:
9907           {
9908             (void) XBell(display,0);
9909             break;
9910           }
9911         }
9912         break;
9913       }
9914       case MotionNotify:
9915       {
9916         /*
9917           Map and unmap Info widget as cursor crosses its boundaries.
9918         */
9919         x=event.xmotion.x;
9920         y=event.xmotion.y;
9921         if (windows->info.mapped != MagickFalse)
9922           {
9923             if ((x < (int) (windows->info.x+windows->info.width)) &&
9924                 (y < (int) (windows->info.y+windows->info.height)))
9925               (void) XWithdrawWindow(display,windows->info.id,
9926                 windows->info.screen);
9927           }
9928         else
9929           if ((x > (int) (windows->info.x+windows->info.width)) ||
9930               (y > (int) (windows->info.y+windows->info.height)))
9931             (void) XMapWindow(display,windows->info.id);
9932         break;
9933       }
9934       default:
9935         break;
9936     }
9937     if (event.xany.window == windows->magnify.id)
9938       {
9939         x=windows->magnify.x-windows->image.x;
9940         y=windows->magnify.y-windows->image.y;
9941       }
9942     x_offset=x;
9943     y_offset=y;
9944     if ((state & UpdateConfigurationState) != 0)
9945       {
9946         CacheView
9947           *image_view;
9948
9949         ExceptionInfo
9950           *exception;
9951
9952         int
9953           x,
9954           y;
9955
9956         /*
9957           Matte edit is relative to image configuration.
9958         */
9959         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9960           MagickTrue);
9961         XPutPixel(windows->image.ximage,x_offset,y_offset,
9962           windows->pixel_info->background_color.pixel);
9963         width=(unsigned int) (*image)->columns;
9964         height=(unsigned int) (*image)->rows;
9965         x=0;
9966         y=0;
9967         if (windows->image.crop_geometry != (char *) NULL)
9968           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
9969             &width,&height);
9970         x_offset=(int)
9971           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
9972         y_offset=(int)
9973           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
9974         if ((x_offset < 0) || (y_offset < 0))
9975           continue;
9976         if ((x_offset >= (int) (*image)->columns) ||
9977             (y_offset >= (int) (*image)->rows))
9978           continue;
9979         if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9980           return(MagickFalse);
9981         (*image)->matte=MagickTrue;
9982         exception=(&(*image)->exception);
9983         image_view=AcquireCacheView(*image);
9984         switch (method)
9985         {
9986           case PointMethod:
9987           default:
9988           {
9989             /*
9990               Update matte information using point algorithm.
9991             */
9992             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
9993               (ssize_t) y_offset,1,1,exception);
9994             if (q == (PixelPacket *) NULL)
9995               break;
9996             q->opacity=(Quantum) StringToLong(matte);
9997             (void) SyncCacheViewAuthenticPixels(image_view,exception);
9998             break;
9999           }
10000           case ReplaceMethod:
10001           {
10002             PixelPacket
10003               target;
10004
10005             /*
10006               Update matte information using replace algorithm.
10007             */
10008             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10009               (ssize_t) y_offset,&target,exception);
10010             for (y=0; y < (int) (*image)->rows; y++)
10011             {
10012               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10013                 (*image)->columns,1,&(*image)->exception);
10014               if (q == (PixelPacket *) NULL)
10015                 break;
10016               for (x=0; x < (int) (*image)->columns; x++)
10017               {
10018                 if (IsColorSimilar(*image,q,&target))
10019                   q->opacity=(Quantum) StringToLong(matte);
10020                 q++;
10021               }
10022               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10023                 break;
10024             }
10025             break;
10026           }
10027           case FloodfillMethod:
10028           case FillToBorderMethod:
10029           {
10030             DrawInfo
10031               *draw_info;
10032
10033             MagickPixelPacket
10034               target;
10035
10036             /*
10037               Update matte information using floodfill algorithm.
10038             */
10039             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10040               (ssize_t) y_offset,&target,exception);
10041             if (method == FillToBorderMethod)
10042               {
10043                 target.red=(MagickRealType)
10044                   ScaleShortToQuantum(border_color.red);
10045                 target.green=(MagickRealType)
10046                   ScaleShortToQuantum(border_color.green);
10047                 target.blue=(MagickRealType)
10048                   ScaleShortToQuantum(border_color.blue);
10049               }
10050             draw_info=CloneDrawInfo(resource_info->image_info,
10051               (DrawInfo *) NULL);
10052             draw_info->fill.opacity=ClampToQuantum(StringToDouble(matte));
10053             (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
10054               (ssize_t) x_offset,(ssize_t) y_offset,
10055               method == FloodfillMethod ? MagickFalse : MagickTrue);
10056             draw_info=DestroyDrawInfo(draw_info);
10057             break;
10058           }
10059           case ResetMethod:
10060           {
10061             /*
10062               Update matte information using reset algorithm.
10063             */
10064             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10065               return(MagickFalse);
10066             for (y=0; y < (int) (*image)->rows; y++)
10067             {
10068               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10069                 (*image)->columns,1,exception);
10070               if (q == (PixelPacket *) NULL)
10071                 break;
10072               for (x=0; x < (int) (*image)->columns; x++)
10073               {
10074                 q->opacity=(Quantum) StringToLong(matte);
10075                 q++;
10076               }
10077               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10078                 break;
10079             }
10080             if (StringToLong(matte) == OpaqueOpacity)
10081               (*image)->matte=MagickFalse;
10082             break;
10083           }
10084         }
10085         image_view=DestroyCacheView(image_view);
10086         state&=(~UpdateConfigurationState);
10087       }
10088   } while ((state & ExitState) == 0);
10089   (void) XSelectInput(display,windows->image.id,
10090     windows->image.attributes.event_mask);
10091   XSetCursorState(display,windows,MagickFalse);
10092   (void) XFreeCursor(display,cursor);
10093   return(MagickTrue);
10094 }
10095 \f
10096 /*
10097 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10098 %                                                                             %
10099 %                                                                             %
10100 %                                                                             %
10101 +   X O p e n I m a g e                                                       %
10102 %                                                                             %
10103 %                                                                             %
10104 %                                                                             %
10105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10106 %
10107 %  XOpenImage() loads an image from a file.
10108 %
10109 %  The format of the XOpenImage method is:
10110 %
10111 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10112 %       XWindows *windows,const unsigned int command)
10113 %
10114 %  A description of each parameter follows:
10115 %
10116 %    o display: Specifies a connection to an X server; returned from
10117 %      XOpenDisplay.
10118 %
10119 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10120 %
10121 %    o windows: Specifies a pointer to a XWindows structure.
10122 %
10123 %    o command: A value other than zero indicates that the file is selected
10124 %      from the command line argument list.
10125 %
10126 */
10127 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10128   XWindows *windows,const MagickBooleanType command)
10129 {
10130   const MagickInfo
10131     *magick_info;
10132
10133   ExceptionInfo
10134     *exception;
10135
10136   Image
10137     *nexus;
10138
10139   ImageInfo
10140     *image_info;
10141
10142   static char
10143     filename[MaxTextExtent] = "\0";
10144
10145   /*
10146     Request file name from user.
10147   */
10148   if (command == MagickFalse)
10149     XFileBrowserWidget(display,windows,"Open",filename);
10150   else
10151     {
10152       char
10153         **filelist,
10154         **files;
10155
10156       int
10157         count,
10158         status;
10159
10160       register int
10161         i,
10162         j;
10163
10164       /*
10165         Select next image from the command line.
10166       */
10167       status=XGetCommand(display,windows->image.id,&files,&count);
10168       if (status == 0)
10169         {
10170           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10171           return((Image *) NULL);
10172         }
10173       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10174       if (filelist == (char **) NULL)
10175         {
10176           ThrowXWindowFatalException(ResourceLimitError,
10177             "MemoryAllocationFailed","...");
10178           (void) XFreeStringList(files);
10179           return((Image *) NULL);
10180         }
10181       j=0;
10182       for (i=1; i < count; i++)
10183         if (*files[i] != '-')
10184           filelist[j++]=files[i];
10185       filelist[j]=(char *) NULL;
10186       XListBrowserWidget(display,windows,&windows->widget,
10187         (const char **) filelist,"Load","Select Image to Load:",filename);
10188       filelist=(char **) RelinquishMagickMemory(filelist);
10189       (void) XFreeStringList(files);
10190     }
10191   if (*filename == '\0')
10192     return((Image *) NULL);
10193   image_info=CloneImageInfo(resource_info->image_info);
10194   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10195     (void *) NULL);
10196   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10197   exception=AcquireExceptionInfo();
10198   (void) SetImageInfo(image_info,0,exception);
10199   if (LocaleCompare(image_info->magick,"X") == 0)
10200     {
10201       char
10202         seconds[MaxTextExtent];
10203
10204       /*
10205         User may want to delay the X server screen grab.
10206       */
10207       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10208       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10209         seconds);
10210       if (*seconds == '\0')
10211         return((Image *) NULL);
10212       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10213     }
10214   magick_info=GetMagickInfo(image_info->magick,exception);
10215   if ((magick_info != (const MagickInfo *) NULL) &&
10216       (magick_info->raw != MagickFalse))
10217     {
10218       char
10219         geometry[MaxTextExtent];
10220
10221       /*
10222         Request image size from the user.
10223       */
10224       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10225       if (image_info->size != (char *) NULL)
10226         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10227       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10228         geometry);
10229       (void) CloneString(&image_info->size,geometry);
10230     }
10231   /*
10232     Load the image.
10233   */
10234   XSetCursorState(display,windows,MagickTrue);
10235   XCheckRefreshWindows(display,windows);
10236   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10237   nexus=ReadImage(image_info,exception);
10238   CatchException(exception);
10239   XSetCursorState(display,windows,MagickFalse);
10240   if (nexus != (Image *) NULL)
10241     XClientMessage(display,windows->image.id,windows->im_protocols,
10242       windows->im_next_image,CurrentTime);
10243   else
10244     {
10245       char
10246         *text,
10247         **textlist;
10248
10249       /*
10250         Unknown image format.
10251       */
10252       text=FileToString(filename,~0,exception);
10253       if (text == (char *) NULL)
10254         return((Image *) NULL);
10255       textlist=StringToList(text);
10256       if (textlist != (char **) NULL)
10257         {
10258           char
10259             title[MaxTextExtent];
10260
10261           register int
10262             i;
10263
10264           (void) FormatMagickString(title,MaxTextExtent,
10265             "Unknown format: %s",filename);
10266           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10267             (const char **) textlist);
10268           for (i=0; textlist[i] != (char *) NULL; i++)
10269             textlist[i]=DestroyString(textlist[i]);
10270           textlist=(char **) RelinquishMagickMemory(textlist);
10271         }
10272       text=DestroyString(text);
10273     }
10274   exception=DestroyExceptionInfo(exception);
10275   image_info=DestroyImageInfo(image_info);
10276   return(nexus);
10277 }
10278 \f
10279 /*
10280 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10281 %                                                                             %
10282 %                                                                             %
10283 %                                                                             %
10284 +   X P a n I m a g e                                                         %
10285 %                                                                             %
10286 %                                                                             %
10287 %                                                                             %
10288 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10289 %
10290 %  XPanImage() pans the image until the mouse button is released.
10291 %
10292 %  The format of the XPanImage method is:
10293 %
10294 %      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10295 %
10296 %  A description of each parameter follows:
10297 %
10298 %    o display: Specifies a connection to an X server;  returned from
10299 %      XOpenDisplay.
10300 %
10301 %    o windows: Specifies a pointer to a XWindows structure.
10302 %
10303 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10304 %      the entire image is refreshed.
10305 %
10306 */
10307 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10308 {
10309   char
10310     text[MaxTextExtent];
10311
10312   Cursor
10313     cursor;
10314
10315   MagickRealType
10316     x_factor,
10317     y_factor;
10318
10319   RectangleInfo
10320     pan_info;
10321
10322   size_t
10323     state;
10324
10325   /*
10326     Define cursor.
10327   */
10328   if ((windows->image.ximage->width > (int) windows->image.width) &&
10329       (windows->image.ximage->height > (int) windows->image.height))
10330     cursor=XCreateFontCursor(display,XC_fleur);
10331   else
10332     if (windows->image.ximage->width > (int) windows->image.width)
10333       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10334     else
10335       if (windows->image.ximage->height > (int) windows->image.height)
10336         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10337       else
10338         cursor=XCreateFontCursor(display,XC_arrow);
10339   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10340   /*
10341     Pan image as pointer moves until the mouse button is released.
10342   */
10343   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10344   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10345   pan_info.width=windows->pan.width*windows->image.width/
10346     windows->image.ximage->width;
10347   pan_info.height=windows->pan.height*windows->image.height/
10348     windows->image.ximage->height;
10349   pan_info.x=0;
10350   pan_info.y=0;
10351   state=UpdateConfigurationState;
10352   do
10353   {
10354     switch (event->type)
10355     {
10356       case ButtonPress:
10357       {
10358         /*
10359           User choose an initial pan location.
10360         */
10361         pan_info.x=(ssize_t) event->xbutton.x;
10362         pan_info.y=(ssize_t) event->xbutton.y;
10363         state|=UpdateConfigurationState;
10364         break;
10365       }
10366       case ButtonRelease:
10367       {
10368         /*
10369           User has finished panning the image.
10370         */
10371         pan_info.x=(ssize_t) event->xbutton.x;
10372         pan_info.y=(ssize_t) event->xbutton.y;
10373         state|=UpdateConfigurationState | ExitState;
10374         break;
10375       }
10376       case MotionNotify:
10377       {
10378         pan_info.x=(ssize_t) event->xmotion.x;
10379         pan_info.y=(ssize_t) event->xmotion.y;
10380         state|=UpdateConfigurationState;
10381       }
10382       default:
10383         break;
10384     }
10385     if ((state & UpdateConfigurationState) != 0)
10386       {
10387         /*
10388           Check boundary conditions.
10389         */
10390         if (pan_info.x < (ssize_t) (pan_info.width/2))
10391           pan_info.x=0;
10392         else
10393           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10394         if (pan_info.x < 0)
10395           pan_info.x=0;
10396         else
10397           if ((int) (pan_info.x+windows->image.width) >
10398               windows->image.ximage->width)
10399             pan_info.x=(ssize_t)
10400               (windows->image.ximage->width-windows->image.width);
10401         if (pan_info.y < (ssize_t) (pan_info.height/2))
10402           pan_info.y=0;
10403         else
10404           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10405         if (pan_info.y < 0)
10406           pan_info.y=0;
10407         else
10408           if ((int) (pan_info.y+windows->image.height) >
10409               windows->image.ximage->height)
10410             pan_info.y=(ssize_t)
10411               (windows->image.ximage->height-windows->image.height);
10412         if ((windows->image.x != (int) pan_info.x) ||
10413             (windows->image.y != (int) pan_info.y))
10414           {
10415             /*
10416               Display image pan offset.
10417             */
10418             windows->image.x=(int) pan_info.x;
10419             windows->image.y=(int) pan_info.y;
10420             (void) FormatMagickString(text,MaxTextExtent," %ux%u%+d%+d ",
10421               windows->image.width,windows->image.height,windows->image.x,
10422               windows->image.y);
10423             XInfoWidget(display,windows,text);
10424             /*
10425               Refresh Image window.
10426             */
10427             XDrawPanRectangle(display,windows);
10428             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10429           }
10430         state&=(~UpdateConfigurationState);
10431       }
10432     /*
10433       Wait for next event.
10434     */
10435     if ((state & ExitState) == 0)
10436       XScreenEvent(display,windows,event);
10437   } while ((state & ExitState) == 0);
10438   /*
10439     Restore cursor.
10440   */
10441   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10442   (void) XFreeCursor(display,cursor);
10443   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10444 }
10445 \f
10446 /*
10447 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10448 %                                                                             %
10449 %                                                                             %
10450 %                                                                             %
10451 +   X P a s t e I m a g e                                                     %
10452 %                                                                             %
10453 %                                                                             %
10454 %                                                                             %
10455 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10456 %
10457 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10458 %  window image at a location the user chooses with the pointer.
10459 %
10460 %  The format of the XPasteImage method is:
10461 %
10462 %      MagickBooleanType XPasteImage(Display *display,
10463 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
10464 %
10465 %  A description of each parameter follows:
10466 %
10467 %    o display: Specifies a connection to an X server;  returned from
10468 %      XOpenDisplay.
10469 %
10470 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10471 %
10472 %    o windows: Specifies a pointer to a XWindows structure.
10473 %
10474 %    o image: the image; returned from ReadImage.
10475 %
10476 */
10477 static MagickBooleanType XPasteImage(Display *display,
10478   XResourceInfo *resource_info,XWindows *windows,Image *image)
10479 {
10480   static const char
10481     *PasteMenu[] =
10482     {
10483       "Operator",
10484       "Help",
10485       "Dismiss",
10486       (char *) NULL
10487     };
10488
10489   static const ModeType
10490     PasteCommands[] =
10491     {
10492       PasteOperatorsCommand,
10493       PasteHelpCommand,
10494       PasteDismissCommand
10495     };
10496
10497   static CompositeOperator
10498     compose = CopyCompositeOp;
10499
10500   char
10501     text[MaxTextExtent];
10502
10503   Cursor
10504     cursor;
10505
10506   Image
10507     *paste_image;
10508
10509   int
10510     entry,
10511     id,
10512     x,
10513     y;
10514
10515   MagickRealType
10516     scale_factor;
10517
10518   RectangleInfo
10519     highlight_info,
10520     paste_info;
10521
10522   unsigned int
10523     height,
10524     width;
10525
10526   size_t
10527     state;
10528
10529   XEvent
10530     event;
10531
10532   /*
10533     Copy image.
10534   */
10535   if (resource_info->copy_image == (Image *) NULL)
10536     return(MagickFalse);
10537   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10538     &image->exception);
10539   /*
10540     Map Command widget.
10541   */
10542   (void) CloneString(&windows->command.name,"Paste");
10543   windows->command.data=1;
10544   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10545   (void) XMapRaised(display,windows->command.id);
10546   XClientMessage(display,windows->image.id,windows->im_protocols,
10547     windows->im_update_widget,CurrentTime);
10548   /*
10549     Track pointer until button 1 is pressed.
10550   */
10551   XSetCursorState(display,windows,MagickFalse);
10552   XQueryPosition(display,windows->image.id,&x,&y);
10553   (void) XSelectInput(display,windows->image.id,
10554     windows->image.attributes.event_mask | PointerMotionMask);
10555   paste_info.x=(ssize_t) windows->image.x+x;
10556   paste_info.y=(ssize_t) windows->image.y+y;
10557   paste_info.width=0;
10558   paste_info.height=0;
10559   cursor=XCreateFontCursor(display,XC_ul_angle);
10560   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10561   state=DefaultState;
10562   do
10563   {
10564     if (windows->info.mapped != MagickFalse)
10565       {
10566         /*
10567           Display pointer position.
10568         */
10569         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
10570           (long) paste_info.x,(long) paste_info.y);
10571         XInfoWidget(display,windows,text);
10572       }
10573     highlight_info=paste_info;
10574     highlight_info.x=paste_info.x-windows->image.x;
10575     highlight_info.y=paste_info.y-windows->image.y;
10576     XHighlightRectangle(display,windows->image.id,
10577       windows->image.highlight_context,&highlight_info);
10578     /*
10579       Wait for next event.
10580     */
10581     XScreenEvent(display,windows,&event);
10582     XHighlightRectangle(display,windows->image.id,
10583       windows->image.highlight_context,&highlight_info);
10584     if (event.xany.window == windows->command.id)
10585       {
10586         /*
10587           Select a command from the Command widget.
10588         */
10589         id=XCommandWidget(display,windows,PasteMenu,&event);
10590         if (id < 0)
10591           continue;
10592         switch (PasteCommands[id])
10593         {
10594           case PasteOperatorsCommand:
10595           {
10596             char
10597               command[MaxTextExtent],
10598               **operators;
10599
10600             /*
10601               Select a command from the pop-up menu.
10602             */
10603             operators=GetMagickOptions(MagickComposeOptions);
10604             if (operators == (char **) NULL)
10605               break;
10606             entry=XMenuWidget(display,windows,PasteMenu[id],
10607               (const char **) operators,command);
10608             if (entry >= 0)
10609               compose=(CompositeOperator) ParseMagickOption(
10610                 MagickComposeOptions,MagickFalse,operators[entry]);
10611             operators=DestroyStringList(operators);
10612             break;
10613           }
10614           case PasteHelpCommand:
10615           {
10616             XTextViewWidget(display,resource_info,windows,MagickFalse,
10617               "Help Viewer - Image Composite",ImagePasteHelp);
10618             break;
10619           }
10620           case PasteDismissCommand:
10621           {
10622             /*
10623               Prematurely exit.
10624             */
10625             state|=EscapeState;
10626             state|=ExitState;
10627             break;
10628           }
10629           default:
10630             break;
10631         }
10632         continue;
10633       }
10634     switch (event.type)
10635     {
10636       case ButtonPress:
10637       {
10638         if (image->debug != MagickFalse)
10639           (void) LogMagickEvent(X11Event,GetMagickModule(),
10640             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10641             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10642         if (event.xbutton.button != Button1)
10643           break;
10644         if (event.xbutton.window != windows->image.id)
10645           break;
10646         /*
10647           Paste rectangle is relative to image configuration.
10648         */
10649         width=(unsigned int) image->columns;
10650         height=(unsigned int) image->rows;
10651         x=0;
10652         y=0;
10653         if (windows->image.crop_geometry != (char *) NULL)
10654           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10655             &width,&height);
10656         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10657         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10658         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10659         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10660         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10661         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10662         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10663         break;
10664       }
10665       case ButtonRelease:
10666       {
10667         if (image->debug != MagickFalse)
10668           (void) LogMagickEvent(X11Event,GetMagickModule(),
10669             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10670             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10671         if (event.xbutton.button != Button1)
10672           break;
10673         if (event.xbutton.window != windows->image.id)
10674           break;
10675         if ((paste_info.width != 0) && (paste_info.height != 0))
10676           {
10677             /*
10678               User has selected the location of the paste image.
10679             */
10680             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10681             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10682             state|=ExitState;
10683           }
10684         break;
10685       }
10686       case Expose:
10687         break;
10688       case KeyPress:
10689       {
10690         char
10691           command[MaxTextExtent];
10692
10693         KeySym
10694           key_symbol;
10695
10696         int
10697           length;
10698
10699         if (event.xkey.window != windows->image.id)
10700           break;
10701         /*
10702           Respond to a user key press.
10703         */
10704         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10705           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10706         *(command+length)='\0';
10707         if (image->debug != MagickFalse)
10708           (void) LogMagickEvent(X11Event,GetMagickModule(),
10709             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10710         switch ((int) key_symbol)
10711         {
10712           case XK_Escape:
10713           case XK_F20:
10714           {
10715             /*
10716               Prematurely exit.
10717             */
10718             paste_image=DestroyImage(paste_image);
10719             state|=EscapeState;
10720             state|=ExitState;
10721             break;
10722           }
10723           case XK_F1:
10724           case XK_Help:
10725           {
10726             (void) XSetFunction(display,windows->image.highlight_context,
10727               GXcopy);
10728             XTextViewWidget(display,resource_info,windows,MagickFalse,
10729               "Help Viewer - Image Composite",ImagePasteHelp);
10730             (void) XSetFunction(display,windows->image.highlight_context,
10731               GXinvert);
10732             break;
10733           }
10734           default:
10735           {
10736             (void) XBell(display,0);
10737             break;
10738           }
10739         }
10740         break;
10741       }
10742       case MotionNotify:
10743       {
10744         /*
10745           Map and unmap Info widget as text cursor crosses its boundaries.
10746         */
10747         x=event.xmotion.x;
10748         y=event.xmotion.y;
10749         if (windows->info.mapped != MagickFalse)
10750           {
10751             if ((x < (int) (windows->info.x+windows->info.width)) &&
10752                 (y < (int) (windows->info.y+windows->info.height)))
10753               (void) XWithdrawWindow(display,windows->info.id,
10754                 windows->info.screen);
10755           }
10756         else
10757           if ((x > (int) (windows->info.x+windows->info.width)) ||
10758               (y > (int) (windows->info.y+windows->info.height)))
10759             (void) XMapWindow(display,windows->info.id);
10760         paste_info.x=(ssize_t) windows->image.x+x;
10761         paste_info.y=(ssize_t) windows->image.y+y;
10762         break;
10763       }
10764       default:
10765       {
10766         if (image->debug != MagickFalse)
10767           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10768             event.type);
10769         break;
10770       }
10771     }
10772   } while ((state & ExitState) == 0);
10773   (void) XSelectInput(display,windows->image.id,
10774     windows->image.attributes.event_mask);
10775   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10776   XSetCursorState(display,windows,MagickFalse);
10777   (void) XFreeCursor(display,cursor);
10778   if ((state & EscapeState) != 0)
10779     return(MagickTrue);
10780   /*
10781     Image pasting is relative to image configuration.
10782   */
10783   XSetCursorState(display,windows,MagickTrue);
10784   XCheckRefreshWindows(display,windows);
10785   width=(unsigned int) image->columns;
10786   height=(unsigned int) image->rows;
10787   x=0;
10788   y=0;
10789   if (windows->image.crop_geometry != (char *) NULL)
10790     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10791   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10792   paste_info.x+=x;
10793   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10794   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10795   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10796   paste_info.y+=y;
10797   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10798   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10799   /*
10800     Paste image with X Image window.
10801   */
10802   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10803   paste_image=DestroyImage(paste_image);
10804   XSetCursorState(display,windows,MagickFalse);
10805   /*
10806     Update image colormap.
10807   */
10808   XConfigureImageColormap(display,resource_info,windows,image);
10809   (void) XConfigureImage(display,resource_info,windows,image);
10810   return(MagickTrue);
10811 }
10812 \f
10813 /*
10814 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10815 %                                                                             %
10816 %                                                                             %
10817 %                                                                             %
10818 +   X P r i n t I m a g e                                                     %
10819 %                                                                             %
10820 %                                                                             %
10821 %                                                                             %
10822 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10823 %
10824 %  XPrintImage() prints an image to a Postscript printer.
10825 %
10826 %  The format of the XPrintImage method is:
10827 %
10828 %      MagickBooleanType XPrintImage(Display *display,
10829 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
10830 %
10831 %  A description of each parameter follows:
10832 %
10833 %    o display: Specifies a connection to an X server; returned from
10834 %      XOpenDisplay.
10835 %
10836 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10837 %
10838 %    o windows: Specifies a pointer to a XWindows structure.
10839 %
10840 %    o image: the image.
10841 %
10842 */
10843 static MagickBooleanType XPrintImage(Display *display,
10844   XResourceInfo *resource_info,XWindows *windows,Image *image)
10845 {
10846   char
10847     filename[MaxTextExtent],
10848     geometry[MaxTextExtent];
10849
10850   Image
10851     *print_image;
10852
10853   ImageInfo
10854     *image_info;
10855
10856   MagickStatusType
10857     status;
10858
10859   /*
10860     Request Postscript page geometry from user.
10861   */
10862   image_info=CloneImageInfo(resource_info->image_info);
10863   (void) FormatMagickString(geometry,MaxTextExtent,"Letter");
10864   if (image_info->page != (char *) NULL)
10865     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10866   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10867     "Select Postscript Page Geometry:",geometry);
10868   if (*geometry == '\0')
10869     return(MagickTrue);
10870   image_info->page=GetPageGeometry(geometry);
10871   /*
10872     Apply image transforms.
10873   */
10874   XSetCursorState(display,windows,MagickTrue);
10875   XCheckRefreshWindows(display,windows);
10876   print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10877   if (print_image == (Image *) NULL)
10878     return(MagickFalse);
10879   (void) FormatMagickString(geometry,MaxTextExtent,"%dx%d!",
10880     windows->image.ximage->width,windows->image.ximage->height);
10881   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10882   /*
10883     Print image.
10884   */
10885   (void) AcquireUniqueFilename(filename);
10886   (void) FormatMagickString(print_image->filename,MaxTextExtent,"print:%s",
10887     filename);
10888   status=WriteImage(image_info,print_image);
10889   (void) RelinquishUniqueFileResource(filename);
10890   print_image=DestroyImage(print_image);
10891   image_info=DestroyImageInfo(image_info);
10892   XSetCursorState(display,windows,MagickFalse);
10893   return(status != 0 ? MagickTrue : MagickFalse);
10894 }
10895 \f
10896 /*
10897 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10898 %                                                                             %
10899 %                                                                             %
10900 %                                                                             %
10901 +   X R O I I m a g e                                                         %
10902 %                                                                             %
10903 %                                                                             %
10904 %                                                                             %
10905 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10906 %
10907 %  XROIImage() applies an image processing technique to a region of interest.
10908 %
10909 %  The format of the XROIImage method is:
10910 %
10911 %      MagickBooleanType XROIImage(Display *display,
10912 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
10913 %
10914 %  A description of each parameter follows:
10915 %
10916 %    o display: Specifies a connection to an X server; returned from
10917 %      XOpenDisplay.
10918 %
10919 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10920 %
10921 %    o windows: Specifies a pointer to a XWindows structure.
10922 %
10923 %    o image: the image; returned from ReadImage.
10924 %
10925 */
10926 static MagickBooleanType XROIImage(Display *display,
10927   XResourceInfo *resource_info,XWindows *windows,Image **image)
10928 {
10929 #define ApplyMenus  7
10930
10931   static const char
10932     *ROIMenu[] =
10933     {
10934       "Help",
10935       "Dismiss",
10936       (char *) NULL
10937     },
10938     *ApplyMenu[] =
10939     {
10940       "File",
10941       "Edit",
10942       "Transform",
10943       "Enhance",
10944       "Effects",
10945       "F/X",
10946       "Miscellany",
10947       "Help",
10948       "Dismiss",
10949       (char *) NULL
10950     },
10951     *FileMenu[] =
10952     {
10953       "Save...",
10954       "Print...",
10955       (char *) NULL
10956     },
10957     *EditMenu[] =
10958     {
10959       "Undo",
10960       "Redo",
10961       (char *) NULL
10962     },
10963     *TransformMenu[] =
10964     {
10965       "Flop",
10966       "Flip",
10967       "Rotate Right",
10968       "Rotate Left",
10969       (char *) NULL
10970     },
10971     *EnhanceMenu[] =
10972     {
10973       "Hue...",
10974       "Saturation...",
10975       "Brightness...",
10976       "Gamma...",
10977       "Spiff",
10978       "Dull",
10979       "Contrast Stretch...",
10980       "Sigmoidal Contrast...",
10981       "Normalize",
10982       "Equalize",
10983       "Negate",
10984       "Grayscale",
10985       "Map...",
10986       "Quantize...",
10987       (char *) NULL
10988     },
10989     *EffectsMenu[] =
10990     {
10991       "Despeckle",
10992       "Emboss",
10993       "Reduce Noise",
10994       "Add Noise",
10995       "Sharpen...",
10996       "Blur...",
10997       "Threshold...",
10998       "Edge Detect...",
10999       "Spread...",
11000       "Shade...",
11001       "Raise...",
11002       "Segment...",
11003       (char *) NULL
11004     },
11005     *FXMenu[] =
11006     {
11007       "Solarize...",
11008       "Sepia Tone...",
11009       "Swirl...",
11010       "Implode...",
11011       "Vignette...",
11012       "Wave...",
11013       "Oil Paint...",
11014       "Charcoal Draw...",
11015       (char *) NULL
11016     },
11017     *MiscellanyMenu[] =
11018     {
11019       "Image Info",
11020       "Zoom Image",
11021       "Show Preview...",
11022       "Show Histogram",
11023       "Show Matte",
11024       (char *) NULL
11025     };
11026
11027   static const char
11028     **Menus[ApplyMenus] =
11029     {
11030       FileMenu,
11031       EditMenu,
11032       TransformMenu,
11033       EnhanceMenu,
11034       EffectsMenu,
11035       FXMenu,
11036       MiscellanyMenu
11037     };
11038
11039   static const CommandType
11040     ApplyCommands[] =
11041     {
11042       NullCommand,
11043       NullCommand,
11044       NullCommand,
11045       NullCommand,
11046       NullCommand,
11047       NullCommand,
11048       NullCommand,
11049       HelpCommand,
11050       QuitCommand
11051     },
11052     FileCommands[] =
11053     {
11054       SaveCommand,
11055       PrintCommand
11056     },
11057     EditCommands[] =
11058     {
11059       UndoCommand,
11060       RedoCommand
11061     },
11062     TransformCommands[] =
11063     {
11064       FlopCommand,
11065       FlipCommand,
11066       RotateRightCommand,
11067       RotateLeftCommand
11068     },
11069     EnhanceCommands[] =
11070     {
11071       HueCommand,
11072       SaturationCommand,
11073       BrightnessCommand,
11074       GammaCommand,
11075       SpiffCommand,
11076       DullCommand,
11077       ContrastStretchCommand,
11078       SigmoidalContrastCommand,
11079       NormalizeCommand,
11080       EqualizeCommand,
11081       NegateCommand,
11082       GrayscaleCommand,
11083       MapCommand,
11084       QuantizeCommand
11085     },
11086     EffectsCommands[] =
11087     {
11088       DespeckleCommand,
11089       EmbossCommand,
11090       ReduceNoiseCommand,
11091       AddNoiseCommand,
11092       SharpenCommand,
11093       BlurCommand,
11094       EdgeDetectCommand,
11095       SpreadCommand,
11096       ShadeCommand,
11097       RaiseCommand,
11098       SegmentCommand
11099     },
11100     FXCommands[] =
11101     {
11102       SolarizeCommand,
11103       SepiaToneCommand,
11104       SwirlCommand,
11105       ImplodeCommand,
11106       VignetteCommand,
11107       WaveCommand,
11108       OilPaintCommand,
11109       CharcoalDrawCommand
11110     },
11111     MiscellanyCommands[] =
11112     {
11113       InfoCommand,
11114       ZoomCommand,
11115       ShowPreviewCommand,
11116       ShowHistogramCommand,
11117       ShowMatteCommand
11118     },
11119     ROICommands[] =
11120     {
11121       ROIHelpCommand,
11122       ROIDismissCommand
11123     };
11124
11125   static const CommandType
11126     *Commands[ApplyMenus] =
11127     {
11128       FileCommands,
11129       EditCommands,
11130       TransformCommands,
11131       EnhanceCommands,
11132       EffectsCommands,
11133       FXCommands,
11134       MiscellanyCommands
11135     };
11136
11137   char
11138     command[MaxTextExtent],
11139     text[MaxTextExtent];
11140
11141   CommandType
11142     command_type;
11143
11144   Cursor
11145     cursor;
11146
11147   Image
11148     *roi_image;
11149
11150   int
11151     entry,
11152     id,
11153     x,
11154     y;
11155
11156   MagickRealType
11157     scale_factor;
11158
11159   MagickProgressMonitor
11160     progress_monitor;
11161
11162   RectangleInfo
11163     crop_info,
11164     highlight_info,
11165     roi_info;
11166
11167   unsigned int
11168     height,
11169     width;
11170
11171   size_t
11172     state;
11173
11174   XEvent
11175     event;
11176
11177   /*
11178     Map Command widget.
11179   */
11180   (void) CloneString(&windows->command.name,"ROI");
11181   windows->command.data=0;
11182   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11183   (void) XMapRaised(display,windows->command.id);
11184   XClientMessage(display,windows->image.id,windows->im_protocols,
11185     windows->im_update_widget,CurrentTime);
11186   /*
11187     Track pointer until button 1 is pressed.
11188   */
11189   XQueryPosition(display,windows->image.id,&x,&y);
11190   (void) XSelectInput(display,windows->image.id,
11191     windows->image.attributes.event_mask | PointerMotionMask);
11192   roi_info.x=(ssize_t) windows->image.x+x;
11193   roi_info.y=(ssize_t) windows->image.y+y;
11194   roi_info.width=0;
11195   roi_info.height=0;
11196   cursor=XCreateFontCursor(display,XC_fleur);
11197   state=DefaultState;
11198   do
11199   {
11200     if (windows->info.mapped != MagickFalse)
11201       {
11202         /*
11203           Display pointer position.
11204         */
11205         (void) FormatMagickString(text,MaxTextExtent," %+ld%+ld ",
11206           (long) roi_info.x,(long) roi_info.y);
11207         XInfoWidget(display,windows,text);
11208       }
11209     /*
11210       Wait for next event.
11211     */
11212     XScreenEvent(display,windows,&event);
11213     if (event.xany.window == windows->command.id)
11214       {
11215         /*
11216           Select a command from the Command widget.
11217         */
11218         id=XCommandWidget(display,windows,ROIMenu,&event);
11219         if (id < 0)
11220           continue;
11221         switch (ROICommands[id])
11222         {
11223           case ROIHelpCommand:
11224           {
11225             XTextViewWidget(display,resource_info,windows,MagickFalse,
11226               "Help Viewer - Region of Interest",ImageROIHelp);
11227             break;
11228           }
11229           case ROIDismissCommand:
11230           {
11231             /*
11232               Prematurely exit.
11233             */
11234             state|=EscapeState;
11235             state|=ExitState;
11236             break;
11237           }
11238           default:
11239             break;
11240         }
11241         continue;
11242       }
11243     switch (event.type)
11244     {
11245       case ButtonPress:
11246       {
11247         if (event.xbutton.button != Button1)
11248           break;
11249         if (event.xbutton.window != windows->image.id)
11250           break;
11251         /*
11252           Note first corner of region of interest rectangle-- exit loop.
11253         */
11254         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11255         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11256         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11257         state|=ExitState;
11258         break;
11259       }
11260       case ButtonRelease:
11261         break;
11262       case Expose:
11263         break;
11264       case KeyPress:
11265       {
11266         KeySym
11267           key_symbol;
11268
11269         if (event.xkey.window != windows->image.id)
11270           break;
11271         /*
11272           Respond to a user key press.
11273         */
11274         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11275           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11276         switch ((int) key_symbol)
11277         {
11278           case XK_Escape:
11279           case XK_F20:
11280           {
11281             /*
11282               Prematurely exit.
11283             */
11284             state|=EscapeState;
11285             state|=ExitState;
11286             break;
11287           }
11288           case XK_F1:
11289           case XK_Help:
11290           {
11291             XTextViewWidget(display,resource_info,windows,MagickFalse,
11292               "Help Viewer - Region of Interest",ImageROIHelp);
11293             break;
11294           }
11295           default:
11296           {
11297             (void) XBell(display,0);
11298             break;
11299           }
11300         }
11301         break;
11302       }
11303       case MotionNotify:
11304       {
11305         /*
11306           Map and unmap Info widget as text cursor crosses its boundaries.
11307         */
11308         x=event.xmotion.x;
11309         y=event.xmotion.y;
11310         if (windows->info.mapped != MagickFalse)
11311           {
11312             if ((x < (int) (windows->info.x+windows->info.width)) &&
11313                 (y < (int) (windows->info.y+windows->info.height)))
11314               (void) XWithdrawWindow(display,windows->info.id,
11315                 windows->info.screen);
11316           }
11317         else
11318           if ((x > (int) (windows->info.x+windows->info.width)) ||
11319               (y > (int) (windows->info.y+windows->info.height)))
11320             (void) XMapWindow(display,windows->info.id);
11321         roi_info.x=(ssize_t) windows->image.x+x;
11322         roi_info.y=(ssize_t) windows->image.y+y;
11323         break;
11324       }
11325       default:
11326         break;
11327     }
11328   } while ((state & ExitState) == 0);
11329   (void) XSelectInput(display,windows->image.id,
11330     windows->image.attributes.event_mask);
11331   if ((state & EscapeState) != 0)
11332     {
11333       /*
11334         User want to exit without region of interest.
11335       */
11336       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11337       (void) XFreeCursor(display,cursor);
11338       return(MagickTrue);
11339     }
11340   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11341   do
11342   {
11343     /*
11344       Size rectangle as pointer moves until the mouse button is released.
11345     */
11346     x=(int) roi_info.x;
11347     y=(int) roi_info.y;
11348     roi_info.width=0;
11349     roi_info.height=0;
11350     state=DefaultState;
11351     do
11352     {
11353       highlight_info=roi_info;
11354       highlight_info.x=roi_info.x-windows->image.x;
11355       highlight_info.y=roi_info.y-windows->image.y;
11356       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11357         {
11358           /*
11359             Display info and draw region of interest rectangle.
11360           */
11361           if (windows->info.mapped == MagickFalse)
11362             (void) XMapWindow(display,windows->info.id);
11363           (void) FormatMagickString(text,MaxTextExtent,
11364             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11365             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11366           XInfoWidget(display,windows,text);
11367           XHighlightRectangle(display,windows->image.id,
11368             windows->image.highlight_context,&highlight_info);
11369         }
11370       else
11371         if (windows->info.mapped != MagickFalse)
11372           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11373       /*
11374         Wait for next event.
11375       */
11376       XScreenEvent(display,windows,&event);
11377       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11378         XHighlightRectangle(display,windows->image.id,
11379           windows->image.highlight_context,&highlight_info);
11380       switch (event.type)
11381       {
11382         case ButtonPress:
11383         {
11384           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11385           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11386           break;
11387         }
11388         case ButtonRelease:
11389         {
11390           /*
11391             User has committed to region of interest rectangle.
11392           */
11393           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11394           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11395           XSetCursorState(display,windows,MagickFalse);
11396           state|=ExitState;
11397           if (LocaleCompare(windows->command.name,"Apply") == 0)
11398             break;
11399           (void) CloneString(&windows->command.name,"Apply");
11400           windows->command.data=ApplyMenus;
11401           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11402           break;
11403         }
11404         case Expose:
11405           break;
11406         case MotionNotify:
11407         {
11408           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11409           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11410         }
11411         default:
11412           break;
11413       }
11414       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11415           ((state & ExitState) != 0))
11416         {
11417           /*
11418             Check boundary conditions.
11419           */
11420           if (roi_info.x < 0)
11421             roi_info.x=0;
11422           else
11423             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11424               roi_info.x=(ssize_t) windows->image.ximage->width;
11425           if ((int) roi_info.x < x)
11426             roi_info.width=(unsigned int) (x-roi_info.x);
11427           else
11428             {
11429               roi_info.width=(unsigned int) (roi_info.x-x);
11430               roi_info.x=(ssize_t) x;
11431             }
11432           if (roi_info.y < 0)
11433             roi_info.y=0;
11434           else
11435             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11436               roi_info.y=(ssize_t) windows->image.ximage->height;
11437           if ((int) roi_info.y < y)
11438             roi_info.height=(unsigned int) (y-roi_info.y);
11439           else
11440             {
11441               roi_info.height=(unsigned int) (roi_info.y-y);
11442               roi_info.y=(ssize_t) y;
11443             }
11444         }
11445     } while ((state & ExitState) == 0);
11446     /*
11447       Wait for user to grab a corner of the rectangle or press return.
11448     */
11449     state=DefaultState;
11450     command_type=NullCommand;
11451     (void) XMapWindow(display,windows->info.id);
11452     do
11453     {
11454       if (windows->info.mapped != MagickFalse)
11455         {
11456           /*
11457             Display pointer position.
11458           */
11459           (void) FormatMagickString(text,MaxTextExtent,
11460             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11461             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11462           XInfoWidget(display,windows,text);
11463         }
11464       highlight_info=roi_info;
11465       highlight_info.x=roi_info.x-windows->image.x;
11466       highlight_info.y=roi_info.y-windows->image.y;
11467       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11468         {
11469           state|=EscapeState;
11470           state|=ExitState;
11471           break;
11472         }
11473       if ((state & UpdateRegionState) != 0)
11474         {
11475           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11476           switch (command_type)
11477           {
11478             case UndoCommand:
11479             case RedoCommand:
11480             {
11481               (void) XMagickCommand(display,resource_info,windows,command_type,
11482                 image);
11483               break;
11484             }
11485             default:
11486             {
11487               /*
11488                 Region of interest is relative to image configuration.
11489               */
11490               progress_monitor=SetImageProgressMonitor(*image,
11491                 (MagickProgressMonitor) NULL,(*image)->client_data);
11492               crop_info=roi_info;
11493               width=(unsigned int) (*image)->columns;
11494               height=(unsigned int) (*image)->rows;
11495               x=0;
11496               y=0;
11497               if (windows->image.crop_geometry != (char *) NULL)
11498                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11499                   &width,&height);
11500               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11501               crop_info.x+=x;
11502               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11503               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11504               scale_factor=(MagickRealType)
11505                 height/windows->image.ximage->height;
11506               crop_info.y+=y;
11507               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11508               crop_info.height=(unsigned int)
11509                 (scale_factor*crop_info.height+0.5);
11510               roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11511               (void) SetImageProgressMonitor(*image,progress_monitor,
11512                 (*image)->client_data);
11513               if (roi_image == (Image *) NULL)
11514                 continue;
11515               /*
11516                 Apply image processing technique to the region of interest.
11517               */
11518               windows->image.orphan=MagickTrue;
11519               (void) XMagickCommand(display,resource_info,windows,command_type,
11520                 &roi_image);
11521               progress_monitor=SetImageProgressMonitor(*image,
11522                 (MagickProgressMonitor) NULL,(*image)->client_data);
11523               (void) XMagickCommand(display,resource_info,windows,
11524                 SaveToUndoBufferCommand,image);
11525               windows->image.orphan=MagickFalse;
11526               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11527                 crop_info.x,crop_info.y);
11528               roi_image=DestroyImage(roi_image);
11529               (void) SetImageProgressMonitor(*image,progress_monitor,
11530                 (*image)->client_data);
11531               break;
11532             }
11533           }
11534           if (command_type != InfoCommand)
11535             {
11536               XConfigureImageColormap(display,resource_info,windows,*image);
11537               (void) XConfigureImage(display,resource_info,windows,*image);
11538             }
11539           XCheckRefreshWindows(display,windows);
11540           XInfoWidget(display,windows,text);
11541           (void) XSetFunction(display,windows->image.highlight_context,
11542             GXinvert);
11543           state&=(~UpdateRegionState);
11544         }
11545       XHighlightRectangle(display,windows->image.id,
11546         windows->image.highlight_context,&highlight_info);
11547       XScreenEvent(display,windows,&event);
11548       if (event.xany.window == windows->command.id)
11549         {
11550           /*
11551             Select a command from the Command widget.
11552           */
11553           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11554           command_type=NullCommand;
11555           id=XCommandWidget(display,windows,ApplyMenu,&event);
11556           if (id >= 0)
11557             {
11558               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11559               command_type=ApplyCommands[id];
11560               if (id < ApplyMenus)
11561                 {
11562                   /*
11563                     Select a command from a pop-up menu.
11564                   */
11565                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11566                     (const char **) Menus[id],command);
11567                   if (entry >= 0)
11568                     {
11569                       (void) CopyMagickString(command,Menus[id][entry],
11570                         MaxTextExtent);
11571                       command_type=Commands[id][entry];
11572                     }
11573                 }
11574             }
11575           (void) XSetFunction(display,windows->image.highlight_context,
11576             GXinvert);
11577           XHighlightRectangle(display,windows->image.id,
11578             windows->image.highlight_context,&highlight_info);
11579           if (command_type == HelpCommand)
11580             {
11581               (void) XSetFunction(display,windows->image.highlight_context,
11582                 GXcopy);
11583               XTextViewWidget(display,resource_info,windows,MagickFalse,
11584                 "Help Viewer - Region of Interest",ImageROIHelp);
11585               (void) XSetFunction(display,windows->image.highlight_context,
11586                 GXinvert);
11587               continue;
11588             }
11589           if (command_type == QuitCommand)
11590             {
11591               /*
11592                 exit.
11593               */
11594               state|=EscapeState;
11595               state|=ExitState;
11596               continue;
11597             }
11598           if (command_type != NullCommand)
11599             state|=UpdateRegionState;
11600           continue;
11601         }
11602       XHighlightRectangle(display,windows->image.id,
11603         windows->image.highlight_context,&highlight_info);
11604       switch (event.type)
11605       {
11606         case ButtonPress:
11607         {
11608           x=windows->image.x;
11609           y=windows->image.y;
11610           if (event.xbutton.button != Button1)
11611             break;
11612           if (event.xbutton.window != windows->image.id)
11613             break;
11614           x=windows->image.x+event.xbutton.x;
11615           y=windows->image.y+event.xbutton.y;
11616           if ((x < (int) (roi_info.x+RoiDelta)) &&
11617               (x > (int) (roi_info.x-RoiDelta)) &&
11618               (y < (int) (roi_info.y+RoiDelta)) &&
11619               (y > (int) (roi_info.y-RoiDelta)))
11620             {
11621               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11622               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11623               state|=UpdateConfigurationState;
11624               break;
11625             }
11626           if ((x < (int) (roi_info.x+RoiDelta)) &&
11627               (x > (int) (roi_info.x-RoiDelta)) &&
11628               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11629               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11630             {
11631               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11632               state|=UpdateConfigurationState;
11633               break;
11634             }
11635           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11636               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11637               (y < (int) (roi_info.y+RoiDelta)) &&
11638               (y > (int) (roi_info.y-RoiDelta)))
11639             {
11640               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11641               state|=UpdateConfigurationState;
11642               break;
11643             }
11644           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11645               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11646               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11647               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11648             {
11649               state|=UpdateConfigurationState;
11650               break;
11651             }
11652         }
11653         case ButtonRelease:
11654         {
11655           if (event.xbutton.window == windows->pan.id)
11656             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11657                 (highlight_info.y != crop_info.y-windows->image.y))
11658               XHighlightRectangle(display,windows->image.id,
11659                 windows->image.highlight_context,&highlight_info);
11660           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11661             event.xbutton.time);
11662           break;
11663         }
11664         case Expose:
11665         {
11666           if (event.xexpose.window == windows->image.id)
11667             if (event.xexpose.count == 0)
11668               {
11669                 event.xexpose.x=(int) highlight_info.x;
11670                 event.xexpose.y=(int) highlight_info.y;
11671                 event.xexpose.width=(int) highlight_info.width;
11672                 event.xexpose.height=(int) highlight_info.height;
11673                 XRefreshWindow(display,&windows->image,&event);
11674               }
11675           if (event.xexpose.window == windows->info.id)
11676             if (event.xexpose.count == 0)
11677               XInfoWidget(display,windows,text);
11678           break;
11679         }
11680         case KeyPress:
11681         {
11682           KeySym
11683             key_symbol;
11684
11685           if (event.xkey.window != windows->image.id)
11686             break;
11687           /*
11688             Respond to a user key press.
11689           */
11690           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11691             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11692           switch ((int) key_symbol)
11693           {
11694             case XK_Shift_L:
11695             case XK_Shift_R:
11696               break;
11697             case XK_Escape:
11698             case XK_F20:
11699               state|=EscapeState;
11700             case XK_Return:
11701             {
11702               state|=ExitState;
11703               break;
11704             }
11705             case XK_Home:
11706             case XK_KP_Home:
11707             {
11708               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11709               roi_info.y=(ssize_t) (windows->image.height/2L-
11710                 roi_info.height/2L);
11711               break;
11712             }
11713             case XK_Left:
11714             case XK_KP_Left:
11715             {
11716               roi_info.x--;
11717               break;
11718             }
11719             case XK_Up:
11720             case XK_KP_Up:
11721             case XK_Next:
11722             {
11723               roi_info.y--;
11724               break;
11725             }
11726             case XK_Right:
11727             case XK_KP_Right:
11728             {
11729               roi_info.x++;
11730               break;
11731             }
11732             case XK_Prior:
11733             case XK_Down:
11734             case XK_KP_Down:
11735             {
11736               roi_info.y++;
11737               break;
11738             }
11739             case XK_F1:
11740             case XK_Help:
11741             {
11742               (void) XSetFunction(display,windows->image.highlight_context,
11743                 GXcopy);
11744               XTextViewWidget(display,resource_info,windows,MagickFalse,
11745                 "Help Viewer - Region of Interest",ImageROIHelp);
11746               (void) XSetFunction(display,windows->image.highlight_context,
11747                 GXinvert);
11748               break;
11749             }
11750             default:
11751             {
11752               command_type=XImageWindowCommand(display,resource_info,windows,
11753                 event.xkey.state,key_symbol,image);
11754               if (command_type != NullCommand)
11755                 state|=UpdateRegionState;
11756               break;
11757             }
11758           }
11759           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11760             event.xkey.time);
11761           break;
11762         }
11763         case KeyRelease:
11764           break;
11765         case MotionNotify:
11766         {
11767           if (event.xbutton.window != windows->image.id)
11768             break;
11769           /*
11770             Map and unmap Info widget as text cursor crosses its boundaries.
11771           */
11772           x=event.xmotion.x;
11773           y=event.xmotion.y;
11774           if (windows->info.mapped != MagickFalse)
11775             {
11776               if ((x < (int) (windows->info.x+windows->info.width)) &&
11777                   (y < (int) (windows->info.y+windows->info.height)))
11778                 (void) XWithdrawWindow(display,windows->info.id,
11779                   windows->info.screen);
11780             }
11781           else
11782             if ((x > (int) (windows->info.x+windows->info.width)) ||
11783                 (y > (int) (windows->info.y+windows->info.height)))
11784               (void) XMapWindow(display,windows->info.id);
11785           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11786           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11787           break;
11788         }
11789         case SelectionRequest:
11790         {
11791           XSelectionEvent
11792             notify;
11793
11794           XSelectionRequestEvent
11795             *request;
11796
11797           /*
11798             Set primary selection.
11799           */
11800           (void) FormatMagickString(text,MaxTextExtent,
11801             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11802             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11803           request=(&(event.xselectionrequest));
11804           (void) XChangeProperty(request->display,request->requestor,
11805             request->property,request->target,8,PropModeReplace,
11806             (unsigned char *) text,(int) strlen(text));
11807           notify.type=SelectionNotify;
11808           notify.display=request->display;
11809           notify.requestor=request->requestor;
11810           notify.selection=request->selection;
11811           notify.target=request->target;
11812           notify.time=request->time;
11813           if (request->property == None)
11814             notify.property=request->target;
11815           else
11816             notify.property=request->property;
11817           (void) XSendEvent(request->display,request->requestor,False,0,
11818             (XEvent *) &notify);
11819         }
11820         default:
11821           break;
11822       }
11823       if ((state & UpdateConfigurationState) != 0)
11824         {
11825           (void) XPutBackEvent(display,&event);
11826           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11827           break;
11828         }
11829     } while ((state & ExitState) == 0);
11830   } while ((state & ExitState) == 0);
11831   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11832   XSetCursorState(display,windows,MagickFalse);
11833   if ((state & EscapeState) != 0)
11834     return(MagickTrue);
11835   return(MagickTrue);
11836 }
11837 \f
11838 /*
11839 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11840 %                                                                             %
11841 %                                                                             %
11842 %                                                                             %
11843 +   X R o t a t e I m a g e                                                   %
11844 %                                                                             %
11845 %                                                                             %
11846 %                                                                             %
11847 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11848 %
11849 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11850 %  rotation angle is computed from the slope of a line drawn by the user.
11851 %
11852 %  The format of the XRotateImage method is:
11853 %
11854 %      MagickBooleanType XRotateImage(Display *display,
11855 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11856 %        Image **image)
11857 %
11858 %  A description of each parameter follows:
11859 %
11860 %    o display: Specifies a connection to an X server; returned from
11861 %      XOpenDisplay.
11862 %
11863 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11864 %
11865 %    o windows: Specifies a pointer to a XWindows structure.
11866 %
11867 %    o degrees: Specifies the number of degrees to rotate the image.
11868 %
11869 %    o image: the image.
11870 %
11871 */
11872 static MagickBooleanType XRotateImage(Display *display,
11873   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11874 {
11875   static const char
11876     *RotateMenu[] =
11877     {
11878       "Pixel Color",
11879       "Direction",
11880       "Help",
11881       "Dismiss",
11882       (char *) NULL
11883     };
11884
11885   static ModeType
11886     direction = HorizontalRotateCommand;
11887
11888   static const ModeType
11889     DirectionCommands[] =
11890     {
11891       HorizontalRotateCommand,
11892       VerticalRotateCommand
11893     },
11894     RotateCommands[] =
11895     {
11896       RotateColorCommand,
11897       RotateDirectionCommand,
11898       RotateHelpCommand,
11899       RotateDismissCommand
11900     };
11901
11902   static unsigned int
11903     pen_id = 0;
11904
11905   char
11906     command[MaxTextExtent],
11907     text[MaxTextExtent];
11908
11909   Image
11910     *rotate_image;
11911
11912   int
11913     id,
11914     x,
11915     y;
11916
11917   MagickRealType
11918     normalized_degrees;
11919
11920   register int
11921     i;
11922
11923   unsigned int
11924     height,
11925     rotations,
11926     width;
11927
11928   if (degrees == 0.0)
11929     {
11930       unsigned int
11931         distance;
11932
11933       size_t
11934         state;
11935
11936       XEvent
11937         event;
11938
11939       XSegment
11940         rotate_info;
11941
11942       /*
11943         Map Command widget.
11944       */
11945       (void) CloneString(&windows->command.name,"Rotate");
11946       windows->command.data=2;
11947       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11948       (void) XMapRaised(display,windows->command.id);
11949       XClientMessage(display,windows->image.id,windows->im_protocols,
11950         windows->im_update_widget,CurrentTime);
11951       /*
11952         Wait for first button press.
11953       */
11954       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11955       XQueryPosition(display,windows->image.id,&x,&y);
11956       rotate_info.x1=x;
11957       rotate_info.y1=y;
11958       rotate_info.x2=x;
11959       rotate_info.y2=y;
11960       state=DefaultState;
11961       do
11962       {
11963         XHighlightLine(display,windows->image.id,
11964           windows->image.highlight_context,&rotate_info);
11965         /*
11966           Wait for next event.
11967         */
11968         XScreenEvent(display,windows,&event);
11969         XHighlightLine(display,windows->image.id,
11970           windows->image.highlight_context,&rotate_info);
11971         if (event.xany.window == windows->command.id)
11972           {
11973             /*
11974               Select a command from the Command widget.
11975             */
11976             id=XCommandWidget(display,windows,RotateMenu,&event);
11977             if (id < 0)
11978               continue;
11979             (void) XSetFunction(display,windows->image.highlight_context,
11980               GXcopy);
11981             switch (RotateCommands[id])
11982             {
11983               case RotateColorCommand:
11984               {
11985                 const char
11986                   *ColorMenu[MaxNumberPens];
11987
11988                 int
11989                   pen_number;
11990
11991                 XColor
11992                   color;
11993
11994                 /*
11995                   Initialize menu selections.
11996                 */
11997                 for (i=0; i < (int) (MaxNumberPens-2); i++)
11998                   ColorMenu[i]=resource_info->pen_colors[i];
11999                 ColorMenu[MaxNumberPens-2]="Browser...";
12000                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12001                 /*
12002                   Select a pen color from the pop-up menu.
12003                 */
12004                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12005                   (const char **) ColorMenu,command);
12006                 if (pen_number < 0)
12007                   break;
12008                 if (pen_number == (MaxNumberPens-2))
12009                   {
12010                     static char
12011                       color_name[MaxTextExtent] = "gray";
12012
12013                     /*
12014                       Select a pen color from a dialog.
12015                     */
12016                     resource_info->pen_colors[pen_number]=color_name;
12017                     XColorBrowserWidget(display,windows,"Select",color_name);
12018                     if (*color_name == '\0')
12019                       break;
12020                   }
12021                 /*
12022                   Set pen color.
12023                 */
12024                 (void) XParseColor(display,windows->map_info->colormap,
12025                   resource_info->pen_colors[pen_number],&color);
12026                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12027                   (unsigned int) MaxColors,&color);
12028                 windows->pixel_info->pen_colors[pen_number]=color;
12029                 pen_id=(unsigned int) pen_number;
12030                 break;
12031               }
12032               case RotateDirectionCommand:
12033               {
12034                 static const char
12035                   *Directions[] =
12036                   {
12037                     "horizontal",
12038                     "vertical",
12039                     (char *) NULL,
12040                   };
12041
12042                 /*
12043                   Select a command from the pop-up menu.
12044                 */
12045                 id=XMenuWidget(display,windows,RotateMenu[id],
12046                   Directions,command);
12047                 if (id >= 0)
12048                   direction=DirectionCommands[id];
12049                 break;
12050               }
12051               case RotateHelpCommand:
12052               {
12053                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12054                   "Help Viewer - Image Rotation",ImageRotateHelp);
12055                 break;
12056               }
12057               case RotateDismissCommand:
12058               {
12059                 /*
12060                   Prematurely exit.
12061                 */
12062                 state|=EscapeState;
12063                 state|=ExitState;
12064                 break;
12065               }
12066               default:
12067                 break;
12068             }
12069             (void) XSetFunction(display,windows->image.highlight_context,
12070               GXinvert);
12071             continue;
12072           }
12073         switch (event.type)
12074         {
12075           case ButtonPress:
12076           {
12077             if (event.xbutton.button != Button1)
12078               break;
12079             if (event.xbutton.window != windows->image.id)
12080               break;
12081             /*
12082               exit loop.
12083             */
12084             (void) XSetFunction(display,windows->image.highlight_context,
12085               GXcopy);
12086             rotate_info.x1=event.xbutton.x;
12087             rotate_info.y1=event.xbutton.y;
12088             state|=ExitState;
12089             break;
12090           }
12091           case ButtonRelease:
12092             break;
12093           case Expose:
12094             break;
12095           case KeyPress:
12096           {
12097             char
12098               command[MaxTextExtent];
12099
12100             KeySym
12101               key_symbol;
12102
12103             if (event.xkey.window != windows->image.id)
12104               break;
12105             /*
12106               Respond to a user key press.
12107             */
12108             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12109               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12110             switch ((int) key_symbol)
12111             {
12112               case XK_Escape:
12113               case XK_F20:
12114               {
12115                 /*
12116                   Prematurely exit.
12117                 */
12118                 state|=EscapeState;
12119                 state|=ExitState;
12120                 break;
12121               }
12122               case XK_F1:
12123               case XK_Help:
12124               {
12125                 (void) XSetFunction(display,windows->image.highlight_context,
12126                   GXcopy);
12127                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12128                   "Help Viewer - Image Rotation",ImageRotateHelp);
12129                 (void) XSetFunction(display,windows->image.highlight_context,
12130                   GXinvert);
12131                 break;
12132               }
12133               default:
12134               {
12135                 (void) XBell(display,0);
12136                 break;
12137               }
12138             }
12139             break;
12140           }
12141           case MotionNotify:
12142           {
12143             rotate_info.x1=event.xmotion.x;
12144             rotate_info.y1=event.xmotion.y;
12145           }
12146         }
12147         rotate_info.x2=rotate_info.x1;
12148         rotate_info.y2=rotate_info.y1;
12149         if (direction == HorizontalRotateCommand)
12150           rotate_info.x2+=32;
12151         else
12152           rotate_info.y2-=32;
12153       } while ((state & ExitState) == 0);
12154       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12155       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12156       if ((state & EscapeState) != 0)
12157         return(MagickTrue);
12158       /*
12159         Draw line as pointer moves until the mouse button is released.
12160       */
12161       distance=0;
12162       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12163       state=DefaultState;
12164       do
12165       {
12166         if (distance > 9)
12167           {
12168             /*
12169               Display info and draw rotation line.
12170             */
12171             if (windows->info.mapped == MagickFalse)
12172               (void) XMapWindow(display,windows->info.id);
12173             (void) FormatMagickString(text,MaxTextExtent," %g",
12174               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12175             XInfoWidget(display,windows,text);
12176             XHighlightLine(display,windows->image.id,
12177               windows->image.highlight_context,&rotate_info);
12178           }
12179         else
12180           if (windows->info.mapped != MagickFalse)
12181             (void) XWithdrawWindow(display,windows->info.id,
12182               windows->info.screen);
12183         /*
12184           Wait for next event.
12185         */
12186         XScreenEvent(display,windows,&event);
12187         if (distance > 9)
12188           XHighlightLine(display,windows->image.id,
12189             windows->image.highlight_context,&rotate_info);
12190         switch (event.type)
12191         {
12192           case ButtonPress:
12193             break;
12194           case ButtonRelease:
12195           {
12196             /*
12197               User has committed to rotation line.
12198             */
12199             rotate_info.x2=event.xbutton.x;
12200             rotate_info.y2=event.xbutton.y;
12201             state|=ExitState;
12202             break;
12203           }
12204           case Expose:
12205             break;
12206           case MotionNotify:
12207           {
12208             rotate_info.x2=event.xmotion.x;
12209             rotate_info.y2=event.xmotion.y;
12210           }
12211           default:
12212             break;
12213         }
12214         /*
12215           Check boundary conditions.
12216         */
12217         if (rotate_info.x2 < 0)
12218           rotate_info.x2=0;
12219         else
12220           if (rotate_info.x2 > (int) windows->image.width)
12221             rotate_info.x2=(short) windows->image.width;
12222         if (rotate_info.y2 < 0)
12223           rotate_info.y2=0;
12224         else
12225           if (rotate_info.y2 > (int) windows->image.height)
12226             rotate_info.y2=(short) windows->image.height;
12227         /*
12228           Compute rotation angle from the slope of the line.
12229         */
12230         degrees=0.0;
12231         distance=(unsigned int)
12232           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12233           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12234         if (distance > 9)
12235           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12236             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12237       } while ((state & ExitState) == 0);
12238       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12239       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12240       if (distance <= 9)
12241         return(MagickTrue);
12242     }
12243   if (direction == VerticalRotateCommand)
12244     degrees-=90.0;
12245   if (degrees == 0.0)
12246     return(MagickTrue);
12247   /*
12248     Rotate image.
12249   */
12250   normalized_degrees=degrees;
12251   while (normalized_degrees < -45.0)
12252     normalized_degrees+=360.0;
12253   for (rotations=0; normalized_degrees > 45.0; rotations++)
12254     normalized_degrees-=90.0;
12255   if (normalized_degrees != 0.0)
12256     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12257   XSetCursorState(display,windows,MagickTrue);
12258   XCheckRefreshWindows(display,windows);
12259   (*image)->background_color.red=ScaleShortToQuantum(
12260     windows->pixel_info->pen_colors[pen_id].red);
12261   (*image)->background_color.green=ScaleShortToQuantum(
12262     windows->pixel_info->pen_colors[pen_id].green);
12263   (*image)->background_color.blue=ScaleShortToQuantum(
12264     windows->pixel_info->pen_colors[pen_id].blue);
12265   rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12266   XSetCursorState(display,windows,MagickFalse);
12267   if (rotate_image == (Image *) NULL)
12268     return(MagickFalse);
12269   *image=DestroyImage(*image);
12270   *image=rotate_image;
12271   if (windows->image.crop_geometry != (char *) NULL)
12272     {
12273       /*
12274         Rotate crop geometry.
12275       */
12276       width=(unsigned int) (*image)->columns;
12277       height=(unsigned int) (*image)->rows;
12278       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12279       switch (rotations % 4)
12280       {
12281         default:
12282         case 0:
12283           break;
12284         case 1:
12285         {
12286           /*
12287             Rotate 90 degrees.
12288           */
12289           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12290             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12291             (int) height-y,x);
12292           break;
12293         }
12294         case 2:
12295         {
12296           /*
12297             Rotate 180 degrees.
12298           */
12299           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12300             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12301           break;
12302         }
12303         case 3:
12304         {
12305           /*
12306             Rotate 270 degrees.
12307           */
12308           (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12309             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12310           break;
12311         }
12312       }
12313     }
12314   if (windows->image.orphan != MagickFalse)
12315     return(MagickTrue);
12316   if (normalized_degrees != 0.0)
12317     {
12318       /*
12319         Update image colormap.
12320       */
12321       windows->image.window_changes.width=(int) (*image)->columns;
12322       windows->image.window_changes.height=(int) (*image)->rows;
12323       if (windows->image.crop_geometry != (char *) NULL)
12324         {
12325           /*
12326             Obtain dimensions of image from crop geometry.
12327           */
12328           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12329             &width,&height);
12330           windows->image.window_changes.width=(int) width;
12331           windows->image.window_changes.height=(int) height;
12332         }
12333       XConfigureImageColormap(display,resource_info,windows,*image);
12334     }
12335   else
12336     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12337       {
12338         windows->image.window_changes.width=windows->image.ximage->height;
12339         windows->image.window_changes.height=windows->image.ximage->width;
12340       }
12341   /*
12342     Update image configuration.
12343   */
12344   (void) XConfigureImage(display,resource_info,windows,*image);
12345   return(MagickTrue);
12346 }
12347 \f
12348 /*
12349 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12350 %                                                                             %
12351 %                                                                             %
12352 %                                                                             %
12353 +   X S a v e I m a g e                                                       %
12354 %                                                                             %
12355 %                                                                             %
12356 %                                                                             %
12357 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12358 %
12359 %  XSaveImage() saves an image to a file.
12360 %
12361 %  The format of the XSaveImage method is:
12362 %
12363 %      MagickBooleanType XSaveImage(Display *display,
12364 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
12365 %
12366 %  A description of each parameter follows:
12367 %
12368 %    o display: Specifies a connection to an X server; returned from
12369 %      XOpenDisplay.
12370 %
12371 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12372 %
12373 %    o windows: Specifies a pointer to a XWindows structure.
12374 %
12375 %    o image: the image.
12376 %
12377 */
12378 static MagickBooleanType XSaveImage(Display *display,
12379   XResourceInfo *resource_info,XWindows *windows,Image *image)
12380 {
12381   char
12382     filename[MaxTextExtent],
12383     geometry[MaxTextExtent];
12384
12385   Image
12386     *save_image;
12387
12388   ImageInfo
12389     *image_info;
12390
12391   MagickStatusType
12392     status;
12393
12394   /*
12395     Request file name from user.
12396   */
12397   if (resource_info->write_filename != (char *) NULL)
12398     (void) CopyMagickString(filename,resource_info->write_filename,
12399       MaxTextExtent);
12400   else
12401     {
12402       char
12403         path[MaxTextExtent];
12404
12405       int
12406         status;
12407
12408       GetPathComponent(image->filename,HeadPath,path);
12409       GetPathComponent(image->filename,TailPath,filename);
12410       status=chdir(path);
12411       if (status == -1)
12412         (void) ThrowMagickException(&image->exception,GetMagickModule(),
12413           FileOpenError,"UnableToOpenFile","%s",path);
12414     }
12415   XFileBrowserWidget(display,windows,"Save",filename);
12416   if (*filename == '\0')
12417     return(MagickTrue);
12418   if (IsPathAccessible(filename) != MagickFalse)
12419     {
12420       int
12421         status;
12422
12423       /*
12424         File exists-- seek user's permission before overwriting.
12425       */
12426       status=XConfirmWidget(display,windows,"Overwrite",filename);
12427       if (status <= 0)
12428         return(MagickTrue);
12429     }
12430   image_info=CloneImageInfo(resource_info->image_info);
12431   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12432   (void) SetImageInfo(image_info,1,&image->exception);
12433   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12434       (LocaleCompare(image_info->magick,"JPG") == 0))
12435     {
12436       char
12437         quality[MaxTextExtent];
12438
12439       int
12440         status;
12441
12442       /*
12443         Request JPEG quality from user.
12444       */
12445       (void) FormatMagickString(quality,MaxTextExtent,"%.20g",(double)
12446         image->quality);
12447       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12448         quality);
12449       if (*quality == '\0')
12450         return(MagickTrue);
12451       image->quality=StringToUnsignedLong(quality);
12452       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12453     }
12454   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12455       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12456       (LocaleCompare(image_info->magick,"PS") == 0) ||
12457       (LocaleCompare(image_info->magick,"PS2") == 0))
12458     {
12459       char
12460         geometry[MaxTextExtent];
12461
12462       /*
12463         Request page geometry from user.
12464       */
12465       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12466       if (LocaleCompare(image_info->magick,"PDF") == 0)
12467         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12468       if (image_info->page != (char *) NULL)
12469         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12470       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12471         "Select page geometry:",geometry);
12472       if (*geometry != '\0')
12473         image_info->page=GetPageGeometry(geometry);
12474     }
12475   /*
12476     Apply image transforms.
12477   */
12478   XSetCursorState(display,windows,MagickTrue);
12479   XCheckRefreshWindows(display,windows);
12480   save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12481   if (save_image == (Image *) NULL)
12482     return(MagickFalse);
12483   (void) FormatMagickString(geometry,MaxTextExtent,"%dx%d!",
12484     windows->image.ximage->width,windows->image.ximage->height);
12485   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12486   /*
12487     Write image.
12488   */
12489   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12490   status=WriteImage(image_info,save_image);
12491   if (status != MagickFalse)
12492     image->taint=MagickFalse;
12493   save_image=DestroyImage(save_image);
12494   image_info=DestroyImageInfo(image_info);
12495   XSetCursorState(display,windows,MagickFalse);
12496   return(status != 0 ? MagickTrue : MagickFalse);
12497 }
12498 \f
12499 /*
12500 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12501 %                                                                             %
12502 %                                                                             %
12503 %                                                                             %
12504 +   X S c r e e n E v e n t                                                   %
12505 %                                                                             %
12506 %                                                                             %
12507 %                                                                             %
12508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12509 %
12510 %  XScreenEvent() handles global events associated with the Pan and Magnify
12511 %  windows.
12512 %
12513 %  The format of the XScreenEvent function is:
12514 %
12515 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12516 %
12517 %  A description of each parameter follows:
12518 %
12519 %    o display: Specifies a pointer to the Display structure;  returned from
12520 %      XOpenDisplay.
12521 %
12522 %    o windows: Specifies a pointer to a XWindows structure.
12523 %
12524 %    o event: Specifies a pointer to a X11 XEvent structure.
12525 %
12526 %
12527 */
12528
12529 #if defined(__cplusplus) || defined(c_plusplus)
12530 extern "C" {
12531 #endif
12532
12533 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12534 {
12535   register XWindows
12536     *windows;
12537
12538   windows=(XWindows *) data;
12539   if ((event->type == ClientMessage) &&
12540       (event->xclient.window == windows->image.id))
12541     return(MagickFalse);
12542   return(MagickTrue);
12543 }
12544
12545 #if defined(__cplusplus) || defined(c_plusplus)
12546 }
12547 #endif
12548
12549 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12550 {
12551   register int
12552     x,
12553     y;
12554
12555   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12556   if (event->xany.window == windows->command.id)
12557     return;
12558   switch (event->type)
12559   {
12560     case ButtonPress:
12561     case ButtonRelease:
12562     {
12563       if ((event->xbutton.button == Button3) &&
12564           (event->xbutton.state & Mod1Mask))
12565         {
12566           /*
12567             Convert Alt-Button3 to Button2.
12568           */
12569           event->xbutton.button=Button2;
12570           event->xbutton.state&=(~Mod1Mask);
12571         }
12572       if (event->xbutton.window == windows->backdrop.id)
12573         {
12574           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12575             event->xbutton.time);
12576           break;
12577         }
12578       if (event->xbutton.window == windows->pan.id)
12579         {
12580           XPanImage(display,windows,event);
12581           break;
12582         }
12583       if (event->xbutton.window == windows->image.id)
12584         if (event->xbutton.button == Button2)
12585           {
12586             /*
12587               Update magnified image.
12588             */
12589             x=event->xbutton.x;
12590             y=event->xbutton.y;
12591             if (x < 0)
12592               x=0;
12593             else
12594               if (x >= (int) windows->image.width)
12595                 x=(int) (windows->image.width-1);
12596             windows->magnify.x=(int) windows->image.x+x;
12597             if (y < 0)
12598               y=0;
12599             else
12600              if (y >= (int) windows->image.height)
12601                y=(int) (windows->image.height-1);
12602             windows->magnify.y=windows->image.y+y;
12603             if (windows->magnify.mapped == MagickFalse)
12604               (void) XMapRaised(display,windows->magnify.id);
12605             XMakeMagnifyImage(display,windows);
12606             if (event->type == ButtonRelease)
12607               (void) XWithdrawWindow(display,windows->info.id,
12608                 windows->info.screen);
12609             break;
12610           }
12611       break;
12612     }
12613     case ClientMessage:
12614     {
12615       /*
12616         If client window delete message, exit.
12617       */
12618       if (event->xclient.message_type != windows->wm_protocols)
12619         break;
12620       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12621         break;
12622       if (event->xclient.window == windows->magnify.id)
12623         {
12624           (void) XWithdrawWindow(display,windows->magnify.id,
12625             windows->magnify.screen);
12626           break;
12627         }
12628       break;
12629     }
12630     case ConfigureNotify:
12631     {
12632       if (event->xconfigure.window == windows->magnify.id)
12633         {
12634           unsigned int
12635             magnify;
12636
12637           /*
12638             Magnify window has a new configuration.
12639           */
12640           windows->magnify.width=(unsigned int) event->xconfigure.width;
12641           windows->magnify.height=(unsigned int) event->xconfigure.height;
12642           if (windows->magnify.mapped == MagickFalse)
12643             break;
12644           magnify=1;
12645           while ((int) magnify <= event->xconfigure.width)
12646             magnify<<=1;
12647           while ((int) magnify <= event->xconfigure.height)
12648             magnify<<=1;
12649           magnify>>=1;
12650           if (((int) magnify != event->xconfigure.width) ||
12651               ((int) magnify != event->xconfigure.height))
12652             {
12653               XWindowChanges
12654                 window_changes;
12655
12656               window_changes.width=(int) magnify;
12657               window_changes.height=(int) magnify;
12658               (void) XReconfigureWMWindow(display,windows->magnify.id,
12659                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12660                 &window_changes);
12661               break;
12662             }
12663           XMakeMagnifyImage(display,windows);
12664           break;
12665         }
12666       break;
12667     }
12668     case Expose:
12669     {
12670       if (event->xexpose.window == windows->image.id)
12671         {
12672           XRefreshWindow(display,&windows->image,event);
12673           break;
12674         }
12675       if (event->xexpose.window == windows->pan.id)
12676         if (event->xexpose.count == 0)
12677           {
12678             XDrawPanRectangle(display,windows);
12679             break;
12680           }
12681       if (event->xexpose.window == windows->magnify.id)
12682         if (event->xexpose.count == 0)
12683           {
12684             XMakeMagnifyImage(display,windows);
12685             break;
12686           }
12687       break;
12688     }
12689     case KeyPress:
12690     {
12691       char
12692         command[MaxTextExtent];
12693
12694       KeySym
12695         key_symbol;
12696
12697       if (event->xkey.window != windows->magnify.id)
12698         break;
12699       /*
12700         Respond to a user key press.
12701       */
12702       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12703         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12704       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12705       break;
12706     }
12707     case MapNotify:
12708     {
12709       if (event->xmap.window == windows->magnify.id)
12710         {
12711           windows->magnify.mapped=MagickTrue;
12712           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12713           break;
12714         }
12715       if (event->xmap.window == windows->info.id)
12716         {
12717           windows->info.mapped=MagickTrue;
12718           break;
12719         }
12720       break;
12721     }
12722     case MotionNotify:
12723     {
12724       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12725       if (event->xmotion.window == windows->image.id)
12726         if (windows->magnify.mapped != MagickFalse)
12727           {
12728             /*
12729               Update magnified image.
12730             */
12731             x=event->xmotion.x;
12732             y=event->xmotion.y;
12733             if (x < 0)
12734               x=0;
12735             else
12736               if (x >= (int) windows->image.width)
12737                 x=(int) (windows->image.width-1);
12738             windows->magnify.x=(int) windows->image.x+x;
12739             if (y < 0)
12740               y=0;
12741             else
12742              if (y >= (int) windows->image.height)
12743                y=(int) (windows->image.height-1);
12744             windows->magnify.y=windows->image.y+y;
12745             XMakeMagnifyImage(display,windows);
12746           }
12747       break;
12748     }
12749     case UnmapNotify:
12750     {
12751       if (event->xunmap.window == windows->magnify.id)
12752         {
12753           windows->magnify.mapped=MagickFalse;
12754           break;
12755         }
12756       if (event->xunmap.window == windows->info.id)
12757         {
12758           windows->info.mapped=MagickFalse;
12759           break;
12760         }
12761       break;
12762     }
12763     default:
12764       break;
12765   }
12766 }
12767 \f
12768 /*
12769 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12770 %                                                                             %
12771 %                                                                             %
12772 %                                                                             %
12773 +   X S e t C r o p G e o m e t r y                                           %
12774 %                                                                             %
12775 %                                                                             %
12776 %                                                                             %
12777 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12778 %
12779 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12780 %  and translates it to a cropping geometry relative to the image.
12781 %
12782 %  The format of the XSetCropGeometry method is:
12783 %
12784 %      void XSetCropGeometry(Display *display,XWindows *windows,
12785 %        RectangleInfo *crop_info,Image *image)
12786 %
12787 %  A description of each parameter follows:
12788 %
12789 %    o display: Specifies a connection to an X server; returned from
12790 %      XOpenDisplay.
12791 %
12792 %    o windows: Specifies a pointer to a XWindows structure.
12793 %
12794 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12795 %      Image window to crop.
12796 %
12797 %    o image: the image.
12798 %
12799 */
12800 static void XSetCropGeometry(Display *display,XWindows *windows,
12801   RectangleInfo *crop_info,Image *image)
12802 {
12803   char
12804     text[MaxTextExtent];
12805
12806   int
12807     x,
12808     y;
12809
12810   MagickRealType
12811     scale_factor;
12812
12813   unsigned int
12814     height,
12815     width;
12816
12817   if (windows->info.mapped != MagickFalse)
12818     {
12819       /*
12820         Display info on cropping rectangle.
12821       */
12822       (void) FormatMagickString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12823         (double) crop_info->width,(double) crop_info->height,(double)
12824         crop_info->x,(double) crop_info->y);
12825       XInfoWidget(display,windows,text);
12826     }
12827   /*
12828     Cropping geometry is relative to any previous crop geometry.
12829   */
12830   x=0;
12831   y=0;
12832   width=(unsigned int) image->columns;
12833   height=(unsigned int) image->rows;
12834   if (windows->image.crop_geometry != (char *) NULL)
12835     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12836   else
12837     windows->image.crop_geometry=AcquireString((char *) NULL);
12838   /*
12839     Define the crop geometry string from the cropping rectangle.
12840   */
12841   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12842   if (crop_info->x > 0)
12843     x+=(int) (scale_factor*crop_info->x+0.5);
12844   width=(unsigned int) (scale_factor*crop_info->width+0.5);
12845   if (width == 0)
12846     width=1;
12847   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12848   if (crop_info->y > 0)
12849     y+=(int) (scale_factor*crop_info->y+0.5);
12850   height=(unsigned int) (scale_factor*crop_info->height+0.5);
12851   if (height == 0)
12852     height=1;
12853   (void) FormatMagickString(windows->image.crop_geometry,MaxTextExtent,
12854     "%ux%u%+d%+d",width,height,x,y);
12855 }
12856 \f
12857 /*
12858 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12859 %                                                                             %
12860 %                                                                             %
12861 %                                                                             %
12862 +   X T i l e I m a g e                                                       %
12863 %                                                                             %
12864 %                                                                             %
12865 %                                                                             %
12866 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12867 %
12868 %  XTileImage() loads or deletes a selected tile from a visual image directory.
12869 %  The load or delete command is chosen from a menu.
12870 %
12871 %  The format of the XTileImage method is:
12872 %
12873 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
12874 %        XWindows *windows,Image *image,XEvent *event)
12875 %
12876 %  A description of each parameter follows:
12877 %
12878 %    o tile_image:  XTileImage reads or deletes the tile image
12879 %      and returns it.  A null image is returned if an error occurs.
12880 %
12881 %    o display: Specifies a connection to an X server;  returned from
12882 %      XOpenDisplay.
12883 %
12884 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12885 %
12886 %    o windows: Specifies a pointer to a XWindows structure.
12887 %
12888 %    o image: the image; returned from ReadImage.
12889 %
12890 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
12891 %      the entire image is refreshed.
12892 %
12893 */
12894 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12895   XWindows *windows,Image *image,XEvent *event)
12896 {
12897   static const char
12898     *VerbMenu[] =
12899     {
12900       "Load",
12901       "Next",
12902       "Former",
12903       "Delete",
12904       "Update",
12905       (char *) NULL,
12906     };
12907
12908   static const ModeType
12909     TileCommands[] =
12910     {
12911       TileLoadCommand,
12912       TileNextCommand,
12913       TileFormerCommand,
12914       TileDeleteCommand,
12915       TileUpdateCommand
12916     };
12917
12918   char
12919     command[MaxTextExtent],
12920     filename[MaxTextExtent];
12921
12922   Image
12923     *tile_image;
12924
12925   int
12926     id,
12927     status,
12928     tile,
12929     x,
12930     y;
12931
12932   MagickRealType
12933     scale_factor;
12934
12935   register char
12936     *p,
12937     *q;
12938
12939   register int
12940     i;
12941
12942   unsigned int
12943     height,
12944     width;
12945
12946   /*
12947     Tile image is relative to montage image configuration.
12948   */
12949   x=0;
12950   y=0;
12951   width=(unsigned int) image->columns;
12952   height=(unsigned int) image->rows;
12953   if (windows->image.crop_geometry != (char *) NULL)
12954     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12955   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12956   event->xbutton.x+=windows->image.x;
12957   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
12958   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12959   event->xbutton.y+=windows->image.y;
12960   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
12961   /*
12962     Determine size and location of each tile in the visual image directory.
12963   */
12964   width=(unsigned int) image->columns;
12965   height=(unsigned int) image->rows;
12966   x=0;
12967   y=0;
12968   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
12969   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
12970     (event->xbutton.x-x)/width;
12971   if (tile < 0)
12972     {
12973       /*
12974         Button press is outside any tile.
12975       */
12976       (void) XBell(display,0);
12977       return((Image *) NULL);
12978     }
12979   /*
12980     Determine file name from the tile directory.
12981   */
12982   p=image->directory;
12983   for (i=tile; (i != 0) && (*p != '\0'); )
12984   {
12985     if (*p == '\n')
12986       i--;
12987     p++;
12988   }
12989   if (*p == '\0')
12990     {
12991       /*
12992         Button press is outside any tile.
12993       */
12994       (void) XBell(display,0);
12995       return((Image *) NULL);
12996     }
12997   /*
12998     Select a command from the pop-up menu.
12999   */
13000   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13001   if (id < 0)
13002     return((Image *) NULL);
13003   q=p;
13004   while ((*q != '\n') && (*q != '\0'))
13005     q++;
13006   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13007   /*
13008     Perform command for the selected tile.
13009   */
13010   XSetCursorState(display,windows,MagickTrue);
13011   XCheckRefreshWindows(display,windows);
13012   tile_image=NewImageList();
13013   switch (TileCommands[id])
13014   {
13015     case TileLoadCommand:
13016     {
13017       /*
13018         Load tile image.
13019       */
13020       XCheckRefreshWindows(display,windows);
13021       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13022         MaxTextExtent);
13023       (void) CopyMagickString(resource_info->image_info->filename,filename,
13024         MaxTextExtent);
13025       tile_image=ReadImage(resource_info->image_info,&image->exception);
13026       CatchException(&image->exception);
13027       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13028       break;
13029     }
13030     case TileNextCommand:
13031     {
13032       /*
13033         Display next image.
13034       */
13035       XClientMessage(display,windows->image.id,windows->im_protocols,
13036         windows->im_next_image,CurrentTime);
13037       break;
13038     }
13039     case TileFormerCommand:
13040     {
13041       /*
13042         Display former image.
13043       */
13044       XClientMessage(display,windows->image.id,windows->im_protocols,
13045         windows->im_former_image,CurrentTime);
13046       break;
13047     }
13048     case TileDeleteCommand:
13049     {
13050       /*
13051         Delete tile image.
13052       */
13053       if (IsPathAccessible(filename) == MagickFalse)
13054         {
13055           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13056           break;
13057         }
13058       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13059       if (status <= 0)
13060         break;
13061       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13062       if (status != MagickFalse)
13063         {
13064           XNoticeWidget(display,windows,"Unable to delete image file:",
13065             filename);
13066           break;
13067         }
13068     }
13069     case TileUpdateCommand:
13070     {
13071       ExceptionInfo
13072         *exception;
13073
13074       int
13075         x_offset,
13076         y_offset;
13077
13078       PixelPacket
13079         pixel;
13080
13081       register int
13082         j;
13083
13084       register PixelPacket
13085         *s;
13086
13087       /*
13088         Ensure all the images exist.
13089       */
13090       tile=0;
13091       for (p=image->directory; *p != '\0'; p++)
13092       {
13093         CacheView
13094           *image_view;
13095
13096         q=p;
13097         while ((*q != '\n') && (*q != '\0'))
13098           q++;
13099         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13100         p=q;
13101         if (IsPathAccessible(filename) != MagickFalse)
13102           {
13103             tile++;
13104             continue;
13105           }
13106         /*
13107           Overwrite tile with background color.
13108         */
13109         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13110         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13111         exception=(&image->exception);
13112         image_view=AcquireCacheView(image);
13113         (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13114         for (i=0; i < (int) height; i++)
13115         {
13116           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13117             y_offset+i,width,1,exception);
13118           if (s == (PixelPacket *) NULL)
13119             break;
13120           for (j=0; j < (int) width; j++)
13121             *s++=pixel;
13122           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13123             break;
13124         }
13125         image_view=DestroyCacheView(image_view);
13126         tile++;
13127       }
13128       windows->image.window_changes.width=(int) image->columns;
13129       windows->image.window_changes.height=(int) image->rows;
13130       XConfigureImageColormap(display,resource_info,windows,image);
13131       (void) XConfigureImage(display,resource_info,windows,image);
13132       break;
13133     }
13134     default:
13135       break;
13136   }
13137   XSetCursorState(display,windows,MagickFalse);
13138   return(tile_image);
13139 }
13140 \f
13141 /*
13142 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13143 %                                                                             %
13144 %                                                                             %
13145 %                                                                             %
13146 +   X T r a n s l a t e I m a g e                                             %
13147 %                                                                             %
13148 %                                                                             %
13149 %                                                                             %
13150 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13151 %
13152 %  XTranslateImage() translates the image within an Image window by one pixel
13153 %  as specified by the key symbol.  If the image has a `montage string the
13154 %  translation is respect to the width and height contained within the string.
13155 %
13156 %  The format of the XTranslateImage method is:
13157 %
13158 %      void XTranslateImage(Display *display,XWindows *windows,
13159 %        Image *image,const KeySym key_symbol)
13160 %
13161 %  A description of each parameter follows:
13162 %
13163 %    o display: Specifies a connection to an X server; returned from
13164 %      XOpenDisplay.
13165 %
13166 %    o windows: Specifies a pointer to a XWindows structure.
13167 %
13168 %    o image: the image.
13169 %
13170 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13171 %      to trim.
13172 %
13173 */
13174 static void XTranslateImage(Display *display,XWindows *windows,
13175   Image *image,const KeySym key_symbol)
13176 {
13177   char
13178     text[MaxTextExtent];
13179
13180   int
13181     x,
13182     y;
13183
13184   unsigned int
13185     x_offset,
13186     y_offset;
13187
13188   /*
13189     User specified a pan position offset.
13190   */
13191   x_offset=windows->image.width;
13192   y_offset=windows->image.height;
13193   if (image->montage != (char *) NULL)
13194     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13195   switch ((int) key_symbol)
13196   {
13197     case XK_Home:
13198     case XK_KP_Home:
13199     {
13200       windows->image.x=(int) windows->image.width/2;
13201       windows->image.y=(int) windows->image.height/2;
13202       break;
13203     }
13204     case XK_Left:
13205     case XK_KP_Left:
13206     {
13207       windows->image.x-=x_offset;
13208       break;
13209     }
13210     case XK_Next:
13211     case XK_Up:
13212     case XK_KP_Up:
13213     {
13214       windows->image.y-=y_offset;
13215       break;
13216     }
13217     case XK_Right:
13218     case XK_KP_Right:
13219     {
13220       windows->image.x+=x_offset;
13221       break;
13222     }
13223     case XK_Prior:
13224     case XK_Down:
13225     case XK_KP_Down:
13226     {
13227       windows->image.y+=y_offset;
13228       break;
13229     }
13230     default:
13231       return;
13232   }
13233   /*
13234     Check boundary conditions.
13235   */
13236   if (windows->image.x < 0)
13237     windows->image.x=0;
13238   else
13239     if ((int) (windows->image.x+windows->image.width) >
13240         windows->image.ximage->width)
13241       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13242   if (windows->image.y < 0)
13243     windows->image.y=0;
13244   else
13245     if ((int) (windows->image.y+windows->image.height) >
13246         windows->image.ximage->height)
13247       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13248   /*
13249     Refresh Image window.
13250   */
13251   (void) FormatMagickString(text,MaxTextExtent," %ux%u%+d%+d ",
13252     windows->image.width,windows->image.height,windows->image.x,
13253     windows->image.y);
13254   XInfoWidget(display,windows,text);
13255   XCheckRefreshWindows(display,windows);
13256   XDrawPanRectangle(display,windows);
13257   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13258   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13259 }
13260 \f
13261 /*
13262 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13263 %                                                                             %
13264 %                                                                             %
13265 %                                                                             %
13266 +   X T r i m I m a g e                                                       %
13267 %                                                                             %
13268 %                                                                             %
13269 %                                                                             %
13270 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13271 %
13272 %  XTrimImage() trims the edges from the Image window.
13273 %
13274 %  The format of the XTrimImage method is:
13275 %
13276 %      MagickBooleanType XTrimImage(Display *display,
13277 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
13278 %
13279 %  A description of each parameter follows:
13280 %
13281 %    o display: Specifies a connection to an X server; returned from
13282 %      XOpenDisplay.
13283 %
13284 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13285 %
13286 %    o windows: Specifies a pointer to a XWindows structure.
13287 %
13288 %    o image: the image.
13289 %
13290 */
13291 static MagickBooleanType XTrimImage(Display *display,
13292   XResourceInfo *resource_info,XWindows *windows,Image *image)
13293 {
13294   RectangleInfo
13295     trim_info;
13296
13297   register int
13298     x,
13299     y;
13300
13301   size_t
13302     background,
13303     pixel;
13304
13305   /*
13306     Trim edges from image.
13307   */
13308   XSetCursorState(display,windows,MagickTrue);
13309   XCheckRefreshWindows(display,windows);
13310   /*
13311     Crop the left edge.
13312   */
13313   background=XGetPixel(windows->image.ximage,0,0);
13314   trim_info.width=(size_t) windows->image.ximage->width;
13315   for (x=0; x < windows->image.ximage->width; x++)
13316   {
13317     for (y=0; y < windows->image.ximage->height; y++)
13318     {
13319       pixel=XGetPixel(windows->image.ximage,x,y);
13320       if (pixel != background)
13321         break;
13322     }
13323     if (y < windows->image.ximage->height)
13324       break;
13325   }
13326   trim_info.x=(ssize_t) x;
13327   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13328     {
13329       XSetCursorState(display,windows,MagickFalse);
13330       return(MagickFalse);
13331     }
13332   /*
13333     Crop the right edge.
13334   */
13335   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13336   for (x=windows->image.ximage->width-1; x != 0; x--)
13337   {
13338     for (y=0; y < windows->image.ximage->height; y++)
13339     {
13340       pixel=XGetPixel(windows->image.ximage,x,y);
13341       if (pixel != background)
13342         break;
13343     }
13344     if (y < windows->image.ximage->height)
13345       break;
13346   }
13347   trim_info.width=(size_t) (x-trim_info.x+1);
13348   /*
13349     Crop the top edge.
13350   */
13351   background=XGetPixel(windows->image.ximage,0,0);
13352   trim_info.height=(size_t) windows->image.ximage->height;
13353   for (y=0; y < windows->image.ximage->height; y++)
13354   {
13355     for (x=0; x < windows->image.ximage->width; x++)
13356     {
13357       pixel=XGetPixel(windows->image.ximage,x,y);
13358       if (pixel != background)
13359         break;
13360     }
13361     if (x < windows->image.ximage->width)
13362       break;
13363   }
13364   trim_info.y=(ssize_t) y;
13365   /*
13366     Crop the bottom edge.
13367   */
13368   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13369   for (y=windows->image.ximage->height-1; y != 0; y--)
13370   {
13371     for (x=0; x < windows->image.ximage->width; x++)
13372     {
13373       pixel=XGetPixel(windows->image.ximage,x,y);
13374       if (pixel != background)
13375         break;
13376     }
13377     if (x < windows->image.ximage->width)
13378       break;
13379   }
13380   trim_info.height=(size_t) y-trim_info.y+1;
13381   if (((unsigned int) trim_info.width != windows->image.width) ||
13382       ((unsigned int) trim_info.height != windows->image.height))
13383     {
13384       /*
13385         Reconfigure Image window as defined by the trimming rectangle.
13386       */
13387       XSetCropGeometry(display,windows,&trim_info,image);
13388       windows->image.window_changes.width=(int) trim_info.width;
13389       windows->image.window_changes.height=(int) trim_info.height;
13390       (void) XConfigureImage(display,resource_info,windows,image);
13391     }
13392   XSetCursorState(display,windows,MagickFalse);
13393   return(MagickTrue);
13394 }
13395 \f
13396 /*
13397 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13398 %                                                                             %
13399 %                                                                             %
13400 %                                                                             %
13401 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13402 %                                                                             %
13403 %                                                                             %
13404 %                                                                             %
13405 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13406 %
13407 %  XVisualDirectoryImage() creates a Visual Image Directory.
13408 %
13409 %  The format of the XVisualDirectoryImage method is:
13410 %
13411 %      Image *XVisualDirectoryImage(Display *display,
13412 %        XResourceInfo *resource_info,XWindows *windows)
13413 %
13414 %  A description of each parameter follows:
13415 %
13416 %    o nexus: Method XVisualDirectoryImage returns a visual image
13417 %      directory if it can be created successfully.  Otherwise a null image
13418 %      is returned.
13419 %
13420 %    o display: Specifies a connection to an X server; returned from
13421 %      XOpenDisplay.
13422 %
13423 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13424 %
13425 %    o windows: Specifies a pointer to a XWindows structure.
13426 %
13427 */
13428 static Image *XVisualDirectoryImage(Display *display,
13429   XResourceInfo *resource_info,XWindows *windows)
13430 {
13431 #define TileImageTag  "Scale/Image"
13432 #define XClientName  "montage"
13433
13434   char
13435     **filelist;
13436
13437   ExceptionInfo
13438     *exception;
13439
13440   Image
13441     *images,
13442     *montage_image,
13443     *next_image,
13444     *thumbnail_image;
13445
13446   ImageInfo
13447     *read_info;
13448
13449   int
13450     number_files;
13451
13452   MagickBooleanType
13453     backdrop;
13454
13455   MagickStatusType
13456     status;
13457
13458   MontageInfo
13459     *montage_info;
13460
13461   RectangleInfo
13462     geometry;
13463
13464   register int
13465     i;
13466
13467   static char
13468     filename[MaxTextExtent] = "\0",
13469     filenames[MaxTextExtent] = "*";
13470
13471   XResourceInfo
13472     background_resources;
13473
13474   /*
13475     Request file name from user.
13476   */
13477   XFileBrowserWidget(display,windows,"Directory",filenames);
13478   if (*filenames == '\0')
13479     return((Image *) NULL);
13480   /*
13481     Expand the filenames.
13482   */
13483   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13484   if (filelist == (char **) NULL)
13485     {
13486       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13487         filenames);
13488       return((Image *) NULL);
13489     }
13490   number_files=1;
13491   filelist[0]=filenames;
13492   status=ExpandFilenames(&number_files,&filelist);
13493   if ((status == MagickFalse) || (number_files == 0))
13494     {
13495       if (number_files == 0)
13496         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13497       else
13498         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13499           filenames);
13500       return((Image *) NULL);
13501     }
13502   /*
13503     Set image background resources.
13504   */
13505   background_resources=(*resource_info);
13506   background_resources.window_id=AcquireString("");
13507   (void) FormatMagickString(background_resources.window_id,MaxTextExtent,
13508     "0x%lx",windows->image.id);
13509   background_resources.backdrop=MagickTrue;
13510   /*
13511     Read each image and convert them to a tile.
13512   */
13513   backdrop=(windows->visual_info->klass == TrueColor) ||
13514     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13515   read_info=CloneImageInfo(resource_info->image_info);
13516   (void) SetImageOption(read_info,"jpeg:size","120x120");
13517   (void) CloneString(&read_info->size,DefaultTileGeometry);
13518   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13519     (void *) NULL);
13520   images=NewImageList();
13521   exception=AcquireExceptionInfo();
13522   XSetCursorState(display,windows,MagickTrue);
13523   XCheckRefreshWindows(display,windows);
13524   for (i=0; i < (int) number_files; i++)
13525   {
13526     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13527     filelist[i]=DestroyString(filelist[i]);
13528     *read_info->magick='\0';
13529     next_image=ReadImage(read_info,exception);
13530     CatchException(exception);
13531     if (next_image != (Image *) NULL)
13532       {
13533         (void) DeleteImageProperty(next_image,"label");
13534         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13535           read_info,next_image,DefaultTileLabel));
13536         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13537           exception);
13538         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13539           geometry.height,exception);
13540         if (thumbnail_image != (Image *) NULL)
13541           {
13542             next_image=DestroyImage(next_image);
13543             next_image=thumbnail_image;
13544           }
13545         if (backdrop)
13546           {
13547             (void) XDisplayBackgroundImage(display,&background_resources,
13548               next_image);
13549             XSetCursorState(display,windows,MagickTrue);
13550           }
13551         AppendImageToList(&images,next_image);
13552         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13553           {
13554             MagickBooleanType
13555               proceed;
13556
13557             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13558               (MagickSizeType) number_files);
13559             if (proceed == MagickFalse)
13560               break;
13561           }
13562       }
13563   }
13564   exception=DestroyExceptionInfo(exception);
13565   filelist=(char **) RelinquishMagickMemory(filelist);
13566   if (images == (Image *) NULL)
13567     {
13568       read_info=DestroyImageInfo(read_info);
13569       XSetCursorState(display,windows,MagickFalse);
13570       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13571       return((Image *) NULL);
13572     }
13573   /*
13574     Create the Visual Image Directory.
13575   */
13576   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13577   montage_info->pointsize=10;
13578   if (resource_info->font != (char *) NULL)
13579     (void) CloneString(&montage_info->font,resource_info->font);
13580   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13581   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13582     images),&images->exception);
13583   images=DestroyImageList(images);
13584   montage_info=DestroyMontageInfo(montage_info);
13585   read_info=DestroyImageInfo(read_info);
13586   XSetCursorState(display,windows,MagickFalse);
13587   if (montage_image == (Image *) NULL)
13588     return(montage_image);
13589   XClientMessage(display,windows->image.id,windows->im_protocols,
13590     windows->im_next_image,CurrentTime);
13591   return(montage_image);
13592 }
13593 \f
13594 /*
13595 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13596 %                                                                             %
13597 %                                                                             %
13598 %                                                                             %
13599 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13600 %                                                                             %
13601 %                                                                             %
13602 %                                                                             %
13603 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13604 %
13605 %  XDisplayBackgroundImage() displays an image in the background of a window.
13606 %
13607 %  The format of the XDisplayBackgroundImage method is:
13608 %
13609 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13610 %        XResourceInfo *resource_info,Image *image)
13611 %
13612 %  A description of each parameter follows:
13613 %
13614 %    o display: Specifies a connection to an X server;  returned from
13615 %      XOpenDisplay.
13616 %
13617 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13618 %
13619 %    o image: the image.
13620 %
13621 */
13622 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13623   XResourceInfo *resource_info,Image *image)
13624 {
13625   char
13626     geometry[MaxTextExtent],
13627     visual_type[MaxTextExtent];
13628
13629   int
13630     height,
13631     status,
13632     width;
13633
13634   RectangleInfo
13635     geometry_info;
13636
13637   static XPixelInfo
13638     pixel;
13639
13640   static XStandardColormap
13641     *map_info;
13642
13643   static XVisualInfo
13644     *visual_info = (XVisualInfo *) NULL;
13645
13646   static XWindowInfo
13647     window_info;
13648
13649   size_t
13650     delay;
13651
13652   Window
13653     root_window;
13654
13655   XGCValues
13656     context_values;
13657
13658   XResourceInfo
13659     resources;
13660
13661   XWindowAttributes
13662     window_attributes;
13663
13664   /*
13665     Determine target window.
13666   */
13667   assert(image != (Image *) NULL);
13668   assert(image->signature == MagickSignature);
13669   if (image->debug != MagickFalse)
13670     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13671   resources=(*resource_info);
13672   window_info.id=(Window) NULL;
13673   root_window=XRootWindow(display,XDefaultScreen(display));
13674   if (LocaleCompare(resources.window_id,"root") == 0)
13675     window_info.id=root_window;
13676   else
13677     {
13678       if (isdigit((unsigned char) *resources.window_id) != 0)
13679         window_info.id=XWindowByID(display,root_window,
13680           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13681       if (window_info.id == (Window) NULL)
13682         window_info.id=XWindowByName(display,root_window,resources.window_id);
13683     }
13684   if (window_info.id == (Window) NULL)
13685     {
13686       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13687         resources.window_id);
13688       return(MagickFalse);
13689     }
13690   /*
13691     Determine window visual id.
13692   */
13693   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13694   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13695   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13696   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13697   if (status != 0)
13698     (void) FormatMagickString(visual_type,MaxTextExtent,"0x%lx",
13699       XVisualIDFromVisual(window_attributes.visual));
13700   if (visual_info == (XVisualInfo *) NULL)
13701     {
13702       /*
13703         Allocate standard colormap.
13704       */
13705       map_info=XAllocStandardColormap();
13706       if (map_info == (XStandardColormap *) NULL)
13707         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13708           image->filename);
13709       map_info->colormap=(Colormap) NULL;
13710       pixel.pixels=(unsigned long *) NULL;
13711       /*
13712         Initialize visual info.
13713       */
13714       resources.map_type=(char *) NULL;
13715       resources.visual_type=visual_type;
13716       visual_info=XBestVisualInfo(display,map_info,&resources);
13717       if (visual_info == (XVisualInfo *) NULL)
13718         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13719           resources.visual_type);
13720       /*
13721         Initialize window info.
13722       */
13723       window_info.ximage=(XImage *) NULL;
13724       window_info.matte_image=(XImage *) NULL;
13725       window_info.pixmap=(Pixmap) NULL;
13726       window_info.matte_pixmap=(Pixmap) NULL;
13727     }
13728   /*
13729     Free previous root colors.
13730   */
13731   if (window_info.id == root_window)
13732     (void) XDestroyWindowColors(display,root_window);
13733   /*
13734     Initialize Standard Colormap.
13735   */
13736   resources.colormap=SharedColormap;
13737   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13738   /*
13739     Graphic context superclass.
13740   */
13741   context_values.background=pixel.background_color.pixel;
13742   context_values.foreground=pixel.foreground_color.pixel;
13743   pixel.annotate_context=XCreateGC(display,window_info.id,
13744     (size_t) (GCBackground | GCForeground),&context_values);
13745   if (pixel.annotate_context == (GC) NULL)
13746     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13747       image->filename);
13748   /*
13749     Initialize Image window attributes.
13750   */
13751   window_info.name=AcquireString("\0");
13752   window_info.icon_name=AcquireString("\0");
13753   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13754     &resources,&window_info);
13755   /*
13756     Create the X image.
13757   */
13758   window_info.width=(unsigned int) image->columns;
13759   window_info.height=(unsigned int) image->rows;
13760   if ((image->columns != window_info.width) ||
13761       (image->rows != window_info.height))
13762     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13763       image->filename);
13764   (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>",
13765     window_attributes.width,window_attributes.height);
13766   geometry_info.width=window_info.width;
13767   geometry_info.height=window_info.height;
13768   geometry_info.x=(ssize_t) window_info.x;
13769   geometry_info.y=(ssize_t) window_info.y;
13770   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13771     &geometry_info.width,&geometry_info.height);
13772   window_info.width=(unsigned int) geometry_info.width;
13773   window_info.height=(unsigned int) geometry_info.height;
13774   window_info.x=(int) geometry_info.x;
13775   window_info.y=(int) geometry_info.y;
13776   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13777     window_info.height);
13778   if (status == MagickFalse)
13779     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13780       image->filename);
13781   window_info.x=0;
13782   window_info.y=0;
13783   if (image->debug != MagickFalse)
13784     {
13785       (void) LogMagickEvent(X11Event,GetMagickModule(),
13786         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13787         (double) image->columns,(double) image->rows);
13788       if (image->colors != 0)
13789         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13790           image->colors);
13791       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13792     }
13793   /*
13794     Adjust image dimensions as specified by backdrop or geometry options.
13795   */
13796   width=(int) window_info.width;
13797   height=(int) window_info.height;
13798   if (resources.backdrop != MagickFalse)
13799     {
13800       /*
13801         Center image on window.
13802       */
13803       window_info.x=(window_attributes.width/2)-
13804         (window_info.ximage->width/2);
13805       window_info.y=(window_attributes.height/2)-
13806         (window_info.ximage->height/2);
13807       width=window_attributes.width;
13808       height=window_attributes.height;
13809     }
13810   if ((resources.image_geometry != (char *) NULL) &&
13811       (*resources.image_geometry != '\0'))
13812     {
13813       char
13814         default_geometry[MaxTextExtent];
13815
13816       int
13817         flags,
13818         gravity;
13819
13820       XSizeHints
13821         *size_hints;
13822
13823       /*
13824         User specified geometry.
13825       */
13826       size_hints=XAllocSizeHints();
13827       if (size_hints == (XSizeHints *) NULL)
13828         ThrowXWindowFatalException(ResourceLimitFatalError,
13829           "MemoryAllocationFailed",image->filename);
13830       size_hints->flags=0L;
13831       (void) FormatMagickString(default_geometry,MaxTextExtent,"%dx%d",
13832         width,height);
13833       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13834         default_geometry,window_info.border_width,size_hints,&window_info.x,
13835         &window_info.y,&width,&height,&gravity);
13836       if (flags & (XValue | YValue))
13837         {
13838           width=window_attributes.width;
13839           height=window_attributes.height;
13840         }
13841       (void) XFree((void *) size_hints);
13842     }
13843   /*
13844     Create the X pixmap.
13845   */
13846   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13847     (unsigned int) height,window_info.depth);
13848   if (window_info.pixmap == (Pixmap) NULL)
13849     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13850       image->filename);
13851   /*
13852     Display pixmap on the window.
13853   */
13854   if (((unsigned int) width > window_info.width) ||
13855       ((unsigned int) height > window_info.height))
13856     (void) XFillRectangle(display,window_info.pixmap,
13857       window_info.annotate_context,0,0,(unsigned int) width,
13858       (unsigned int) height);
13859   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13860     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13861     window_info.width,(unsigned int) window_info.height);
13862   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13863   (void) XClearWindow(display,window_info.id);
13864   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13865   XDelay(display,delay == 0UL ? 10UL : delay);
13866   (void) XSync(display,MagickFalse);
13867   return(window_info.id == root_window ? MagickTrue : MagickFalse);
13868 }
13869 \f
13870 /*
13871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13872 %                                                                             %
13873 %                                                                             %
13874 %                                                                             %
13875 +   X D i s p l a y I m a g e                                                 %
13876 %                                                                             %
13877 %                                                                             %
13878 %                                                                             %
13879 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13880 %
13881 %  XDisplayImage() displays an image via X11.  A new image is created and
13882 %  returned if the user interactively transforms the displayed image.
13883 %
13884 %  The format of the XDisplayImage method is:
13885 %
13886 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13887 %        char **argv,int argc,Image **image,size_t *state)
13888 %
13889 %  A description of each parameter follows:
13890 %
13891 %    o nexus:  Method XDisplayImage returns an image when the
13892 %      user chooses 'Open Image' from the command menu or picks a tile
13893 %      from the image directory.  Otherwise a null image is returned.
13894 %
13895 %    o display: Specifies a connection to an X server;  returned from
13896 %      XOpenDisplay.
13897 %
13898 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13899 %
13900 %    o argv: Specifies the application's argument list.
13901 %
13902 %    o argc: Specifies the number of arguments.
13903 %
13904 %    o image: Specifies an address to an address of an Image structure;
13905 %
13906 */
13907 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13908   char **argv,int argc,Image **image,size_t *state)
13909 {
13910 #define MagnifySize  256  /* must be a power of 2 */
13911 #define MagickMenus  10
13912 #define MagickTitle  "Commands"
13913
13914   static const char
13915     *CommandMenu[] =
13916     {
13917       "File",
13918       "Edit",
13919       "View",
13920       "Transform",
13921       "Enhance",
13922       "Effects",
13923       "F/X",
13924       "Image Edit",
13925       "Miscellany",
13926       "Help",
13927       (char *) NULL
13928     },
13929     *FileMenu[] =
13930     {
13931       "Open...",
13932       "Next",
13933       "Former",
13934       "Select...",
13935       "Save...",
13936       "Print...",
13937       "Delete...",
13938       "New...",
13939       "Visual Directory...",
13940       "Quit",
13941       (char *) NULL
13942     },
13943     *EditMenu[] =
13944     {
13945       "Undo",
13946       "Redo",
13947       "Cut",
13948       "Copy",
13949       "Paste",
13950       (char *) NULL
13951     },
13952     *ViewMenu[] =
13953     {
13954       "Half Size",
13955       "Original Size",
13956       "Double Size",
13957       "Resize...",
13958       "Apply",
13959       "Refresh",
13960       "Restore",
13961       (char *) NULL
13962     },
13963     *TransformMenu[] =
13964     {
13965       "Crop",
13966       "Chop",
13967       "Flop",
13968       "Flip",
13969       "Rotate Right",
13970       "Rotate Left",
13971       "Rotate...",
13972       "Shear...",
13973       "Roll...",
13974       "Trim Edges",
13975       (char *) NULL
13976     },
13977     *EnhanceMenu[] =
13978     {
13979       "Hue...",
13980       "Saturation...",
13981       "Brightness...",
13982       "Gamma...",
13983       "Spiff",
13984       "Dull",
13985       "Contrast Stretch...",
13986       "Sigmoidal Contrast...",
13987       "Normalize",
13988       "Equalize",
13989       "Negate",
13990       "Grayscale",
13991       "Map...",
13992       "Quantize...",
13993       (char *) NULL
13994     },
13995     *EffectsMenu[] =
13996     {
13997       "Despeckle",
13998       "Emboss",
13999       "Reduce Noise",
14000       "Add Noise...",
14001       "Sharpen...",
14002       "Blur...",
14003       "Threshold...",
14004       "Edge Detect...",
14005       "Spread...",
14006       "Shade...",
14007       "Raise...",
14008       "Segment...",
14009       (char *) NULL
14010     },
14011     *FXMenu[] =
14012     {
14013       "Solarize...",
14014       "Sepia Tone...",
14015       "Swirl...",
14016       "Implode...",
14017       "Vignette...",
14018       "Wave...",
14019       "Oil Paint...",
14020       "Charcoal Draw...",
14021       (char *) NULL
14022     },
14023     *ImageEditMenu[] =
14024     {
14025       "Annotate...",
14026       "Draw...",
14027       "Color...",
14028       "Matte...",
14029       "Composite...",
14030       "Add Border...",
14031       "Add Frame...",
14032       "Comment...",
14033       "Launch...",
14034       "Region of Interest...",
14035       (char *) NULL
14036     },
14037     *MiscellanyMenu[] =
14038     {
14039       "Image Info",
14040       "Zoom Image",
14041       "Show Preview...",
14042       "Show Histogram",
14043       "Show Matte",
14044       "Background...",
14045       "Slide Show...",
14046       "Preferences...",
14047       (char *) NULL
14048     },
14049     *HelpMenu[] =
14050     {
14051       "Overview",
14052       "Browse Documentation",
14053       "About Display",
14054       (char *) NULL
14055     },
14056     *ShortCutsMenu[] =
14057     {
14058       "Next",
14059       "Former",
14060       "Open...",
14061       "Save...",
14062       "Print...",
14063       "Undo",
14064       "Restore",
14065       "Image Info",
14066       "Quit",
14067       (char *) NULL
14068     },
14069     *VirtualMenu[] =
14070     {
14071       "Image Info",
14072       "Print",
14073       "Next",
14074       "Quit",
14075       (char *) NULL
14076     };
14077
14078   static const char
14079     **Menus[MagickMenus] =
14080     {
14081       FileMenu,
14082       EditMenu,
14083       ViewMenu,
14084       TransformMenu,
14085       EnhanceMenu,
14086       EffectsMenu,
14087       FXMenu,
14088       ImageEditMenu,
14089       MiscellanyMenu,
14090       HelpMenu
14091     };
14092
14093   static CommandType
14094     CommandMenus[] =
14095     {
14096       NullCommand,
14097       NullCommand,
14098       NullCommand,
14099       NullCommand,
14100       NullCommand,
14101       NullCommand,
14102       NullCommand,
14103       NullCommand,
14104       NullCommand,
14105       NullCommand,
14106     },
14107     FileCommands[] =
14108     {
14109       OpenCommand,
14110       NextCommand,
14111       FormerCommand,
14112       SelectCommand,
14113       SaveCommand,
14114       PrintCommand,
14115       DeleteCommand,
14116       NewCommand,
14117       VisualDirectoryCommand,
14118       QuitCommand
14119     },
14120     EditCommands[] =
14121     {
14122       UndoCommand,
14123       RedoCommand,
14124       CutCommand,
14125       CopyCommand,
14126       PasteCommand
14127     },
14128     ViewCommands[] =
14129     {
14130       HalfSizeCommand,
14131       OriginalSizeCommand,
14132       DoubleSizeCommand,
14133       ResizeCommand,
14134       ApplyCommand,
14135       RefreshCommand,
14136       RestoreCommand
14137     },
14138     TransformCommands[] =
14139     {
14140       CropCommand,
14141       ChopCommand,
14142       FlopCommand,
14143       FlipCommand,
14144       RotateRightCommand,
14145       RotateLeftCommand,
14146       RotateCommand,
14147       ShearCommand,
14148       RollCommand,
14149       TrimCommand
14150     },
14151     EnhanceCommands[] =
14152     {
14153       HueCommand,
14154       SaturationCommand,
14155       BrightnessCommand,
14156       GammaCommand,
14157       SpiffCommand,
14158       DullCommand,
14159       ContrastStretchCommand,
14160       SigmoidalContrastCommand,
14161       NormalizeCommand,
14162       EqualizeCommand,
14163       NegateCommand,
14164       GrayscaleCommand,
14165       MapCommand,
14166       QuantizeCommand
14167     },
14168     EffectsCommands[] =
14169     {
14170       DespeckleCommand,
14171       EmbossCommand,
14172       ReduceNoiseCommand,
14173       AddNoiseCommand,
14174       SharpenCommand,
14175       BlurCommand,
14176       ThresholdCommand,
14177       EdgeDetectCommand,
14178       SpreadCommand,
14179       ShadeCommand,
14180       RaiseCommand,
14181       SegmentCommand
14182     },
14183     FXCommands[] =
14184     {
14185       SolarizeCommand,
14186       SepiaToneCommand,
14187       SwirlCommand,
14188       ImplodeCommand,
14189       VignetteCommand,
14190       WaveCommand,
14191       OilPaintCommand,
14192       CharcoalDrawCommand
14193     },
14194     ImageEditCommands[] =
14195     {
14196       AnnotateCommand,
14197       DrawCommand,
14198       ColorCommand,
14199       MatteCommand,
14200       CompositeCommand,
14201       AddBorderCommand,
14202       AddFrameCommand,
14203       CommentCommand,
14204       LaunchCommand,
14205       RegionofInterestCommand
14206     },
14207     MiscellanyCommands[] =
14208     {
14209       InfoCommand,
14210       ZoomCommand,
14211       ShowPreviewCommand,
14212       ShowHistogramCommand,
14213       ShowMatteCommand,
14214       BackgroundCommand,
14215       SlideShowCommand,
14216       PreferencesCommand
14217     },
14218     HelpCommands[] =
14219     {
14220       HelpCommand,
14221       BrowseDocumentationCommand,
14222       VersionCommand
14223     },
14224     ShortCutsCommands[] =
14225     {
14226       NextCommand,
14227       FormerCommand,
14228       OpenCommand,
14229       SaveCommand,
14230       PrintCommand,
14231       UndoCommand,
14232       RestoreCommand,
14233       InfoCommand,
14234       QuitCommand
14235     },
14236     VirtualCommands[] =
14237     {
14238       InfoCommand,
14239       PrintCommand,
14240       NextCommand,
14241       QuitCommand
14242     };
14243
14244   static CommandType
14245     *Commands[MagickMenus] =
14246     {
14247       FileCommands,
14248       EditCommands,
14249       ViewCommands,
14250       TransformCommands,
14251       EnhanceCommands,
14252       EffectsCommands,
14253       FXCommands,
14254       ImageEditCommands,
14255       MiscellanyCommands,
14256       HelpCommands
14257     };
14258
14259   char
14260     command[MaxTextExtent],
14261     *cwd,
14262     geometry[MaxTextExtent],
14263     resource_name[MaxTextExtent];
14264
14265   CommandType
14266     command_type;
14267
14268   Image
14269     *display_image,
14270     *nexus;
14271
14272   int
14273     entry,
14274     id;
14275
14276   KeySym
14277     key_symbol;
14278
14279   MagickStatusType
14280     context_mask,
14281     status;
14282
14283   RectangleInfo
14284     geometry_info;
14285
14286   register int
14287     i;
14288
14289   static char
14290     working_directory[MaxTextExtent];
14291
14292   static XPoint
14293     vid_info;
14294
14295   static XWindowInfo
14296     *magick_windows[MaxXWindows];
14297
14298   static unsigned int
14299     number_windows;
14300
14301   struct stat
14302     attributes;
14303
14304   time_t
14305     timer,
14306     timestamp,
14307     update_time;
14308
14309   unsigned int
14310     height,
14311     width;
14312
14313   size_t
14314     delay;
14315
14316   WarningHandler
14317     warning_handler;
14318
14319   Window
14320     root_window;
14321
14322   XClassHint
14323     *class_hints;
14324
14325   XEvent
14326     event;
14327
14328   XFontStruct
14329     *font_info;
14330
14331   XGCValues
14332     context_values;
14333
14334   XPixelInfo
14335     *icon_pixel,
14336     *pixel;
14337
14338   XResourceInfo
14339     *icon_resources;
14340
14341   XStandardColormap
14342     *icon_map,
14343     *map_info;
14344
14345   XVisualInfo
14346     *icon_visual,
14347     *visual_info;
14348
14349   XWindowChanges
14350     window_changes;
14351
14352   XWindows
14353     *windows;
14354
14355   XWMHints
14356     *manager_hints;
14357
14358   assert(image != (Image **) NULL);
14359   assert((*image)->signature == MagickSignature);
14360   if ((*image)->debug != MagickFalse)
14361     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14362   display_image=(*image);
14363   warning_handler=(WarningHandler) NULL;
14364   windows=XSetWindows((XWindows *) ~0);
14365   if (windows != (XWindows *) NULL)
14366     {
14367       int
14368         status;
14369
14370       status=chdir(working_directory);
14371       if (status == -1)
14372         (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14373           FileOpenError,"UnableToOpenFile","%s",working_directory);
14374       warning_handler=resource_info->display_warnings ?
14375         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14376       warning_handler=resource_info->display_warnings ?
14377         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14378     }
14379   else
14380     {
14381       /*
14382         Allocate windows structure.
14383       */
14384       resource_info->colors=display_image->colors;
14385       windows=XSetWindows(XInitializeWindows(display,resource_info));
14386       if (windows == (XWindows *) NULL)
14387         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14388           (*image)->filename);
14389       /*
14390         Initialize window id's.
14391       */
14392       number_windows=0;
14393       magick_windows[number_windows++]=(&windows->icon);
14394       magick_windows[number_windows++]=(&windows->backdrop);
14395       magick_windows[number_windows++]=(&windows->image);
14396       magick_windows[number_windows++]=(&windows->info);
14397       magick_windows[number_windows++]=(&windows->command);
14398       magick_windows[number_windows++]=(&windows->widget);
14399       magick_windows[number_windows++]=(&windows->popup);
14400       magick_windows[number_windows++]=(&windows->magnify);
14401       magick_windows[number_windows++]=(&windows->pan);
14402       for (i=0; i < (int) number_windows; i++)
14403         magick_windows[i]->id=(Window) NULL;
14404       vid_info.x=0;
14405       vid_info.y=0;
14406     }
14407   /*
14408     Initialize font info.
14409   */
14410   if (windows->font_info != (XFontStruct *) NULL)
14411     (void) XFreeFont(display,windows->font_info);
14412   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14413   if (windows->font_info == (XFontStruct *) NULL)
14414     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14415       resource_info->font);
14416   /*
14417     Initialize Standard Colormap.
14418   */
14419   map_info=windows->map_info;
14420   icon_map=windows->icon_map;
14421   visual_info=windows->visual_info;
14422   icon_visual=windows->icon_visual;
14423   pixel=windows->pixel_info;
14424   icon_pixel=windows->icon_pixel;
14425   font_info=windows->font_info;
14426   icon_resources=windows->icon_resources;
14427   class_hints=windows->class_hints;
14428   manager_hints=windows->manager_hints;
14429   root_window=XRootWindow(display,visual_info->screen);
14430   nexus=NewImageList();
14431   if (display_image->debug != MagickFalse)
14432     {
14433       (void) LogMagickEvent(X11Event,GetMagickModule(),
14434         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14435         (double) display_image->scene,(double) display_image->columns,
14436         (double) display_image->rows);
14437       if (display_image->colors != 0)
14438         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14439           display_image->colors);
14440       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14441         display_image->magick);
14442     }
14443   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14444     map_info,pixel);
14445   display_image->taint=MagickFalse;
14446   /*
14447     Initialize graphic context.
14448   */
14449   windows->context.id=(Window) NULL;
14450   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14451     resource_info,&windows->context);
14452   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14453   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14454   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14455   manager_hints->flags=InputHint | StateHint;
14456   manager_hints->input=MagickFalse;
14457   manager_hints->initial_state=WithdrawnState;
14458   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14459     &windows->context);
14460   if (display_image->debug != MagickFalse)
14461     (void) LogMagickEvent(X11Event,GetMagickModule(),
14462       "Window id: 0x%lx (context)",windows->context.id);
14463   context_values.background=pixel->background_color.pixel;
14464   context_values.font=font_info->fid;
14465   context_values.foreground=pixel->foreground_color.pixel;
14466   context_values.graphics_exposures=MagickFalse;
14467   context_mask=(MagickStatusType)
14468     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14469   if (pixel->annotate_context != (GC) NULL)
14470     (void) XFreeGC(display,pixel->annotate_context);
14471   pixel->annotate_context=XCreateGC(display,windows->context.id,
14472     context_mask,&context_values);
14473   if (pixel->annotate_context == (GC) NULL)
14474     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14475       display_image->filename);
14476   context_values.background=pixel->depth_color.pixel;
14477   if (pixel->widget_context != (GC) NULL)
14478     (void) XFreeGC(display,pixel->widget_context);
14479   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14480     &context_values);
14481   if (pixel->widget_context == (GC) NULL)
14482     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14483       display_image->filename);
14484   context_values.background=pixel->foreground_color.pixel;
14485   context_values.foreground=pixel->background_color.pixel;
14486   context_values.plane_mask=context_values.background ^
14487     context_values.foreground;
14488   if (pixel->highlight_context != (GC) NULL)
14489     (void) XFreeGC(display,pixel->highlight_context);
14490   pixel->highlight_context=XCreateGC(display,windows->context.id,
14491     (size_t) (context_mask | GCPlaneMask),&context_values);
14492   if (pixel->highlight_context == (GC) NULL)
14493     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14494       display_image->filename);
14495   (void) XDestroyWindow(display,windows->context.id);
14496   /*
14497     Initialize icon window.
14498   */
14499   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14500     icon_resources,&windows->icon);
14501   windows->icon.geometry=resource_info->icon_geometry;
14502   XBestIconSize(display,&windows->icon,display_image);
14503   windows->icon.attributes.colormap=XDefaultColormap(display,
14504     icon_visual->screen);
14505   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14506   manager_hints->flags=InputHint | StateHint;
14507   manager_hints->input=MagickFalse;
14508   manager_hints->initial_state=IconicState;
14509   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14510     &windows->icon);
14511   if (display_image->debug != MagickFalse)
14512     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14513       windows->icon.id);
14514   /*
14515     Initialize graphic context for icon window.
14516   */
14517   if (icon_pixel->annotate_context != (GC) NULL)
14518     (void) XFreeGC(display,icon_pixel->annotate_context);
14519   context_values.background=icon_pixel->background_color.pixel;
14520   context_values.foreground=icon_pixel->foreground_color.pixel;
14521   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14522     (size_t) (GCBackground | GCForeground),&context_values);
14523   if (icon_pixel->annotate_context == (GC) NULL)
14524     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14525       display_image->filename);
14526   windows->icon.annotate_context=icon_pixel->annotate_context;
14527   /*
14528     Initialize Image window.
14529   */
14530   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14531     &windows->image);
14532   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14533   if (resource_info->use_shared_memory == MagickFalse)
14534     windows->image.shared_memory=MagickFalse;
14535   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14536     {
14537       char
14538         *title;
14539
14540       title=InterpretImageProperties(resource_info->image_info,display_image,
14541         resource_info->title);
14542       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14543       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14544       title=DestroyString(title);
14545     }
14546   else
14547     {
14548       char
14549         filename[MaxTextExtent];
14550
14551       /*
14552         Window name is the base of the filename.
14553       */
14554       GetPathComponent(display_image->magick_filename,TailPath,filename);
14555       if (GetImageListLength(display_image) == 1)
14556         (void) FormatMagickString(windows->image.name,MaxTextExtent,
14557           "%s: %s",MagickPackageName,filename);
14558       else
14559         (void) FormatMagickString(windows->image.name,MaxTextExtent,
14560           "%s: %s[%.20g of %.20g]",MagickPackageName,filename,(double)
14561           display_image->scene,(double) GetImageListLength(display_image));
14562       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14563     }
14564   if (resource_info->immutable)
14565     windows->image.immutable=MagickTrue;
14566   windows->image.use_pixmap=resource_info->use_pixmap;
14567   windows->image.geometry=resource_info->image_geometry;
14568   (void) FormatMagickString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14569     XDisplayWidth(display,visual_info->screen),
14570     XDisplayHeight(display,visual_info->screen));
14571   geometry_info.width=display_image->columns;
14572   geometry_info.height=display_image->rows;
14573   geometry_info.x=0;
14574   geometry_info.y=0;
14575   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14576     &geometry_info.width,&geometry_info.height);
14577   windows->image.width=(unsigned int) geometry_info.width;
14578   windows->image.height=(unsigned int) geometry_info.height;
14579   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14580     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14581     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14582     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14583   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14584     resource_info,&windows->backdrop);
14585   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14586     {
14587       /*
14588         Initialize backdrop window.
14589       */
14590       windows->backdrop.x=0;
14591       windows->backdrop.y=0;
14592       (void) CloneString(&windows->backdrop.name,"Backdrop");
14593       windows->backdrop.flags=(size_t) (USSize | USPosition);
14594       windows->backdrop.width=(unsigned int)
14595         XDisplayWidth(display,visual_info->screen);
14596       windows->backdrop.height=(unsigned int)
14597         XDisplayHeight(display,visual_info->screen);
14598       windows->backdrop.border_width=0;
14599       windows->backdrop.immutable=MagickTrue;
14600       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14601         ButtonReleaseMask;
14602       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14603         StructureNotifyMask;
14604       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14605       manager_hints->icon_window=windows->icon.id;
14606       manager_hints->input=MagickTrue;
14607       manager_hints->initial_state=resource_info->iconic ? IconicState :
14608         NormalState;
14609       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14610         &windows->backdrop);
14611       if (display_image->debug != MagickFalse)
14612         (void) LogMagickEvent(X11Event,GetMagickModule(),
14613           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14614       (void) XMapWindow(display,windows->backdrop.id);
14615       (void) XClearWindow(display,windows->backdrop.id);
14616       if (windows->image.id != (Window) NULL)
14617         {
14618           (void) XDestroyWindow(display,windows->image.id);
14619           windows->image.id=(Window) NULL;
14620         }
14621       /*
14622         Position image in the center the backdrop.
14623       */
14624       windows->image.flags|=USPosition;
14625       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14626         (windows->image.width/2);
14627       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14628         (windows->image.height/2);
14629     }
14630   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14631   manager_hints->icon_window=windows->icon.id;
14632   manager_hints->input=MagickTrue;
14633   manager_hints->initial_state=resource_info->iconic ? IconicState :
14634     NormalState;
14635   if (windows->group_leader.id != (Window) NULL)
14636     {
14637       /*
14638         Follow the leader.
14639       */
14640       manager_hints->flags|=WindowGroupHint;
14641       manager_hints->window_group=windows->group_leader.id;
14642       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14643       if (display_image->debug != MagickFalse)
14644         (void) LogMagickEvent(X11Event,GetMagickModule(),
14645           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14646     }
14647   XMakeWindow(display,
14648     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14649     argv,argc,class_hints,manager_hints,&windows->image);
14650   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14651     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14652   if (windows->group_leader.id != (Window) NULL)
14653     (void) XSetTransientForHint(display,windows->image.id,
14654       windows->group_leader.id);
14655   if (display_image->debug != MagickFalse)
14656     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14657       windows->image.id);
14658   /*
14659     Initialize Info widget.
14660   */
14661   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14662     &windows->info);
14663   (void) CloneString(&windows->info.name,"Info");
14664   (void) CloneString(&windows->info.icon_name,"Info");
14665   windows->info.border_width=1;
14666   windows->info.x=2;
14667   windows->info.y=2;
14668   windows->info.flags|=PPosition;
14669   windows->info.attributes.win_gravity=UnmapGravity;
14670   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14671     StructureNotifyMask;
14672   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14673   manager_hints->input=MagickFalse;
14674   manager_hints->initial_state=NormalState;
14675   manager_hints->window_group=windows->image.id;
14676   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14677     &windows->info);
14678   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14679     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14680   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14681     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14682   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14683   if (windows->image.mapped != MagickFalse)
14684     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14685   if (display_image->debug != MagickFalse)
14686     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14687       windows->info.id);
14688   /*
14689     Initialize Command widget.
14690   */
14691   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14692     resource_info,&windows->command);
14693   windows->command.data=MagickMenus;
14694   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14695   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.command",
14696     resource_info->client_name);
14697   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14698     resource_name,"geometry",(char *) NULL);
14699   (void) CloneString(&windows->command.name,MagickTitle);
14700   windows->command.border_width=0;
14701   windows->command.flags|=PPosition;
14702   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14703     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14704     OwnerGrabButtonMask | StructureNotifyMask;
14705   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14706   manager_hints->input=MagickTrue;
14707   manager_hints->initial_state=NormalState;
14708   manager_hints->window_group=windows->image.id;
14709   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14710     &windows->command);
14711   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14712     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14713     HighlightHeight);
14714   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14715     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14716   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14717   if (windows->command.mapped != MagickFalse)
14718     (void) XMapRaised(display,windows->command.id);
14719   if (display_image->debug != MagickFalse)
14720     (void) LogMagickEvent(X11Event,GetMagickModule(),
14721       "Window id: 0x%lx (command)",windows->command.id);
14722   /*
14723     Initialize Widget window.
14724   */
14725   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14726     resource_info,&windows->widget);
14727   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.widget",
14728     resource_info->client_name);
14729   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14730     resource_name,"geometry",(char *) NULL);
14731   windows->widget.border_width=0;
14732   windows->widget.flags|=PPosition;
14733   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14734     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14735     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14736     StructureNotifyMask;
14737   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14738   manager_hints->input=MagickTrue;
14739   manager_hints->initial_state=NormalState;
14740   manager_hints->window_group=windows->image.id;
14741   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14742     &windows->widget);
14743   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14744     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14745   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14746     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14747   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14748   if (display_image->debug != MagickFalse)
14749     (void) LogMagickEvent(X11Event,GetMagickModule(),
14750       "Window id: 0x%lx (widget)",windows->widget.id);
14751   /*
14752     Initialize popup window.
14753   */
14754   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14755     resource_info,&windows->popup);
14756   windows->popup.border_width=0;
14757   windows->popup.flags|=PPosition;
14758   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14759     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14760     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14761   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14762   manager_hints->input=MagickTrue;
14763   manager_hints->initial_state=NormalState;
14764   manager_hints->window_group=windows->image.id;
14765   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14766     &windows->popup);
14767   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14768     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14769   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14770     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14771   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14772   if (display_image->debug != MagickFalse)
14773     (void) LogMagickEvent(X11Event,GetMagickModule(),
14774       "Window id: 0x%lx (pop up)",windows->popup.id);
14775   /*
14776     Initialize Magnify window and cursor.
14777   */
14778   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14779     resource_info,&windows->magnify);
14780   if (resource_info->use_shared_memory == MagickFalse)
14781     windows->magnify.shared_memory=MagickFalse;
14782   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.magnify",
14783     resource_info->client_name);
14784   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14785     resource_name,"geometry",(char *) NULL);
14786   (void) FormatMagickString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14787     resource_info->magnify);
14788   if (windows->magnify.cursor != (Cursor) NULL)
14789     (void) XFreeCursor(display,windows->magnify.cursor);
14790   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14791     map_info->colormap,resource_info->background_color,
14792     resource_info->foreground_color);
14793   if (windows->magnify.cursor == (Cursor) NULL)
14794     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14795       display_image->filename);
14796   windows->magnify.width=MagnifySize;
14797   windows->magnify.height=MagnifySize;
14798   windows->magnify.flags|=PPosition;
14799   windows->magnify.min_width=MagnifySize;
14800   windows->magnify.min_height=MagnifySize;
14801   windows->magnify.width_inc=MagnifySize;
14802   windows->magnify.height_inc=MagnifySize;
14803   windows->magnify.data=resource_info->magnify;
14804   windows->magnify.attributes.cursor=windows->magnify.cursor;
14805   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14806     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14807     StructureNotifyMask;
14808   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14809   manager_hints->input=MagickTrue;
14810   manager_hints->initial_state=NormalState;
14811   manager_hints->window_group=windows->image.id;
14812   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14813     &windows->magnify);
14814   if (display_image->debug != MagickFalse)
14815     (void) LogMagickEvent(X11Event,GetMagickModule(),
14816       "Window id: 0x%lx (magnify)",windows->magnify.id);
14817   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14818   /*
14819     Initialize panning window.
14820   */
14821   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14822     resource_info,&windows->pan);
14823   (void) CloneString(&windows->pan.name,"Pan Icon");
14824   windows->pan.width=windows->icon.width;
14825   windows->pan.height=windows->icon.height;
14826   (void) FormatMagickString(resource_name,MaxTextExtent,"%s.pan",
14827     resource_info->client_name);
14828   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14829     resource_name,"geometry",(char *) NULL);
14830   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14831     &windows->pan.width,&windows->pan.height);
14832   windows->pan.flags|=PPosition;
14833   windows->pan.immutable=MagickTrue;
14834   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14835     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14836     StructureNotifyMask;
14837   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14838   manager_hints->input=MagickFalse;
14839   manager_hints->initial_state=NormalState;
14840   manager_hints->window_group=windows->image.id;
14841   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14842     &windows->pan);
14843   if (display_image->debug != MagickFalse)
14844     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14845       windows->pan.id);
14846   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14847   if (windows->info.mapped != MagickFalse)
14848     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14849   if ((windows->image.mapped == MagickFalse) ||
14850       (windows->backdrop.id != (Window) NULL))
14851     (void) XMapWindow(display,windows->image.id);
14852   /*
14853     Set our progress monitor and warning handlers.
14854   */
14855   if (warning_handler == (WarningHandler) NULL)
14856     {
14857       warning_handler=resource_info->display_warnings ?
14858         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14859       warning_handler=resource_info->display_warnings ?
14860         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14861     }
14862   /*
14863     Initialize Image and Magnify X images.
14864   */
14865   windows->image.x=0;
14866   windows->image.y=0;
14867   windows->magnify.shape=MagickFalse;
14868   width=(unsigned int) display_image->columns;
14869   height=(unsigned int) display_image->rows;
14870   if ((display_image->columns != width) || (display_image->rows != height))
14871     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14872       display_image->filename);
14873   status=XMakeImage(display,resource_info,&windows->image,display_image,
14874     width,height);
14875   if (status == MagickFalse)
14876     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14877       display_image->filename);
14878   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14879     windows->magnify.width,windows->magnify.height);
14880   if (status == MagickFalse)
14881     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14882       display_image->filename);
14883   if (windows->magnify.mapped != MagickFalse)
14884     (void) XMapRaised(display,windows->magnify.id);
14885   if (windows->pan.mapped != MagickFalse)
14886     (void) XMapRaised(display,windows->pan.id);
14887   windows->image.window_changes.width=(int) display_image->columns;
14888   windows->image.window_changes.height=(int) display_image->rows;
14889   (void) XConfigureImage(display,resource_info,windows,display_image);
14890   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14891   (void) XSync(display,MagickFalse);
14892   /*
14893     Respond to events.
14894   */
14895   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14896   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14897   update_time=0;
14898   if (resource_info->update != MagickFalse)
14899     {
14900       MagickBooleanType
14901         status;
14902
14903       /*
14904         Determine when file data was last modified.
14905       */
14906       status=GetPathAttributes(display_image->filename,&attributes);
14907       if (status != MagickFalse)
14908         update_time=attributes.st_mtime;
14909     }
14910   *state&=(~FormerImageState);
14911   *state&=(~MontageImageState);
14912   *state&=(~NextImageState);
14913   do
14914   {
14915     /*
14916       Handle a window event.
14917     */
14918     if (windows->image.mapped != MagickFalse)
14919       if ((display_image->delay != 0) || (resource_info->update != 0))
14920         {
14921           if (timer < time((time_t *) NULL))
14922             {
14923               if (resource_info->update == MagickFalse)
14924                 *state|=NextImageState | ExitState;
14925               else
14926                 {
14927                   MagickBooleanType
14928                     status;
14929
14930                   /*
14931                     Determine if image file was modified.
14932                   */
14933                   status=GetPathAttributes(display_image->filename,&attributes);
14934                   if (status != MagickFalse)
14935                     if (update_time != attributes.st_mtime)
14936                       {
14937                         /*
14938                           Redisplay image.
14939                         */
14940                         (void) FormatMagickString(
14941                           resource_info->image_info->filename,MaxTextExtent,
14942                           "%s:%s",display_image->magick,
14943                           display_image->filename);
14944                         nexus=ReadImage(resource_info->image_info,
14945                           &display_image->exception);
14946                         if (nexus != (Image *) NULL)
14947                           {
14948                             nexus=DestroyImage(nexus);
14949                             *state|=NextImageState | ExitState;
14950                           }
14951                       }
14952                   delay=display_image->delay/MagickMax(
14953                     display_image->ticks_per_second,1L);
14954                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14955                 }
14956             }
14957           if (XEventsQueued(display,QueuedAfterFlush) == 0)
14958             {
14959               /*
14960                 Do not block if delay > 0.
14961               */
14962               XDelay(display,SuspendTime << 2);
14963               continue;
14964             }
14965         }
14966     timestamp=time((time_t *) NULL);
14967     (void) XNextEvent(display,&event);
14968     if (windows->image.stasis == MagickFalse)
14969       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14970         MagickTrue : MagickFalse;
14971     if (windows->magnify.stasis == MagickFalse)
14972       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14973         MagickTrue : MagickFalse;
14974     if (event.xany.window == windows->command.id)
14975       {
14976         /*
14977           Select a command from the Command widget.
14978         */
14979         id=XCommandWidget(display,windows,CommandMenu,&event);
14980         if (id < 0)
14981           continue;
14982         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
14983         command_type=CommandMenus[id];
14984         if (id < MagickMenus)
14985           {
14986             /*
14987               Select a command from a pop-up menu.
14988             */
14989             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
14990               command);
14991             if (entry < 0)
14992               continue;
14993             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
14994             command_type=Commands[id][entry];
14995           }
14996         if (command_type != NullCommand)
14997           nexus=XMagickCommand(display,resource_info,windows,command_type,
14998             &display_image);
14999         continue;
15000       }
15001     switch (event.type)
15002     {
15003       case ButtonPress:
15004       {
15005         if (display_image->debug != MagickFalse)
15006           (void) LogMagickEvent(X11Event,GetMagickModule(),
15007             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15008             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15009         if ((event.xbutton.button == Button3) &&
15010             (event.xbutton.state & Mod1Mask))
15011           {
15012             /*
15013               Convert Alt-Button3 to Button2.
15014             */
15015             event.xbutton.button=Button2;
15016             event.xbutton.state&=(~Mod1Mask);
15017           }
15018         if (event.xbutton.window == windows->backdrop.id)
15019           {
15020             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15021               event.xbutton.time);
15022             break;
15023           }
15024         if (event.xbutton.window == windows->image.id)
15025           {
15026             switch (event.xbutton.button)
15027             {
15028               case Button1:
15029               {
15030                 if (resource_info->immutable)
15031                   {
15032                     /*
15033                       Select a command from the Virtual menu.
15034                     */
15035                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15036                       command);
15037                     if (entry >= 0)
15038                       nexus=XMagickCommand(display,resource_info,windows,
15039                         VirtualCommands[entry],&display_image);
15040                     break;
15041                   }
15042                 /*
15043                   Map/unmap Command widget.
15044                 */
15045                 if (windows->command.mapped != MagickFalse)
15046                   (void) XWithdrawWindow(display,windows->command.id,
15047                     windows->command.screen);
15048                 else
15049                   {
15050                     (void) XCommandWidget(display,windows,CommandMenu,
15051                       (XEvent *) NULL);
15052                     (void) XMapRaised(display,windows->command.id);
15053                   }
15054                 break;
15055               }
15056               case Button2:
15057               {
15058                 /*
15059                   User pressed the image magnify button.
15060                 */
15061                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15062                   &display_image);
15063                 XMagnifyImage(display,windows,&event);
15064                 break;
15065               }
15066               case Button3:
15067               {
15068                 if (resource_info->immutable)
15069                   {
15070                     /*
15071                       Select a command from the Virtual menu.
15072                     */
15073                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15074                       command);
15075                     if (entry >= 0)
15076                       nexus=XMagickCommand(display,resource_info,windows,
15077                         VirtualCommands[entry],&display_image);
15078                     break;
15079                   }
15080                 if (display_image->montage != (char *) NULL)
15081                   {
15082                     /*
15083                       Open or delete a tile from a visual image directory.
15084                     */
15085                     nexus=XTileImage(display,resource_info,windows,
15086                       display_image,&event);
15087                     if (nexus != (Image *) NULL)
15088                       *state|=MontageImageState | NextImageState | ExitState;
15089                     vid_info.x=(short int) windows->image.x;
15090                     vid_info.y=(short int) windows->image.y;
15091                     break;
15092                   }
15093                 /*
15094                   Select a command from the Short Cuts menu.
15095                 */
15096                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15097                   command);
15098                 if (entry >= 0)
15099                   nexus=XMagickCommand(display,resource_info,windows,
15100                     ShortCutsCommands[entry],&display_image);
15101                 break;
15102               }
15103               case Button4:
15104               {
15105                 /*
15106                   Wheel up.
15107                 */
15108                 XTranslateImage(display,windows,*image,XK_Up);
15109                 break;
15110               }
15111               case Button5:
15112               {
15113                 /*
15114                   Wheel down.
15115                 */
15116                 XTranslateImage(display,windows,*image,XK_Down);
15117                 break;
15118               }
15119               default:
15120                 break;
15121             }
15122             break;
15123           }
15124         if (event.xbutton.window == windows->magnify.id)
15125           {
15126             int
15127               factor;
15128
15129             static const char
15130               *MagnifyMenu[] =
15131               {
15132                 "2",
15133                 "4",
15134                 "5",
15135                 "6",
15136                 "7",
15137                 "8",
15138                 "9",
15139                 "3",
15140                 (char *) NULL,
15141               };
15142
15143             static KeySym
15144               MagnifyCommands[] =
15145               {
15146                 XK_2,
15147                 XK_4,
15148                 XK_5,
15149                 XK_6,
15150                 XK_7,
15151                 XK_8,
15152                 XK_9,
15153                 XK_3
15154               };
15155
15156             /*
15157               Select a magnify factor from the pop-up menu.
15158             */
15159             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15160             if (factor >= 0)
15161               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15162             break;
15163           }
15164         if (event.xbutton.window == windows->pan.id)
15165           {
15166             switch (event.xbutton.button)
15167             {
15168               case Button4:
15169               {
15170                 /*
15171                   Wheel up.
15172                 */
15173                 XTranslateImage(display,windows,*image,XK_Up);
15174                 break;
15175               }
15176               case Button5:
15177               {
15178                 /*
15179                   Wheel down.
15180                 */
15181                 XTranslateImage(display,windows,*image,XK_Down);
15182                 break;
15183               }
15184               default:
15185               {
15186                 XPanImage(display,windows,&event);
15187                 break;
15188               }
15189             }
15190             break;
15191           }
15192         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15193           1L);
15194         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15195         break;
15196       }
15197       case ButtonRelease:
15198       {
15199         if (display_image->debug != MagickFalse)
15200           (void) LogMagickEvent(X11Event,GetMagickModule(),
15201             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15202             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15203         break;
15204       }
15205       case ClientMessage:
15206       {
15207         if (display_image->debug != MagickFalse)
15208           (void) LogMagickEvent(X11Event,GetMagickModule(),
15209             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15210             event.xclient.message_type,event.xclient.format,(unsigned long)
15211             event.xclient.data.l[0]);
15212         if (event.xclient.message_type == windows->im_protocols)
15213           {
15214             if (*event.xclient.data.l == (long) windows->im_update_widget)
15215               {
15216                 (void) CloneString(&windows->command.name,MagickTitle);
15217                 windows->command.data=MagickMenus;
15218                 (void) XCommandWidget(display,windows,CommandMenu,
15219                   (XEvent *) NULL);
15220                 break;
15221               }
15222             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15223               {
15224                 /*
15225                   Update graphic context and window colormap.
15226                 */
15227                 for (i=0; i < (int) number_windows; i++)
15228                 {
15229                   if (magick_windows[i]->id == windows->icon.id)
15230                     continue;
15231                   context_values.background=pixel->background_color.pixel;
15232                   context_values.foreground=pixel->foreground_color.pixel;
15233                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15234                     context_mask,&context_values);
15235                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15236                     context_mask,&context_values);
15237                   context_values.background=pixel->foreground_color.pixel;
15238                   context_values.foreground=pixel->background_color.pixel;
15239                   context_values.plane_mask=context_values.background ^
15240                     context_values.foreground;
15241                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15242                     (size_t) (context_mask | GCPlaneMask),
15243                     &context_values);
15244                   magick_windows[i]->attributes.background_pixel=
15245                     pixel->background_color.pixel;
15246                   magick_windows[i]->attributes.border_pixel=
15247                     pixel->border_color.pixel;
15248                   magick_windows[i]->attributes.colormap=map_info->colormap;
15249                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15250                     (unsigned long) magick_windows[i]->mask,
15251                     &magick_windows[i]->attributes);
15252                 }
15253                 if (windows->pan.mapped != MagickFalse)
15254                   {
15255                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15256                       windows->pan.pixmap);
15257                     (void) XClearWindow(display,windows->pan.id);
15258                     XDrawPanRectangle(display,windows);
15259                   }
15260                 if (windows->backdrop.id != (Window) NULL)
15261                   (void) XInstallColormap(display,map_info->colormap);
15262                 break;
15263               }
15264             if (*event.xclient.data.l == (long) windows->im_former_image)
15265               {
15266                 *state|=FormerImageState | ExitState;
15267                 break;
15268               }
15269             if (*event.xclient.data.l == (long) windows->im_next_image)
15270               {
15271                 *state|=NextImageState | ExitState;
15272                 break;
15273               }
15274             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15275               {
15276                 *state|=RetainColorsState;
15277                 break;
15278               }
15279             if (*event.xclient.data.l == (long) windows->im_exit)
15280               {
15281                 *state|=ExitState;
15282                 break;
15283               }
15284             break;
15285           }
15286         if (event.xclient.message_type == windows->dnd_protocols)
15287           {
15288             Atom
15289               selection,
15290               type;
15291
15292             int
15293               format,
15294               status;
15295
15296             unsigned char
15297               *data;
15298
15299             unsigned long
15300               after,
15301               length;
15302
15303             /*
15304               Display image named by the Drag-and-Drop selection.
15305             */
15306             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15307               break;
15308             selection=XInternAtom(display,"DndSelection",MagickFalse);
15309             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15310               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15311               &length,&after,&data);
15312             if ((status != Success) || (length == 0))
15313               break;
15314             if (*event.xclient.data.l == 2)
15315               {
15316                 /*
15317                   Offix DND.
15318                 */
15319                 (void) CopyMagickString(resource_info->image_info->filename,
15320                   (char *) data,MaxTextExtent);
15321               }
15322             else
15323               {
15324                 /*
15325                   XDND.
15326                 */
15327                 if (strncmp((char *) data, "file:", 5) != 0)
15328                   {
15329                     (void) XFree((void *) data);
15330                     break;
15331                   }
15332                 (void) CopyMagickString(resource_info->image_info->filename,
15333                   ((char *) data)+5,MaxTextExtent);
15334               }
15335             nexus=ReadImage(resource_info->image_info,
15336               &display_image->exception);
15337             CatchException(&display_image->exception);
15338             if (nexus != (Image *) NULL)
15339               *state|=NextImageState | ExitState;
15340             (void) XFree((void *) data);
15341             break;
15342           }
15343         /*
15344           If client window delete message, exit.
15345         */
15346         if (event.xclient.message_type != windows->wm_protocols)
15347           break;
15348         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15349           break;
15350         (void) XWithdrawWindow(display,event.xclient.window,
15351           visual_info->screen);
15352         if (event.xclient.window == windows->image.id)
15353           {
15354             *state|=ExitState;
15355             break;
15356           }
15357         if (event.xclient.window == windows->pan.id)
15358           {
15359             /*
15360               Restore original image size when pan window is deleted.
15361             */
15362             windows->image.window_changes.width=windows->image.ximage->width;
15363             windows->image.window_changes.height=windows->image.ximage->height;
15364             (void) XConfigureImage(display,resource_info,windows,
15365               display_image);
15366           }
15367         break;
15368       }
15369       case ConfigureNotify:
15370       {
15371         if (display_image->debug != MagickFalse)
15372           (void) LogMagickEvent(X11Event,GetMagickModule(),
15373             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15374             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15375             event.xconfigure.y,event.xconfigure.send_event);
15376         if (event.xconfigure.window == windows->image.id)
15377           {
15378             /*
15379               Image window has a new configuration.
15380             */
15381             if (event.xconfigure.send_event != 0)
15382               {
15383                 XWindowChanges
15384                   window_changes;
15385
15386                 /*
15387                   Position the transient windows relative of the Image window.
15388                 */
15389                 if (windows->command.geometry == (char *) NULL)
15390                   if (windows->command.mapped == MagickFalse)
15391                     {
15392                       windows->command.x=event.xconfigure.x-
15393                         windows->command.width-25;
15394                       windows->command.y=event.xconfigure.y;
15395                       XConstrainWindowPosition(display,&windows->command);
15396                       window_changes.x=windows->command.x;
15397                       window_changes.y=windows->command.y;
15398                       (void) XReconfigureWMWindow(display,windows->command.id,
15399                         windows->command.screen,(unsigned int) (CWX | CWY),
15400                         &window_changes);
15401                     }
15402                 if (windows->widget.geometry == (char *) NULL)
15403                   if (windows->widget.mapped == MagickFalse)
15404                     {
15405                       windows->widget.x=event.xconfigure.x+
15406                         event.xconfigure.width/10;
15407                       windows->widget.y=event.xconfigure.y+
15408                         event.xconfigure.height/10;
15409                       XConstrainWindowPosition(display,&windows->widget);
15410                       window_changes.x=windows->widget.x;
15411                       window_changes.y=windows->widget.y;
15412                       (void) XReconfigureWMWindow(display,windows->widget.id,
15413                         windows->widget.screen,(unsigned int) (CWX | CWY),
15414                         &window_changes);
15415                     }
15416                 if (windows->magnify.geometry == (char *) NULL)
15417                   if (windows->magnify.mapped == MagickFalse)
15418                     {
15419                       windows->magnify.x=event.xconfigure.x+
15420                         event.xconfigure.width+25;
15421                       windows->magnify.y=event.xconfigure.y;
15422                       XConstrainWindowPosition(display,&windows->magnify);
15423                       window_changes.x=windows->magnify.x;
15424                       window_changes.y=windows->magnify.y;
15425                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15426                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15427                         &window_changes);
15428                     }
15429                 if (windows->pan.geometry == (char *) NULL)
15430                   if (windows->pan.mapped == MagickFalse)
15431                     {
15432                       windows->pan.x=event.xconfigure.x+
15433                         event.xconfigure.width+25;
15434                       windows->pan.y=event.xconfigure.y+
15435                         windows->magnify.height+50;
15436                       XConstrainWindowPosition(display,&windows->pan);
15437                       window_changes.x=windows->pan.x;
15438                       window_changes.y=windows->pan.y;
15439                       (void) XReconfigureWMWindow(display,windows->pan.id,
15440                         windows->pan.screen,(unsigned int) (CWX | CWY),
15441                         &window_changes);
15442                     }
15443               }
15444             if ((event.xconfigure.width == (int) windows->image.width) &&
15445                 (event.xconfigure.height == (int) windows->image.height))
15446               break;
15447             windows->image.width=(unsigned int) event.xconfigure.width;
15448             windows->image.height=(unsigned int) event.xconfigure.height;
15449             windows->image.x=0;
15450             windows->image.y=0;
15451             if (display_image->montage != (char *) NULL)
15452               {
15453                 windows->image.x=vid_info.x;
15454                 windows->image.y=vid_info.y;
15455               }
15456             if ((windows->image.mapped != MagickFalse) &&
15457                 (windows->image.stasis != MagickFalse))
15458               {
15459                 /*
15460                   Update image window configuration.
15461                 */
15462                 windows->image.window_changes.width=event.xconfigure.width;
15463                 windows->image.window_changes.height=event.xconfigure.height;
15464                 (void) XConfigureImage(display,resource_info,windows,
15465                   display_image);
15466               }
15467             /*
15468               Update pan window configuration.
15469             */
15470             if ((event.xconfigure.width < windows->image.ximage->width) ||
15471                 (event.xconfigure.height < windows->image.ximage->height))
15472               {
15473                 (void) XMapRaised(display,windows->pan.id);
15474                 XDrawPanRectangle(display,windows);
15475               }
15476             else
15477               if (windows->pan.mapped != MagickFalse)
15478                 (void) XWithdrawWindow(display,windows->pan.id,
15479                   windows->pan.screen);
15480             break;
15481           }
15482         if (event.xconfigure.window == windows->magnify.id)
15483           {
15484             unsigned int
15485               magnify;
15486
15487             /*
15488               Magnify window has a new configuration.
15489             */
15490             windows->magnify.width=(unsigned int) event.xconfigure.width;
15491             windows->magnify.height=(unsigned int) event.xconfigure.height;
15492             if (windows->magnify.mapped == MagickFalse)
15493               break;
15494             magnify=1;
15495             while ((int) magnify <= event.xconfigure.width)
15496               magnify<<=1;
15497             while ((int) magnify <= event.xconfigure.height)
15498               magnify<<=1;
15499             magnify>>=1;
15500             if (((int) magnify != event.xconfigure.width) ||
15501                 ((int) magnify != event.xconfigure.height))
15502               {
15503                 window_changes.width=(int) magnify;
15504                 window_changes.height=(int) magnify;
15505                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15506                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15507                   &window_changes);
15508                 break;
15509               }
15510             if ((windows->magnify.mapped != MagickFalse) &&
15511                 (windows->magnify.stasis != MagickFalse))
15512               {
15513                 status=XMakeImage(display,resource_info,&windows->magnify,
15514                   display_image,windows->magnify.width,windows->magnify.height);
15515                 XMakeMagnifyImage(display,windows);
15516               }
15517             break;
15518           }
15519         if ((windows->magnify.mapped != MagickFalse) &&
15520             (event.xconfigure.window == windows->pan.id))
15521           {
15522             /*
15523               Pan icon window has a new configuration.
15524             */
15525             if (event.xconfigure.send_event != 0)
15526               {
15527                 windows->pan.x=event.xconfigure.x;
15528                 windows->pan.y=event.xconfigure.y;
15529               }
15530             windows->pan.width=(unsigned int) event.xconfigure.width;
15531             windows->pan.height=(unsigned int) event.xconfigure.height;
15532             break;
15533           }
15534         if (event.xconfigure.window == windows->icon.id)
15535           {
15536             /*
15537               Icon window has a new configuration.
15538             */
15539             windows->icon.width=(unsigned int) event.xconfigure.width;
15540             windows->icon.height=(unsigned int) event.xconfigure.height;
15541             break;
15542           }
15543         break;
15544       }
15545       case DestroyNotify:
15546       {
15547         /*
15548           Group leader has exited.
15549         */
15550         if (display_image->debug != MagickFalse)
15551           (void) LogMagickEvent(X11Event,GetMagickModule(),
15552             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15553         if (event.xdestroywindow.window == windows->group_leader.id)
15554           {
15555             *state|=ExitState;
15556             break;
15557           }
15558         break;
15559       }
15560       case EnterNotify:
15561       {
15562         /*
15563           Selectively install colormap.
15564         */
15565         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15566           if (event.xcrossing.mode != NotifyUngrab)
15567             XInstallColormap(display,map_info->colormap);
15568         break;
15569       }
15570       case Expose:
15571       {
15572         if (display_image->debug != MagickFalse)
15573           (void) LogMagickEvent(X11Event,GetMagickModule(),
15574             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15575             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15576             event.xexpose.y);
15577         /*
15578           Refresh windows that are now exposed.
15579         */
15580         if ((event.xexpose.window == windows->image.id) &&
15581             (windows->image.mapped != MagickFalse))
15582           {
15583             XRefreshWindow(display,&windows->image,&event);
15584             delay=display_image->delay/MagickMax(
15585               display_image->ticks_per_second,1L);
15586             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15587             break;
15588           }
15589         if ((event.xexpose.window == windows->magnify.id) &&
15590             (windows->magnify.mapped != MagickFalse))
15591           {
15592             XMakeMagnifyImage(display,windows);
15593             break;
15594           }
15595         if (event.xexpose.window == windows->pan.id)
15596           {
15597             XDrawPanRectangle(display,windows);
15598             break;
15599           }
15600         if (event.xexpose.window == windows->icon.id)
15601           {
15602             XRefreshWindow(display,&windows->icon,&event);
15603             break;
15604           }
15605         break;
15606       }
15607       case KeyPress:
15608       {
15609         int
15610           length;
15611
15612         /*
15613           Respond to a user key press.
15614         */
15615         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15616           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15617         *(command+length)='\0';
15618         if (display_image->debug != MagickFalse)
15619           (void) LogMagickEvent(X11Event,GetMagickModule(),
15620             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15621             key_symbol,command);
15622         if (event.xkey.window == windows->image.id)
15623           {
15624             command_type=XImageWindowCommand(display,resource_info,windows,
15625               event.xkey.state,key_symbol,&display_image);
15626             if (command_type != NullCommand)
15627               nexus=XMagickCommand(display,resource_info,windows,command_type,
15628                 &display_image);
15629           }
15630         if (event.xkey.window == windows->magnify.id)
15631           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15632         if (event.xkey.window == windows->pan.id)
15633           {
15634             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15635               (void) XWithdrawWindow(display,windows->pan.id,
15636                 windows->pan.screen);
15637             else
15638               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15639                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15640                   "Help Viewer - Image Pan",ImagePanHelp);
15641               else
15642                 XTranslateImage(display,windows,*image,key_symbol);
15643           }
15644         delay=display_image->delay/MagickMax(
15645           display_image->ticks_per_second,1L);
15646         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15647         break;
15648       }
15649       case KeyRelease:
15650       {
15651         /*
15652           Respond to a user key release.
15653         */
15654         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15655           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15656         if (display_image->debug != MagickFalse)
15657           (void) LogMagickEvent(X11Event,GetMagickModule(),
15658             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15659         break;
15660       }
15661       case LeaveNotify:
15662       {
15663         /*
15664           Selectively uninstall colormap.
15665         */
15666         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15667           if (event.xcrossing.mode != NotifyUngrab)
15668             XUninstallColormap(display,map_info->colormap);
15669         break;
15670       }
15671       case MapNotify:
15672       {
15673         if (display_image->debug != MagickFalse)
15674           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15675             event.xmap.window);
15676         if (event.xmap.window == windows->backdrop.id)
15677           {
15678             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15679               CurrentTime);
15680             windows->backdrop.mapped=MagickTrue;
15681             break;
15682           }
15683         if (event.xmap.window == windows->image.id)
15684           {
15685             if (windows->backdrop.id != (Window) NULL)
15686               (void) XInstallColormap(display,map_info->colormap);
15687             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15688               {
15689                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15690                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15691               }
15692             if (((int) windows->image.width < windows->image.ximage->width) ||
15693                 ((int) windows->image.height < windows->image.ximage->height))
15694               (void) XMapRaised(display,windows->pan.id);
15695             windows->image.mapped=MagickTrue;
15696             break;
15697           }
15698         if (event.xmap.window == windows->magnify.id)
15699           {
15700             XMakeMagnifyImage(display,windows);
15701             windows->magnify.mapped=MagickTrue;
15702             (void) XWithdrawWindow(display,windows->info.id,
15703               windows->info.screen);
15704             break;
15705           }
15706         if (event.xmap.window == windows->pan.id)
15707           {
15708             XMakePanImage(display,resource_info,windows,display_image);
15709             windows->pan.mapped=MagickTrue;
15710             break;
15711           }
15712         if (event.xmap.window == windows->info.id)
15713           {
15714             windows->info.mapped=MagickTrue;
15715             break;
15716           }
15717         if (event.xmap.window == windows->icon.id)
15718           {
15719             MagickBooleanType
15720               taint;
15721
15722             /*
15723               Create an icon image.
15724             */
15725             taint=display_image->taint;
15726             XMakeStandardColormap(display,icon_visual,icon_resources,
15727               display_image,icon_map,icon_pixel);
15728             (void) XMakeImage(display,icon_resources,&windows->icon,
15729               display_image,windows->icon.width,windows->icon.height);
15730             display_image->taint=taint;
15731             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15732               windows->icon.pixmap);
15733             (void) XClearWindow(display,windows->icon.id);
15734             (void) XWithdrawWindow(display,windows->info.id,
15735               windows->info.screen);
15736             windows->icon.mapped=MagickTrue;
15737             break;
15738           }
15739         if (event.xmap.window == windows->command.id)
15740           {
15741             windows->command.mapped=MagickTrue;
15742             break;
15743           }
15744         if (event.xmap.window == windows->popup.id)
15745           {
15746             windows->popup.mapped=MagickTrue;
15747             break;
15748           }
15749         if (event.xmap.window == windows->widget.id)
15750           {
15751             windows->widget.mapped=MagickTrue;
15752             break;
15753           }
15754         break;
15755       }
15756       case MappingNotify:
15757       {
15758         (void) XRefreshKeyboardMapping(&event.xmapping);
15759         break;
15760       }
15761       case NoExpose:
15762         break;
15763       case PropertyNotify:
15764       {
15765         Atom
15766           type;
15767
15768         int
15769           format,
15770           status;
15771
15772         unsigned char
15773           *data;
15774
15775         unsigned long
15776           after,
15777           length;
15778
15779         if (display_image->debug != MagickFalse)
15780           (void) LogMagickEvent(X11Event,GetMagickModule(),
15781             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15782             event.xproperty.atom,event.xproperty.state);
15783         if (event.xproperty.atom != windows->im_remote_command)
15784           break;
15785         /*
15786           Display image named by the remote command protocol.
15787         */
15788         status=XGetWindowProperty(display,event.xproperty.window,
15789           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15790           AnyPropertyType,&type,&format,&length,&after,&data);
15791         if ((status != Success) || (length == 0))
15792           break;
15793         if (LocaleCompare((char *) data,"-quit") == 0)
15794           {
15795             XClientMessage(display,windows->image.id,windows->im_protocols,
15796               windows->im_exit,CurrentTime);
15797             (void) XFree((void *) data);
15798             break;
15799           }
15800         (void) CopyMagickString(resource_info->image_info->filename,
15801           (char *) data,MaxTextExtent);
15802         (void) XFree((void *) data);
15803         nexus=ReadImage(resource_info->image_info,&display_image->exception);
15804         CatchException(&display_image->exception);
15805         if (nexus != (Image *) NULL)
15806           *state|=NextImageState | ExitState;
15807         break;
15808       }
15809       case ReparentNotify:
15810       {
15811         if (display_image->debug != MagickFalse)
15812           (void) LogMagickEvent(X11Event,GetMagickModule(),
15813             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15814             event.xreparent.window);
15815         break;
15816       }
15817       case UnmapNotify:
15818       {
15819         if (display_image->debug != MagickFalse)
15820           (void) LogMagickEvent(X11Event,GetMagickModule(),
15821             "Unmap Notify: 0x%lx",event.xunmap.window);
15822         if (event.xunmap.window == windows->backdrop.id)
15823           {
15824             windows->backdrop.mapped=MagickFalse;
15825             break;
15826           }
15827         if (event.xunmap.window == windows->image.id)
15828           {
15829             windows->image.mapped=MagickFalse;
15830             break;
15831           }
15832         if (event.xunmap.window == windows->magnify.id)
15833           {
15834             windows->magnify.mapped=MagickFalse;
15835             break;
15836           }
15837         if (event.xunmap.window == windows->pan.id)
15838           {
15839             windows->pan.mapped=MagickFalse;
15840             break;
15841           }
15842         if (event.xunmap.window == windows->info.id)
15843           {
15844             windows->info.mapped=MagickFalse;
15845             break;
15846           }
15847         if (event.xunmap.window == windows->icon.id)
15848           {
15849             if (map_info->colormap == icon_map->colormap)
15850               XConfigureImageColormap(display,resource_info,windows,
15851                 display_image);
15852             (void) XFreeStandardColormap(display,icon_visual,icon_map,
15853               icon_pixel);
15854             windows->icon.mapped=MagickFalse;
15855             break;
15856           }
15857         if (event.xunmap.window == windows->command.id)
15858           {
15859             windows->command.mapped=MagickFalse;
15860             break;
15861           }
15862         if (event.xunmap.window == windows->popup.id)
15863           {
15864             if (windows->backdrop.id != (Window) NULL)
15865               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15866                 CurrentTime);
15867             windows->popup.mapped=MagickFalse;
15868             break;
15869           }
15870         if (event.xunmap.window == windows->widget.id)
15871           {
15872             if (windows->backdrop.id != (Window) NULL)
15873               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15874                 CurrentTime);
15875             windows->widget.mapped=MagickFalse;
15876             break;
15877           }
15878         break;
15879       }
15880       default:
15881       {
15882         if (display_image->debug != MagickFalse)
15883           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15884             event.type);
15885         break;
15886       }
15887     }
15888   } while (!(*state & ExitState));
15889   if ((*state & ExitState) == 0)
15890     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15891       &display_image);
15892   else
15893     if (resource_info->confirm_edit != MagickFalse)
15894       {
15895         /*
15896           Query user if image has changed.
15897         */
15898         if ((resource_info->immutable == MagickFalse) &&
15899             (display_image->taint != MagickFalse))
15900           {
15901             int
15902               status;
15903
15904             status=XConfirmWidget(display,windows,"Your image changed.",
15905               "Do you want to save it");
15906             if (status == 0)
15907               *state&=(~ExitState);
15908             else
15909               if (status > 0)
15910                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15911                   &display_image);
15912           }
15913       }
15914   if ((windows->visual_info->klass == GrayScale) ||
15915       (windows->visual_info->klass == PseudoColor) ||
15916       (windows->visual_info->klass == DirectColor))
15917     {
15918       /*
15919         Withdraw pan and Magnify window.
15920       */
15921       if (windows->info.mapped != MagickFalse)
15922         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15923       if (windows->magnify.mapped != MagickFalse)
15924         (void) XWithdrawWindow(display,windows->magnify.id,
15925           windows->magnify.screen);
15926       if (windows->command.mapped != MagickFalse)
15927         (void) XWithdrawWindow(display,windows->command.id,
15928           windows->command.screen);
15929     }
15930   if (windows->pan.mapped != MagickFalse)
15931     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15932   if (resource_info->backdrop == MagickFalse)
15933     if (windows->backdrop.mapped)
15934       {
15935         (void) XWithdrawWindow(display,windows->backdrop.id,
15936           windows->backdrop.screen);
15937         (void) XDestroyWindow(display,windows->backdrop.id);
15938         windows->backdrop.id=(Window) NULL;
15939         (void) XWithdrawWindow(display,windows->image.id,
15940           windows->image.screen);
15941         (void) XDestroyWindow(display,windows->image.id);
15942         windows->image.id=(Window) NULL;
15943       }
15944   XSetCursorState(display,windows,MagickTrue);
15945   XCheckRefreshWindows(display,windows);
15946   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15947     *state&=(~ExitState);
15948   if (*state & ExitState)
15949     {
15950       /*
15951         Free Standard Colormap.
15952       */
15953       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
15954       if (resource_info->map_type == (char *) NULL)
15955         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
15956       /*
15957         Free X resources.
15958       */
15959       if (resource_info->copy_image != (Image *) NULL)
15960         {
15961           resource_info->copy_image=DestroyImage(resource_info->copy_image);
15962           resource_info->copy_image=NewImageList();
15963         }
15964       DestroyXResources();
15965     }
15966   (void) XSync(display,MagickFalse);
15967   /*
15968     Restore our progress monitor and warning handlers.
15969   */
15970   (void) SetErrorHandler(warning_handler);
15971   (void) SetWarningHandler(warning_handler);
15972   /*
15973     Change to home directory.
15974   */
15975   cwd=getcwd(working_directory,MaxTextExtent);
15976   {
15977     int
15978       status;
15979
15980     status=chdir(resource_info->home_directory);
15981     if (status == -1)
15982       (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
15983         FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
15984   }
15985   *image=display_image;
15986   return(nexus);
15987 }
15988 #else
15989 \f
15990 /*
15991 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15992 %                                                                             %
15993 %                                                                             %
15994 %                                                                             %
15995 +   D i s p l a y I m a g e s                                                 %
15996 %                                                                             %
15997 %                                                                             %
15998 %                                                                             %
15999 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16000 %
16001 %  DisplayImages() displays an image sequence to any X window screen.  It
16002 %  returns a value other than 0 if successful.  Check the exception member
16003 %  of image to determine the reason for any failure.
16004 %
16005 %  The format of the DisplayImages method is:
16006 %
16007 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16008 %        Image *images)
16009 %
16010 %  A description of each parameter follows:
16011 %
16012 %    o image_info: the image info.
16013 %
16014 %    o image: the image.
16015 %
16016 */
16017 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16018   Image *image)
16019 {
16020   assert(image_info != (const ImageInfo *) NULL);
16021   assert(image_info->signature == MagickSignature);
16022   assert(image != (Image *) NULL);
16023   assert(image->signature == MagickSignature);
16024   if (image->debug != MagickFalse)
16025     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16026   (void) ThrowMagickException(&image->exception,GetMagickModule(),
16027     MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16028     image->filename);
16029   return(MagickFalse);
16030 }
16031 \f
16032 /*
16033 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16034 %                                                                             %
16035 %                                                                             %
16036 %                                                                             %
16037 +   R e m o t e D i s p l a y C o m m a n d                                   %
16038 %                                                                             %
16039 %                                                                             %
16040 %                                                                             %
16041 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16042 %
16043 %  RemoteDisplayCommand() encourages a remote display program to display the
16044 %  specified image filename.
16045 %
16046 %  The format of the RemoteDisplayCommand method is:
16047 %
16048 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16049 %        const char *window,const char *filename,ExceptionInfo *exception)
16050 %
16051 %  A description of each parameter follows:
16052 %
16053 %    o image_info: the image info.
16054 %
16055 %    o window: Specifies the name or id of an X window.
16056 %
16057 %    o filename: the name of the image filename to display.
16058 %
16059 %    o exception: return any errors or warnings in this structure.
16060 %
16061 */
16062 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16063   const char *window,const char *filename,ExceptionInfo *exception)
16064 {
16065   assert(image_info != (const ImageInfo *) NULL);
16066   assert(image_info->signature == MagickSignature);
16067   assert(filename != (char *) NULL);
16068   (void) window;
16069   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16070   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16071     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16072   return(MagickFalse);
16073 }
16074 #endif