]> granicus.if.org Git - imagemagick/blob - magick/display.c
(no commit message)
[imagemagick] / magick / display.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %               DDDD   IIIII  SSSSS  PPPP   L       AAA   Y   Y               %
7 %               D   D    I    SS     P   P  L      A   A   Y Y                %
8 %               D   D    I     SSS   PPPP   L      AAAAA    Y                 %
9 %               D   D    I       SS  P      L      A   A    Y                 %
10 %               DDDD   IIIII  SSSSS  P      LLLLL  A   A    Y                 %
11 %                                                                             %
12 %                                                                             %
13 %        MagickCore Methods to Interactively Display and Edit an Image        %
14 %                                                                             %
15 %                             Software Design                                 %
16 %                               John Cristy                                   %
17 %                                July 1992                                    %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "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) FormatLocaleString(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=LocaleToDouble(RotateMenu[entry],(char **) NULL);
2109                 break;
2110               }
2111             (void) XDialogWidget(display,windows,"OK","Enter rotation angle:",
2112               angle);
2113             if (*angle == '\0')
2114               break;
2115             degrees=LocaleToDouble(angle,(char **) NULL);
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) FormatLocaleString(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) FormatLocaleString(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) FormatLocaleString(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) FormatLocaleString(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 **) GetCommandOptions(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) ParseCommandOption(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) FormatLocaleString(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=GetCommandOptions(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) ParseCommandOption(
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=LocaleToDouble(factor,(char **) NULL);
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   MagickStatusType
4369     status;
4370
4371   size_t
4372     mask,
4373     height,
4374     width;
4375
4376   ssize_t
4377     x,
4378     y;
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) FormatLocaleString(geometry,MaxTextExtent,"%s>!",
4424       resource_info->image_geometry);
4425   else
4426     (void) FormatLocaleString(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) FormatLocaleString(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) FormatLocaleString(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) FormatLocaleString(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) FormatLocaleString(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) FormatLocaleString(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) FormatLocaleString(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) FormatLocaleString(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) FormatLocaleString(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) FormatLocaleString(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) FormatLocaleString(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) FormatLocaleString(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) FormatLocaleString(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 may transform the image and return a new
6381 %      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       char
6417         image_geometry[MaxTextExtent];
6418
6419       /*
6420         Undo the last image transformation.
6421       */
6422       if (undo_image == (Image *) NULL)
6423         {
6424           (void) XBell(display,0);
6425           return;
6426         }
6427       cache_image=undo_image;
6428       undo_image=GetPreviousImageInList(undo_image);
6429       windows->image.window_changes.width=(int) cache_image->columns;
6430       windows->image.window_changes.height=(int) cache_image->rows;
6431       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
6432         windows->image.ximage->width,windows->image.ximage->height);
6433       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
6434       if (windows->image.crop_geometry != (char *) NULL)
6435         windows->image.crop_geometry=(char *)
6436           RelinquishMagickMemory(windows->image.crop_geometry);
6437       windows->image.crop_geometry=cache_image->geometry;
6438       if (redo_image != (Image *) NULL)
6439         redo_image=DestroyImage(redo_image);
6440       redo_image=(*image);
6441       *image=cache_image->list;
6442       cache_image=DestroyImage(cache_image);
6443       if (windows->image.orphan != MagickFalse)
6444         return;
6445       XConfigureImageColormap(display,resource_info,windows,*image);
6446       (void) XConfigureImage(display,resource_info,windows,*image);
6447       return;
6448     }
6449     case CutCommand:
6450     case PasteCommand:
6451     case ApplyCommand:
6452     case HalfSizeCommand:
6453     case OriginalSizeCommand:
6454     case DoubleSizeCommand:
6455     case ResizeCommand:
6456     case TrimCommand:
6457     case CropCommand:
6458     case ChopCommand:
6459     case FlipCommand:
6460     case FlopCommand:
6461     case RotateRightCommand:
6462     case RotateLeftCommand:
6463     case RotateCommand:
6464     case ShearCommand:
6465     case RollCommand:
6466     case NegateCommand:
6467     case ContrastStretchCommand:
6468     case SigmoidalContrastCommand:
6469     case NormalizeCommand:
6470     case EqualizeCommand:
6471     case HueCommand:
6472     case SaturationCommand:
6473     case BrightnessCommand:
6474     case GammaCommand:
6475     case SpiffCommand:
6476     case DullCommand:
6477     case GrayscaleCommand:
6478     case MapCommand:
6479     case QuantizeCommand:
6480     case DespeckleCommand:
6481     case EmbossCommand:
6482     case ReduceNoiseCommand:
6483     case AddNoiseCommand:
6484     case SharpenCommand:
6485     case BlurCommand:
6486     case ThresholdCommand:
6487     case EdgeDetectCommand:
6488     case SpreadCommand:
6489     case ShadeCommand:
6490     case RaiseCommand:
6491     case SegmentCommand:
6492     case SolarizeCommand:
6493     case SepiaToneCommand:
6494     case SwirlCommand:
6495     case ImplodeCommand:
6496     case VignetteCommand:
6497     case WaveCommand:
6498     case OilPaintCommand:
6499     case CharcoalDrawCommand:
6500     case AnnotateCommand:
6501     case AddBorderCommand:
6502     case AddFrameCommand:
6503     case CompositeCommand:
6504     case CommentCommand:
6505     case LaunchCommand:
6506     case RegionofInterestCommand:
6507     case SaveToUndoBufferCommand:
6508     case RedoCommand:
6509     {
6510       Image
6511         *previous_image;
6512
6513       ssize_t
6514         bytes;
6515
6516       bytes=(ssize_t) ((*image)->columns*(*image)->rows*sizeof(PixelPacket));
6517       if (undo_image != (Image *) NULL)
6518         {
6519           /*
6520             Ensure the undo cache has enough memory available.
6521           */
6522           previous_image=undo_image;
6523           while (previous_image != (Image *) NULL)
6524           {
6525             bytes+=previous_image->list->columns*previous_image->list->rows*
6526               sizeof(PixelPacket);
6527             if (bytes <= (ssize_t) (resource_info->undo_cache << 20))
6528               {
6529                 previous_image=GetPreviousImageInList(previous_image);
6530                 continue;
6531               }
6532             bytes-=previous_image->list->columns*previous_image->list->rows*
6533               sizeof(PixelPacket);
6534             if (previous_image == undo_image)
6535               undo_image=NewImageList();
6536             else
6537               previous_image->next->previous=NewImageList();
6538             break;
6539           }
6540           while (previous_image != (Image *) NULL)
6541           {
6542             /*
6543               Delete any excess memory from undo cache.
6544             */
6545             cache_image=previous_image;
6546             previous_image=GetPreviousImageInList(previous_image);
6547             cache_image->list=DestroyImage(cache_image->list);
6548             cache_image=DestroyImage(cache_image);
6549           }
6550         }
6551       if (bytes > (ssize_t) (resource_info->undo_cache << 20))
6552         break;
6553       /*
6554         Save image before transformations are applied.
6555       */
6556       cache_image=AcquireImage((ImageInfo *) NULL);
6557       if (cache_image == (Image *) NULL)
6558         break;
6559       XSetCursorState(display,windows,MagickTrue);
6560       XCheckRefreshWindows(display,windows);
6561       cache_image->list=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
6562       XSetCursorState(display,windows,MagickFalse);
6563       if (cache_image->list == (Image *) NULL)
6564         {
6565           cache_image=DestroyImage(cache_image);
6566           break;
6567         }
6568       cache_image->columns=(size_t) windows->image.ximage->width;
6569       cache_image->rows=(size_t) windows->image.ximage->height;
6570       cache_image->geometry=windows->image.crop_geometry;
6571       if (windows->image.crop_geometry != (char *) NULL)
6572         {
6573           cache_image->geometry=AcquireString((char *) NULL);
6574           (void) CopyMagickString(cache_image->geometry,
6575             windows->image.crop_geometry,MaxTextExtent);
6576         }
6577       if (undo_image == (Image *) NULL)
6578         {
6579           undo_image=cache_image;
6580           break;
6581         }
6582       undo_image->next=cache_image;
6583       undo_image->next->previous=undo_image;
6584       undo_image=undo_image->next;
6585       break;
6586     }
6587     default:
6588       break;
6589   }
6590   if (command == RedoCommand)
6591     {
6592       /*
6593         Redo the last image transformation.
6594       */
6595       if (redo_image == (Image *) NULL)
6596         {
6597           (void) XBell(display,0);
6598           return;
6599         }
6600       windows->image.window_changes.width=(int) redo_image->columns;
6601       windows->image.window_changes.height=(int) redo_image->rows;
6602       if (windows->image.crop_geometry != (char *) NULL)
6603         windows->image.crop_geometry=(char *)
6604           RelinquishMagickMemory(windows->image.crop_geometry);
6605       windows->image.crop_geometry=redo_image->geometry;
6606       *image=DestroyImage(*image);
6607       *image=redo_image;
6608       redo_image=NewImageList();
6609       if (windows->image.orphan != MagickFalse)
6610         return;
6611       XConfigureImageColormap(display,resource_info,windows,*image);
6612       (void) XConfigureImage(display,resource_info,windows,*image);
6613       return;
6614     }
6615   if (command != InfoCommand)
6616     return;
6617   /*
6618     Display image info.
6619   */
6620   XSetCursorState(display,windows,MagickTrue);
6621   XCheckRefreshWindows(display,windows);
6622   XDisplayImageInfo(display,resource_info,windows,undo_image,*image);
6623   XSetCursorState(display,windows,MagickFalse);
6624 }
6625 \f
6626 /*
6627 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6628 %                                                                             %
6629 %                                                                             %
6630 %                                                                             %
6631 +   X I m a g e W i n d o w C o m m a n d                                     %
6632 %                                                                             %
6633 %                                                                             %
6634 %                                                                             %
6635 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6636 %
6637 %  XImageWindowCommand() makes a transform to the image or Image window as
6638 %  specified by a user menu button or keyboard command.
6639 %
6640 %  The format of the XMagickCommand method is:
6641 %
6642 %      CommandType XImageWindowCommand(Display *display,
6643 %        XResourceInfo *resource_info,XWindows *windows,
6644 %        const MagickStatusType state,KeySym key_symbol,Image **image)
6645 %
6646 %  A description of each parameter follows:
6647 %
6648 %    o nexus:  Method XImageWindowCommand returns an image when the
6649 %      user chooses 'Open Image' from the command menu.  Otherwise a null
6650 %      image is returned.
6651 %
6652 %    o display: Specifies a connection to an X server; returned from
6653 %      XOpenDisplay.
6654 %
6655 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
6656 %
6657 %    o windows: Specifies a pointer to a XWindows structure.
6658 %
6659 %    o state: key mask.
6660 %
6661 %    o key_symbol: Specifies a command to perform.
6662 %
6663 %    o image: the image;  XImageWIndowCommand
6664 %      may transform the image and return a new image pointer.
6665 %
6666 */
6667 static CommandType XImageWindowCommand(Display *display,
6668   XResourceInfo *resource_info,XWindows *windows,const MagickStatusType state,
6669   KeySym key_symbol,Image **image)
6670 {
6671   static char
6672     delta[MaxTextExtent] = "";
6673
6674   static const char
6675     Digits[] = "01234567890";
6676
6677   static KeySym
6678     last_symbol = XK_0;
6679
6680   if ((key_symbol >= XK_0) && (key_symbol <= XK_9))
6681     {
6682       if (((last_symbol < XK_0) || (last_symbol > XK_9)))
6683         {
6684           *delta='\0';
6685           resource_info->quantum=1;
6686         }
6687       last_symbol=key_symbol;
6688       delta[strlen(delta)+1]='\0';
6689       delta[strlen(delta)]=Digits[key_symbol-XK_0];
6690       resource_info->quantum=StringToLong(delta);
6691       return(NullCommand);
6692     }
6693   last_symbol=key_symbol;
6694   if (resource_info->immutable)
6695     {
6696       /*
6697         Virtual image window has a restricted command set.
6698       */
6699       switch (key_symbol)
6700       {
6701         case XK_question:
6702           return(InfoCommand);
6703         case XK_p:
6704         case XK_Print:
6705           return(PrintCommand);
6706         case XK_space:
6707           return(NextCommand);
6708         case XK_q:
6709         case XK_Escape:
6710           return(QuitCommand);
6711         default:
6712           break;
6713       }
6714       return(NullCommand);
6715     }
6716   switch ((int) key_symbol)
6717   {
6718     case XK_o:
6719     {
6720       if ((state & ControlMask) == 0)
6721         break;
6722       return(OpenCommand);
6723     }
6724     case XK_space:
6725       return(NextCommand);
6726     case XK_BackSpace:
6727       return(FormerCommand);
6728     case XK_s:
6729     {
6730       if ((state & Mod1Mask) != 0)
6731         return(SwirlCommand);
6732       if ((state & ControlMask) == 0)
6733         return(ShearCommand);
6734       return(SaveCommand);
6735     }
6736     case XK_p:
6737     case XK_Print:
6738     {
6739       if ((state & Mod1Mask) != 0)
6740         return(OilPaintCommand);
6741       if ((state & Mod4Mask) != 0)
6742         return(ColorCommand);
6743       if ((state & ControlMask) == 0)
6744         return(NullCommand);
6745       return(PrintCommand);
6746     }
6747     case XK_d:
6748     {
6749       if ((state & Mod4Mask) != 0)
6750         return(DrawCommand);
6751       if ((state & ControlMask) == 0)
6752         return(NullCommand);
6753       return(DeleteCommand);
6754     }
6755     case XK_Select:
6756     {
6757       if ((state & ControlMask) == 0)
6758         return(NullCommand);
6759       return(SelectCommand);
6760     }
6761     case XK_n:
6762     {
6763       if ((state & ControlMask) == 0)
6764         return(NullCommand);
6765       return(NewCommand);
6766     }
6767     case XK_q:
6768     case XK_Escape:
6769       return(QuitCommand);
6770     case XK_z:
6771     case XK_Undo:
6772     {
6773       if ((state & ControlMask) == 0)
6774         return(NullCommand);
6775       return(UndoCommand);
6776     }
6777     case XK_r:
6778     case XK_Redo:
6779     {
6780       if ((state & ControlMask) == 0)
6781         return(RollCommand);
6782       return(RedoCommand);
6783     }
6784     case XK_x:
6785     {
6786       if ((state & ControlMask) == 0)
6787         return(NullCommand);
6788       return(CutCommand);
6789     }
6790     case XK_c:
6791     {
6792       if ((state & Mod1Mask) != 0)
6793         return(CharcoalDrawCommand);
6794       if ((state & ControlMask) == 0)
6795         return(CropCommand);
6796       return(CopyCommand);
6797     }
6798     case XK_v:
6799     case XK_Insert:
6800     {
6801       if ((state & Mod4Mask) != 0)
6802         return(CompositeCommand);
6803       if ((state & ControlMask) == 0)
6804         return(FlipCommand);
6805       return(PasteCommand);
6806     }
6807     case XK_less:
6808       return(HalfSizeCommand);
6809     case XK_minus:
6810       return(OriginalSizeCommand);
6811     case XK_greater:
6812       return(DoubleSizeCommand);
6813     case XK_percent:
6814       return(ResizeCommand);
6815     case XK_at:
6816       return(RefreshCommand);
6817     case XK_bracketleft:
6818       return(ChopCommand);
6819     case XK_h:
6820       return(FlopCommand);
6821     case XK_slash:
6822       return(RotateRightCommand);
6823     case XK_backslash:
6824       return(RotateLeftCommand);
6825     case XK_asterisk:
6826       return(RotateCommand);
6827     case XK_t:
6828       return(TrimCommand);
6829     case XK_H:
6830       return(HueCommand);
6831     case XK_S:
6832       return(SaturationCommand);
6833     case XK_L:
6834       return(BrightnessCommand);
6835     case XK_G:
6836       return(GammaCommand);
6837     case XK_C:
6838       return(SpiffCommand);
6839     case XK_Z:
6840       return(DullCommand);
6841     case XK_N:
6842       return(NormalizeCommand);
6843     case XK_equal:
6844       return(EqualizeCommand);
6845     case XK_asciitilde:
6846       return(NegateCommand);
6847     case XK_period:
6848       return(GrayscaleCommand);
6849     case XK_numbersign:
6850       return(QuantizeCommand);
6851     case XK_F2:
6852       return(DespeckleCommand);
6853     case XK_F3:
6854       return(EmbossCommand);
6855     case XK_F4:
6856       return(ReduceNoiseCommand);
6857     case XK_F5:
6858       return(AddNoiseCommand);
6859     case XK_F6:
6860       return(SharpenCommand);
6861     case XK_F7:
6862       return(BlurCommand);
6863     case XK_F8:
6864       return(ThresholdCommand);
6865     case XK_F9:
6866       return(EdgeDetectCommand);
6867     case XK_F10:
6868       return(SpreadCommand);
6869     case XK_F11:
6870       return(ShadeCommand);
6871     case XK_F12:
6872       return(RaiseCommand);
6873     case XK_F13:
6874       return(SegmentCommand);
6875     case XK_i:
6876     {
6877       if ((state & Mod1Mask) == 0)
6878         return(NullCommand);
6879       return(ImplodeCommand);
6880     }
6881     case XK_w:
6882     {
6883       if ((state & Mod1Mask) == 0)
6884         return(NullCommand);
6885       return(WaveCommand);
6886     }
6887     case XK_m:
6888     {
6889       if ((state & Mod4Mask) == 0)
6890         return(NullCommand);
6891       return(MatteCommand);
6892     }
6893     case XK_b:
6894     {
6895       if ((state & Mod4Mask) == 0)
6896         return(NullCommand);
6897       return(AddBorderCommand);
6898     }
6899     case XK_f:
6900     {
6901       if ((state & Mod4Mask) == 0)
6902         return(NullCommand);
6903       return(AddFrameCommand);
6904     }
6905     case XK_exclam:
6906     {
6907       if ((state & Mod4Mask) == 0)
6908         return(NullCommand);
6909       return(CommentCommand);
6910     }
6911     case XK_a:
6912     {
6913       if ((state & Mod1Mask) != 0)
6914         return(ApplyCommand);
6915       if ((state & Mod4Mask) != 0)
6916         return(AnnotateCommand);
6917       if ((state & ControlMask) == 0)
6918         return(NullCommand);
6919       return(RegionofInterestCommand);
6920     }
6921     case XK_question:
6922       return(InfoCommand);
6923     case XK_plus:
6924       return(ZoomCommand);
6925     case XK_P:
6926     {
6927       if ((state & ShiftMask) == 0)
6928         return(NullCommand);
6929       return(ShowPreviewCommand);
6930     }
6931     case XK_Execute:
6932       return(LaunchCommand);
6933     case XK_F1:
6934       return(HelpCommand);
6935     case XK_Find:
6936       return(BrowseDocumentationCommand);
6937     case XK_Menu:
6938     {
6939       (void) XMapRaised(display,windows->command.id);
6940       return(NullCommand);
6941     }
6942     case XK_Next:
6943     case XK_Prior:
6944     case XK_Home:
6945     case XK_KP_Home:
6946     {
6947       XTranslateImage(display,windows,*image,key_symbol);
6948       return(NullCommand);
6949     }
6950     case XK_Up:
6951     case XK_KP_Up:
6952     case XK_Down:
6953     case XK_KP_Down:
6954     case XK_Left:
6955     case XK_KP_Left:
6956     case XK_Right:
6957     case XK_KP_Right:
6958     {
6959       if ((state & Mod1Mask) != 0)
6960         {
6961           RectangleInfo
6962             crop_info;
6963
6964           /*
6965             Trim one pixel from edge of image.
6966           */
6967           crop_info.x=0;
6968           crop_info.y=0;
6969           crop_info.width=(size_t) windows->image.ximage->width;
6970           crop_info.height=(size_t) windows->image.ximage->height;
6971           if ((key_symbol == XK_Up) || (key_symbol == XK_KP_Up))
6972             {
6973               if (resource_info->quantum >= (int) crop_info.height)
6974                 resource_info->quantum=(int) crop_info.height-1;
6975               crop_info.height-=resource_info->quantum;
6976             }
6977           if ((key_symbol == XK_Down) || (key_symbol == XK_KP_Down))
6978             {
6979               if (resource_info->quantum >= (int) (crop_info.height-crop_info.y))
6980                 resource_info->quantum=(int) (crop_info.height-crop_info.y-1);
6981               crop_info.y+=resource_info->quantum;
6982               crop_info.height-=resource_info->quantum;
6983             }
6984           if ((key_symbol == XK_Left) || (key_symbol == XK_KP_Left))
6985             {
6986               if (resource_info->quantum >= (int) crop_info.width)
6987                 resource_info->quantum=(int) crop_info.width-1;
6988               crop_info.width-=resource_info->quantum;
6989             }
6990           if ((key_symbol == XK_Right) || (key_symbol == XK_KP_Right))
6991             {
6992               if (resource_info->quantum >= (int) (crop_info.width-crop_info.x))
6993                 resource_info->quantum=(int) (crop_info.width-crop_info.x-1);
6994               crop_info.x+=resource_info->quantum;
6995               crop_info.width-=resource_info->quantum;
6996             }
6997           if ((int) (windows->image.x+windows->image.width) >
6998               (int) crop_info.width)
6999             windows->image.x=(int) (crop_info.width-windows->image.width);
7000           if ((int) (windows->image.y+windows->image.height) >
7001               (int) crop_info.height)
7002             windows->image.y=(int) (crop_info.height-windows->image.height);
7003           XSetCropGeometry(display,windows,&crop_info,*image);
7004           windows->image.window_changes.width=(int) crop_info.width;
7005           windows->image.window_changes.height=(int) crop_info.height;
7006           (void) XSetWindowBackgroundPixmap(display,windows->image.id,None);
7007           (void) XConfigureImage(display,resource_info,windows,*image);
7008           return(NullCommand);
7009         }
7010       XTranslateImage(display,windows,*image,key_symbol);
7011       return(NullCommand);
7012     }
7013     default:
7014       return(NullCommand);
7015   }
7016   return(NullCommand);
7017 }
7018 \f
7019 /*
7020 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7021 %                                                                             %
7022 %                                                                             %
7023 %                                                                             %
7024 +   X M a g i c k C o m m a n d                                               %
7025 %                                                                             %
7026 %                                                                             %
7027 %                                                                             %
7028 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
7029 %
7030 %  XMagickCommand() makes a transform to the image or Image window as
7031 %  specified by a user menu button or keyboard command.
7032 %
7033 %  The format of the XMagickCommand method is:
7034 %
7035 %      Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7036 %        XWindows *windows,const CommandType command,Image **image)
7037 %
7038 %  A description of each parameter follows:
7039 %
7040 %    o nexus:  Method XMagickCommand returns an image when the
7041 %      user chooses 'Load Image' from the command menu.  Otherwise a null
7042 %      image is returned.
7043 %
7044 %    o display: Specifies a connection to an X server; returned from
7045 %      XOpenDisplay.
7046 %
7047 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
7048 %
7049 %    o windows: Specifies a pointer to a XWindows structure.
7050 %
7051 %    o command: Specifies a command to perform.
7052 %
7053 %    o image: the image;  XMagickCommand
7054 %      may transform the image and return a new image pointer.
7055 %
7056 */
7057 static Image *XMagickCommand(Display *display,XResourceInfo *resource_info,
7058   XWindows *windows,const CommandType command,Image **image)
7059 {
7060   char
7061     filename[MaxTextExtent],
7062     geometry[MaxTextExtent],
7063     modulate_factors[MaxTextExtent];
7064
7065   GeometryInfo
7066     geometry_info;
7067
7068   Image
7069     *nexus;
7070
7071   ImageInfo
7072     *image_info;
7073
7074   int
7075     x,
7076     y;
7077
7078   MagickStatusType
7079     flags,
7080     status;
7081
7082   QuantizeInfo
7083     quantize_info;
7084
7085   RectangleInfo
7086     page_geometry;
7087
7088   register int
7089     i;
7090
7091   static char
7092     color[MaxTextExtent] = "gray";
7093
7094   unsigned int
7095     height,
7096     width;
7097
7098   /*
7099     Process user command.
7100   */
7101   XCheckRefreshWindows(display,windows);
7102   XImageCache(display,resource_info,windows,command,image);
7103   nexus=NewImageList();
7104   windows->image.window_changes.width=windows->image.ximage->width;
7105   windows->image.window_changes.height=windows->image.ximage->height;
7106   image_info=CloneImageInfo(resource_info->image_info);
7107   SetGeometryInfo(&geometry_info);
7108   GetQuantizeInfo(&quantize_info);
7109   switch (command)
7110   {
7111     case OpenCommand:
7112     {
7113       /*
7114         Load image.
7115       */
7116       nexus=XOpenImage(display,resource_info,windows,MagickFalse);
7117       break;
7118     }
7119     case NextCommand:
7120     {
7121       /*
7122         Display next image.
7123       */
7124       for (i=0; i < resource_info->quantum; i++)
7125         XClientMessage(display,windows->image.id,windows->im_protocols,
7126           windows->im_next_image,CurrentTime);
7127       break;
7128     }
7129     case FormerCommand:
7130     {
7131       /*
7132         Display former image.
7133       */
7134       for (i=0; i < resource_info->quantum; i++)
7135         XClientMessage(display,windows->image.id,windows->im_protocols,
7136           windows->im_former_image,CurrentTime);
7137       break;
7138     }
7139     case SelectCommand:
7140     {
7141       int
7142         status;
7143
7144       /*
7145         Select image.
7146       */
7147       status=chdir(resource_info->home_directory);
7148       if (status == -1)
7149         (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
7150           FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
7151       nexus=XOpenImage(display,resource_info,windows,MagickTrue);
7152       break;
7153     }
7154     case SaveCommand:
7155     {
7156       /*
7157         Save image.
7158       */
7159       status=XSaveImage(display,resource_info,windows,*image);
7160       if (status == MagickFalse)
7161         {
7162           XNoticeWidget(display,windows,"Unable to write X image:",
7163             (*image)->filename);
7164           break;
7165         }
7166       break;
7167     }
7168     case PrintCommand:
7169     {
7170       /*
7171         Print image.
7172       */
7173       status=XPrintImage(display,resource_info,windows,*image);
7174       if (status == MagickFalse)
7175         {
7176           XNoticeWidget(display,windows,"Unable to print X image:",
7177             (*image)->filename);
7178           break;
7179         }
7180       break;
7181     }
7182     case DeleteCommand:
7183     {
7184       static char
7185         filename[MaxTextExtent] = "\0";
7186
7187       /*
7188         Delete image file.
7189       */
7190       XFileBrowserWidget(display,windows,"Delete",filename);
7191       if (*filename == '\0')
7192         break;
7193       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
7194       if (status != MagickFalse)
7195         XNoticeWidget(display,windows,"Unable to delete image file:",filename);
7196       break;
7197     }
7198     case NewCommand:
7199     {
7200       int
7201         status;
7202
7203       static char
7204         color[MaxTextExtent] = "gray",
7205         geometry[MaxTextExtent] = "640x480";
7206
7207       static const char
7208         *format = "gradient";
7209
7210       /*
7211         Query user for canvas geometry.
7212       */
7213       status=XDialogWidget(display,windows,"New","Enter image geometry:",
7214         geometry);
7215       if (*geometry == '\0')
7216         break;
7217       if (status == 0)
7218         format="xc";
7219       XColorBrowserWidget(display,windows,"Select",color);
7220       if (*color == '\0')
7221         break;
7222       /*
7223         Create canvas.
7224       */
7225       (void) FormatLocaleString(image_info->filename,MaxTextExtent,
7226         "%s:%s",format,color);
7227       (void) CloneString(&image_info->size,geometry);
7228       nexus=ReadImage(image_info,&(*image)->exception);
7229       CatchException(&(*image)->exception);
7230       XClientMessage(display,windows->image.id,windows->im_protocols,
7231         windows->im_next_image,CurrentTime);
7232       break;
7233     }
7234     case VisualDirectoryCommand:
7235     {
7236       /*
7237         Visual Image directory.
7238       */
7239       nexus=XVisualDirectoryImage(display,resource_info,windows);
7240       break;
7241     }
7242     case QuitCommand:
7243     {
7244       /*
7245         exit program.
7246       */
7247       if (resource_info->confirm_exit == MagickFalse)
7248         XClientMessage(display,windows->image.id,windows->im_protocols,
7249           windows->im_exit,CurrentTime);
7250       else
7251         {
7252           int
7253             status;
7254
7255           /*
7256             Confirm program exit.
7257           */
7258           status=XConfirmWidget(display,windows,"Do you really want to exit",
7259             resource_info->client_name);
7260           if (status > 0)
7261             XClientMessage(display,windows->image.id,windows->im_protocols,
7262               windows->im_exit,CurrentTime);
7263         }
7264       break;
7265     }
7266     case CutCommand:
7267     {
7268       /*
7269         Cut image.
7270       */
7271       (void) XCropImage(display,resource_info,windows,*image,CutMode);
7272       break;
7273     }
7274     case CopyCommand:
7275     {
7276       /*
7277         Copy image.
7278       */
7279       (void) XCropImage(display,resource_info,windows,*image,CopyMode);
7280       break;
7281     }
7282     case PasteCommand:
7283     {
7284       /*
7285         Paste image.
7286       */
7287       status=XPasteImage(display,resource_info,windows,*image);
7288       if (status == MagickFalse)
7289         {
7290           XNoticeWidget(display,windows,"Unable to paste X image",
7291             (*image)->filename);
7292           break;
7293         }
7294       break;
7295     }
7296     case HalfSizeCommand:
7297     {
7298       /*
7299         Half image size.
7300       */
7301       windows->image.window_changes.width=windows->image.ximage->width/2;
7302       windows->image.window_changes.height=windows->image.ximage->height/2;
7303       (void) XConfigureImage(display,resource_info,windows,*image);
7304       break;
7305     }
7306     case OriginalSizeCommand:
7307     {
7308       /*
7309         Original image size.
7310       */
7311       windows->image.window_changes.width=(int) (*image)->columns;
7312       windows->image.window_changes.height=(int) (*image)->rows;
7313       (void) XConfigureImage(display,resource_info,windows,*image);
7314       break;
7315     }
7316     case DoubleSizeCommand:
7317     {
7318       /*
7319         Double the image size.
7320       */
7321       windows->image.window_changes.width=windows->image.ximage->width << 1;
7322       windows->image.window_changes.height=windows->image.ximage->height << 1;
7323       (void) XConfigureImage(display,resource_info,windows,*image);
7324       break;
7325     }
7326     case ResizeCommand:
7327     {
7328       int
7329         status;
7330
7331       size_t
7332         height,
7333         width;
7334
7335       ssize_t
7336         x,
7337         y;
7338
7339       /*
7340         Resize image.
7341       */
7342       width=(size_t) windows->image.ximage->width;
7343       height=(size_t) windows->image.ximage->height;
7344       x=0;
7345       y=0;
7346       (void) FormatLocaleString(geometry,MaxTextExtent,"%.20gx%.20g+0+0",
7347         (double) width,(double) height);
7348       status=XDialogWidget(display,windows,"Resize",
7349         "Enter resize geometry (e.g. 640x480, 200%):",geometry);
7350       if (*geometry == '\0')
7351         break;
7352       if (status == 0)
7353         (void) ConcatenateMagickString(geometry,"!",MaxTextExtent);
7354       (void) ParseMetaGeometry(geometry,&x,&y,&width,&height);
7355       windows->image.window_changes.width=(int) width;
7356       windows->image.window_changes.height=(int) height;
7357       (void) XConfigureImage(display,resource_info,windows,*image);
7358       break;
7359     }
7360     case ApplyCommand:
7361     {
7362       char
7363         image_geometry[MaxTextExtent];
7364
7365       if ((windows->image.crop_geometry == (char *) NULL) &&
7366           ((int) (*image)->columns == windows->image.ximage->width) &&
7367           ((int) (*image)->rows == windows->image.ximage->height))
7368         break;
7369       /*
7370         Apply size transforms to image.
7371       */
7372       XSetCursorState(display,windows,MagickTrue);
7373       XCheckRefreshWindows(display,windows);
7374       /*
7375         Crop and/or scale displayed image.
7376       */
7377       (void) FormatLocaleString(image_geometry,MaxTextExtent,"%dx%d!",
7378         windows->image.ximage->width,windows->image.ximage->height);
7379       (void) TransformImage(image,windows->image.crop_geometry,image_geometry);
7380       if (windows->image.crop_geometry != (char *) NULL)
7381         windows->image.crop_geometry=(char *)
7382           RelinquishMagickMemory(windows->image.crop_geometry);
7383       windows->image.x=0;
7384       windows->image.y=0;
7385       XConfigureImageColormap(display,resource_info,windows,*image);
7386       (void) XConfigureImage(display,resource_info,windows,*image);
7387       break;
7388     }
7389     case RefreshCommand:
7390     {
7391       (void) XConfigureImage(display,resource_info,windows,*image);
7392       break;
7393     }
7394     case RestoreCommand:
7395     {
7396       /*
7397         Restore Image window to its original size.
7398       */
7399       if ((windows->image.width == (unsigned int) (*image)->columns) &&
7400           (windows->image.height == (unsigned int) (*image)->rows) &&
7401           (windows->image.crop_geometry == (char *) NULL))
7402         {
7403           (void) XBell(display,0);
7404           break;
7405         }
7406       windows->image.window_changes.width=(int) (*image)->columns;
7407       windows->image.window_changes.height=(int) (*image)->rows;
7408       if (windows->image.crop_geometry != (char *) NULL)
7409         {
7410           windows->image.crop_geometry=(char *)
7411             RelinquishMagickMemory(windows->image.crop_geometry);
7412           windows->image.crop_geometry=(char *) NULL;
7413           windows->image.x=0;
7414           windows->image.y=0;
7415         }
7416       XConfigureImageColormap(display,resource_info,windows,*image);
7417       (void) XConfigureImage(display,resource_info,windows,*image);
7418       break;
7419     }
7420     case CropCommand:
7421     {
7422       /*
7423         Crop image.
7424       */
7425       (void) XCropImage(display,resource_info,windows,*image,CropMode);
7426       break;
7427     }
7428     case ChopCommand:
7429     {
7430       /*
7431         Chop image.
7432       */
7433       status=XChopImage(display,resource_info,windows,image);
7434       if (status == MagickFalse)
7435         {
7436           XNoticeWidget(display,windows,"Unable to cut X image",
7437             (*image)->filename);
7438           break;
7439         }
7440       break;
7441     }
7442     case FlopCommand:
7443     {
7444       Image
7445         *flop_image;
7446
7447       /*
7448         Flop image scanlines.
7449       */
7450       XSetCursorState(display,windows,MagickTrue);
7451       XCheckRefreshWindows(display,windows);
7452       flop_image=FlopImage(*image,&(*image)->exception);
7453       if (flop_image != (Image *) NULL)
7454         {
7455           *image=DestroyImage(*image);
7456           *image=flop_image;
7457         }
7458       CatchException(&(*image)->exception);
7459       XSetCursorState(display,windows,MagickFalse);
7460       if (windows->image.crop_geometry != (char *) NULL)
7461         {
7462           /*
7463             Flop crop geometry.
7464           */
7465           width=(unsigned int) (*image)->columns;
7466           height=(unsigned int) (*image)->rows;
7467           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7468             &width,&height);
7469           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7470             "%ux%u%+d%+d",width,height,(int) (*image)->columns-(int) width-x,y);
7471         }
7472       if (windows->image.orphan != MagickFalse)
7473         break;
7474       (void) XConfigureImage(display,resource_info,windows,*image);
7475       break;
7476     }
7477     case FlipCommand:
7478     {
7479       Image
7480         *flip_image;
7481
7482       /*
7483         Flip image scanlines.
7484       */
7485       XSetCursorState(display,windows,MagickTrue);
7486       XCheckRefreshWindows(display,windows);
7487       flip_image=FlipImage(*image,&(*image)->exception);
7488       if (flip_image != (Image *) NULL)
7489         {
7490           *image=DestroyImage(*image);
7491           *image=flip_image;
7492         }
7493       CatchException(&(*image)->exception);
7494       XSetCursorState(display,windows,MagickFalse);
7495       if (windows->image.crop_geometry != (char *) NULL)
7496         {
7497           /*
7498             Flip crop geometry.
7499           */
7500           width=(unsigned int) (*image)->columns;
7501           height=(unsigned int) (*image)->rows;
7502           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
7503             &width,&height);
7504           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
7505             "%ux%u%+d%+d",width,height,x,(int) (*image)->rows-(int) height-y);
7506         }
7507       if (windows->image.orphan != MagickFalse)
7508         break;
7509       (void) XConfigureImage(display,resource_info,windows,*image);
7510       break;
7511     }
7512     case RotateRightCommand:
7513     {
7514       /*
7515         Rotate image 90 degrees clockwise.
7516       */
7517       status=XRotateImage(display,resource_info,windows,90.0,image);
7518       if (status == MagickFalse)
7519         {
7520           XNoticeWidget(display,windows,"Unable to rotate X image",
7521             (*image)->filename);
7522           break;
7523         }
7524       break;
7525     }
7526     case RotateLeftCommand:
7527     {
7528       /*
7529         Rotate image 90 degrees counter-clockwise.
7530       */
7531       status=XRotateImage(display,resource_info,windows,-90.0,image);
7532       if (status == MagickFalse)
7533         {
7534           XNoticeWidget(display,windows,"Unable to rotate X image",
7535             (*image)->filename);
7536           break;
7537         }
7538       break;
7539     }
7540     case RotateCommand:
7541     {
7542       /*
7543         Rotate image.
7544       */
7545       status=XRotateImage(display,resource_info,windows,0.0,image);
7546       if (status == MagickFalse)
7547         {
7548           XNoticeWidget(display,windows,"Unable to rotate X image",
7549             (*image)->filename);
7550           break;
7551         }
7552       break;
7553     }
7554     case ShearCommand:
7555     {
7556       Image
7557         *shear_image;
7558
7559       static char
7560         geometry[MaxTextExtent] = "45.0x45.0";
7561
7562       /*
7563         Query user for shear color and geometry.
7564       */
7565       XColorBrowserWidget(display,windows,"Select",color);
7566       if (*color == '\0')
7567         break;
7568       (void) XDialogWidget(display,windows,"Shear","Enter shear geometry:",
7569         geometry);
7570       if (*geometry == '\0')
7571         break;
7572       /*
7573         Shear image.
7574       */
7575       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7576       XSetCursorState(display,windows,MagickTrue);
7577       XCheckRefreshWindows(display,windows);
7578       (void) QueryColorDatabase(color,&(*image)->background_color,
7579         &(*image)->exception);
7580       flags=ParseGeometry(geometry,&geometry_info);
7581       if ((flags & SigmaValue) == 0)
7582         geometry_info.sigma=geometry_info.rho;
7583       shear_image=ShearImage(*image,geometry_info.rho,geometry_info.sigma,
7584         &(*image)->exception);
7585       if (shear_image != (Image *) NULL)
7586         {
7587           *image=DestroyImage(*image);
7588           *image=shear_image;
7589         }
7590       CatchException(&(*image)->exception);
7591       XSetCursorState(display,windows,MagickFalse);
7592       if (windows->image.orphan != MagickFalse)
7593         break;
7594       windows->image.window_changes.width=(int) (*image)->columns;
7595       windows->image.window_changes.height=(int) (*image)->rows;
7596       XConfigureImageColormap(display,resource_info,windows,*image);
7597       (void) XConfigureImage(display,resource_info,windows,*image);
7598       break;
7599     }
7600     case RollCommand:
7601     {
7602       Image
7603         *roll_image;
7604
7605       static char
7606         geometry[MaxTextExtent] = "+2+2";
7607
7608       /*
7609         Query user for the roll geometry.
7610       */
7611       (void) XDialogWidget(display,windows,"Roll","Enter roll geometry:",
7612         geometry);
7613       if (*geometry == '\0')
7614         break;
7615       /*
7616         Roll image.
7617       */
7618       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
7619       XSetCursorState(display,windows,MagickTrue);
7620       XCheckRefreshWindows(display,windows);
7621       (void) ParsePageGeometry(*image,geometry,&page_geometry,
7622         &(*image)->exception);
7623       roll_image=RollImage(*image,page_geometry.x,page_geometry.y,
7624         &(*image)->exception);
7625       if (roll_image != (Image *) NULL)
7626         {
7627           *image=DestroyImage(*image);
7628           *image=roll_image;
7629         }
7630       CatchException(&(*image)->exception);
7631       XSetCursorState(display,windows,MagickFalse);
7632       if (windows->image.orphan != MagickFalse)
7633         break;
7634       windows->image.window_changes.width=(int) (*image)->columns;
7635       windows->image.window_changes.height=(int) (*image)->rows;
7636       XConfigureImageColormap(display,resource_info,windows,*image);
7637       (void) XConfigureImage(display,resource_info,windows,*image);
7638       break;
7639     }
7640     case TrimCommand:
7641     {
7642       static char
7643         fuzz[MaxTextExtent];
7644
7645       /*
7646         Query user for the fuzz factor.
7647       */
7648       (void) FormatLocaleString(fuzz,MaxTextExtent,"%g%%",100.0*
7649         (*image)->fuzz/(QuantumRange+1.0));
7650       (void) XDialogWidget(display,windows,"Trim","Enter fuzz factor:",fuzz);
7651       if (*fuzz == '\0')
7652         break;
7653       (*image)->fuzz=SiPrefixToDouble(fuzz,(double) QuantumRange+1.0);
7654       /*
7655         Trim image.
7656       */
7657       status=XTrimImage(display,resource_info,windows,*image);
7658       if (status == MagickFalse)
7659         {
7660           XNoticeWidget(display,windows,"Unable to trim X image",
7661             (*image)->filename);
7662           break;
7663         }
7664       break;
7665     }
7666     case HueCommand:
7667     {
7668       static char
7669         hue_percent[MaxTextExtent] = "110";
7670
7671       /*
7672         Query user for percent hue change.
7673       */
7674       (void) XDialogWidget(display,windows,"Apply",
7675         "Enter percent change in image hue (0-200):",hue_percent);
7676       if (*hue_percent == '\0')
7677         break;
7678       /*
7679         Vary the image hue.
7680       */
7681       XSetCursorState(display,windows,MagickTrue);
7682       XCheckRefreshWindows(display,windows);
7683       (void) CopyMagickString(modulate_factors,"100.0/100.0/",MaxTextExtent);
7684       (void) ConcatenateMagickString(modulate_factors,hue_percent,
7685         MaxTextExtent);
7686       (void) ModulateImage(*image,modulate_factors);
7687       XSetCursorState(display,windows,MagickFalse);
7688       if (windows->image.orphan != MagickFalse)
7689         break;
7690       XConfigureImageColormap(display,resource_info,windows,*image);
7691       (void) XConfigureImage(display,resource_info,windows,*image);
7692       break;
7693     }
7694     case SaturationCommand:
7695     {
7696       static char
7697         saturation_percent[MaxTextExtent] = "110";
7698
7699       /*
7700         Query user for percent saturation change.
7701       */
7702       (void) XDialogWidget(display,windows,"Apply",
7703         "Enter percent change in color saturation (0-200):",saturation_percent);
7704       if (*saturation_percent == '\0')
7705         break;
7706       /*
7707         Vary color saturation.
7708       */
7709       XSetCursorState(display,windows,MagickTrue);
7710       XCheckRefreshWindows(display,windows);
7711       (void) CopyMagickString(modulate_factors,"100.0/",MaxTextExtent);
7712       (void) ConcatenateMagickString(modulate_factors,saturation_percent,
7713         MaxTextExtent);
7714       (void) ModulateImage(*image,modulate_factors);
7715       XSetCursorState(display,windows,MagickFalse);
7716       if (windows->image.orphan != MagickFalse)
7717         break;
7718       XConfigureImageColormap(display,resource_info,windows,*image);
7719       (void) XConfigureImage(display,resource_info,windows,*image);
7720       break;
7721     }
7722     case BrightnessCommand:
7723     {
7724       static char
7725         brightness_percent[MaxTextExtent] = "110";
7726
7727       /*
7728         Query user for percent brightness change.
7729       */
7730       (void) XDialogWidget(display,windows,"Apply",
7731         "Enter percent change in color brightness (0-200):",brightness_percent);
7732       if (*brightness_percent == '\0')
7733         break;
7734       /*
7735         Vary the color brightness.
7736       */
7737       XSetCursorState(display,windows,MagickTrue);
7738       XCheckRefreshWindows(display,windows);
7739       (void) CopyMagickString(modulate_factors,brightness_percent,
7740         MaxTextExtent);
7741       (void) ModulateImage(*image,modulate_factors);
7742       XSetCursorState(display,windows,MagickFalse);
7743       if (windows->image.orphan != MagickFalse)
7744         break;
7745       XConfigureImageColormap(display,resource_info,windows,*image);
7746       (void) XConfigureImage(display,resource_info,windows,*image);
7747       break;
7748     }
7749     case GammaCommand:
7750     {
7751       static char
7752         factor[MaxTextExtent] = "1.6";
7753
7754       /*
7755         Query user for gamma value.
7756       */
7757       (void) XDialogWidget(display,windows,"Gamma",
7758         "Enter gamma value (e.g. 1.0,1.0,1.6):",factor);
7759       if (*factor == '\0')
7760         break;
7761       /*
7762         Gamma correct image.
7763       */
7764       XSetCursorState(display,windows,MagickTrue);
7765       XCheckRefreshWindows(display,windows);
7766       (void) GammaImage(*image,factor);
7767       XSetCursorState(display,windows,MagickFalse);
7768       if (windows->image.orphan != MagickFalse)
7769         break;
7770       XConfigureImageColormap(display,resource_info,windows,*image);
7771       (void) XConfigureImage(display,resource_info,windows,*image);
7772       break;
7773     }
7774     case SpiffCommand:
7775     {
7776       /*
7777         Sharpen the image contrast.
7778       */
7779       XSetCursorState(display,windows,MagickTrue);
7780       XCheckRefreshWindows(display,windows);
7781       (void) ContrastImage(*image,MagickTrue);
7782       XSetCursorState(display,windows,MagickFalse);
7783       if (windows->image.orphan != MagickFalse)
7784         break;
7785       XConfigureImageColormap(display,resource_info,windows,*image);
7786       (void) XConfigureImage(display,resource_info,windows,*image);
7787       break;
7788     }
7789     case DullCommand:
7790     {
7791       /*
7792         Dull the image contrast.
7793       */
7794       XSetCursorState(display,windows,MagickTrue);
7795       XCheckRefreshWindows(display,windows);
7796       (void) ContrastImage(*image,MagickFalse);
7797       XSetCursorState(display,windows,MagickFalse);
7798       if (windows->image.orphan != MagickFalse)
7799         break;
7800       XConfigureImageColormap(display,resource_info,windows,*image);
7801       (void) XConfigureImage(display,resource_info,windows,*image);
7802       break;
7803     }
7804     case ContrastStretchCommand:
7805     {
7806       double
7807         black_point,
7808         white_point;
7809
7810       static char
7811         levels[MaxTextExtent] = "1%";
7812
7813       /*
7814         Query user for gamma value.
7815       */
7816       (void) XDialogWidget(display,windows,"Contrast Stretch",
7817         "Enter black and white points:",levels);
7818       if (*levels == '\0')
7819         break;
7820       /*
7821         Contrast stretch image.
7822       */
7823       XSetCursorState(display,windows,MagickTrue);
7824       XCheckRefreshWindows(display,windows);
7825       flags=ParseGeometry(levels,&geometry_info);
7826       black_point=geometry_info.rho;
7827       white_point=(flags & SigmaValue) != 0 ? geometry_info.sigma : black_point;
7828       if ((flags & PercentValue) != 0)
7829         {
7830           black_point*=(double) (*image)->columns*(*image)->rows/100.0;
7831           white_point*=(double) (*image)->columns*(*image)->rows/100.0;
7832         }
7833       white_point=(MagickRealType) (*image)->columns*(*image)->rows-white_point;
7834       (void) ContrastStretchImageChannel(*image,DefaultChannels,black_point,
7835         white_point);
7836       XSetCursorState(display,windows,MagickFalse);
7837       if (windows->image.orphan != MagickFalse)
7838         break;
7839       XConfigureImageColormap(display,resource_info,windows,*image);
7840       (void) XConfigureImage(display,resource_info,windows,*image);
7841       break;
7842     }
7843     case SigmoidalContrastCommand:
7844     {
7845       static char
7846         levels[MaxTextExtent] = "3x50%";
7847
7848       /*
7849         Query user for gamma value.
7850       */
7851       (void) XDialogWidget(display,windows,"Sigmoidal Contrast",
7852         "Enter contrast and midpoint:",levels);
7853       if (*levels == '\0')
7854         break;
7855       /*
7856         Contrast stretch image.
7857       */
7858       XSetCursorState(display,windows,MagickTrue);
7859       XCheckRefreshWindows(display,windows);
7860       (void) SigmoidalContrastImage(*image,MagickTrue,levels);
7861       XSetCursorState(display,windows,MagickFalse);
7862       if (windows->image.orphan != MagickFalse)
7863         break;
7864       XConfigureImageColormap(display,resource_info,windows,*image);
7865       (void) XConfigureImage(display,resource_info,windows,*image);
7866       break;
7867     }
7868     case NormalizeCommand:
7869     {
7870       /*
7871         Perform histogram normalization on the image.
7872       */
7873       XSetCursorState(display,windows,MagickTrue);
7874       XCheckRefreshWindows(display,windows);
7875       (void) NormalizeImage(*image);
7876       XSetCursorState(display,windows,MagickFalse);
7877       if (windows->image.orphan != MagickFalse)
7878         break;
7879       XConfigureImageColormap(display,resource_info,windows,*image);
7880       (void) XConfigureImage(display,resource_info,windows,*image);
7881       break;
7882     }
7883     case EqualizeCommand:
7884     {
7885       /*
7886         Perform histogram equalization on the image.
7887       */
7888       XSetCursorState(display,windows,MagickTrue);
7889       XCheckRefreshWindows(display,windows);
7890       (void) EqualizeImage(*image);
7891       XSetCursorState(display,windows,MagickFalse);
7892       if (windows->image.orphan != MagickFalse)
7893         break;
7894       XConfigureImageColormap(display,resource_info,windows,*image);
7895       (void) XConfigureImage(display,resource_info,windows,*image);
7896       break;
7897     }
7898     case NegateCommand:
7899     {
7900       /*
7901         Negate colors in image.
7902       */
7903       XSetCursorState(display,windows,MagickTrue);
7904       XCheckRefreshWindows(display,windows);
7905       (void) NegateImage(*image,MagickFalse);
7906       XSetCursorState(display,windows,MagickFalse);
7907       if (windows->image.orphan != MagickFalse)
7908         break;
7909       XConfigureImageColormap(display,resource_info,windows,*image);
7910       (void) XConfigureImage(display,resource_info,windows,*image);
7911       break;
7912     }
7913     case GrayscaleCommand:
7914     {
7915       /*
7916         Convert image to grayscale.
7917       */
7918       XSetCursorState(display,windows,MagickTrue);
7919       XCheckRefreshWindows(display,windows);
7920       (void) SetImageType(*image,(*image)->matte == MagickFalse ?
7921         GrayscaleType : GrayscaleMatteType);
7922       XSetCursorState(display,windows,MagickFalse);
7923       if (windows->image.orphan != MagickFalse)
7924         break;
7925       XConfigureImageColormap(display,resource_info,windows,*image);
7926       (void) XConfigureImage(display,resource_info,windows,*image);
7927       break;
7928     }
7929     case MapCommand:
7930     {
7931       Image
7932         *affinity_image;
7933
7934       static char
7935         filename[MaxTextExtent] = "\0";
7936
7937       /*
7938         Request image file name from user.
7939       */
7940       XFileBrowserWidget(display,windows,"Map",filename);
7941       if (*filename == '\0')
7942         break;
7943       /*
7944         Map image.
7945       */
7946       XSetCursorState(display,windows,MagickTrue);
7947       XCheckRefreshWindows(display,windows);
7948       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
7949       affinity_image=ReadImage(image_info,&(*image)->exception);
7950       if (affinity_image != (Image *) NULL)
7951         {
7952           (void) RemapImage(&quantize_info,*image,affinity_image);
7953           affinity_image=DestroyImage(affinity_image);
7954         }
7955       CatchException(&(*image)->exception);
7956       XSetCursorState(display,windows,MagickFalse);
7957       if (windows->image.orphan != MagickFalse)
7958         break;
7959       XConfigureImageColormap(display,resource_info,windows,*image);
7960       (void) XConfigureImage(display,resource_info,windows,*image);
7961       break;
7962     }
7963     case QuantizeCommand:
7964     {
7965       int
7966         status;
7967
7968       static char
7969         colors[MaxTextExtent] = "256";
7970
7971       /*
7972         Query user for maximum number of colors.
7973       */
7974       status=XDialogWidget(display,windows,"Quantize",
7975         "Maximum number of colors:",colors);
7976       if (*colors == '\0')
7977         break;
7978       /*
7979         Color reduce the image.
7980       */
7981       XSetCursorState(display,windows,MagickTrue);
7982       XCheckRefreshWindows(display,windows);
7983       quantize_info.number_colors=StringToUnsignedLong(colors);
7984       quantize_info.dither=status != 0 ? MagickTrue : MagickFalse;
7985       (void) QuantizeImage(&quantize_info,*image);
7986       XSetCursorState(display,windows,MagickFalse);
7987       if (windows->image.orphan != MagickFalse)
7988         break;
7989       XConfigureImageColormap(display,resource_info,windows,*image);
7990       (void) XConfigureImage(display,resource_info,windows,*image);
7991       break;
7992     }
7993     case DespeckleCommand:
7994     {
7995       Image
7996         *despeckle_image;
7997
7998       /*
7999         Despeckle image.
8000       */
8001       XSetCursorState(display,windows,MagickTrue);
8002       XCheckRefreshWindows(display,windows);
8003       despeckle_image=DespeckleImage(*image,&(*image)->exception);
8004       if (despeckle_image != (Image *) NULL)
8005         {
8006           *image=DestroyImage(*image);
8007           *image=despeckle_image;
8008         }
8009       CatchException(&(*image)->exception);
8010       XSetCursorState(display,windows,MagickFalse);
8011       if (windows->image.orphan != MagickFalse)
8012         break;
8013       XConfigureImageColormap(display,resource_info,windows,*image);
8014       (void) XConfigureImage(display,resource_info,windows,*image);
8015       break;
8016     }
8017     case EmbossCommand:
8018     {
8019       Image
8020         *emboss_image;
8021
8022       static char
8023         radius[MaxTextExtent] = "0.0x1.0";
8024
8025       /*
8026         Query user for emboss radius.
8027       */
8028       (void) XDialogWidget(display,windows,"Emboss",
8029         "Enter the emboss radius and standard deviation:",radius);
8030       if (*radius == '\0')
8031         break;
8032       /*
8033         Reduce noise in the image.
8034       */
8035       XSetCursorState(display,windows,MagickTrue);
8036       XCheckRefreshWindows(display,windows);
8037       flags=ParseGeometry(radius,&geometry_info);
8038       if ((flags & SigmaValue) == 0)
8039         geometry_info.sigma=1.0;
8040       emboss_image=EmbossImage(*image,geometry_info.rho,geometry_info.sigma,
8041         &(*image)->exception);
8042       if (emboss_image != (Image *) NULL)
8043         {
8044           *image=DestroyImage(*image);
8045           *image=emboss_image;
8046         }
8047       CatchException(&(*image)->exception);
8048       XSetCursorState(display,windows,MagickFalse);
8049       if (windows->image.orphan != MagickFalse)
8050         break;
8051       XConfigureImageColormap(display,resource_info,windows,*image);
8052       (void) XConfigureImage(display,resource_info,windows,*image);
8053       break;
8054     }
8055     case ReduceNoiseCommand:
8056     {
8057       Image
8058         *noise_image;
8059
8060       static char
8061         radius[MaxTextExtent] = "0";
8062
8063       /*
8064         Query user for noise radius.
8065       */
8066       (void) XDialogWidget(display,windows,"Reduce Noise",
8067         "Enter the noise radius:",radius);
8068       if (*radius == '\0')
8069         break;
8070       /*
8071         Reduce noise in the image.
8072       */
8073       XSetCursorState(display,windows,MagickTrue);
8074       XCheckRefreshWindows(display,windows);
8075       flags=ParseGeometry(radius,&geometry_info);
8076       noise_image=StatisticImage(*image,NonpeakStatistic,(size_t)
8077         geometry_info.rho,(size_t) geometry_info.rho,&(*image)->exception);
8078       if (noise_image != (Image *) NULL)
8079         {
8080           *image=DestroyImage(*image);
8081           *image=noise_image;
8082         }
8083       CatchException(&(*image)->exception);
8084       XSetCursorState(display,windows,MagickFalse);
8085       if (windows->image.orphan != MagickFalse)
8086         break;
8087       XConfigureImageColormap(display,resource_info,windows,*image);
8088       (void) XConfigureImage(display,resource_info,windows,*image);
8089       break;
8090     }
8091     case AddNoiseCommand:
8092     {
8093       char
8094         **noises;
8095
8096       Image
8097         *noise_image;
8098
8099       static char
8100         noise_type[MaxTextExtent] = "Gaussian";
8101
8102       /*
8103         Add noise to the image.
8104       */
8105       noises=GetCommandOptions(MagickNoiseOptions);
8106       if (noises == (char **) NULL)
8107         break;
8108       XListBrowserWidget(display,windows,&windows->widget,
8109         (const char **) noises,"Add Noise",
8110         "Select a type of noise to add to your image:",noise_type);
8111       noises=DestroyStringList(noises);
8112       if (*noise_type == '\0')
8113         break;
8114       XSetCursorState(display,windows,MagickTrue);
8115       XCheckRefreshWindows(display,windows);
8116       noise_image=AddNoiseImage(*image,(NoiseType) ParseCommandOption(
8117         MagickNoiseOptions,MagickFalse,noise_type),&(*image)->exception);
8118       if (noise_image != (Image *) NULL)
8119         {
8120           *image=DestroyImage(*image);
8121           *image=noise_image;
8122         }
8123       CatchException(&(*image)->exception);
8124       XSetCursorState(display,windows,MagickFalse);
8125       if (windows->image.orphan != MagickFalse)
8126         break;
8127       XConfigureImageColormap(display,resource_info,windows,*image);
8128       (void) XConfigureImage(display,resource_info,windows,*image);
8129       break;
8130     }
8131     case SharpenCommand:
8132     {
8133       Image
8134         *sharp_image;
8135
8136       static char
8137         radius[MaxTextExtent] = "0.0x1.0";
8138
8139       /*
8140         Query user for sharpen radius.
8141       */
8142       (void) XDialogWidget(display,windows,"Sharpen",
8143         "Enter the sharpen radius and standard deviation:",radius);
8144       if (*radius == '\0')
8145         break;
8146       /*
8147         Sharpen image scanlines.
8148       */
8149       XSetCursorState(display,windows,MagickTrue);
8150       XCheckRefreshWindows(display,windows);
8151       flags=ParseGeometry(radius,&geometry_info);
8152       sharp_image=SharpenImage(*image,geometry_info.rho,geometry_info.sigma,
8153         &(*image)->exception);
8154       if (sharp_image != (Image *) NULL)
8155         {
8156           *image=DestroyImage(*image);
8157           *image=sharp_image;
8158         }
8159       CatchException(&(*image)->exception);
8160       XSetCursorState(display,windows,MagickFalse);
8161       if (windows->image.orphan != MagickFalse)
8162         break;
8163       XConfigureImageColormap(display,resource_info,windows,*image);
8164       (void) XConfigureImage(display,resource_info,windows,*image);
8165       break;
8166     }
8167     case BlurCommand:
8168     {
8169       Image
8170         *blur_image;
8171
8172       static char
8173         radius[MaxTextExtent] = "0.0x1.0";
8174
8175       /*
8176         Query user for blur radius.
8177       */
8178       (void) XDialogWidget(display,windows,"Blur",
8179         "Enter the blur radius and standard deviation:",radius);
8180       if (*radius == '\0')
8181         break;
8182       /*
8183         Blur an image.
8184       */
8185       XSetCursorState(display,windows,MagickTrue);
8186       XCheckRefreshWindows(display,windows);
8187       flags=ParseGeometry(radius,&geometry_info);
8188       blur_image=BlurImage(*image,geometry_info.rho,geometry_info.sigma,
8189         &(*image)->exception);
8190       if (blur_image != (Image *) NULL)
8191         {
8192           *image=DestroyImage(*image);
8193           *image=blur_image;
8194         }
8195       CatchException(&(*image)->exception);
8196       XSetCursorState(display,windows,MagickFalse);
8197       if (windows->image.orphan != MagickFalse)
8198         break;
8199       XConfigureImageColormap(display,resource_info,windows,*image);
8200       (void) XConfigureImage(display,resource_info,windows,*image);
8201       break;
8202     }
8203     case ThresholdCommand:
8204     {
8205       double
8206         threshold;
8207
8208       static char
8209         factor[MaxTextExtent] = "128";
8210
8211       /*
8212         Query user for threshold value.
8213       */
8214       (void) XDialogWidget(display,windows,"Threshold",
8215         "Enter threshold value:",factor);
8216       if (*factor == '\0')
8217         break;
8218       /*
8219         Gamma correct image.
8220       */
8221       XSetCursorState(display,windows,MagickTrue);
8222       XCheckRefreshWindows(display,windows);
8223       threshold=SiPrefixToDouble(factor,QuantumRange);
8224       (void) BilevelImage(*image,threshold);
8225       XSetCursorState(display,windows,MagickFalse);
8226       if (windows->image.orphan != MagickFalse)
8227         break;
8228       XConfigureImageColormap(display,resource_info,windows,*image);
8229       (void) XConfigureImage(display,resource_info,windows,*image);
8230       break;
8231     }
8232     case EdgeDetectCommand:
8233     {
8234       Image
8235         *edge_image;
8236
8237       static char
8238         radius[MaxTextExtent] = "0";
8239
8240       /*
8241         Query user for edge factor.
8242       */
8243       (void) XDialogWidget(display,windows,"Detect Edges",
8244         "Enter the edge detect radius:",radius);
8245       if (*radius == '\0')
8246         break;
8247       /*
8248         Detect edge in image.
8249       */
8250       XSetCursorState(display,windows,MagickTrue);
8251       XCheckRefreshWindows(display,windows);
8252       flags=ParseGeometry(radius,&geometry_info);
8253       edge_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8254       if (edge_image != (Image *) NULL)
8255         {
8256           *image=DestroyImage(*image);
8257           *image=edge_image;
8258         }
8259       CatchException(&(*image)->exception);
8260       XSetCursorState(display,windows,MagickFalse);
8261       if (windows->image.orphan != MagickFalse)
8262         break;
8263       XConfigureImageColormap(display,resource_info,windows,*image);
8264       (void) XConfigureImage(display,resource_info,windows,*image);
8265       break;
8266     }
8267     case SpreadCommand:
8268     {
8269       Image
8270         *spread_image;
8271
8272       static char
8273         amount[MaxTextExtent] = "2";
8274
8275       /*
8276         Query user for spread amount.
8277       */
8278       (void) XDialogWidget(display,windows,"Spread",
8279         "Enter the displacement amount:",amount);
8280       if (*amount == '\0')
8281         break;
8282       /*
8283         Displace image pixels by a random amount.
8284       */
8285       XSetCursorState(display,windows,MagickTrue);
8286       XCheckRefreshWindows(display,windows);
8287       flags=ParseGeometry(amount,&geometry_info);
8288       spread_image=EdgeImage(*image,geometry_info.rho,&(*image)->exception);
8289       if (spread_image != (Image *) NULL)
8290         {
8291           *image=DestroyImage(*image);
8292           *image=spread_image;
8293         }
8294       CatchException(&(*image)->exception);
8295       XSetCursorState(display,windows,MagickFalse);
8296       if (windows->image.orphan != MagickFalse)
8297         break;
8298       XConfigureImageColormap(display,resource_info,windows,*image);
8299       (void) XConfigureImage(display,resource_info,windows,*image);
8300       break;
8301     }
8302     case ShadeCommand:
8303     {
8304       Image
8305         *shade_image;
8306
8307       int
8308         status;
8309
8310       static char
8311         geometry[MaxTextExtent] = "30x30";
8312
8313       /*
8314         Query user for the shade geometry.
8315       */
8316       status=XDialogWidget(display,windows,"Shade",
8317         "Enter the azimuth and elevation of the light source:",geometry);
8318       if (*geometry == '\0')
8319         break;
8320       /*
8321         Shade image pixels.
8322       */
8323       XSetCursorState(display,windows,MagickTrue);
8324       XCheckRefreshWindows(display,windows);
8325       flags=ParseGeometry(geometry,&geometry_info);
8326       if ((flags & SigmaValue) == 0)
8327         geometry_info.sigma=1.0;
8328       shade_image=ShadeImage(*image,status != 0 ? MagickFalse : MagickTrue,
8329         geometry_info.rho,geometry_info.sigma,&(*image)->exception);
8330       if (shade_image != (Image *) NULL)
8331         {
8332           *image=DestroyImage(*image);
8333           *image=shade_image;
8334         }
8335       CatchException(&(*image)->exception);
8336       XSetCursorState(display,windows,MagickFalse);
8337       if (windows->image.orphan != MagickFalse)
8338         break;
8339       XConfigureImageColormap(display,resource_info,windows,*image);
8340       (void) XConfigureImage(display,resource_info,windows,*image);
8341       break;
8342     }
8343     case RaiseCommand:
8344     {
8345       static char
8346         bevel_width[MaxTextExtent] = "10";
8347
8348       /*
8349         Query user for bevel width.
8350       */
8351       (void) XDialogWidget(display,windows,"Raise","Bevel width:",bevel_width);
8352       if (*bevel_width == '\0')
8353         break;
8354       /*
8355         Raise an image.
8356       */
8357       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8358       XSetCursorState(display,windows,MagickTrue);
8359       XCheckRefreshWindows(display,windows);
8360       (void) ParsePageGeometry(*image,bevel_width,&page_geometry,
8361         &(*image)->exception);
8362       (void) RaiseImage(*image,&page_geometry,MagickTrue);
8363       XSetCursorState(display,windows,MagickFalse);
8364       if (windows->image.orphan != MagickFalse)
8365         break;
8366       XConfigureImageColormap(display,resource_info,windows,*image);
8367       (void) XConfigureImage(display,resource_info,windows,*image);
8368       break;
8369     }
8370     case SegmentCommand:
8371     {
8372       static char
8373         threshold[MaxTextExtent] = "1.0x1.5";
8374
8375       /*
8376         Query user for smoothing threshold.
8377       */
8378       (void) XDialogWidget(display,windows,"Segment","Smooth threshold:",
8379         threshold);
8380       if (*threshold == '\0')
8381         break;
8382       /*
8383         Segment an image.
8384       */
8385       XSetCursorState(display,windows,MagickTrue);
8386       XCheckRefreshWindows(display,windows);
8387       flags=ParseGeometry(threshold,&geometry_info);
8388       if ((flags & SigmaValue) == 0)
8389         geometry_info.sigma=1.0;
8390       (void) SegmentImage(*image,RGBColorspace,MagickFalse,geometry_info.rho,
8391         geometry_info.sigma);
8392       XSetCursorState(display,windows,MagickFalse);
8393       if (windows->image.orphan != MagickFalse)
8394         break;
8395       XConfigureImageColormap(display,resource_info,windows,*image);
8396       (void) XConfigureImage(display,resource_info,windows,*image);
8397       break;
8398     }
8399     case SepiaToneCommand:
8400     {
8401       double
8402         threshold;
8403
8404       Image
8405         *sepia_image;
8406
8407       static char
8408         factor[MaxTextExtent] = "80%";
8409
8410       /*
8411         Query user for sepia-tone factor.
8412       */
8413       (void) XDialogWidget(display,windows,"Sepia Tone",
8414         "Enter the sepia tone factor (0 - 99.9%):",factor);
8415       if (*factor == '\0')
8416         break;
8417       /*
8418         Sepia tone image pixels.
8419       */
8420       XSetCursorState(display,windows,MagickTrue);
8421       XCheckRefreshWindows(display,windows);
8422       threshold=SiPrefixToDouble(factor,QuantumRange);
8423       sepia_image=SepiaToneImage(*image,threshold,&(*image)->exception);
8424       if (sepia_image != (Image *) NULL)
8425         {
8426           *image=DestroyImage(*image);
8427           *image=sepia_image;
8428         }
8429       CatchException(&(*image)->exception);
8430       XSetCursorState(display,windows,MagickFalse);
8431       if (windows->image.orphan != MagickFalse)
8432         break;
8433       XConfigureImageColormap(display,resource_info,windows,*image);
8434       (void) XConfigureImage(display,resource_info,windows,*image);
8435       break;
8436     }
8437     case SolarizeCommand:
8438     {
8439       double
8440         threshold;
8441
8442       static char
8443         factor[MaxTextExtent] = "60%";
8444
8445       /*
8446         Query user for solarize factor.
8447       */
8448       (void) XDialogWidget(display,windows,"Solarize",
8449         "Enter the solarize factor (0 - 99.9%):",factor);
8450       if (*factor == '\0')
8451         break;
8452       /*
8453         Solarize image pixels.
8454       */
8455       XSetCursorState(display,windows,MagickTrue);
8456       XCheckRefreshWindows(display,windows);
8457       threshold=SiPrefixToDouble(factor,QuantumRange);
8458       (void) SolarizeImage(*image,threshold);
8459       XSetCursorState(display,windows,MagickFalse);
8460       if (windows->image.orphan != MagickFalse)
8461         break;
8462       XConfigureImageColormap(display,resource_info,windows,*image);
8463       (void) XConfigureImage(display,resource_info,windows,*image);
8464       break;
8465     }
8466     case SwirlCommand:
8467     {
8468       Image
8469         *swirl_image;
8470
8471       static char
8472         degrees[MaxTextExtent] = "60";
8473
8474       /*
8475         Query user for swirl angle.
8476       */
8477       (void) XDialogWidget(display,windows,"Swirl","Enter the swirl angle:",
8478         degrees);
8479       if (*degrees == '\0')
8480         break;
8481       /*
8482         Swirl image pixels about the center.
8483       */
8484       XSetCursorState(display,windows,MagickTrue);
8485       XCheckRefreshWindows(display,windows);
8486       flags=ParseGeometry(degrees,&geometry_info);
8487       swirl_image=SwirlImage(*image,geometry_info.rho,&(*image)->exception);
8488       if (swirl_image != (Image *) NULL)
8489         {
8490           *image=DestroyImage(*image);
8491           *image=swirl_image;
8492         }
8493       CatchException(&(*image)->exception);
8494       XSetCursorState(display,windows,MagickFalse);
8495       if (windows->image.orphan != MagickFalse)
8496         break;
8497       XConfigureImageColormap(display,resource_info,windows,*image);
8498       (void) XConfigureImage(display,resource_info,windows,*image);
8499       break;
8500     }
8501     case ImplodeCommand:
8502     {
8503       Image
8504         *implode_image;
8505
8506       static char
8507         factor[MaxTextExtent] = "0.3";
8508
8509       /*
8510         Query user for implode factor.
8511       */
8512       (void) XDialogWidget(display,windows,"Implode",
8513         "Enter the implosion/explosion factor (-1.0 - 1.0):",factor);
8514       if (*factor == '\0')
8515         break;
8516       /*
8517         Implode image pixels about the center.
8518       */
8519       XSetCursorState(display,windows,MagickTrue);
8520       XCheckRefreshWindows(display,windows);
8521       flags=ParseGeometry(factor,&geometry_info);
8522       implode_image=ImplodeImage(*image,geometry_info.rho,&(*image)->exception);
8523       if (implode_image != (Image *) NULL)
8524         {
8525           *image=DestroyImage(*image);
8526           *image=implode_image;
8527         }
8528       CatchException(&(*image)->exception);
8529       XSetCursorState(display,windows,MagickFalse);
8530       if (windows->image.orphan != MagickFalse)
8531         break;
8532       XConfigureImageColormap(display,resource_info,windows,*image);
8533       (void) XConfigureImage(display,resource_info,windows,*image);
8534       break;
8535     }
8536     case VignetteCommand:
8537     {
8538       Image
8539         *vignette_image;
8540
8541       static char
8542         geometry[MaxTextExtent] = "0x20";
8543
8544       /*
8545         Query user for the vignette geometry.
8546       */
8547       (void) XDialogWidget(display,windows,"Vignette",
8548         "Enter the radius, sigma, and x and y offsets:",geometry);
8549       if (*geometry == '\0')
8550         break;
8551       /*
8552         Soften the edges of the image in vignette style
8553       */
8554       XSetCursorState(display,windows,MagickTrue);
8555       XCheckRefreshWindows(display,windows);
8556       flags=ParseGeometry(geometry,&geometry_info);
8557       if ((flags & SigmaValue) == 0)
8558         geometry_info.sigma=1.0;
8559       if ((flags & XiValue) == 0)
8560         geometry_info.xi=0.1*(*image)->columns;
8561       if ((flags & PsiValue) == 0)
8562         geometry_info.psi=0.1*(*image)->rows;
8563       vignette_image=VignetteImage(*image,geometry_info.rho,geometry_info.sigma,
8564         (ssize_t) ceil(geometry_info.xi-0.5),(ssize_t) ceil(geometry_info.psi-
8565         0.5),&(*image)->exception);
8566       if (vignette_image != (Image *) NULL)
8567         {
8568           *image=DestroyImage(*image);
8569           *image=vignette_image;
8570         }
8571       CatchException(&(*image)->exception);
8572       XSetCursorState(display,windows,MagickFalse);
8573       if (windows->image.orphan != MagickFalse)
8574         break;
8575       XConfigureImageColormap(display,resource_info,windows,*image);
8576       (void) XConfigureImage(display,resource_info,windows,*image);
8577       break;
8578     }
8579     case WaveCommand:
8580     {
8581       Image
8582         *wave_image;
8583
8584       static char
8585         geometry[MaxTextExtent] = "25x150";
8586
8587       /*
8588         Query user for the wave geometry.
8589       */
8590       (void) XDialogWidget(display,windows,"Wave",
8591         "Enter the amplitude and length of the wave:",geometry);
8592       if (*geometry == '\0')
8593         break;
8594       /*
8595         Alter an image along a sine wave.
8596       */
8597       XSetCursorState(display,windows,MagickTrue);
8598       XCheckRefreshWindows(display,windows);
8599       flags=ParseGeometry(geometry,&geometry_info);
8600       if ((flags & SigmaValue) == 0)
8601         geometry_info.sigma=1.0;
8602       wave_image=WaveImage(*image,geometry_info.rho,geometry_info.sigma,
8603         &(*image)->exception);
8604       if (wave_image != (Image *) NULL)
8605         {
8606           *image=DestroyImage(*image);
8607           *image=wave_image;
8608         }
8609       CatchException(&(*image)->exception);
8610       XSetCursorState(display,windows,MagickFalse);
8611       if (windows->image.orphan != MagickFalse)
8612         break;
8613       XConfigureImageColormap(display,resource_info,windows,*image);
8614       (void) XConfigureImage(display,resource_info,windows,*image);
8615       break;
8616     }
8617     case OilPaintCommand:
8618     {
8619       Image
8620         *paint_image;
8621
8622       static char
8623         radius[MaxTextExtent] = "0";
8624
8625       /*
8626         Query user for circular neighborhood radius.
8627       */
8628       (void) XDialogWidget(display,windows,"Oil Paint",
8629         "Enter the mask radius:",radius);
8630       if (*radius == '\0')
8631         break;
8632       /*
8633         OilPaint image scanlines.
8634       */
8635       XSetCursorState(display,windows,MagickTrue);
8636       XCheckRefreshWindows(display,windows);
8637       flags=ParseGeometry(radius,&geometry_info);
8638       paint_image=OilPaintImage(*image,geometry_info.rho,&(*image)->exception);
8639       if (paint_image != (Image *) NULL)
8640         {
8641           *image=DestroyImage(*image);
8642           *image=paint_image;
8643         }
8644       CatchException(&(*image)->exception);
8645       XSetCursorState(display,windows,MagickFalse);
8646       if (windows->image.orphan != MagickFalse)
8647         break;
8648       XConfigureImageColormap(display,resource_info,windows,*image);
8649       (void) XConfigureImage(display,resource_info,windows,*image);
8650       break;
8651     }
8652     case CharcoalDrawCommand:
8653     {
8654       Image
8655         *charcoal_image;
8656
8657       static char
8658         radius[MaxTextExtent] = "0x1";
8659
8660       /*
8661         Query user for charcoal radius.
8662       */
8663       (void) XDialogWidget(display,windows,"Charcoal Draw",
8664         "Enter the charcoal radius and sigma:",radius);
8665       if (*radius == '\0')
8666         break;
8667       /*
8668         Charcoal the image.
8669       */
8670       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8671       XSetCursorState(display,windows,MagickTrue);
8672       XCheckRefreshWindows(display,windows);
8673       flags=ParseGeometry(radius,&geometry_info);
8674       if ((flags & SigmaValue) == 0)
8675         geometry_info.sigma=geometry_info.rho;
8676       charcoal_image=CharcoalImage(*image,geometry_info.rho,geometry_info.sigma,
8677         &(*image)->exception);
8678       if (charcoal_image != (Image *) NULL)
8679         {
8680           *image=DestroyImage(*image);
8681           *image=charcoal_image;
8682         }
8683       CatchException(&(*image)->exception);
8684       XSetCursorState(display,windows,MagickFalse);
8685       if (windows->image.orphan != MagickFalse)
8686         break;
8687       XConfigureImageColormap(display,resource_info,windows,*image);
8688       (void) XConfigureImage(display,resource_info,windows,*image);
8689       break;
8690     }
8691     case AnnotateCommand:
8692     {
8693       /*
8694         Annotate the image with text.
8695       */
8696       status=XAnnotateEditImage(display,resource_info,windows,*image);
8697       if (status == MagickFalse)
8698         {
8699           XNoticeWidget(display,windows,"Unable to annotate X image",
8700             (*image)->filename);
8701           break;
8702         }
8703       break;
8704     }
8705     case DrawCommand:
8706     {
8707       /*
8708         Draw image.
8709       */
8710       status=XDrawEditImage(display,resource_info,windows,image);
8711       if (status == MagickFalse)
8712         {
8713           XNoticeWidget(display,windows,"Unable to draw on the X image",
8714             (*image)->filename);
8715           break;
8716         }
8717       break;
8718     }
8719     case ColorCommand:
8720     {
8721       /*
8722         Color edit.
8723       */
8724       status=XColorEditImage(display,resource_info,windows,image);
8725       if (status == MagickFalse)
8726         {
8727           XNoticeWidget(display,windows,"Unable to pixel edit X image",
8728             (*image)->filename);
8729           break;
8730         }
8731       break;
8732     }
8733     case MatteCommand:
8734     {
8735       /*
8736         Matte edit.
8737       */
8738       status=XMatteEditImage(display,resource_info,windows,image);
8739       if (status == MagickFalse)
8740         {
8741           XNoticeWidget(display,windows,"Unable to matte edit X image",
8742             (*image)->filename);
8743           break;
8744         }
8745       break;
8746     }
8747     case CompositeCommand:
8748     {
8749       /*
8750         Composite image.
8751       */
8752       status=XCompositeImage(display,resource_info,windows,*image);
8753       if (status == MagickFalse)
8754         {
8755           XNoticeWidget(display,windows,"Unable to composite X image",
8756             (*image)->filename);
8757           break;
8758         }
8759       break;
8760     }
8761     case AddBorderCommand:
8762     {
8763       Image
8764         *border_image;
8765
8766       static char
8767         geometry[MaxTextExtent] = "6x6";
8768
8769       /*
8770         Query user for border color and geometry.
8771       */
8772       XColorBrowserWidget(display,windows,"Select",color);
8773       if (*color == '\0')
8774         break;
8775       (void) XDialogWidget(display,windows,"Add Border",
8776         "Enter border geometry:",geometry);
8777       if (*geometry == '\0')
8778         break;
8779       /*
8780         Add a border to the image.
8781       */
8782       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8783       XSetCursorState(display,windows,MagickTrue);
8784       XCheckRefreshWindows(display,windows);
8785       (void) QueryColorDatabase(color,&(*image)->border_color,
8786         &(*image)->exception);
8787       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8788         &(*image)->exception);
8789       border_image=BorderImage(*image,&page_geometry,&(*image)->exception);
8790       if (border_image != (Image *) NULL)
8791         {
8792           *image=DestroyImage(*image);
8793           *image=border_image;
8794         }
8795       CatchException(&(*image)->exception);
8796       XSetCursorState(display,windows,MagickFalse);
8797       if (windows->image.orphan != MagickFalse)
8798         break;
8799       windows->image.window_changes.width=(int) (*image)->columns;
8800       windows->image.window_changes.height=(int) (*image)->rows;
8801       XConfigureImageColormap(display,resource_info,windows,*image);
8802       (void) XConfigureImage(display,resource_info,windows,*image);
8803       break;
8804     }
8805     case AddFrameCommand:
8806     {
8807       FrameInfo
8808         frame_info;
8809
8810       Image
8811         *frame_image;
8812
8813       static char
8814         geometry[MaxTextExtent] = "6x6";
8815
8816       /*
8817         Query user for frame color and geometry.
8818       */
8819       XColorBrowserWidget(display,windows,"Select",color);
8820       if (*color == '\0')
8821         break;
8822       (void) XDialogWidget(display,windows,"Add Frame","Enter frame geometry:",
8823         geometry);
8824       if (*geometry == '\0')
8825         break;
8826       /*
8827         Surround image with an ornamental border.
8828       */
8829       (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
8830       XSetCursorState(display,windows,MagickTrue);
8831       XCheckRefreshWindows(display,windows);
8832       (void) QueryColorDatabase(color,&(*image)->matte_color,
8833         &(*image)->exception);
8834       (void) ParsePageGeometry(*image,geometry,&page_geometry,
8835         &(*image)->exception);
8836       frame_info.width=page_geometry.width;
8837       frame_info.height=page_geometry.height;
8838       frame_info.outer_bevel=page_geometry.x;
8839       frame_info.inner_bevel=page_geometry.y;
8840       frame_info.x=(ssize_t) frame_info.width;
8841       frame_info.y=(ssize_t) frame_info.height;
8842       frame_info.width=(*image)->columns+2*frame_info.width;
8843       frame_info.height=(*image)->rows+2*frame_info.height;
8844       frame_image=FrameImage(*image,&frame_info,&(*image)->exception);
8845       if (frame_image != (Image *) NULL)
8846         {
8847           *image=DestroyImage(*image);
8848           *image=frame_image;
8849         }
8850       CatchException(&(*image)->exception);
8851       XSetCursorState(display,windows,MagickFalse);
8852       if (windows->image.orphan != MagickFalse)
8853         break;
8854       windows->image.window_changes.width=(int) (*image)->columns;
8855       windows->image.window_changes.height=(int) (*image)->rows;
8856       XConfigureImageColormap(display,resource_info,windows,*image);
8857       (void) XConfigureImage(display,resource_info,windows,*image);
8858       break;
8859     }
8860     case CommentCommand:
8861     {
8862       const char
8863         *value;
8864
8865       FILE
8866         *file;
8867
8868       int
8869         unique_file;
8870
8871       /*
8872         Edit image comment.
8873       */
8874       unique_file=AcquireUniqueFileResource(image_info->filename);
8875       if (unique_file == -1)
8876         XNoticeWidget(display,windows,"Unable to edit image comment",
8877           image_info->filename);
8878       value=GetImageProperty(*image,"comment");
8879       if (value == (char *) NULL)
8880         unique_file=close(unique_file)-1;
8881       else
8882         {
8883           register const char
8884             *p;
8885
8886           file=fdopen(unique_file,"w");
8887           if (file == (FILE *) NULL)
8888             {
8889               XNoticeWidget(display,windows,"Unable to edit image comment",
8890                 image_info->filename);
8891               break;
8892             }
8893           for (p=value; *p != '\0'; p++)
8894             (void) fputc((int) *p,file);
8895           (void) fputc('\n',file);
8896           (void) fclose(file);
8897         }
8898       XSetCursorState(display,windows,MagickTrue);
8899       XCheckRefreshWindows(display,windows);
8900       status=InvokeDelegate(image_info,*image,"edit",(char *) NULL,
8901         &(*image)->exception);
8902       if (status == MagickFalse)
8903         XNoticeWidget(display,windows,"Unable to edit image comment",
8904           (char *) NULL);
8905       else
8906         {
8907           char
8908             *comment;
8909
8910           comment=FileToString(image_info->filename,~0UL,&(*image)->exception);
8911           if (comment != (char *) NULL)
8912             {
8913               (void) SetImageProperty(*image,"comment",comment);
8914               (*image)->taint=MagickTrue;
8915             }
8916         }
8917       (void) RelinquishUniqueFileResource(image_info->filename);
8918       XSetCursorState(display,windows,MagickFalse);
8919       break;
8920     }
8921     case LaunchCommand:
8922     {
8923       /*
8924         Launch program.
8925       */
8926       XSetCursorState(display,windows,MagickTrue);
8927       XCheckRefreshWindows(display,windows);
8928       (void) AcquireUniqueFilename(filename);
8929       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"launch:%s",
8930         filename);
8931       status=WriteImage(image_info,*image);
8932       if (status == MagickFalse)
8933         XNoticeWidget(display,windows,"Unable to launch image editor",
8934           (char *) NULL);
8935       else
8936         {
8937           nexus=ReadImage(resource_info->image_info,&(*image)->exception);
8938           CatchException(&(*image)->exception);
8939           XClientMessage(display,windows->image.id,windows->im_protocols,
8940             windows->im_next_image,CurrentTime);
8941         }
8942       (void) RelinquishUniqueFileResource(filename);
8943       XSetCursorState(display,windows,MagickFalse);
8944       break;
8945     }
8946     case RegionofInterestCommand:
8947     {
8948       /*
8949         Apply an image processing technique to a region of interest.
8950       */
8951       (void) XROIImage(display,resource_info,windows,image);
8952       break;
8953     }
8954     case InfoCommand:
8955       break;
8956     case ZoomCommand:
8957     {
8958       /*
8959         Zoom image.
8960       */
8961       if (windows->magnify.mapped != MagickFalse)
8962         (void) XRaiseWindow(display,windows->magnify.id);
8963       else
8964         {
8965           /*
8966             Make magnify image.
8967           */
8968           XSetCursorState(display,windows,MagickTrue);
8969           (void) XMapRaised(display,windows->magnify.id);
8970           XSetCursorState(display,windows,MagickFalse);
8971         }
8972       break;
8973     }
8974     case ShowPreviewCommand:
8975     {
8976       char
8977         **previews;
8978
8979       Image
8980         *preview_image;
8981
8982       static char
8983         preview_type[MaxTextExtent] = "Gamma";
8984
8985       /*
8986         Select preview type from menu.
8987       */
8988       previews=GetCommandOptions(MagickPreviewOptions);
8989       if (previews == (char **) NULL)
8990         break;
8991       XListBrowserWidget(display,windows,&windows->widget,
8992         (const char **) previews,"Preview",
8993         "Select an enhancement, effect, or F/X:",preview_type);
8994       previews=DestroyStringList(previews);
8995       if (*preview_type == '\0')
8996         break;
8997       /*
8998         Show image preview.
8999       */
9000       XSetCursorState(display,windows,MagickTrue);
9001       XCheckRefreshWindows(display,windows);
9002       image_info->preview_type=(PreviewType)
9003         ParseCommandOption(MagickPreviewOptions,MagickFalse,preview_type);
9004       image_info->group=(ssize_t) windows->image.id;
9005       (void) DeleteImageProperty(*image,"label");
9006       (void) SetImageProperty(*image,"label","Preview");
9007       (void) AcquireUniqueFilename(filename);
9008       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"preview:%s",
9009         filename);
9010       status=WriteImage(image_info,*image);
9011       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9012       preview_image=ReadImage(image_info,&(*image)->exception);
9013       (void) RelinquishUniqueFileResource(filename);
9014       if (preview_image == (Image *) NULL)
9015         break;
9016       (void) FormatLocaleString(preview_image->filename,MaxTextExtent,"show:%s",
9017         filename);
9018       status=WriteImage(image_info,preview_image);
9019       preview_image=DestroyImage(preview_image);
9020       if (status == MagickFalse)
9021         XNoticeWidget(display,windows,"Unable to show image preview",
9022           (*image)->filename);
9023       XDelay(display,1500);
9024       XSetCursorState(display,windows,MagickFalse);
9025       break;
9026     }
9027     case ShowHistogramCommand:
9028     {
9029       Image
9030         *histogram_image;
9031
9032       /*
9033         Show image histogram.
9034       */
9035       XSetCursorState(display,windows,MagickTrue);
9036       XCheckRefreshWindows(display,windows);
9037       image_info->group=(ssize_t) windows->image.id;
9038       (void) DeleteImageProperty(*image,"label");
9039       (void) SetImageProperty(*image,"label","Histogram");
9040       (void) AcquireUniqueFilename(filename);
9041       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"histogram:%s",
9042         filename);
9043       status=WriteImage(image_info,*image);
9044       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9045       histogram_image=ReadImage(image_info,&(*image)->exception);
9046       (void) RelinquishUniqueFileResource(filename);
9047       if (histogram_image == (Image *) NULL)
9048         break;
9049       (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
9050         "show:%s",filename);
9051       status=WriteImage(image_info,histogram_image);
9052       histogram_image=DestroyImage(histogram_image);
9053       if (status == MagickFalse)
9054         XNoticeWidget(display,windows,"Unable to show histogram",
9055           (*image)->filename);
9056       XDelay(display,1500);
9057       XSetCursorState(display,windows,MagickFalse);
9058       break;
9059     }
9060     case ShowMatteCommand:
9061     {
9062       Image
9063         *matte_image;
9064
9065       if ((*image)->matte == MagickFalse)
9066         {
9067           XNoticeWidget(display,windows,
9068             "Image does not have any matte information",(*image)->filename);
9069           break;
9070         }
9071       /*
9072         Show image matte.
9073       */
9074       XSetCursorState(display,windows,MagickTrue);
9075       XCheckRefreshWindows(display,windows);
9076       image_info->group=(ssize_t) windows->image.id;
9077       (void) DeleteImageProperty(*image,"label");
9078       (void) SetImageProperty(*image,"label","Matte");
9079       (void) AcquireUniqueFilename(filename);
9080       (void) FormatLocaleString((*image)->filename,MaxTextExtent,"matte:%s",
9081         filename);
9082       status=WriteImage(image_info,*image);
9083       (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
9084       matte_image=ReadImage(image_info,&(*image)->exception);
9085       (void) RelinquishUniqueFileResource(filename);
9086       if (matte_image == (Image *) NULL)
9087         break;
9088       (void) FormatLocaleString(matte_image->filename,MaxTextExtent,"show:%s",
9089         filename);
9090       status=WriteImage(image_info,matte_image);
9091       matte_image=DestroyImage(matte_image);
9092       if (status == MagickFalse)
9093         XNoticeWidget(display,windows,"Unable to show matte",
9094           (*image)->filename);
9095       XDelay(display,1500);
9096       XSetCursorState(display,windows,MagickFalse);
9097       break;
9098     }
9099     case BackgroundCommand:
9100     {
9101       /*
9102         Background image.
9103       */
9104       status=XBackgroundImage(display,resource_info,windows,image);
9105       if (status == MagickFalse)
9106         break;
9107       nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9108       if (nexus != (Image *) NULL)
9109         XClientMessage(display,windows->image.id,windows->im_protocols,
9110           windows->im_next_image,CurrentTime);
9111       break;
9112     }
9113     case SlideShowCommand:
9114     {
9115       static char
9116         delay[MaxTextExtent] = "5";
9117
9118       /*
9119         Display next image after pausing.
9120       */
9121       (void) XDialogWidget(display,windows,"Slide Show",
9122         "Pause how many 1/100ths of a second between images:",delay);
9123       if (*delay == '\0')
9124         break;
9125       resource_info->delay=StringToUnsignedLong(delay);
9126       XClientMessage(display,windows->image.id,windows->im_protocols,
9127         windows->im_next_image,CurrentTime);
9128       break;
9129     }
9130     case PreferencesCommand:
9131     {
9132       /*
9133         Set user preferences.
9134       */
9135       status=XPreferencesWidget(display,resource_info,windows);
9136       if (status == MagickFalse)
9137         break;
9138       nexus=CloneImage(*image,0,0,MagickTrue,&(*image)->exception);
9139       if (nexus != (Image *) NULL)
9140         XClientMessage(display,windows->image.id,windows->im_protocols,
9141           windows->im_next_image,CurrentTime);
9142       break;
9143     }
9144     case HelpCommand:
9145     {
9146       /*
9147         User requested help.
9148       */
9149       XTextViewWidget(display,resource_info,windows,MagickFalse,
9150         "Help Viewer - Display",DisplayHelp);
9151       break;
9152     }
9153     case BrowseDocumentationCommand:
9154     {
9155       Atom
9156         mozilla_atom;
9157
9158       Window
9159         mozilla_window,
9160         root_window;
9161
9162       /*
9163         Browse the ImageMagick documentation.
9164       */
9165       root_window=XRootWindow(display,XDefaultScreen(display));
9166       mozilla_atom=XInternAtom(display,"_MOZILLA_VERSION",MagickFalse);
9167       mozilla_window=XWindowByProperty(display,root_window,mozilla_atom);
9168       if (mozilla_window != (Window) NULL)
9169         {
9170           char
9171             command[MaxTextExtent],
9172             *url;
9173
9174           /*
9175             Display documentation using Netscape remote control.
9176           */
9177           url=GetMagickHomeURL();
9178           (void) FormatLocaleString(command,MaxTextExtent,
9179             "openurl(%s,new-tab)",url);
9180           url=DestroyString(url);
9181           mozilla_atom=XInternAtom(display,"_MOZILLA_COMMAND",MagickFalse);
9182           (void) XChangeProperty(display,mozilla_window,mozilla_atom,XA_STRING,
9183             8,PropModeReplace,(unsigned char *) command,(int) strlen(command));
9184           XSetCursorState(display,windows,MagickFalse);
9185           break;
9186         }
9187       XSetCursorState(display,windows,MagickTrue);
9188       XCheckRefreshWindows(display,windows);
9189       status=InvokeDelegate(image_info,*image,"browse",(char *) NULL,
9190         &(*image)->exception);
9191       if (status == MagickFalse)
9192         XNoticeWidget(display,windows,"Unable to browse documentation",
9193           (char *) NULL);
9194       XDelay(display,1500);
9195       XSetCursorState(display,windows,MagickFalse);
9196       break;
9197     }
9198     case VersionCommand:
9199     {
9200       XNoticeWidget(display,windows,GetMagickVersion((size_t *) NULL),
9201         GetMagickCopyright());
9202       break;
9203     }
9204     case SaveToUndoBufferCommand:
9205       break;
9206     default:
9207     {
9208       (void) XBell(display,0);
9209       break;
9210     }
9211   }
9212   image_info=DestroyImageInfo(image_info);
9213   return(nexus);
9214 }
9215 \f
9216 /*
9217 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9218 %                                                                             %
9219 %                                                                             %
9220 %                                                                             %
9221 +   X M a g n i f y I m a g e                                                 %
9222 %                                                                             %
9223 %                                                                             %
9224 %                                                                             %
9225 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9226 %
9227 %  XMagnifyImage() magnifies portions of the image as indicated by the pointer.
9228 %  The magnified portion is displayed in a separate window.
9229 %
9230 %  The format of the XMagnifyImage method is:
9231 %
9232 %      void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9233 %
9234 %  A description of each parameter follows:
9235 %
9236 %    o display: Specifies a connection to an X server;  returned from
9237 %      XOpenDisplay.
9238 %
9239 %    o windows: Specifies a pointer to a XWindows structure.
9240 %
9241 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
9242 %      the entire image is refreshed.
9243 %
9244 */
9245 static void XMagnifyImage(Display *display,XWindows *windows,XEvent *event)
9246 {
9247   char
9248     text[MaxTextExtent];
9249
9250   register int
9251     x,
9252     y;
9253
9254   size_t
9255     state;
9256
9257   /*
9258     Update magnified image until the mouse button is released.
9259   */
9260   (void) XCheckDefineCursor(display,windows->image.id,windows->magnify.cursor);
9261   state=DefaultState;
9262   x=event->xbutton.x;
9263   y=event->xbutton.y;
9264   windows->magnify.x=(int) windows->image.x+x;
9265   windows->magnify.y=(int) windows->image.y+y;
9266   do
9267   {
9268     /*
9269       Map and unmap Info widget as text cursor crosses its boundaries.
9270     */
9271     if (windows->info.mapped != MagickFalse)
9272       {
9273         if ((x < (int) (windows->info.x+windows->info.width)) &&
9274             (y < (int) (windows->info.y+windows->info.height)))
9275           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
9276       }
9277     else
9278       if ((x > (int) (windows->info.x+windows->info.width)) ||
9279           (y > (int) (windows->info.y+windows->info.height)))
9280         (void) XMapWindow(display,windows->info.id);
9281     if (windows->info.mapped != MagickFalse)
9282       {
9283         /*
9284           Display pointer position.
9285         */
9286         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9287           windows->magnify.x,windows->magnify.y);
9288         XInfoWidget(display,windows,text);
9289       }
9290     /*
9291       Wait for next event.
9292     */
9293     XScreenEvent(display,windows,event);
9294     switch (event->type)
9295     {
9296       case ButtonPress:
9297         break;
9298       case ButtonRelease:
9299       {
9300         /*
9301           User has finished magnifying image.
9302         */
9303         x=event->xbutton.x;
9304         y=event->xbutton.y;
9305         state|=ExitState;
9306         break;
9307       }
9308       case Expose:
9309         break;
9310       case MotionNotify:
9311       {
9312         x=event->xmotion.x;
9313         y=event->xmotion.y;
9314         break;
9315       }
9316       default:
9317         break;
9318     }
9319     /*
9320       Check boundary conditions.
9321     */
9322     if (x < 0)
9323       x=0;
9324     else
9325       if (x >= (int) windows->image.width)
9326         x=(int) windows->image.width-1;
9327     if (y < 0)
9328       y=0;
9329     else
9330      if (y >= (int) windows->image.height)
9331        y=(int) windows->image.height-1;
9332   } while ((state & ExitState) == 0);
9333   /*
9334     Display magnified image.
9335   */
9336   XSetCursorState(display,windows,MagickFalse);
9337 }
9338 \f
9339 /*
9340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9341 %                                                                             %
9342 %                                                                             %
9343 %                                                                             %
9344 +   X M a g n i f y W i n d o w C o m m a n d                                 %
9345 %                                                                             %
9346 %                                                                             %
9347 %                                                                             %
9348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9349 %
9350 %  XMagnifyWindowCommand() moves the image within an Magnify window by one
9351 %  pixel as specified by the key symbol.
9352 %
9353 %  The format of the XMagnifyWindowCommand method is:
9354 %
9355 %      void XMagnifyWindowCommand(Display *display,XWindows *windows,
9356 %        const MagickStatusType state,const KeySym key_symbol)
9357 %
9358 %  A description of each parameter follows:
9359 %
9360 %    o display: Specifies a connection to an X server; returned from
9361 %      XOpenDisplay.
9362 %
9363 %    o windows: Specifies a pointer to a XWindows structure.
9364 %
9365 %    o state: key mask.
9366 %
9367 %    o key_symbol: Specifies a KeySym which indicates which side of the image
9368 %      to trim.
9369 %
9370 */
9371 static void XMagnifyWindowCommand(Display *display,XWindows *windows,
9372   const MagickStatusType state,const KeySym key_symbol)
9373 {
9374   unsigned int
9375     quantum;
9376
9377   /*
9378     User specified a magnify factor or position.
9379   */
9380   quantum=1;
9381   if ((state & Mod1Mask) != 0)
9382     quantum=10;
9383   switch ((int) key_symbol)
9384   {
9385     case QuitCommand:
9386     {
9387       (void) XWithdrawWindow(display,windows->magnify.id,
9388         windows->magnify.screen);
9389       break;
9390     }
9391     case XK_Home:
9392     case XK_KP_Home:
9393     {
9394       windows->magnify.x=(int) windows->image.width/2;
9395       windows->magnify.y=(int) windows->image.height/2;
9396       break;
9397     }
9398     case XK_Left:
9399     case XK_KP_Left:
9400     {
9401       if (windows->magnify.x > 0)
9402         windows->magnify.x-=quantum;
9403       break;
9404     }
9405     case XK_Up:
9406     case XK_KP_Up:
9407     {
9408       if (windows->magnify.y > 0)
9409         windows->magnify.y-=quantum;
9410       break;
9411     }
9412     case XK_Right:
9413     case XK_KP_Right:
9414     {
9415       if (windows->magnify.x < (int) (windows->image.ximage->width-1))
9416         windows->magnify.x+=quantum;
9417       break;
9418     }
9419     case XK_Down:
9420     case XK_KP_Down:
9421     {
9422       if (windows->magnify.y < (int) (windows->image.ximage->height-1))
9423         windows->magnify.y+=quantum;
9424       break;
9425     }
9426     case XK_0:
9427     case XK_1:
9428     case XK_2:
9429     case XK_3:
9430     case XK_4:
9431     case XK_5:
9432     case XK_6:
9433     case XK_7:
9434     case XK_8:
9435     case XK_9:
9436     {
9437       windows->magnify.data=(key_symbol-XK_0);
9438       break;
9439     }
9440     case XK_KP_0:
9441     case XK_KP_1:
9442     case XK_KP_2:
9443     case XK_KP_3:
9444     case XK_KP_4:
9445     case XK_KP_5:
9446     case XK_KP_6:
9447     case XK_KP_7:
9448     case XK_KP_8:
9449     case XK_KP_9:
9450     {
9451       windows->magnify.data=(key_symbol-XK_KP_0);
9452       break;
9453     }
9454     default:
9455       break;
9456   }
9457   XMakeMagnifyImage(display,windows);
9458 }
9459 \f
9460 /*
9461 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9462 %                                                                             %
9463 %                                                                             %
9464 %                                                                             %
9465 +   X M a k e P a n I m a g e                                                 %
9466 %                                                                             %
9467 %                                                                             %
9468 %                                                                             %
9469 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9470 %
9471 %  XMakePanImage() creates a thumbnail of the image and displays it in the Pan
9472 %  icon window.
9473 %
9474 %  The format of the XMakePanImage method is:
9475 %
9476 %        void XMakePanImage(Display *display,XResourceInfo *resource_info,
9477 %          XWindows *windows,Image *image)
9478 %
9479 %  A description of each parameter follows:
9480 %
9481 %    o display: Specifies a connection to an X server;  returned from
9482 %      XOpenDisplay.
9483 %
9484 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9485 %
9486 %    o windows: Specifies a pointer to a XWindows structure.
9487 %
9488 %    o image: the image.
9489 %
9490 */
9491 static void XMakePanImage(Display *display,XResourceInfo *resource_info,
9492   XWindows *windows,Image *image)
9493 {
9494   MagickStatusType
9495     status;
9496
9497   /*
9498     Create and display image for panning icon.
9499   */
9500   XSetCursorState(display,windows,MagickTrue);
9501   XCheckRefreshWindows(display,windows);
9502   windows->pan.x=(int) windows->image.x;
9503   windows->pan.y=(int) windows->image.y;
9504   status=XMakeImage(display,resource_info,&windows->pan,image,
9505     windows->pan.width,windows->pan.height);
9506   if (status == MagickFalse)
9507     ThrowXWindowFatalException(XServerError,image->exception.reason,
9508       image->exception.description);
9509   (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
9510     windows->pan.pixmap);
9511   (void) XClearWindow(display,windows->pan.id);
9512   XDrawPanRectangle(display,windows);
9513   XSetCursorState(display,windows,MagickFalse);
9514 }
9515 \f
9516 /*
9517 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9518 %                                                                             %
9519 %                                                                             %
9520 %                                                                             %
9521 +   X M a t t a E d i t I m a g e                                             %
9522 %                                                                             %
9523 %                                                                             %
9524 %                                                                             %
9525 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
9526 %
9527 %  XMatteEditImage() allows the user to interactively change the Matte channel
9528 %  of an image.  If the image is PseudoClass it is promoted to DirectClass
9529 %  before the matte information is stored.
9530 %
9531 %  The format of the XMatteEditImage method is:
9532 %
9533 %      MagickBooleanType XMatteEditImage(Display *display,
9534 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
9535 %
9536 %  A description of each parameter follows:
9537 %
9538 %    o display: Specifies a connection to an X server;  returned from
9539 %      XOpenDisplay.
9540 %
9541 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
9542 %
9543 %    o windows: Specifies a pointer to a XWindows structure.
9544 %
9545 %    o image: the image; returned from ReadImage.
9546 %
9547 */
9548 static MagickBooleanType XMatteEditImage(Display *display,
9549   XResourceInfo *resource_info,XWindows *windows,Image **image)
9550 {
9551   static char
9552     matte[MaxTextExtent] = "0";
9553
9554   static const char
9555     *MatteEditMenu[] =
9556     {
9557       "Method",
9558       "Border Color",
9559       "Fuzz",
9560       "Matte Value",
9561       "Undo",
9562       "Help",
9563       "Dismiss",
9564       (char *) NULL
9565     };
9566
9567   static const ModeType
9568     MatteEditCommands[] =
9569     {
9570       MatteEditMethod,
9571       MatteEditBorderCommand,
9572       MatteEditFuzzCommand,
9573       MatteEditValueCommand,
9574       MatteEditUndoCommand,
9575       MatteEditHelpCommand,
9576       MatteEditDismissCommand
9577     };
9578
9579   static PaintMethod
9580     method = PointMethod;
9581
9582   static XColor
9583     border_color = { 0, 0, 0, 0, 0, 0 };
9584
9585   char
9586     command[MaxTextExtent],
9587     text[MaxTextExtent];
9588
9589   Cursor
9590     cursor;
9591
9592   int
9593     entry,
9594     id,
9595     x,
9596     x_offset,
9597     y,
9598     y_offset;
9599
9600   register int
9601     i;
9602
9603   register PixelPacket
9604     *q;
9605
9606   unsigned int
9607     height,
9608     width;
9609
9610   size_t
9611     state;
9612
9613   XEvent
9614     event;
9615
9616   /*
9617     Map Command widget.
9618   */
9619   (void) CloneString(&windows->command.name,"Matte Edit");
9620   windows->command.data=4;
9621   (void) XCommandWidget(display,windows,MatteEditMenu,(XEvent *) NULL);
9622   (void) XMapRaised(display,windows->command.id);
9623   XClientMessage(display,windows->image.id,windows->im_protocols,
9624     windows->im_update_widget,CurrentTime);
9625   /*
9626     Make cursor.
9627   */
9628   cursor=XMakeCursor(display,windows->image.id,windows->map_info->colormap,
9629     resource_info->background_color,resource_info->foreground_color);
9630   (void) XCheckDefineCursor(display,windows->image.id,cursor);
9631   /*
9632     Track pointer until button 1 is pressed.
9633   */
9634   XQueryPosition(display,windows->image.id,&x,&y);
9635   (void) XSelectInput(display,windows->image.id,
9636     windows->image.attributes.event_mask | PointerMotionMask);
9637   state=DefaultState;
9638   do
9639   {
9640     if (windows->info.mapped != MagickFalse)
9641       {
9642         /*
9643           Display pointer position.
9644         */
9645         (void) FormatLocaleString(text,MaxTextExtent," %+d%+d ",
9646           x+windows->image.x,y+windows->image.y);
9647         XInfoWidget(display,windows,text);
9648       }
9649     /*
9650       Wait for next event.
9651     */
9652     XScreenEvent(display,windows,&event);
9653     if (event.xany.window == windows->command.id)
9654       {
9655         /*
9656           Select a command from the Command widget.
9657         */
9658         id=XCommandWidget(display,windows,MatteEditMenu,&event);
9659         if (id < 0)
9660           {
9661             (void) XCheckDefineCursor(display,windows->image.id,cursor);
9662             continue;
9663           }
9664         switch (MatteEditCommands[id])
9665         {
9666           case MatteEditMethod:
9667           {
9668             char
9669               **methods;
9670
9671             /*
9672               Select a method from the pop-up menu.
9673             */
9674             methods=GetCommandOptions(MagickMethodOptions);
9675             if (methods == (char **) NULL)
9676               break;
9677             entry=XMenuWidget(display,windows,MatteEditMenu[id],
9678               (const char **) methods,command);
9679             if (entry >= 0)
9680               method=(PaintMethod) ParseCommandOption(MagickMethodOptions,
9681                 MagickFalse,methods[entry]);
9682             methods=DestroyStringList(methods);
9683             break;
9684           }
9685           case MatteEditBorderCommand:
9686           {
9687             const char
9688               *ColorMenu[MaxNumberPens];
9689
9690             int
9691               pen_number;
9692
9693             /*
9694               Initialize menu selections.
9695             */
9696             for (i=0; i < (int) (MaxNumberPens-2); i++)
9697               ColorMenu[i]=resource_info->pen_colors[i];
9698             ColorMenu[MaxNumberPens-2]="Browser...";
9699             ColorMenu[MaxNumberPens-1]=(const char *) NULL;
9700             /*
9701               Select a pen color from the pop-up menu.
9702             */
9703             pen_number=XMenuWidget(display,windows,MatteEditMenu[id],
9704               (const char **) ColorMenu,command);
9705             if (pen_number < 0)
9706               break;
9707             if (pen_number == (MaxNumberPens-2))
9708               {
9709                 static char
9710                   color_name[MaxTextExtent] = "gray";
9711
9712                 /*
9713                   Select a pen color from a dialog.
9714                 */
9715                 resource_info->pen_colors[pen_number]=color_name;
9716                 XColorBrowserWidget(display,windows,"Select",color_name);
9717                 if (*color_name == '\0')
9718                   break;
9719               }
9720             /*
9721               Set border color.
9722             */
9723             (void) XParseColor(display,windows->map_info->colormap,
9724               resource_info->pen_colors[pen_number],&border_color);
9725             break;
9726           }
9727           case MatteEditFuzzCommand:
9728           {
9729             static char
9730               fuzz[MaxTextExtent];
9731
9732             static const char
9733               *FuzzMenu[] =
9734               {
9735                 "0%",
9736                 "2%",
9737                 "5%",
9738                 "10%",
9739                 "15%",
9740                 "Dialog...",
9741                 (char *) NULL,
9742               };
9743
9744             /*
9745               Select a command from the pop-up menu.
9746             */
9747             entry=XMenuWidget(display,windows,MatteEditMenu[id],FuzzMenu,
9748               command);
9749             if (entry < 0)
9750               break;
9751             if (entry != 5)
9752               {
9753                 (*image)->fuzz=SiPrefixToDouble(FuzzMenu[entry],1.0*QuantumRange+
9754                   1.0);
9755                 break;
9756               }
9757             (void) CopyMagickString(fuzz,"20%",MaxTextExtent);
9758             (void) XDialogWidget(display,windows,"Ok",
9759               "Enter fuzz factor (0.0 - 99.9%):",fuzz);
9760             if (*fuzz == '\0')
9761               break;
9762             (void) ConcatenateMagickString(fuzz,"%",MaxTextExtent);
9763             (*image)->fuzz=SiPrefixToDouble(fuzz,1.0*QuantumRange+1.0);
9764             break;
9765           }
9766           case MatteEditValueCommand:
9767           {
9768             static char
9769               message[MaxTextExtent];
9770
9771             static const char
9772               *MatteMenu[] =
9773               {
9774                 "Opaque",
9775                 "Transparent",
9776                 "Dialog...",
9777                 (char *) NULL,
9778               };
9779
9780             /*
9781               Select a command from the pop-up menu.
9782             */
9783             entry=XMenuWidget(display,windows,MatteEditMenu[id],MatteMenu,
9784               command);
9785             if (entry < 0)
9786               break;
9787             if (entry != 2)
9788               {
9789                 (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9790                   OpaqueOpacity);
9791                 if (LocaleCompare(MatteMenu[entry],"Transparent") == 0)
9792                   (void) FormatLocaleString(matte,MaxTextExtent,QuantumFormat,
9793                     (Quantum) TransparentOpacity);
9794                 break;
9795               }
9796             (void) FormatLocaleString(message,MaxTextExtent,
9797               "Enter matte value (0 - " QuantumFormat "):",(Quantum)
9798               QuantumRange);
9799             (void) XDialogWidget(display,windows,"Matte",message,matte);
9800             if (*matte == '\0')
9801               break;
9802             break;
9803           }
9804           case MatteEditUndoCommand:
9805           {
9806             (void) XMagickCommand(display,resource_info,windows,UndoCommand,
9807               image);
9808             break;
9809           }
9810           case MatteEditHelpCommand:
9811           {
9812             XTextViewWidget(display,resource_info,windows,MagickFalse,
9813               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9814             break;
9815           }
9816           case MatteEditDismissCommand:
9817           {
9818             /*
9819               Prematurely exit.
9820             */
9821             state|=EscapeState;
9822             state|=ExitState;
9823             break;
9824           }
9825           default:
9826             break;
9827         }
9828         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9829         continue;
9830       }
9831     switch (event.type)
9832     {
9833       case ButtonPress:
9834       {
9835         if (event.xbutton.button != Button1)
9836           break;
9837         if ((event.xbutton.window != windows->image.id) &&
9838             (event.xbutton.window != windows->magnify.id))
9839           break;
9840         /*
9841           Update matte data.
9842         */
9843         x=event.xbutton.x;
9844         y=event.xbutton.y;
9845         (void) XMagickCommand(display,resource_info,windows,
9846           SaveToUndoBufferCommand,image);
9847         state|=UpdateConfigurationState;
9848         break;
9849       }
9850       case ButtonRelease:
9851       {
9852         if (event.xbutton.button != Button1)
9853           break;
9854         if ((event.xbutton.window != windows->image.id) &&
9855             (event.xbutton.window != windows->magnify.id))
9856           break;
9857         /*
9858           Update colormap information.
9859         */
9860         x=event.xbutton.x;
9861         y=event.xbutton.y;
9862         XConfigureImageColormap(display,resource_info,windows,*image);
9863         (void) XConfigureImage(display,resource_info,windows,*image);
9864         XInfoWidget(display,windows,text);
9865         (void) XCheckDefineCursor(display,windows->image.id,cursor);
9866         state&=(~UpdateConfigurationState);
9867         break;
9868       }
9869       case Expose:
9870         break;
9871       case KeyPress:
9872       {
9873         char
9874           command[MaxTextExtent];
9875
9876         KeySym
9877           key_symbol;
9878
9879         if (event.xkey.window == windows->magnify.id)
9880           {
9881             Window
9882               window;
9883
9884             window=windows->magnify.id;
9885             while (XCheckWindowEvent(display,window,KeyPressMask,&event)) ;
9886           }
9887         if (event.xkey.window != windows->image.id)
9888           break;
9889         /*
9890           Respond to a user key press.
9891         */
9892         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
9893           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
9894         switch ((int) key_symbol)
9895         {
9896           case XK_Escape:
9897           case XK_F20:
9898           {
9899             /*
9900               Prematurely exit.
9901             */
9902             state|=ExitState;
9903             break;
9904           }
9905           case XK_F1:
9906           case XK_Help:
9907           {
9908             XTextViewWidget(display,resource_info,windows,MagickFalse,
9909               "Help Viewer - Matte Edit",ImageMatteEditHelp);
9910             break;
9911           }
9912           default:
9913           {
9914             (void) XBell(display,0);
9915             break;
9916           }
9917         }
9918         break;
9919       }
9920       case MotionNotify:
9921       {
9922         /*
9923           Map and unmap Info widget as cursor crosses its boundaries.
9924         */
9925         x=event.xmotion.x;
9926         y=event.xmotion.y;
9927         if (windows->info.mapped != MagickFalse)
9928           {
9929             if ((x < (int) (windows->info.x+windows->info.width)) &&
9930                 (y < (int) (windows->info.y+windows->info.height)))
9931               (void) XWithdrawWindow(display,windows->info.id,
9932                 windows->info.screen);
9933           }
9934         else
9935           if ((x > (int) (windows->info.x+windows->info.width)) ||
9936               (y > (int) (windows->info.y+windows->info.height)))
9937             (void) XMapWindow(display,windows->info.id);
9938         break;
9939       }
9940       default:
9941         break;
9942     }
9943     if (event.xany.window == windows->magnify.id)
9944       {
9945         x=windows->magnify.x-windows->image.x;
9946         y=windows->magnify.y-windows->image.y;
9947       }
9948     x_offset=x;
9949     y_offset=y;
9950     if ((state & UpdateConfigurationState) != 0)
9951       {
9952         CacheView
9953           *image_view;
9954
9955         ExceptionInfo
9956           *exception;
9957
9958         int
9959           x,
9960           y;
9961
9962         /*
9963           Matte edit is relative to image configuration.
9964         */
9965         (void) XClearArea(display,windows->image.id,x_offset,y_offset,1,1,
9966           MagickTrue);
9967         XPutPixel(windows->image.ximage,x_offset,y_offset,
9968           windows->pixel_info->background_color.pixel);
9969         width=(unsigned int) (*image)->columns;
9970         height=(unsigned int) (*image)->rows;
9971         x=0;
9972         y=0;
9973         if (windows->image.crop_geometry != (char *) NULL)
9974           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
9975             &width,&height);
9976         x_offset=(int)
9977           (width*(windows->image.x+x_offset)/windows->image.ximage->width+x);
9978         y_offset=(int)
9979           (height*(windows->image.y+y_offset)/windows->image.ximage->height+y);
9980         if ((x_offset < 0) || (y_offset < 0))
9981           continue;
9982         if ((x_offset >= (int) (*image)->columns) ||
9983             (y_offset >= (int) (*image)->rows))
9984           continue;
9985         if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
9986           return(MagickFalse);
9987         (*image)->matte=MagickTrue;
9988         exception=(&(*image)->exception);
9989         image_view=AcquireCacheView(*image);
9990         switch (method)
9991         {
9992           case PointMethod:
9993           default:
9994           {
9995             /*
9996               Update matte information using point algorithm.
9997             */
9998             q=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,
9999               (ssize_t) y_offset,1,1,exception);
10000             if (q == (PixelPacket *) NULL)
10001               break;
10002             q->opacity=(Quantum) StringToLong(matte);
10003             (void) SyncCacheViewAuthenticPixels(image_view,exception);
10004             break;
10005           }
10006           case ReplaceMethod:
10007           {
10008             PixelPacket
10009               target;
10010
10011             /*
10012               Update matte information using replace algorithm.
10013             */
10014             (void) GetOneCacheViewVirtualPixel(image_view,(ssize_t) x_offset,
10015               (ssize_t) y_offset,&target,exception);
10016             for (y=0; y < (int) (*image)->rows; y++)
10017             {
10018               q=GetCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10019                 (*image)->columns,1,&(*image)->exception);
10020               if (q == (PixelPacket *) NULL)
10021                 break;
10022               for (x=0; x < (int) (*image)->columns; x++)
10023               {
10024                 if (IsColorSimilar(*image,q,&target))
10025                   q->opacity=(Quantum) StringToLong(matte);
10026                 q++;
10027               }
10028               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10029                 break;
10030             }
10031             break;
10032           }
10033           case FloodfillMethod:
10034           case FillToBorderMethod:
10035           {
10036             DrawInfo
10037               *draw_info;
10038
10039             MagickPixelPacket
10040               target;
10041
10042             /*
10043               Update matte information using floodfill algorithm.
10044             */
10045             (void) GetOneVirtualMagickPixel(*image,(ssize_t) x_offset,
10046               (ssize_t) y_offset,&target,exception);
10047             if (method == FillToBorderMethod)
10048               {
10049                 target.red=(MagickRealType)
10050                   ScaleShortToQuantum(border_color.red);
10051                 target.green=(MagickRealType)
10052                   ScaleShortToQuantum(border_color.green);
10053                 target.blue=(MagickRealType)
10054                   ScaleShortToQuantum(border_color.blue);
10055               }
10056             draw_info=CloneDrawInfo(resource_info->image_info,
10057               (DrawInfo *) NULL);
10058             draw_info->fill.opacity=ClampToQuantum(LocaleToDouble(matte,
10059               (char **) NULL));
10060             (void) FloodfillPaintImage(*image,OpacityChannel,draw_info,&target,
10061               (ssize_t) x_offset,(ssize_t) y_offset,
10062               method == FloodfillMethod ? MagickFalse : MagickTrue);
10063             draw_info=DestroyDrawInfo(draw_info);
10064             break;
10065           }
10066           case ResetMethod:
10067           {
10068             /*
10069               Update matte information using reset algorithm.
10070             */
10071             if (SetImageStorageClass(*image,DirectClass) == MagickFalse)
10072               return(MagickFalse);
10073             for (y=0; y < (int) (*image)->rows; y++)
10074             {
10075               q=QueueCacheViewAuthenticPixels(image_view,0,(ssize_t) y,
10076                 (*image)->columns,1,exception);
10077               if (q == (PixelPacket *) NULL)
10078                 break;
10079               for (x=0; x < (int) (*image)->columns; x++)
10080               {
10081                 q->opacity=(Quantum) StringToLong(matte);
10082                 q++;
10083               }
10084               if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
10085                 break;
10086             }
10087             if (StringToLong(matte) == OpaqueOpacity)
10088               (*image)->matte=MagickFalse;
10089             break;
10090           }
10091         }
10092         image_view=DestroyCacheView(image_view);
10093         state&=(~UpdateConfigurationState);
10094       }
10095   } while ((state & ExitState) == 0);
10096   (void) XSelectInput(display,windows->image.id,
10097     windows->image.attributes.event_mask);
10098   XSetCursorState(display,windows,MagickFalse);
10099   (void) XFreeCursor(display,cursor);
10100   return(MagickTrue);
10101 }
10102 \f
10103 /*
10104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10105 %                                                                             %
10106 %                                                                             %
10107 %                                                                             %
10108 +   X O p e n I m a g e                                                       %
10109 %                                                                             %
10110 %                                                                             %
10111 %                                                                             %
10112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10113 %
10114 %  XOpenImage() loads an image from a file.
10115 %
10116 %  The format of the XOpenImage method is:
10117 %
10118 %     Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10119 %       XWindows *windows,const unsigned int command)
10120 %
10121 %  A description of each parameter follows:
10122 %
10123 %    o display: Specifies a connection to an X server; returned from
10124 %      XOpenDisplay.
10125 %
10126 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10127 %
10128 %    o windows: Specifies a pointer to a XWindows structure.
10129 %
10130 %    o command: A value other than zero indicates that the file is selected
10131 %      from the command line argument list.
10132 %
10133 */
10134 static Image *XOpenImage(Display *display,XResourceInfo *resource_info,
10135   XWindows *windows,const MagickBooleanType command)
10136 {
10137   const MagickInfo
10138     *magick_info;
10139
10140   ExceptionInfo
10141     *exception;
10142
10143   Image
10144     *nexus;
10145
10146   ImageInfo
10147     *image_info;
10148
10149   static char
10150     filename[MaxTextExtent] = "\0";
10151
10152   /*
10153     Request file name from user.
10154   */
10155   if (command == MagickFalse)
10156     XFileBrowserWidget(display,windows,"Open",filename);
10157   else
10158     {
10159       char
10160         **filelist,
10161         **files;
10162
10163       int
10164         count,
10165         status;
10166
10167       register int
10168         i,
10169         j;
10170
10171       /*
10172         Select next image from the command line.
10173       */
10174       status=XGetCommand(display,windows->image.id,&files,&count);
10175       if (status == 0)
10176         {
10177           ThrowXWindowFatalException(XServerError,"UnableToGetProperty","...");
10178           return((Image *) NULL);
10179         }
10180       filelist=(char **) AcquireQuantumMemory((size_t) count,sizeof(*filelist));
10181       if (filelist == (char **) NULL)
10182         {
10183           ThrowXWindowFatalException(ResourceLimitError,
10184             "MemoryAllocationFailed","...");
10185           (void) XFreeStringList(files);
10186           return((Image *) NULL);
10187         }
10188       j=0;
10189       for (i=1; i < count; i++)
10190         if (*files[i] != '-')
10191           filelist[j++]=files[i];
10192       filelist[j]=(char *) NULL;
10193       XListBrowserWidget(display,windows,&windows->widget,
10194         (const char **) filelist,"Load","Select Image to Load:",filename);
10195       filelist=(char **) RelinquishMagickMemory(filelist);
10196       (void) XFreeStringList(files);
10197     }
10198   if (*filename == '\0')
10199     return((Image *) NULL);
10200   image_info=CloneImageInfo(resource_info->image_info);
10201   (void) SetImageInfoProgressMonitor(image_info,(MagickProgressMonitor) NULL,
10202     (void *) NULL);
10203   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10204   exception=AcquireExceptionInfo();
10205   (void) SetImageInfo(image_info,0,exception);
10206   if (LocaleCompare(image_info->magick,"X") == 0)
10207     {
10208       char
10209         seconds[MaxTextExtent];
10210
10211       /*
10212         User may want to delay the X server screen grab.
10213       */
10214       (void) CopyMagickString(seconds,"0",MaxTextExtent);
10215       (void) XDialogWidget(display,windows,"Grab","Enter any delay in seconds:",
10216         seconds);
10217       if (*seconds == '\0')
10218         return((Image *) NULL);
10219       XDelay(display,(size_t) (1000*StringToLong(seconds)));
10220     }
10221   magick_info=GetMagickInfo(image_info->magick,exception);
10222   if ((magick_info != (const MagickInfo *) NULL) &&
10223       (magick_info->raw != MagickFalse))
10224     {
10225       char
10226         geometry[MaxTextExtent];
10227
10228       /*
10229         Request image size from the user.
10230       */
10231       (void) CopyMagickString(geometry,"512x512",MaxTextExtent);
10232       if (image_info->size != (char *) NULL)
10233         (void) CopyMagickString(geometry,image_info->size,MaxTextExtent);
10234       (void) XDialogWidget(display,windows,"Load","Enter the image geometry:",
10235         geometry);
10236       (void) CloneString(&image_info->size,geometry);
10237     }
10238   /*
10239     Load the image.
10240   */
10241   XSetCursorState(display,windows,MagickTrue);
10242   XCheckRefreshWindows(display,windows);
10243   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
10244   nexus=ReadImage(image_info,exception);
10245   CatchException(exception);
10246   XSetCursorState(display,windows,MagickFalse);
10247   if (nexus != (Image *) NULL)
10248     XClientMessage(display,windows->image.id,windows->im_protocols,
10249       windows->im_next_image,CurrentTime);
10250   else
10251     {
10252       char
10253         *text,
10254         **textlist;
10255
10256       /*
10257         Unknown image format.
10258       */
10259       text=FileToString(filename,~0,exception);
10260       if (text == (char *) NULL)
10261         return((Image *) NULL);
10262       textlist=StringToList(text);
10263       if (textlist != (char **) NULL)
10264         {
10265           char
10266             title[MaxTextExtent];
10267
10268           register int
10269             i;
10270
10271           (void) FormatLocaleString(title,MaxTextExtent,
10272             "Unknown format: %s",filename);
10273           XTextViewWidget(display,resource_info,windows,MagickTrue,title,
10274             (const char **) textlist);
10275           for (i=0; textlist[i] != (char *) NULL; i++)
10276             textlist[i]=DestroyString(textlist[i]);
10277           textlist=(char **) RelinquishMagickMemory(textlist);
10278         }
10279       text=DestroyString(text);
10280     }
10281   exception=DestroyExceptionInfo(exception);
10282   image_info=DestroyImageInfo(image_info);
10283   return(nexus);
10284 }
10285 \f
10286 /*
10287 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10288 %                                                                             %
10289 %                                                                             %
10290 %                                                                             %
10291 +   X P a n I m a g e                                                         %
10292 %                                                                             %
10293 %                                                                             %
10294 %                                                                             %
10295 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10296 %
10297 %  XPanImage() pans the image until the mouse button is released.
10298 %
10299 %  The format of the XPanImage method is:
10300 %
10301 %      void XPanImage(Display *display,XWindows *windows,XEvent *event)
10302 %
10303 %  A description of each parameter follows:
10304 %
10305 %    o display: Specifies a connection to an X server;  returned from
10306 %      XOpenDisplay.
10307 %
10308 %    o windows: Specifies a pointer to a XWindows structure.
10309 %
10310 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
10311 %      the entire image is refreshed.
10312 %
10313 */
10314 static void XPanImage(Display *display,XWindows *windows,XEvent *event)
10315 {
10316   char
10317     text[MaxTextExtent];
10318
10319   Cursor
10320     cursor;
10321
10322   MagickRealType
10323     x_factor,
10324     y_factor;
10325
10326   RectangleInfo
10327     pan_info;
10328
10329   size_t
10330     state;
10331
10332   /*
10333     Define cursor.
10334   */
10335   if ((windows->image.ximage->width > (int) windows->image.width) &&
10336       (windows->image.ximage->height > (int) windows->image.height))
10337     cursor=XCreateFontCursor(display,XC_fleur);
10338   else
10339     if (windows->image.ximage->width > (int) windows->image.width)
10340       cursor=XCreateFontCursor(display,XC_sb_h_double_arrow);
10341     else
10342       if (windows->image.ximage->height > (int) windows->image.height)
10343         cursor=XCreateFontCursor(display,XC_sb_v_double_arrow);
10344       else
10345         cursor=XCreateFontCursor(display,XC_arrow);
10346   (void) XCheckDefineCursor(display,windows->pan.id,cursor);
10347   /*
10348     Pan image as pointer moves until the mouse button is released.
10349   */
10350   x_factor=(MagickRealType) windows->image.ximage->width/windows->pan.width;
10351   y_factor=(MagickRealType) windows->image.ximage->height/windows->pan.height;
10352   pan_info.width=windows->pan.width*windows->image.width/
10353     windows->image.ximage->width;
10354   pan_info.height=windows->pan.height*windows->image.height/
10355     windows->image.ximage->height;
10356   pan_info.x=0;
10357   pan_info.y=0;
10358   state=UpdateConfigurationState;
10359   do
10360   {
10361     switch (event->type)
10362     {
10363       case ButtonPress:
10364       {
10365         /*
10366           User choose an initial pan location.
10367         */
10368         pan_info.x=(ssize_t) event->xbutton.x;
10369         pan_info.y=(ssize_t) event->xbutton.y;
10370         state|=UpdateConfigurationState;
10371         break;
10372       }
10373       case ButtonRelease:
10374       {
10375         /*
10376           User has finished panning the image.
10377         */
10378         pan_info.x=(ssize_t) event->xbutton.x;
10379         pan_info.y=(ssize_t) event->xbutton.y;
10380         state|=UpdateConfigurationState | ExitState;
10381         break;
10382       }
10383       case MotionNotify:
10384       {
10385         pan_info.x=(ssize_t) event->xmotion.x;
10386         pan_info.y=(ssize_t) event->xmotion.y;
10387         state|=UpdateConfigurationState;
10388       }
10389       default:
10390         break;
10391     }
10392     if ((state & UpdateConfigurationState) != 0)
10393       {
10394         /*
10395           Check boundary conditions.
10396         */
10397         if (pan_info.x < (ssize_t) (pan_info.width/2))
10398           pan_info.x=0;
10399         else
10400           pan_info.x=(ssize_t) (x_factor*(pan_info.x-(pan_info.width/2)));
10401         if (pan_info.x < 0)
10402           pan_info.x=0;
10403         else
10404           if ((int) (pan_info.x+windows->image.width) >
10405               windows->image.ximage->width)
10406             pan_info.x=(ssize_t)
10407               (windows->image.ximage->width-windows->image.width);
10408         if (pan_info.y < (ssize_t) (pan_info.height/2))
10409           pan_info.y=0;
10410         else
10411           pan_info.y=(ssize_t) (y_factor*(pan_info.y-(pan_info.height/2)));
10412         if (pan_info.y < 0)
10413           pan_info.y=0;
10414         else
10415           if ((int) (pan_info.y+windows->image.height) >
10416               windows->image.ximage->height)
10417             pan_info.y=(ssize_t)
10418               (windows->image.ximage->height-windows->image.height);
10419         if ((windows->image.x != (int) pan_info.x) ||
10420             (windows->image.y != (int) pan_info.y))
10421           {
10422             /*
10423               Display image pan offset.
10424             */
10425             windows->image.x=(int) pan_info.x;
10426             windows->image.y=(int) pan_info.y;
10427             (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
10428               windows->image.width,windows->image.height,windows->image.x,
10429               windows->image.y);
10430             XInfoWidget(display,windows,text);
10431             /*
10432               Refresh Image window.
10433             */
10434             XDrawPanRectangle(display,windows);
10435             XRefreshWindow(display,&windows->image,(XEvent *) NULL);
10436           }
10437         state&=(~UpdateConfigurationState);
10438       }
10439     /*
10440       Wait for next event.
10441     */
10442     if ((state & ExitState) == 0)
10443       XScreenEvent(display,windows,event);
10444   } while ((state & ExitState) == 0);
10445   /*
10446     Restore cursor.
10447   */
10448   (void) XCheckDefineCursor(display,windows->pan.id,windows->pan.cursor);
10449   (void) XFreeCursor(display,cursor);
10450   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
10451 }
10452 \f
10453 /*
10454 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10455 %                                                                             %
10456 %                                                                             %
10457 %                                                                             %
10458 +   X P a s t e I m a g e                                                     %
10459 %                                                                             %
10460 %                                                                             %
10461 %                                                                             %
10462 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10463 %
10464 %  XPasteImage() pastes an image previously saved with XCropImage in the X
10465 %  window image at a location the user chooses with the pointer.
10466 %
10467 %  The format of the XPasteImage method is:
10468 %
10469 %      MagickBooleanType XPasteImage(Display *display,
10470 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
10471 %
10472 %  A description of each parameter follows:
10473 %
10474 %    o display: Specifies a connection to an X server;  returned from
10475 %      XOpenDisplay.
10476 %
10477 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10478 %
10479 %    o windows: Specifies a pointer to a XWindows structure.
10480 %
10481 %    o image: the image; returned from ReadImage.
10482 %
10483 */
10484 static MagickBooleanType XPasteImage(Display *display,
10485   XResourceInfo *resource_info,XWindows *windows,Image *image)
10486 {
10487   static const char
10488     *PasteMenu[] =
10489     {
10490       "Operator",
10491       "Help",
10492       "Dismiss",
10493       (char *) NULL
10494     };
10495
10496   static const ModeType
10497     PasteCommands[] =
10498     {
10499       PasteOperatorsCommand,
10500       PasteHelpCommand,
10501       PasteDismissCommand
10502     };
10503
10504   static CompositeOperator
10505     compose = CopyCompositeOp;
10506
10507   char
10508     text[MaxTextExtent];
10509
10510   Cursor
10511     cursor;
10512
10513   Image
10514     *paste_image;
10515
10516   int
10517     entry,
10518     id,
10519     x,
10520     y;
10521
10522   MagickRealType
10523     scale_factor;
10524
10525   RectangleInfo
10526     highlight_info,
10527     paste_info;
10528
10529   unsigned int
10530     height,
10531     width;
10532
10533   size_t
10534     state;
10535
10536   XEvent
10537     event;
10538
10539   /*
10540     Copy image.
10541   */
10542   if (resource_info->copy_image == (Image *) NULL)
10543     return(MagickFalse);
10544   paste_image=CloneImage(resource_info->copy_image,0,0,MagickTrue,
10545     &image->exception);
10546   /*
10547     Map Command widget.
10548   */
10549   (void) CloneString(&windows->command.name,"Paste");
10550   windows->command.data=1;
10551   (void) XCommandWidget(display,windows,PasteMenu,(XEvent *) NULL);
10552   (void) XMapRaised(display,windows->command.id);
10553   XClientMessage(display,windows->image.id,windows->im_protocols,
10554     windows->im_update_widget,CurrentTime);
10555   /*
10556     Track pointer until button 1 is pressed.
10557   */
10558   XSetCursorState(display,windows,MagickFalse);
10559   XQueryPosition(display,windows->image.id,&x,&y);
10560   (void) XSelectInput(display,windows->image.id,
10561     windows->image.attributes.event_mask | PointerMotionMask);
10562   paste_info.x=(ssize_t) windows->image.x+x;
10563   paste_info.y=(ssize_t) windows->image.y+y;
10564   paste_info.width=0;
10565   paste_info.height=0;
10566   cursor=XCreateFontCursor(display,XC_ul_angle);
10567   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
10568   state=DefaultState;
10569   do
10570   {
10571     if (windows->info.mapped != MagickFalse)
10572       {
10573         /*
10574           Display pointer position.
10575         */
10576         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
10577           (long) paste_info.x,(long) paste_info.y);
10578         XInfoWidget(display,windows,text);
10579       }
10580     highlight_info=paste_info;
10581     highlight_info.x=paste_info.x-windows->image.x;
10582     highlight_info.y=paste_info.y-windows->image.y;
10583     XHighlightRectangle(display,windows->image.id,
10584       windows->image.highlight_context,&highlight_info);
10585     /*
10586       Wait for next event.
10587     */
10588     XScreenEvent(display,windows,&event);
10589     XHighlightRectangle(display,windows->image.id,
10590       windows->image.highlight_context,&highlight_info);
10591     if (event.xany.window == windows->command.id)
10592       {
10593         /*
10594           Select a command from the Command widget.
10595         */
10596         id=XCommandWidget(display,windows,PasteMenu,&event);
10597         if (id < 0)
10598           continue;
10599         switch (PasteCommands[id])
10600         {
10601           case PasteOperatorsCommand:
10602           {
10603             char
10604               command[MaxTextExtent],
10605               **operators;
10606
10607             /*
10608               Select a command from the pop-up menu.
10609             */
10610             operators=GetCommandOptions(MagickComposeOptions);
10611             if (operators == (char **) NULL)
10612               break;
10613             entry=XMenuWidget(display,windows,PasteMenu[id],
10614               (const char **) operators,command);
10615             if (entry >= 0)
10616               compose=(CompositeOperator) ParseCommandOption(
10617                 MagickComposeOptions,MagickFalse,operators[entry]);
10618             operators=DestroyStringList(operators);
10619             break;
10620           }
10621           case PasteHelpCommand:
10622           {
10623             XTextViewWidget(display,resource_info,windows,MagickFalse,
10624               "Help Viewer - Image Composite",ImagePasteHelp);
10625             break;
10626           }
10627           case PasteDismissCommand:
10628           {
10629             /*
10630               Prematurely exit.
10631             */
10632             state|=EscapeState;
10633             state|=ExitState;
10634             break;
10635           }
10636           default:
10637             break;
10638         }
10639         continue;
10640       }
10641     switch (event.type)
10642     {
10643       case ButtonPress:
10644       {
10645         if (image->debug != MagickFalse)
10646           (void) LogMagickEvent(X11Event,GetMagickModule(),
10647             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
10648             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10649         if (event.xbutton.button != Button1)
10650           break;
10651         if (event.xbutton.window != windows->image.id)
10652           break;
10653         /*
10654           Paste rectangle is relative to image configuration.
10655         */
10656         width=(unsigned int) image->columns;
10657         height=(unsigned int) image->rows;
10658         x=0;
10659         y=0;
10660         if (windows->image.crop_geometry != (char *) NULL)
10661           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
10662             &width,&height);
10663         scale_factor=(MagickRealType) windows->image.ximage->width/width;
10664         paste_info.width=(unsigned int) (scale_factor*paste_image->columns+0.5);
10665         scale_factor=(MagickRealType) windows->image.ximage->height/height;
10666         paste_info.height=(unsigned int) (scale_factor*paste_image->rows+0.5);
10667         (void) XCheckDefineCursor(display,windows->image.id,cursor);
10668         paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10669         paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10670         break;
10671       }
10672       case ButtonRelease:
10673       {
10674         if (image->debug != MagickFalse)
10675           (void) LogMagickEvent(X11Event,GetMagickModule(),
10676             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
10677             event.xbutton.button,event.xbutton.x,event.xbutton.y);
10678         if (event.xbutton.button != Button1)
10679           break;
10680         if (event.xbutton.window != windows->image.id)
10681           break;
10682         if ((paste_info.width != 0) && (paste_info.height != 0))
10683           {
10684             /*
10685               User has selected the location of the paste image.
10686             */
10687             paste_info.x=(ssize_t) windows->image.x+event.xbutton.x;
10688             paste_info.y=(ssize_t) windows->image.y+event.xbutton.y;
10689             state|=ExitState;
10690           }
10691         break;
10692       }
10693       case Expose:
10694         break;
10695       case KeyPress:
10696       {
10697         char
10698           command[MaxTextExtent];
10699
10700         KeySym
10701           key_symbol;
10702
10703         int
10704           length;
10705
10706         if (event.xkey.window != windows->image.id)
10707           break;
10708         /*
10709           Respond to a user key press.
10710         */
10711         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
10712           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
10713         *(command+length)='\0';
10714         if (image->debug != MagickFalse)
10715           (void) LogMagickEvent(X11Event,GetMagickModule(),
10716             "Key press: 0x%lx (%s)",(long) key_symbol,command);
10717         switch ((int) key_symbol)
10718         {
10719           case XK_Escape:
10720           case XK_F20:
10721           {
10722             /*
10723               Prematurely exit.
10724             */
10725             paste_image=DestroyImage(paste_image);
10726             state|=EscapeState;
10727             state|=ExitState;
10728             break;
10729           }
10730           case XK_F1:
10731           case XK_Help:
10732           {
10733             (void) XSetFunction(display,windows->image.highlight_context,
10734               GXcopy);
10735             XTextViewWidget(display,resource_info,windows,MagickFalse,
10736               "Help Viewer - Image Composite",ImagePasteHelp);
10737             (void) XSetFunction(display,windows->image.highlight_context,
10738               GXinvert);
10739             break;
10740           }
10741           default:
10742           {
10743             (void) XBell(display,0);
10744             break;
10745           }
10746         }
10747         break;
10748       }
10749       case MotionNotify:
10750       {
10751         /*
10752           Map and unmap Info widget as text cursor crosses its boundaries.
10753         */
10754         x=event.xmotion.x;
10755         y=event.xmotion.y;
10756         if (windows->info.mapped != MagickFalse)
10757           {
10758             if ((x < (int) (windows->info.x+windows->info.width)) &&
10759                 (y < (int) (windows->info.y+windows->info.height)))
10760               (void) XWithdrawWindow(display,windows->info.id,
10761                 windows->info.screen);
10762           }
10763         else
10764           if ((x > (int) (windows->info.x+windows->info.width)) ||
10765               (y > (int) (windows->info.y+windows->info.height)))
10766             (void) XMapWindow(display,windows->info.id);
10767         paste_info.x=(ssize_t) windows->image.x+x;
10768         paste_info.y=(ssize_t) windows->image.y+y;
10769         break;
10770       }
10771       default:
10772       {
10773         if (image->debug != MagickFalse)
10774           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
10775             event.type);
10776         break;
10777       }
10778     }
10779   } while ((state & ExitState) == 0);
10780   (void) XSelectInput(display,windows->image.id,
10781     windows->image.attributes.event_mask);
10782   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
10783   XSetCursorState(display,windows,MagickFalse);
10784   (void) XFreeCursor(display,cursor);
10785   if ((state & EscapeState) != 0)
10786     return(MagickTrue);
10787   /*
10788     Image pasting is relative to image configuration.
10789   */
10790   XSetCursorState(display,windows,MagickTrue);
10791   XCheckRefreshWindows(display,windows);
10792   width=(unsigned int) image->columns;
10793   height=(unsigned int) image->rows;
10794   x=0;
10795   y=0;
10796   if (windows->image.crop_geometry != (char *) NULL)
10797     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
10798   scale_factor=(MagickRealType) width/windows->image.ximage->width;
10799   paste_info.x+=x;
10800   paste_info.x=(ssize_t) (scale_factor*paste_info.x+0.5);
10801   paste_info.width=(unsigned int) (scale_factor*paste_info.width+0.5);
10802   scale_factor=(MagickRealType) height/windows->image.ximage->height;
10803   paste_info.y+=y;
10804   paste_info.y=(ssize_t) (scale_factor*paste_info.y*scale_factor+0.5);
10805   paste_info.height=(unsigned int) (scale_factor*paste_info.height+0.5);
10806   /*
10807     Paste image with X Image window.
10808   */
10809   (void) CompositeImage(image,compose,paste_image,paste_info.x,paste_info.y);
10810   paste_image=DestroyImage(paste_image);
10811   XSetCursorState(display,windows,MagickFalse);
10812   /*
10813     Update image colormap.
10814   */
10815   XConfigureImageColormap(display,resource_info,windows,image);
10816   (void) XConfigureImage(display,resource_info,windows,image);
10817   return(MagickTrue);
10818 }
10819 \f
10820 /*
10821 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10822 %                                                                             %
10823 %                                                                             %
10824 %                                                                             %
10825 +   X P r i n t I m a g e                                                     %
10826 %                                                                             %
10827 %                                                                             %
10828 %                                                                             %
10829 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10830 %
10831 %  XPrintImage() prints an image to a Postscript printer.
10832 %
10833 %  The format of the XPrintImage method is:
10834 %
10835 %      MagickBooleanType XPrintImage(Display *display,
10836 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
10837 %
10838 %  A description of each parameter follows:
10839 %
10840 %    o display: Specifies a connection to an X server; returned from
10841 %      XOpenDisplay.
10842 %
10843 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10844 %
10845 %    o windows: Specifies a pointer to a XWindows structure.
10846 %
10847 %    o image: the image.
10848 %
10849 */
10850 static MagickBooleanType XPrintImage(Display *display,
10851   XResourceInfo *resource_info,XWindows *windows,Image *image)
10852 {
10853   char
10854     filename[MaxTextExtent],
10855     geometry[MaxTextExtent];
10856
10857   Image
10858     *print_image;
10859
10860   ImageInfo
10861     *image_info;
10862
10863   MagickStatusType
10864     status;
10865
10866   /*
10867     Request Postscript page geometry from user.
10868   */
10869   image_info=CloneImageInfo(resource_info->image_info);
10870   (void) FormatLocaleString(geometry,MaxTextExtent,"Letter");
10871   if (image_info->page != (char *) NULL)
10872     (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
10873   XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
10874     "Select Postscript Page Geometry:",geometry);
10875   if (*geometry == '\0')
10876     return(MagickTrue);
10877   image_info->page=GetPageGeometry(geometry);
10878   /*
10879     Apply image transforms.
10880   */
10881   XSetCursorState(display,windows,MagickTrue);
10882   XCheckRefreshWindows(display,windows);
10883   print_image=CloneImage(image,0,0,MagickTrue,&image->exception);
10884   if (print_image == (Image *) NULL)
10885     return(MagickFalse);
10886   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
10887     windows->image.ximage->width,windows->image.ximage->height);
10888   (void) TransformImage(&print_image,windows->image.crop_geometry,geometry);
10889   /*
10890     Print image.
10891   */
10892   (void) AcquireUniqueFilename(filename);
10893   (void) FormatLocaleString(print_image->filename,MaxTextExtent,"print:%s",
10894     filename);
10895   status=WriteImage(image_info,print_image);
10896   (void) RelinquishUniqueFileResource(filename);
10897   print_image=DestroyImage(print_image);
10898   image_info=DestroyImageInfo(image_info);
10899   XSetCursorState(display,windows,MagickFalse);
10900   return(status != 0 ? MagickTrue : MagickFalse);
10901 }
10902 \f
10903 /*
10904 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10905 %                                                                             %
10906 %                                                                             %
10907 %                                                                             %
10908 +   X R O I I m a g e                                                         %
10909 %                                                                             %
10910 %                                                                             %
10911 %                                                                             %
10912 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10913 %
10914 %  XROIImage() applies an image processing technique to a region of interest.
10915 %
10916 %  The format of the XROIImage method is:
10917 %
10918 %      MagickBooleanType XROIImage(Display *display,
10919 %        XResourceInfo *resource_info,XWindows *windows,Image **image)
10920 %
10921 %  A description of each parameter follows:
10922 %
10923 %    o display: Specifies a connection to an X server; returned from
10924 %      XOpenDisplay.
10925 %
10926 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
10927 %
10928 %    o windows: Specifies a pointer to a XWindows structure.
10929 %
10930 %    o image: the image; returned from ReadImage.
10931 %
10932 */
10933 static MagickBooleanType XROIImage(Display *display,
10934   XResourceInfo *resource_info,XWindows *windows,Image **image)
10935 {
10936 #define ApplyMenus  7
10937
10938   static const char
10939     *ROIMenu[] =
10940     {
10941       "Help",
10942       "Dismiss",
10943       (char *) NULL
10944     },
10945     *ApplyMenu[] =
10946     {
10947       "File",
10948       "Edit",
10949       "Transform",
10950       "Enhance",
10951       "Effects",
10952       "F/X",
10953       "Miscellany",
10954       "Help",
10955       "Dismiss",
10956       (char *) NULL
10957     },
10958     *FileMenu[] =
10959     {
10960       "Save...",
10961       "Print...",
10962       (char *) NULL
10963     },
10964     *EditMenu[] =
10965     {
10966       "Undo",
10967       "Redo",
10968       (char *) NULL
10969     },
10970     *TransformMenu[] =
10971     {
10972       "Flop",
10973       "Flip",
10974       "Rotate Right",
10975       "Rotate Left",
10976       (char *) NULL
10977     },
10978     *EnhanceMenu[] =
10979     {
10980       "Hue...",
10981       "Saturation...",
10982       "Brightness...",
10983       "Gamma...",
10984       "Spiff",
10985       "Dull",
10986       "Contrast Stretch...",
10987       "Sigmoidal Contrast...",
10988       "Normalize",
10989       "Equalize",
10990       "Negate",
10991       "Grayscale",
10992       "Map...",
10993       "Quantize...",
10994       (char *) NULL
10995     },
10996     *EffectsMenu[] =
10997     {
10998       "Despeckle",
10999       "Emboss",
11000       "Reduce Noise",
11001       "Add Noise",
11002       "Sharpen...",
11003       "Blur...",
11004       "Threshold...",
11005       "Edge Detect...",
11006       "Spread...",
11007       "Shade...",
11008       "Raise...",
11009       "Segment...",
11010       (char *) NULL
11011     },
11012     *FXMenu[] =
11013     {
11014       "Solarize...",
11015       "Sepia Tone...",
11016       "Swirl...",
11017       "Implode...",
11018       "Vignette...",
11019       "Wave...",
11020       "Oil Paint...",
11021       "Charcoal Draw...",
11022       (char *) NULL
11023     },
11024     *MiscellanyMenu[] =
11025     {
11026       "Image Info",
11027       "Zoom Image",
11028       "Show Preview...",
11029       "Show Histogram",
11030       "Show Matte",
11031       (char *) NULL
11032     };
11033
11034   static const char
11035     **Menus[ApplyMenus] =
11036     {
11037       FileMenu,
11038       EditMenu,
11039       TransformMenu,
11040       EnhanceMenu,
11041       EffectsMenu,
11042       FXMenu,
11043       MiscellanyMenu
11044     };
11045
11046   static const CommandType
11047     ApplyCommands[] =
11048     {
11049       NullCommand,
11050       NullCommand,
11051       NullCommand,
11052       NullCommand,
11053       NullCommand,
11054       NullCommand,
11055       NullCommand,
11056       HelpCommand,
11057       QuitCommand
11058     },
11059     FileCommands[] =
11060     {
11061       SaveCommand,
11062       PrintCommand
11063     },
11064     EditCommands[] =
11065     {
11066       UndoCommand,
11067       RedoCommand
11068     },
11069     TransformCommands[] =
11070     {
11071       FlopCommand,
11072       FlipCommand,
11073       RotateRightCommand,
11074       RotateLeftCommand
11075     },
11076     EnhanceCommands[] =
11077     {
11078       HueCommand,
11079       SaturationCommand,
11080       BrightnessCommand,
11081       GammaCommand,
11082       SpiffCommand,
11083       DullCommand,
11084       ContrastStretchCommand,
11085       SigmoidalContrastCommand,
11086       NormalizeCommand,
11087       EqualizeCommand,
11088       NegateCommand,
11089       GrayscaleCommand,
11090       MapCommand,
11091       QuantizeCommand
11092     },
11093     EffectsCommands[] =
11094     {
11095       DespeckleCommand,
11096       EmbossCommand,
11097       ReduceNoiseCommand,
11098       AddNoiseCommand,
11099       SharpenCommand,
11100       BlurCommand,
11101       EdgeDetectCommand,
11102       SpreadCommand,
11103       ShadeCommand,
11104       RaiseCommand,
11105       SegmentCommand
11106     },
11107     FXCommands[] =
11108     {
11109       SolarizeCommand,
11110       SepiaToneCommand,
11111       SwirlCommand,
11112       ImplodeCommand,
11113       VignetteCommand,
11114       WaveCommand,
11115       OilPaintCommand,
11116       CharcoalDrawCommand
11117     },
11118     MiscellanyCommands[] =
11119     {
11120       InfoCommand,
11121       ZoomCommand,
11122       ShowPreviewCommand,
11123       ShowHistogramCommand,
11124       ShowMatteCommand
11125     },
11126     ROICommands[] =
11127     {
11128       ROIHelpCommand,
11129       ROIDismissCommand
11130     };
11131
11132   static const CommandType
11133     *Commands[ApplyMenus] =
11134     {
11135       FileCommands,
11136       EditCommands,
11137       TransformCommands,
11138       EnhanceCommands,
11139       EffectsCommands,
11140       FXCommands,
11141       MiscellanyCommands
11142     };
11143
11144   char
11145     command[MaxTextExtent],
11146     text[MaxTextExtent];
11147
11148   CommandType
11149     command_type;
11150
11151   Cursor
11152     cursor;
11153
11154   Image
11155     *roi_image;
11156
11157   int
11158     entry,
11159     id,
11160     x,
11161     y;
11162
11163   MagickRealType
11164     scale_factor;
11165
11166   MagickProgressMonitor
11167     progress_monitor;
11168
11169   RectangleInfo
11170     crop_info,
11171     highlight_info,
11172     roi_info;
11173
11174   unsigned int
11175     height,
11176     width;
11177
11178   size_t
11179     state;
11180
11181   XEvent
11182     event;
11183
11184   /*
11185     Map Command widget.
11186   */
11187   (void) CloneString(&windows->command.name,"ROI");
11188   windows->command.data=0;
11189   (void) XCommandWidget(display,windows,ROIMenu,(XEvent *) NULL);
11190   (void) XMapRaised(display,windows->command.id);
11191   XClientMessage(display,windows->image.id,windows->im_protocols,
11192     windows->im_update_widget,CurrentTime);
11193   /*
11194     Track pointer until button 1 is pressed.
11195   */
11196   XQueryPosition(display,windows->image.id,&x,&y);
11197   (void) XSelectInput(display,windows->image.id,
11198     windows->image.attributes.event_mask | PointerMotionMask);
11199   roi_info.x=(ssize_t) windows->image.x+x;
11200   roi_info.y=(ssize_t) windows->image.y+y;
11201   roi_info.width=0;
11202   roi_info.height=0;
11203   cursor=XCreateFontCursor(display,XC_fleur);
11204   state=DefaultState;
11205   do
11206   {
11207     if (windows->info.mapped != MagickFalse)
11208       {
11209         /*
11210           Display pointer position.
11211         */
11212         (void) FormatLocaleString(text,MaxTextExtent," %+ld%+ld ",
11213           (long) roi_info.x,(long) roi_info.y);
11214         XInfoWidget(display,windows,text);
11215       }
11216     /*
11217       Wait for next event.
11218     */
11219     XScreenEvent(display,windows,&event);
11220     if (event.xany.window == windows->command.id)
11221       {
11222         /*
11223           Select a command from the Command widget.
11224         */
11225         id=XCommandWidget(display,windows,ROIMenu,&event);
11226         if (id < 0)
11227           continue;
11228         switch (ROICommands[id])
11229         {
11230           case ROIHelpCommand:
11231           {
11232             XTextViewWidget(display,resource_info,windows,MagickFalse,
11233               "Help Viewer - Region of Interest",ImageROIHelp);
11234             break;
11235           }
11236           case ROIDismissCommand:
11237           {
11238             /*
11239               Prematurely exit.
11240             */
11241             state|=EscapeState;
11242             state|=ExitState;
11243             break;
11244           }
11245           default:
11246             break;
11247         }
11248         continue;
11249       }
11250     switch (event.type)
11251     {
11252       case ButtonPress:
11253       {
11254         if (event.xbutton.button != Button1)
11255           break;
11256         if (event.xbutton.window != windows->image.id)
11257           break;
11258         /*
11259           Note first corner of region of interest rectangle-- exit loop.
11260         */
11261         (void) XCheckDefineCursor(display,windows->image.id,cursor);
11262         roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11263         roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11264         state|=ExitState;
11265         break;
11266       }
11267       case ButtonRelease:
11268         break;
11269       case Expose:
11270         break;
11271       case KeyPress:
11272       {
11273         KeySym
11274           key_symbol;
11275
11276         if (event.xkey.window != windows->image.id)
11277           break;
11278         /*
11279           Respond to a user key press.
11280         */
11281         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11282           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11283         switch ((int) key_symbol)
11284         {
11285           case XK_Escape:
11286           case XK_F20:
11287           {
11288             /*
11289               Prematurely exit.
11290             */
11291             state|=EscapeState;
11292             state|=ExitState;
11293             break;
11294           }
11295           case XK_F1:
11296           case XK_Help:
11297           {
11298             XTextViewWidget(display,resource_info,windows,MagickFalse,
11299               "Help Viewer - Region of Interest",ImageROIHelp);
11300             break;
11301           }
11302           default:
11303           {
11304             (void) XBell(display,0);
11305             break;
11306           }
11307         }
11308         break;
11309       }
11310       case MotionNotify:
11311       {
11312         /*
11313           Map and unmap Info widget as text cursor crosses its boundaries.
11314         */
11315         x=event.xmotion.x;
11316         y=event.xmotion.y;
11317         if (windows->info.mapped != MagickFalse)
11318           {
11319             if ((x < (int) (windows->info.x+windows->info.width)) &&
11320                 (y < (int) (windows->info.y+windows->info.height)))
11321               (void) XWithdrawWindow(display,windows->info.id,
11322                 windows->info.screen);
11323           }
11324         else
11325           if ((x > (int) (windows->info.x+windows->info.width)) ||
11326               (y > (int) (windows->info.y+windows->info.height)))
11327             (void) XMapWindow(display,windows->info.id);
11328         roi_info.x=(ssize_t) windows->image.x+x;
11329         roi_info.y=(ssize_t) windows->image.y+y;
11330         break;
11331       }
11332       default:
11333         break;
11334     }
11335   } while ((state & ExitState) == 0);
11336   (void) XSelectInput(display,windows->image.id,
11337     windows->image.attributes.event_mask);
11338   if ((state & EscapeState) != 0)
11339     {
11340       /*
11341         User want to exit without region of interest.
11342       */
11343       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11344       (void) XFreeCursor(display,cursor);
11345       return(MagickTrue);
11346     }
11347   (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11348   do
11349   {
11350     /*
11351       Size rectangle as pointer moves until the mouse button is released.
11352     */
11353     x=(int) roi_info.x;
11354     y=(int) roi_info.y;
11355     roi_info.width=0;
11356     roi_info.height=0;
11357     state=DefaultState;
11358     do
11359     {
11360       highlight_info=roi_info;
11361       highlight_info.x=roi_info.x-windows->image.x;
11362       highlight_info.y=roi_info.y-windows->image.y;
11363       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11364         {
11365           /*
11366             Display info and draw region of interest rectangle.
11367           */
11368           if (windows->info.mapped == MagickFalse)
11369             (void) XMapWindow(display,windows->info.id);
11370           (void) FormatLocaleString(text,MaxTextExtent,
11371             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11372             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11373           XInfoWidget(display,windows,text);
11374           XHighlightRectangle(display,windows->image.id,
11375             windows->image.highlight_context,&highlight_info);
11376         }
11377       else
11378         if (windows->info.mapped != MagickFalse)
11379           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
11380       /*
11381         Wait for next event.
11382       */
11383       XScreenEvent(display,windows,&event);
11384       if ((highlight_info.width > 3) && (highlight_info.height > 3))
11385         XHighlightRectangle(display,windows->image.id,
11386           windows->image.highlight_context,&highlight_info);
11387       switch (event.type)
11388       {
11389         case ButtonPress:
11390         {
11391           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11392           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11393           break;
11394         }
11395         case ButtonRelease:
11396         {
11397           /*
11398             User has committed to region of interest rectangle.
11399           */
11400           roi_info.x=(ssize_t) windows->image.x+event.xbutton.x;
11401           roi_info.y=(ssize_t) windows->image.y+event.xbutton.y;
11402           XSetCursorState(display,windows,MagickFalse);
11403           state|=ExitState;
11404           if (LocaleCompare(windows->command.name,"Apply") == 0)
11405             break;
11406           (void) CloneString(&windows->command.name,"Apply");
11407           windows->command.data=ApplyMenus;
11408           (void) XCommandWidget(display,windows,ApplyMenu,(XEvent *) NULL);
11409           break;
11410         }
11411         case Expose:
11412           break;
11413         case MotionNotify:
11414         {
11415           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11416           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11417         }
11418         default:
11419           break;
11420       }
11421       if ((((int) roi_info.x != x) && ((int) roi_info.y != y)) ||
11422           ((state & ExitState) != 0))
11423         {
11424           /*
11425             Check boundary conditions.
11426           */
11427           if (roi_info.x < 0)
11428             roi_info.x=0;
11429           else
11430             if (roi_info.x > (ssize_t) windows->image.ximage->width)
11431               roi_info.x=(ssize_t) windows->image.ximage->width;
11432           if ((int) roi_info.x < x)
11433             roi_info.width=(unsigned int) (x-roi_info.x);
11434           else
11435             {
11436               roi_info.width=(unsigned int) (roi_info.x-x);
11437               roi_info.x=(ssize_t) x;
11438             }
11439           if (roi_info.y < 0)
11440             roi_info.y=0;
11441           else
11442             if (roi_info.y > (ssize_t) windows->image.ximage->height)
11443               roi_info.y=(ssize_t) windows->image.ximage->height;
11444           if ((int) roi_info.y < y)
11445             roi_info.height=(unsigned int) (y-roi_info.y);
11446           else
11447             {
11448               roi_info.height=(unsigned int) (roi_info.y-y);
11449               roi_info.y=(ssize_t) y;
11450             }
11451         }
11452     } while ((state & ExitState) == 0);
11453     /*
11454       Wait for user to grab a corner of the rectangle or press return.
11455     */
11456     state=DefaultState;
11457     command_type=NullCommand;
11458     (void) XMapWindow(display,windows->info.id);
11459     do
11460     {
11461       if (windows->info.mapped != MagickFalse)
11462         {
11463           /*
11464             Display pointer position.
11465           */
11466           (void) FormatLocaleString(text,MaxTextExtent,
11467             " %.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11468             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11469           XInfoWidget(display,windows,text);
11470         }
11471       highlight_info=roi_info;
11472       highlight_info.x=roi_info.x-windows->image.x;
11473       highlight_info.y=roi_info.y-windows->image.y;
11474       if ((highlight_info.width <= 3) || (highlight_info.height <= 3))
11475         {
11476           state|=EscapeState;
11477           state|=ExitState;
11478           break;
11479         }
11480       if ((state & UpdateRegionState) != 0)
11481         {
11482           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11483           switch (command_type)
11484           {
11485             case UndoCommand:
11486             case RedoCommand:
11487             {
11488               (void) XMagickCommand(display,resource_info,windows,command_type,
11489                 image);
11490               break;
11491             }
11492             default:
11493             {
11494               /*
11495                 Region of interest is relative to image configuration.
11496               */
11497               progress_monitor=SetImageProgressMonitor(*image,
11498                 (MagickProgressMonitor) NULL,(*image)->client_data);
11499               crop_info=roi_info;
11500               width=(unsigned int) (*image)->columns;
11501               height=(unsigned int) (*image)->rows;
11502               x=0;
11503               y=0;
11504               if (windows->image.crop_geometry != (char *) NULL)
11505                 (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
11506                   &width,&height);
11507               scale_factor=(MagickRealType) width/windows->image.ximage->width;
11508               crop_info.x+=x;
11509               crop_info.x=(ssize_t) (scale_factor*crop_info.x+0.5);
11510               crop_info.width=(unsigned int) (scale_factor*crop_info.width+0.5);
11511               scale_factor=(MagickRealType)
11512                 height/windows->image.ximage->height;
11513               crop_info.y+=y;
11514               crop_info.y=(ssize_t) (scale_factor*crop_info.y+0.5);
11515               crop_info.height=(unsigned int)
11516                 (scale_factor*crop_info.height+0.5);
11517               roi_image=CropImage(*image,&crop_info,&(*image)->exception);
11518               (void) SetImageProgressMonitor(*image,progress_monitor,
11519                 (*image)->client_data);
11520               if (roi_image == (Image *) NULL)
11521                 continue;
11522               /*
11523                 Apply image processing technique to the region of interest.
11524               */
11525               windows->image.orphan=MagickTrue;
11526               (void) XMagickCommand(display,resource_info,windows,command_type,
11527                 &roi_image);
11528               progress_monitor=SetImageProgressMonitor(*image,
11529                 (MagickProgressMonitor) NULL,(*image)->client_data);
11530               (void) XMagickCommand(display,resource_info,windows,
11531                 SaveToUndoBufferCommand,image);
11532               windows->image.orphan=MagickFalse;
11533               (void) CompositeImage(*image,CopyCompositeOp,roi_image,
11534                 crop_info.x,crop_info.y);
11535               roi_image=DestroyImage(roi_image);
11536               (void) SetImageProgressMonitor(*image,progress_monitor,
11537                 (*image)->client_data);
11538               break;
11539             }
11540           }
11541           if (command_type != InfoCommand)
11542             {
11543               XConfigureImageColormap(display,resource_info,windows,*image);
11544               (void) XConfigureImage(display,resource_info,windows,*image);
11545             }
11546           XCheckRefreshWindows(display,windows);
11547           XInfoWidget(display,windows,text);
11548           (void) XSetFunction(display,windows->image.highlight_context,
11549             GXinvert);
11550           state&=(~UpdateRegionState);
11551         }
11552       XHighlightRectangle(display,windows->image.id,
11553         windows->image.highlight_context,&highlight_info);
11554       XScreenEvent(display,windows,&event);
11555       if (event.xany.window == windows->command.id)
11556         {
11557           /*
11558             Select a command from the Command widget.
11559           */
11560           (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11561           command_type=NullCommand;
11562           id=XCommandWidget(display,windows,ApplyMenu,&event);
11563           if (id >= 0)
11564             {
11565               (void) CopyMagickString(command,ApplyMenu[id],MaxTextExtent);
11566               command_type=ApplyCommands[id];
11567               if (id < ApplyMenus)
11568                 {
11569                   /*
11570                     Select a command from a pop-up menu.
11571                   */
11572                   entry=XMenuWidget(display,windows,ApplyMenu[id],
11573                     (const char **) Menus[id],command);
11574                   if (entry >= 0)
11575                     {
11576                       (void) CopyMagickString(command,Menus[id][entry],
11577                         MaxTextExtent);
11578                       command_type=Commands[id][entry];
11579                     }
11580                 }
11581             }
11582           (void) XSetFunction(display,windows->image.highlight_context,
11583             GXinvert);
11584           XHighlightRectangle(display,windows->image.id,
11585             windows->image.highlight_context,&highlight_info);
11586           if (command_type == HelpCommand)
11587             {
11588               (void) XSetFunction(display,windows->image.highlight_context,
11589                 GXcopy);
11590               XTextViewWidget(display,resource_info,windows,MagickFalse,
11591                 "Help Viewer - Region of Interest",ImageROIHelp);
11592               (void) XSetFunction(display,windows->image.highlight_context,
11593                 GXinvert);
11594               continue;
11595             }
11596           if (command_type == QuitCommand)
11597             {
11598               /*
11599                 exit.
11600               */
11601               state|=EscapeState;
11602               state|=ExitState;
11603               continue;
11604             }
11605           if (command_type != NullCommand)
11606             state|=UpdateRegionState;
11607           continue;
11608         }
11609       XHighlightRectangle(display,windows->image.id,
11610         windows->image.highlight_context,&highlight_info);
11611       switch (event.type)
11612       {
11613         case ButtonPress:
11614         {
11615           x=windows->image.x;
11616           y=windows->image.y;
11617           if (event.xbutton.button != Button1)
11618             break;
11619           if (event.xbutton.window != windows->image.id)
11620             break;
11621           x=windows->image.x+event.xbutton.x;
11622           y=windows->image.y+event.xbutton.y;
11623           if ((x < (int) (roi_info.x+RoiDelta)) &&
11624               (x > (int) (roi_info.x-RoiDelta)) &&
11625               (y < (int) (roi_info.y+RoiDelta)) &&
11626               (y > (int) (roi_info.y-RoiDelta)))
11627             {
11628               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11629               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11630               state|=UpdateConfigurationState;
11631               break;
11632             }
11633           if ((x < (int) (roi_info.x+RoiDelta)) &&
11634               (x > (int) (roi_info.x-RoiDelta)) &&
11635               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11636               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11637             {
11638               roi_info.x=(ssize_t) (roi_info.x+roi_info.width);
11639               state|=UpdateConfigurationState;
11640               break;
11641             }
11642           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11643               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11644               (y < (int) (roi_info.y+RoiDelta)) &&
11645               (y > (int) (roi_info.y-RoiDelta)))
11646             {
11647               roi_info.y=(ssize_t) (roi_info.y+roi_info.height);
11648               state|=UpdateConfigurationState;
11649               break;
11650             }
11651           if ((x < (int) (roi_info.x+roi_info.width+RoiDelta)) &&
11652               (x > (int) (roi_info.x+roi_info.width-RoiDelta)) &&
11653               (y < (int) (roi_info.y+roi_info.height+RoiDelta)) &&
11654               (y > (int) (roi_info.y+roi_info.height-RoiDelta)))
11655             {
11656               state|=UpdateConfigurationState;
11657               break;
11658             }
11659         }
11660         case ButtonRelease:
11661         {
11662           if (event.xbutton.window == windows->pan.id)
11663             if ((highlight_info.x != crop_info.x-windows->image.x) ||
11664                 (highlight_info.y != crop_info.y-windows->image.y))
11665               XHighlightRectangle(display,windows->image.id,
11666                 windows->image.highlight_context,&highlight_info);
11667           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11668             event.xbutton.time);
11669           break;
11670         }
11671         case Expose:
11672         {
11673           if (event.xexpose.window == windows->image.id)
11674             if (event.xexpose.count == 0)
11675               {
11676                 event.xexpose.x=(int) highlight_info.x;
11677                 event.xexpose.y=(int) highlight_info.y;
11678                 event.xexpose.width=(int) highlight_info.width;
11679                 event.xexpose.height=(int) highlight_info.height;
11680                 XRefreshWindow(display,&windows->image,&event);
11681               }
11682           if (event.xexpose.window == windows->info.id)
11683             if (event.xexpose.count == 0)
11684               XInfoWidget(display,windows,text);
11685           break;
11686         }
11687         case KeyPress:
11688         {
11689           KeySym
11690             key_symbol;
11691
11692           if (event.xkey.window != windows->image.id)
11693             break;
11694           /*
11695             Respond to a user key press.
11696           */
11697           (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
11698             sizeof(command),&key_symbol,(XComposeStatus *) NULL);
11699           switch ((int) key_symbol)
11700           {
11701             case XK_Shift_L:
11702             case XK_Shift_R:
11703               break;
11704             case XK_Escape:
11705             case XK_F20:
11706               state|=EscapeState;
11707             case XK_Return:
11708             {
11709               state|=ExitState;
11710               break;
11711             }
11712             case XK_Home:
11713             case XK_KP_Home:
11714             {
11715               roi_info.x=(ssize_t) (windows->image.width/2L-roi_info.width/2L);
11716               roi_info.y=(ssize_t) (windows->image.height/2L-
11717                 roi_info.height/2L);
11718               break;
11719             }
11720             case XK_Left:
11721             case XK_KP_Left:
11722             {
11723               roi_info.x--;
11724               break;
11725             }
11726             case XK_Up:
11727             case XK_KP_Up:
11728             case XK_Next:
11729             {
11730               roi_info.y--;
11731               break;
11732             }
11733             case XK_Right:
11734             case XK_KP_Right:
11735             {
11736               roi_info.x++;
11737               break;
11738             }
11739             case XK_Prior:
11740             case XK_Down:
11741             case XK_KP_Down:
11742             {
11743               roi_info.y++;
11744               break;
11745             }
11746             case XK_F1:
11747             case XK_Help:
11748             {
11749               (void) XSetFunction(display,windows->image.highlight_context,
11750                 GXcopy);
11751               XTextViewWidget(display,resource_info,windows,MagickFalse,
11752                 "Help Viewer - Region of Interest",ImageROIHelp);
11753               (void) XSetFunction(display,windows->image.highlight_context,
11754                 GXinvert);
11755               break;
11756             }
11757             default:
11758             {
11759               command_type=XImageWindowCommand(display,resource_info,windows,
11760                 event.xkey.state,key_symbol,image);
11761               if (command_type != NullCommand)
11762                 state|=UpdateRegionState;
11763               break;
11764             }
11765           }
11766           (void) XSetSelectionOwner(display,XA_PRIMARY,windows->image.id,
11767             event.xkey.time);
11768           break;
11769         }
11770         case KeyRelease:
11771           break;
11772         case MotionNotify:
11773         {
11774           if (event.xbutton.window != windows->image.id)
11775             break;
11776           /*
11777             Map and unmap Info widget as text cursor crosses its boundaries.
11778           */
11779           x=event.xmotion.x;
11780           y=event.xmotion.y;
11781           if (windows->info.mapped != MagickFalse)
11782             {
11783               if ((x < (int) (windows->info.x+windows->info.width)) &&
11784                   (y < (int) (windows->info.y+windows->info.height)))
11785                 (void) XWithdrawWindow(display,windows->info.id,
11786                   windows->info.screen);
11787             }
11788           else
11789             if ((x > (int) (windows->info.x+windows->info.width)) ||
11790                 (y > (int) (windows->info.y+windows->info.height)))
11791               (void) XMapWindow(display,windows->info.id);
11792           roi_info.x=(ssize_t) windows->image.x+event.xmotion.x;
11793           roi_info.y=(ssize_t) windows->image.y+event.xmotion.y;
11794           break;
11795         }
11796         case SelectionRequest:
11797         {
11798           XSelectionEvent
11799             notify;
11800
11801           XSelectionRequestEvent
11802             *request;
11803
11804           /*
11805             Set primary selection.
11806           */
11807           (void) FormatLocaleString(text,MaxTextExtent,
11808             "%.20gx%.20g%+.20g%+.20g",(double) roi_info.width,(double)
11809             roi_info.height,(double) roi_info.x,(double) roi_info.y);
11810           request=(&(event.xselectionrequest));
11811           (void) XChangeProperty(request->display,request->requestor,
11812             request->property,request->target,8,PropModeReplace,
11813             (unsigned char *) text,(int) strlen(text));
11814           notify.type=SelectionNotify;
11815           notify.display=request->display;
11816           notify.requestor=request->requestor;
11817           notify.selection=request->selection;
11818           notify.target=request->target;
11819           notify.time=request->time;
11820           if (request->property == None)
11821             notify.property=request->target;
11822           else
11823             notify.property=request->property;
11824           (void) XSendEvent(request->display,request->requestor,False,0,
11825             (XEvent *) &notify);
11826         }
11827         default:
11828           break;
11829       }
11830       if ((state & UpdateConfigurationState) != 0)
11831         {
11832           (void) XPutBackEvent(display,&event);
11833           (void) XCheckDefineCursor(display,windows->image.id,cursor);
11834           break;
11835         }
11836     } while ((state & ExitState) == 0);
11837   } while ((state & ExitState) == 0);
11838   (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
11839   XSetCursorState(display,windows,MagickFalse);
11840   if ((state & EscapeState) != 0)
11841     return(MagickTrue);
11842   return(MagickTrue);
11843 }
11844 \f
11845 /*
11846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11847 %                                                                             %
11848 %                                                                             %
11849 %                                                                             %
11850 +   X R o t a t e I m a g e                                                   %
11851 %                                                                             %
11852 %                                                                             %
11853 %                                                                             %
11854 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11855 %
11856 %  XRotateImage() rotates the X image.  If the degrees parameter if zero, the
11857 %  rotation angle is computed from the slope of a line drawn by the user.
11858 %
11859 %  The format of the XRotateImage method is:
11860 %
11861 %      MagickBooleanType XRotateImage(Display *display,
11862 %        XResourceInfo *resource_info,XWindows *windows,double degrees,
11863 %        Image **image)
11864 %
11865 %  A description of each parameter follows:
11866 %
11867 %    o display: Specifies a connection to an X server; returned from
11868 %      XOpenDisplay.
11869 %
11870 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
11871 %
11872 %    o windows: Specifies a pointer to a XWindows structure.
11873 %
11874 %    o degrees: Specifies the number of degrees to rotate the image.
11875 %
11876 %    o image: the image.
11877 %
11878 */
11879 static MagickBooleanType XRotateImage(Display *display,
11880   XResourceInfo *resource_info,XWindows *windows,double degrees,Image **image)
11881 {
11882   static const char
11883     *RotateMenu[] =
11884     {
11885       "Pixel Color",
11886       "Direction",
11887       "Help",
11888       "Dismiss",
11889       (char *) NULL
11890     };
11891
11892   static ModeType
11893     direction = HorizontalRotateCommand;
11894
11895   static const ModeType
11896     DirectionCommands[] =
11897     {
11898       HorizontalRotateCommand,
11899       VerticalRotateCommand
11900     },
11901     RotateCommands[] =
11902     {
11903       RotateColorCommand,
11904       RotateDirectionCommand,
11905       RotateHelpCommand,
11906       RotateDismissCommand
11907     };
11908
11909   static unsigned int
11910     pen_id = 0;
11911
11912   char
11913     command[MaxTextExtent],
11914     text[MaxTextExtent];
11915
11916   Image
11917     *rotate_image;
11918
11919   int
11920     id,
11921     x,
11922     y;
11923
11924   MagickRealType
11925     normalized_degrees;
11926
11927   register int
11928     i;
11929
11930   unsigned int
11931     height,
11932     rotations,
11933     width;
11934
11935   if (degrees == 0.0)
11936     {
11937       unsigned int
11938         distance;
11939
11940       size_t
11941         state;
11942
11943       XEvent
11944         event;
11945
11946       XSegment
11947         rotate_info;
11948
11949       /*
11950         Map Command widget.
11951       */
11952       (void) CloneString(&windows->command.name,"Rotate");
11953       windows->command.data=2;
11954       (void) XCommandWidget(display,windows,RotateMenu,(XEvent *) NULL);
11955       (void) XMapRaised(display,windows->command.id);
11956       XClientMessage(display,windows->image.id,windows->im_protocols,
11957         windows->im_update_widget,CurrentTime);
11958       /*
11959         Wait for first button press.
11960       */
11961       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
11962       XQueryPosition(display,windows->image.id,&x,&y);
11963       rotate_info.x1=x;
11964       rotate_info.y1=y;
11965       rotate_info.x2=x;
11966       rotate_info.y2=y;
11967       state=DefaultState;
11968       do
11969       {
11970         XHighlightLine(display,windows->image.id,
11971           windows->image.highlight_context,&rotate_info);
11972         /*
11973           Wait for next event.
11974         */
11975         XScreenEvent(display,windows,&event);
11976         XHighlightLine(display,windows->image.id,
11977           windows->image.highlight_context,&rotate_info);
11978         if (event.xany.window == windows->command.id)
11979           {
11980             /*
11981               Select a command from the Command widget.
11982             */
11983             id=XCommandWidget(display,windows,RotateMenu,&event);
11984             if (id < 0)
11985               continue;
11986             (void) XSetFunction(display,windows->image.highlight_context,
11987               GXcopy);
11988             switch (RotateCommands[id])
11989             {
11990               case RotateColorCommand:
11991               {
11992                 const char
11993                   *ColorMenu[MaxNumberPens];
11994
11995                 int
11996                   pen_number;
11997
11998                 XColor
11999                   color;
12000
12001                 /*
12002                   Initialize menu selections.
12003                 */
12004                 for (i=0; i < (int) (MaxNumberPens-2); i++)
12005                   ColorMenu[i]=resource_info->pen_colors[i];
12006                 ColorMenu[MaxNumberPens-2]="Browser...";
12007                 ColorMenu[MaxNumberPens-1]=(const char *) NULL;
12008                 /*
12009                   Select a pen color from the pop-up menu.
12010                 */
12011                 pen_number=XMenuWidget(display,windows,RotateMenu[id],
12012                   (const char **) ColorMenu,command);
12013                 if (pen_number < 0)
12014                   break;
12015                 if (pen_number == (MaxNumberPens-2))
12016                   {
12017                     static char
12018                       color_name[MaxTextExtent] = "gray";
12019
12020                     /*
12021                       Select a pen color from a dialog.
12022                     */
12023                     resource_info->pen_colors[pen_number]=color_name;
12024                     XColorBrowserWidget(display,windows,"Select",color_name);
12025                     if (*color_name == '\0')
12026                       break;
12027                   }
12028                 /*
12029                   Set pen color.
12030                 */
12031                 (void) XParseColor(display,windows->map_info->colormap,
12032                   resource_info->pen_colors[pen_number],&color);
12033                 XBestPixel(display,windows->map_info->colormap,(XColor *) NULL,
12034                   (unsigned int) MaxColors,&color);
12035                 windows->pixel_info->pen_colors[pen_number]=color;
12036                 pen_id=(unsigned int) pen_number;
12037                 break;
12038               }
12039               case RotateDirectionCommand:
12040               {
12041                 static const char
12042                   *Directions[] =
12043                   {
12044                     "horizontal",
12045                     "vertical",
12046                     (char *) NULL,
12047                   };
12048
12049                 /*
12050                   Select a command from the pop-up menu.
12051                 */
12052                 id=XMenuWidget(display,windows,RotateMenu[id],
12053                   Directions,command);
12054                 if (id >= 0)
12055                   direction=DirectionCommands[id];
12056                 break;
12057               }
12058               case RotateHelpCommand:
12059               {
12060                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12061                   "Help Viewer - Image Rotation",ImageRotateHelp);
12062                 break;
12063               }
12064               case RotateDismissCommand:
12065               {
12066                 /*
12067                   Prematurely exit.
12068                 */
12069                 state|=EscapeState;
12070                 state|=ExitState;
12071                 break;
12072               }
12073               default:
12074                 break;
12075             }
12076             (void) XSetFunction(display,windows->image.highlight_context,
12077               GXinvert);
12078             continue;
12079           }
12080         switch (event.type)
12081         {
12082           case ButtonPress:
12083           {
12084             if (event.xbutton.button != Button1)
12085               break;
12086             if (event.xbutton.window != windows->image.id)
12087               break;
12088             /*
12089               exit loop.
12090             */
12091             (void) XSetFunction(display,windows->image.highlight_context,
12092               GXcopy);
12093             rotate_info.x1=event.xbutton.x;
12094             rotate_info.y1=event.xbutton.y;
12095             state|=ExitState;
12096             break;
12097           }
12098           case ButtonRelease:
12099             break;
12100           case Expose:
12101             break;
12102           case KeyPress:
12103           {
12104             char
12105               command[MaxTextExtent];
12106
12107             KeySym
12108               key_symbol;
12109
12110             if (event.xkey.window != windows->image.id)
12111               break;
12112             /*
12113               Respond to a user key press.
12114             */
12115             (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
12116               sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12117             switch ((int) key_symbol)
12118             {
12119               case XK_Escape:
12120               case XK_F20:
12121               {
12122                 /*
12123                   Prematurely exit.
12124                 */
12125                 state|=EscapeState;
12126                 state|=ExitState;
12127                 break;
12128               }
12129               case XK_F1:
12130               case XK_Help:
12131               {
12132                 (void) XSetFunction(display,windows->image.highlight_context,
12133                   GXcopy);
12134                 XTextViewWidget(display,resource_info,windows,MagickFalse,
12135                   "Help Viewer - Image Rotation",ImageRotateHelp);
12136                 (void) XSetFunction(display,windows->image.highlight_context,
12137                   GXinvert);
12138                 break;
12139               }
12140               default:
12141               {
12142                 (void) XBell(display,0);
12143                 break;
12144               }
12145             }
12146             break;
12147           }
12148           case MotionNotify:
12149           {
12150             rotate_info.x1=event.xmotion.x;
12151             rotate_info.y1=event.xmotion.y;
12152           }
12153         }
12154         rotate_info.x2=rotate_info.x1;
12155         rotate_info.y2=rotate_info.y1;
12156         if (direction == HorizontalRotateCommand)
12157           rotate_info.x2+=32;
12158         else
12159           rotate_info.y2-=32;
12160       } while ((state & ExitState) == 0);
12161       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12162       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12163       if ((state & EscapeState) != 0)
12164         return(MagickTrue);
12165       /*
12166         Draw line as pointer moves until the mouse button is released.
12167       */
12168       distance=0;
12169       (void) XSetFunction(display,windows->image.highlight_context,GXinvert);
12170       state=DefaultState;
12171       do
12172       {
12173         if (distance > 9)
12174           {
12175             /*
12176               Display info and draw rotation line.
12177             */
12178             if (windows->info.mapped == MagickFalse)
12179               (void) XMapWindow(display,windows->info.id);
12180             (void) FormatLocaleString(text,MaxTextExtent," %g",
12181               direction == VerticalRotateCommand ? degrees-90.0 : degrees);
12182             XInfoWidget(display,windows,text);
12183             XHighlightLine(display,windows->image.id,
12184               windows->image.highlight_context,&rotate_info);
12185           }
12186         else
12187           if (windows->info.mapped != MagickFalse)
12188             (void) XWithdrawWindow(display,windows->info.id,
12189               windows->info.screen);
12190         /*
12191           Wait for next event.
12192         */
12193         XScreenEvent(display,windows,&event);
12194         if (distance > 9)
12195           XHighlightLine(display,windows->image.id,
12196             windows->image.highlight_context,&rotate_info);
12197         switch (event.type)
12198         {
12199           case ButtonPress:
12200             break;
12201           case ButtonRelease:
12202           {
12203             /*
12204               User has committed to rotation line.
12205             */
12206             rotate_info.x2=event.xbutton.x;
12207             rotate_info.y2=event.xbutton.y;
12208             state|=ExitState;
12209             break;
12210           }
12211           case Expose:
12212             break;
12213           case MotionNotify:
12214           {
12215             rotate_info.x2=event.xmotion.x;
12216             rotate_info.y2=event.xmotion.y;
12217           }
12218           default:
12219             break;
12220         }
12221         /*
12222           Check boundary conditions.
12223         */
12224         if (rotate_info.x2 < 0)
12225           rotate_info.x2=0;
12226         else
12227           if (rotate_info.x2 > (int) windows->image.width)
12228             rotate_info.x2=(short) windows->image.width;
12229         if (rotate_info.y2 < 0)
12230           rotate_info.y2=0;
12231         else
12232           if (rotate_info.y2 > (int) windows->image.height)
12233             rotate_info.y2=(short) windows->image.height;
12234         /*
12235           Compute rotation angle from the slope of the line.
12236         */
12237         degrees=0.0;
12238         distance=(unsigned int)
12239           ((rotate_info.x2-rotate_info.x1+1)*(rotate_info.x2-rotate_info.x1+1))+
12240           ((rotate_info.y2-rotate_info.y1+1)*(rotate_info.y2-rotate_info.y1+1));
12241         if (distance > 9)
12242           degrees=RadiansToDegrees(-atan2((double) (rotate_info.y2-
12243             rotate_info.y1),(double) (rotate_info.x2-rotate_info.x1)));
12244       } while ((state & ExitState) == 0);
12245       (void) XSetFunction(display,windows->image.highlight_context,GXcopy);
12246       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12247       if (distance <= 9)
12248         return(MagickTrue);
12249     }
12250   if (direction == VerticalRotateCommand)
12251     degrees-=90.0;
12252   if (degrees == 0.0)
12253     return(MagickTrue);
12254   /*
12255     Rotate image.
12256   */
12257   normalized_degrees=degrees;
12258   while (normalized_degrees < -45.0)
12259     normalized_degrees+=360.0;
12260   for (rotations=0; normalized_degrees > 45.0; rotations++)
12261     normalized_degrees-=90.0;
12262   if (normalized_degrees != 0.0)
12263     (void) XMagickCommand(display,resource_info,windows,ApplyCommand,image);
12264   XSetCursorState(display,windows,MagickTrue);
12265   XCheckRefreshWindows(display,windows);
12266   (*image)->background_color.red=ScaleShortToQuantum(
12267     windows->pixel_info->pen_colors[pen_id].red);
12268   (*image)->background_color.green=ScaleShortToQuantum(
12269     windows->pixel_info->pen_colors[pen_id].green);
12270   (*image)->background_color.blue=ScaleShortToQuantum(
12271     windows->pixel_info->pen_colors[pen_id].blue);
12272   rotate_image=RotateImage(*image,degrees,&(*image)->exception);
12273   XSetCursorState(display,windows,MagickFalse);
12274   if (rotate_image == (Image *) NULL)
12275     return(MagickFalse);
12276   *image=DestroyImage(*image);
12277   *image=rotate_image;
12278   if (windows->image.crop_geometry != (char *) NULL)
12279     {
12280       /*
12281         Rotate crop geometry.
12282       */
12283       width=(unsigned int) (*image)->columns;
12284       height=(unsigned int) (*image)->rows;
12285       (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12286       switch (rotations % 4)
12287       {
12288         default:
12289         case 0:
12290           break;
12291         case 1:
12292         {
12293           /*
12294             Rotate 90 degrees.
12295           */
12296           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12297             "%ux%u%+d%+d",height,width,(int) (*image)->columns-
12298             (int) height-y,x);
12299           break;
12300         }
12301         case 2:
12302         {
12303           /*
12304             Rotate 180 degrees.
12305           */
12306           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12307             "%ux%u%+d%+d",width,height,(int) width-x,(int) height-y);
12308           break;
12309         }
12310         case 3:
12311         {
12312           /*
12313             Rotate 270 degrees.
12314           */
12315           (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12316             "%ux%u%+d%+d",height,width,y,(int) (*image)->rows-(int) width-x);
12317           break;
12318         }
12319       }
12320     }
12321   if (windows->image.orphan != MagickFalse)
12322     return(MagickTrue);
12323   if (normalized_degrees != 0.0)
12324     {
12325       /*
12326         Update image colormap.
12327       */
12328       windows->image.window_changes.width=(int) (*image)->columns;
12329       windows->image.window_changes.height=(int) (*image)->rows;
12330       if (windows->image.crop_geometry != (char *) NULL)
12331         {
12332           /*
12333             Obtain dimensions of image from crop geometry.
12334           */
12335           (void) XParseGeometry(windows->image.crop_geometry,&x,&y,
12336             &width,&height);
12337           windows->image.window_changes.width=(int) width;
12338           windows->image.window_changes.height=(int) height;
12339         }
12340       XConfigureImageColormap(display,resource_info,windows,*image);
12341     }
12342   else
12343     if (((rotations % 4) == 1) || ((rotations % 4) == 3))
12344       {
12345         windows->image.window_changes.width=windows->image.ximage->height;
12346         windows->image.window_changes.height=windows->image.ximage->width;
12347       }
12348   /*
12349     Update image configuration.
12350   */
12351   (void) XConfigureImage(display,resource_info,windows,*image);
12352   return(MagickTrue);
12353 }
12354 \f
12355 /*
12356 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12357 %                                                                             %
12358 %                                                                             %
12359 %                                                                             %
12360 +   X S a v e I m a g e                                                       %
12361 %                                                                             %
12362 %                                                                             %
12363 %                                                                             %
12364 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12365 %
12366 %  XSaveImage() saves an image to a file.
12367 %
12368 %  The format of the XSaveImage method is:
12369 %
12370 %      MagickBooleanType XSaveImage(Display *display,
12371 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
12372 %
12373 %  A description of each parameter follows:
12374 %
12375 %    o display: Specifies a connection to an X server; returned from
12376 %      XOpenDisplay.
12377 %
12378 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12379 %
12380 %    o windows: Specifies a pointer to a XWindows structure.
12381 %
12382 %    o image: the image.
12383 %
12384 */
12385 static MagickBooleanType XSaveImage(Display *display,
12386   XResourceInfo *resource_info,XWindows *windows,Image *image)
12387 {
12388   char
12389     filename[MaxTextExtent],
12390     geometry[MaxTextExtent];
12391
12392   Image
12393     *save_image;
12394
12395   ImageInfo
12396     *image_info;
12397
12398   MagickStatusType
12399     status;
12400
12401   /*
12402     Request file name from user.
12403   */
12404   if (resource_info->write_filename != (char *) NULL)
12405     (void) CopyMagickString(filename,resource_info->write_filename,
12406       MaxTextExtent);
12407   else
12408     {
12409       char
12410         path[MaxTextExtent];
12411
12412       int
12413         status;
12414
12415       GetPathComponent(image->filename,HeadPath,path);
12416       GetPathComponent(image->filename,TailPath,filename);
12417       status=chdir(path);
12418       if (status == -1)
12419         (void) ThrowMagickException(&image->exception,GetMagickModule(),
12420           FileOpenError,"UnableToOpenFile","%s",path);
12421     }
12422   XFileBrowserWidget(display,windows,"Save",filename);
12423   if (*filename == '\0')
12424     return(MagickTrue);
12425   if (IsPathAccessible(filename) != MagickFalse)
12426     {
12427       int
12428         status;
12429
12430       /*
12431         File exists-- seek user's permission before overwriting.
12432       */
12433       status=XConfirmWidget(display,windows,"Overwrite",filename);
12434       if (status <= 0)
12435         return(MagickTrue);
12436     }
12437   image_info=CloneImageInfo(resource_info->image_info);
12438   (void) CopyMagickString(image_info->filename,filename,MaxTextExtent);
12439   (void) SetImageInfo(image_info,1,&image->exception);
12440   if ((LocaleCompare(image_info->magick,"JPEG") == 0) ||
12441       (LocaleCompare(image_info->magick,"JPG") == 0))
12442     {
12443       char
12444         quality[MaxTextExtent];
12445
12446       int
12447         status;
12448
12449       /*
12450         Request JPEG quality from user.
12451       */
12452       (void) FormatLocaleString(quality,MaxTextExtent,"%.20g",(double)
12453         image->quality);
12454       status=XDialogWidget(display,windows,"Save","Enter JPEG quality:",
12455         quality);
12456       if (*quality == '\0')
12457         return(MagickTrue);
12458       image->quality=StringToUnsignedLong(quality);
12459       image_info->interlace=status != 0 ? NoInterlace : PlaneInterlace;
12460     }
12461   if ((LocaleCompare(image_info->magick,"EPS") == 0) ||
12462       (LocaleCompare(image_info->magick,"PDF") == 0) ||
12463       (LocaleCompare(image_info->magick,"PS") == 0) ||
12464       (LocaleCompare(image_info->magick,"PS2") == 0))
12465     {
12466       char
12467         geometry[MaxTextExtent];
12468
12469       /*
12470         Request page geometry from user.
12471       */
12472       (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12473       if (LocaleCompare(image_info->magick,"PDF") == 0)
12474         (void) CopyMagickString(geometry,PSPageGeometry,MaxTextExtent);
12475       if (image_info->page != (char *) NULL)
12476         (void) CopyMagickString(geometry,image_info->page,MaxTextExtent);
12477       XListBrowserWidget(display,windows,&windows->widget,PageSizes,"Select",
12478         "Select page geometry:",geometry);
12479       if (*geometry != '\0')
12480         image_info->page=GetPageGeometry(geometry);
12481     }
12482   /*
12483     Apply image transforms.
12484   */
12485   XSetCursorState(display,windows,MagickTrue);
12486   XCheckRefreshWindows(display,windows);
12487   save_image=CloneImage(image,0,0,MagickTrue,&image->exception);
12488   if (save_image == (Image *) NULL)
12489     return(MagickFalse);
12490   (void) FormatLocaleString(geometry,MaxTextExtent,"%dx%d!",
12491     windows->image.ximage->width,windows->image.ximage->height);
12492   (void) TransformImage(&save_image,windows->image.crop_geometry,geometry);
12493   /*
12494     Write image.
12495   */
12496   (void) CopyMagickString(save_image->filename,filename,MaxTextExtent);
12497   status=WriteImage(image_info,save_image);
12498   if (status != MagickFalse)
12499     image->taint=MagickFalse;
12500   save_image=DestroyImage(save_image);
12501   image_info=DestroyImageInfo(image_info);
12502   XSetCursorState(display,windows,MagickFalse);
12503   return(status != 0 ? MagickTrue : MagickFalse);
12504 }
12505 \f
12506 /*
12507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12508 %                                                                             %
12509 %                                                                             %
12510 %                                                                             %
12511 +   X S c r e e n E v e n t                                                   %
12512 %                                                                             %
12513 %                                                                             %
12514 %                                                                             %
12515 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12516 %
12517 %  XScreenEvent() handles global events associated with the Pan and Magnify
12518 %  windows.
12519 %
12520 %  The format of the XScreenEvent function is:
12521 %
12522 %      void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12523 %
12524 %  A description of each parameter follows:
12525 %
12526 %    o display: Specifies a pointer to the Display structure;  returned from
12527 %      XOpenDisplay.
12528 %
12529 %    o windows: Specifies a pointer to a XWindows structure.
12530 %
12531 %    o event: Specifies a pointer to a X11 XEvent structure.
12532 %
12533 %
12534 */
12535
12536 #if defined(__cplusplus) || defined(c_plusplus)
12537 extern "C" {
12538 #endif
12539
12540 static int XPredicate(Display *magick_unused(display),XEvent *event,char *data)
12541 {
12542   register XWindows
12543     *windows;
12544
12545   windows=(XWindows *) data;
12546   if ((event->type == ClientMessage) &&
12547       (event->xclient.window == windows->image.id))
12548     return(MagickFalse);
12549   return(MagickTrue);
12550 }
12551
12552 #if defined(__cplusplus) || defined(c_plusplus)
12553 }
12554 #endif
12555
12556 static void XScreenEvent(Display *display,XWindows *windows,XEvent *event)
12557 {
12558   register int
12559     x,
12560     y;
12561
12562   (void) XIfEvent(display,event,XPredicate,(char *) windows);
12563   if (event->xany.window == windows->command.id)
12564     return;
12565   switch (event->type)
12566   {
12567     case ButtonPress:
12568     case ButtonRelease:
12569     {
12570       if ((event->xbutton.button == Button3) &&
12571           (event->xbutton.state & Mod1Mask))
12572         {
12573           /*
12574             Convert Alt-Button3 to Button2.
12575           */
12576           event->xbutton.button=Button2;
12577           event->xbutton.state&=(~Mod1Mask);
12578         }
12579       if (event->xbutton.window == windows->backdrop.id)
12580         {
12581           (void) XSetInputFocus(display,event->xbutton.window,RevertToParent,
12582             event->xbutton.time);
12583           break;
12584         }
12585       if (event->xbutton.window == windows->pan.id)
12586         {
12587           XPanImage(display,windows,event);
12588           break;
12589         }
12590       if (event->xbutton.window == windows->image.id)
12591         if (event->xbutton.button == Button2)
12592           {
12593             /*
12594               Update magnified image.
12595             */
12596             x=event->xbutton.x;
12597             y=event->xbutton.y;
12598             if (x < 0)
12599               x=0;
12600             else
12601               if (x >= (int) windows->image.width)
12602                 x=(int) (windows->image.width-1);
12603             windows->magnify.x=(int) windows->image.x+x;
12604             if (y < 0)
12605               y=0;
12606             else
12607              if (y >= (int) windows->image.height)
12608                y=(int) (windows->image.height-1);
12609             windows->magnify.y=windows->image.y+y;
12610             if (windows->magnify.mapped == MagickFalse)
12611               (void) XMapRaised(display,windows->magnify.id);
12612             XMakeMagnifyImage(display,windows);
12613             if (event->type == ButtonRelease)
12614               (void) XWithdrawWindow(display,windows->info.id,
12615                 windows->info.screen);
12616             break;
12617           }
12618       break;
12619     }
12620     case ClientMessage:
12621     {
12622       /*
12623         If client window delete message, exit.
12624       */
12625       if (event->xclient.message_type != windows->wm_protocols)
12626         break;
12627       if (*event->xclient.data.l != (long) windows->wm_delete_window)
12628         break;
12629       if (event->xclient.window == windows->magnify.id)
12630         {
12631           (void) XWithdrawWindow(display,windows->magnify.id,
12632             windows->magnify.screen);
12633           break;
12634         }
12635       break;
12636     }
12637     case ConfigureNotify:
12638     {
12639       if (event->xconfigure.window == windows->magnify.id)
12640         {
12641           unsigned int
12642             magnify;
12643
12644           /*
12645             Magnify window has a new configuration.
12646           */
12647           windows->magnify.width=(unsigned int) event->xconfigure.width;
12648           windows->magnify.height=(unsigned int) event->xconfigure.height;
12649           if (windows->magnify.mapped == MagickFalse)
12650             break;
12651           magnify=1;
12652           while ((int) magnify <= event->xconfigure.width)
12653             magnify<<=1;
12654           while ((int) magnify <= event->xconfigure.height)
12655             magnify<<=1;
12656           magnify>>=1;
12657           if (((int) magnify != event->xconfigure.width) ||
12658               ((int) magnify != event->xconfigure.height))
12659             {
12660               XWindowChanges
12661                 window_changes;
12662
12663               window_changes.width=(int) magnify;
12664               window_changes.height=(int) magnify;
12665               (void) XReconfigureWMWindow(display,windows->magnify.id,
12666                 windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
12667                 &window_changes);
12668               break;
12669             }
12670           XMakeMagnifyImage(display,windows);
12671           break;
12672         }
12673       break;
12674     }
12675     case Expose:
12676     {
12677       if (event->xexpose.window == windows->image.id)
12678         {
12679           XRefreshWindow(display,&windows->image,event);
12680           break;
12681         }
12682       if (event->xexpose.window == windows->pan.id)
12683         if (event->xexpose.count == 0)
12684           {
12685             XDrawPanRectangle(display,windows);
12686             break;
12687           }
12688       if (event->xexpose.window == windows->magnify.id)
12689         if (event->xexpose.count == 0)
12690           {
12691             XMakeMagnifyImage(display,windows);
12692             break;
12693           }
12694       break;
12695     }
12696     case KeyPress:
12697     {
12698       char
12699         command[MaxTextExtent];
12700
12701       KeySym
12702         key_symbol;
12703
12704       if (event->xkey.window != windows->magnify.id)
12705         break;
12706       /*
12707         Respond to a user key press.
12708       */
12709       (void) XLookupString((XKeyEvent *) &event->xkey,command,(int)
12710         sizeof(command),&key_symbol,(XComposeStatus *) NULL);
12711       XMagnifyWindowCommand(display,windows,event->xkey.state,key_symbol);
12712       break;
12713     }
12714     case MapNotify:
12715     {
12716       if (event->xmap.window == windows->magnify.id)
12717         {
12718           windows->magnify.mapped=MagickTrue;
12719           (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
12720           break;
12721         }
12722       if (event->xmap.window == windows->info.id)
12723         {
12724           windows->info.mapped=MagickTrue;
12725           break;
12726         }
12727       break;
12728     }
12729     case MotionNotify:
12730     {
12731       while (XCheckMaskEvent(display,ButtonMotionMask,event)) ;
12732       if (event->xmotion.window == windows->image.id)
12733         if (windows->magnify.mapped != MagickFalse)
12734           {
12735             /*
12736               Update magnified image.
12737             */
12738             x=event->xmotion.x;
12739             y=event->xmotion.y;
12740             if (x < 0)
12741               x=0;
12742             else
12743               if (x >= (int) windows->image.width)
12744                 x=(int) (windows->image.width-1);
12745             windows->magnify.x=(int) windows->image.x+x;
12746             if (y < 0)
12747               y=0;
12748             else
12749              if (y >= (int) windows->image.height)
12750                y=(int) (windows->image.height-1);
12751             windows->magnify.y=windows->image.y+y;
12752             XMakeMagnifyImage(display,windows);
12753           }
12754       break;
12755     }
12756     case UnmapNotify:
12757     {
12758       if (event->xunmap.window == windows->magnify.id)
12759         {
12760           windows->magnify.mapped=MagickFalse;
12761           break;
12762         }
12763       if (event->xunmap.window == windows->info.id)
12764         {
12765           windows->info.mapped=MagickFalse;
12766           break;
12767         }
12768       break;
12769     }
12770     default:
12771       break;
12772   }
12773 }
12774 \f
12775 /*
12776 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12777 %                                                                             %
12778 %                                                                             %
12779 %                                                                             %
12780 +   X S e t C r o p G e o m e t r y                                           %
12781 %                                                                             %
12782 %                                                                             %
12783 %                                                                             %
12784 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12785 %
12786 %  XSetCropGeometry() accepts a cropping geometry relative to the Image window
12787 %  and translates it to a cropping geometry relative to the image.
12788 %
12789 %  The format of the XSetCropGeometry method is:
12790 %
12791 %      void XSetCropGeometry(Display *display,XWindows *windows,
12792 %        RectangleInfo *crop_info,Image *image)
12793 %
12794 %  A description of each parameter follows:
12795 %
12796 %    o display: Specifies a connection to an X server; returned from
12797 %      XOpenDisplay.
12798 %
12799 %    o windows: Specifies a pointer to a XWindows structure.
12800 %
12801 %    o crop_info:  A pointer to a RectangleInfo that defines a region of the
12802 %      Image window to crop.
12803 %
12804 %    o image: the image.
12805 %
12806 */
12807 static void XSetCropGeometry(Display *display,XWindows *windows,
12808   RectangleInfo *crop_info,Image *image)
12809 {
12810   char
12811     text[MaxTextExtent];
12812
12813   int
12814     x,
12815     y;
12816
12817   MagickRealType
12818     scale_factor;
12819
12820   unsigned int
12821     height,
12822     width;
12823
12824   if (windows->info.mapped != MagickFalse)
12825     {
12826       /*
12827         Display info on cropping rectangle.
12828       */
12829       (void) FormatLocaleString(text,MaxTextExtent," %.20gx%.20g%+.20g%+.20g",
12830         (double) crop_info->width,(double) crop_info->height,(double)
12831         crop_info->x,(double) crop_info->y);
12832       XInfoWidget(display,windows,text);
12833     }
12834   /*
12835     Cropping geometry is relative to any previous crop geometry.
12836   */
12837   x=0;
12838   y=0;
12839   width=(unsigned int) image->columns;
12840   height=(unsigned int) image->rows;
12841   if (windows->image.crop_geometry != (char *) NULL)
12842     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12843   else
12844     windows->image.crop_geometry=AcquireString((char *) NULL);
12845   /*
12846     Define the crop geometry string from the cropping rectangle.
12847   */
12848   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12849   if (crop_info->x > 0)
12850     x+=(int) (scale_factor*crop_info->x+0.5);
12851   width=(unsigned int) (scale_factor*crop_info->width+0.5);
12852   if (width == 0)
12853     width=1;
12854   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12855   if (crop_info->y > 0)
12856     y+=(int) (scale_factor*crop_info->y+0.5);
12857   height=(unsigned int) (scale_factor*crop_info->height+0.5);
12858   if (height == 0)
12859     height=1;
12860   (void) FormatLocaleString(windows->image.crop_geometry,MaxTextExtent,
12861     "%ux%u%+d%+d",width,height,x,y);
12862 }
12863 \f
12864 /*
12865 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12866 %                                                                             %
12867 %                                                                             %
12868 %                                                                             %
12869 +   X T i l e I m a g e                                                       %
12870 %                                                                             %
12871 %                                                                             %
12872 %                                                                             %
12873 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12874 %
12875 %  XTileImage() loads or deletes a selected tile from a visual image directory.
12876 %  The load or delete command is chosen from a menu.
12877 %
12878 %  The format of the XTileImage method is:
12879 %
12880 %      Image *XTileImage(Display *display,XResourceInfo *resource_info,
12881 %        XWindows *windows,Image *image,XEvent *event)
12882 %
12883 %  A description of each parameter follows:
12884 %
12885 %    o tile_image:  XTileImage reads or deletes the tile image
12886 %      and returns it.  A null image is returned if an error occurs.
12887 %
12888 %    o display: Specifies a connection to an X server;  returned from
12889 %      XOpenDisplay.
12890 %
12891 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
12892 %
12893 %    o windows: Specifies a pointer to a XWindows structure.
12894 %
12895 %    o image: the image; returned from ReadImage.
12896 %
12897 %    o event: Specifies a pointer to a XEvent structure.  If it is NULL,
12898 %      the entire image is refreshed.
12899 %
12900 */
12901 static Image *XTileImage(Display *display,XResourceInfo *resource_info,
12902   XWindows *windows,Image *image,XEvent *event)
12903 {
12904   static const char
12905     *VerbMenu[] =
12906     {
12907       "Load",
12908       "Next",
12909       "Former",
12910       "Delete",
12911       "Update",
12912       (char *) NULL,
12913     };
12914
12915   static const ModeType
12916     TileCommands[] =
12917     {
12918       TileLoadCommand,
12919       TileNextCommand,
12920       TileFormerCommand,
12921       TileDeleteCommand,
12922       TileUpdateCommand
12923     };
12924
12925   char
12926     command[MaxTextExtent],
12927     filename[MaxTextExtent];
12928
12929   Image
12930     *tile_image;
12931
12932   int
12933     id,
12934     status,
12935     tile,
12936     x,
12937     y;
12938
12939   MagickRealType
12940     scale_factor;
12941
12942   register char
12943     *p,
12944     *q;
12945
12946   register int
12947     i;
12948
12949   unsigned int
12950     height,
12951     width;
12952
12953   /*
12954     Tile image is relative to montage image configuration.
12955   */
12956   x=0;
12957   y=0;
12958   width=(unsigned int) image->columns;
12959   height=(unsigned int) image->rows;
12960   if (windows->image.crop_geometry != (char *) NULL)
12961     (void) XParseGeometry(windows->image.crop_geometry,&x,&y,&width,&height);
12962   scale_factor=(MagickRealType) width/windows->image.ximage->width;
12963   event->xbutton.x+=windows->image.x;
12964   event->xbutton.x=(int) (scale_factor*event->xbutton.x+x+0.5);
12965   scale_factor=(MagickRealType) height/windows->image.ximage->height;
12966   event->xbutton.y+=windows->image.y;
12967   event->xbutton.y=(int) (scale_factor*event->xbutton.y+y+0.5);
12968   /*
12969     Determine size and location of each tile in the visual image directory.
12970   */
12971   width=(unsigned int) image->columns;
12972   height=(unsigned int) image->rows;
12973   x=0;
12974   y=0;
12975   (void) XParseGeometry(image->montage,&x,&y,&width,&height);
12976   tile=((event->xbutton.y-y)/height)*(((int) image->columns-x)/width)+
12977     (event->xbutton.x-x)/width;
12978   if (tile < 0)
12979     {
12980       /*
12981         Button press is outside any tile.
12982       */
12983       (void) XBell(display,0);
12984       return((Image *) NULL);
12985     }
12986   /*
12987     Determine file name from the tile directory.
12988   */
12989   p=image->directory;
12990   for (i=tile; (i != 0) && (*p != '\0'); )
12991   {
12992     if (*p == '\n')
12993       i--;
12994     p++;
12995   }
12996   if (*p == '\0')
12997     {
12998       /*
12999         Button press is outside any tile.
13000       */
13001       (void) XBell(display,0);
13002       return((Image *) NULL);
13003     }
13004   /*
13005     Select a command from the pop-up menu.
13006   */
13007   id=XMenuWidget(display,windows,"Tile Verb",VerbMenu,command);
13008   if (id < 0)
13009     return((Image *) NULL);
13010   q=p;
13011   while ((*q != '\n') && (*q != '\0'))
13012     q++;
13013   (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13014   /*
13015     Perform command for the selected tile.
13016   */
13017   XSetCursorState(display,windows,MagickTrue);
13018   XCheckRefreshWindows(display,windows);
13019   tile_image=NewImageList();
13020   switch (TileCommands[id])
13021   {
13022     case TileLoadCommand:
13023     {
13024       /*
13025         Load tile image.
13026       */
13027       XCheckRefreshWindows(display,windows);
13028       (void) CopyMagickString(resource_info->image_info->magick,"MIFF",
13029         MaxTextExtent);
13030       (void) CopyMagickString(resource_info->image_info->filename,filename,
13031         MaxTextExtent);
13032       tile_image=ReadImage(resource_info->image_info,&image->exception);
13033       CatchException(&image->exception);
13034       (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13035       break;
13036     }
13037     case TileNextCommand:
13038     {
13039       /*
13040         Display next image.
13041       */
13042       XClientMessage(display,windows->image.id,windows->im_protocols,
13043         windows->im_next_image,CurrentTime);
13044       break;
13045     }
13046     case TileFormerCommand:
13047     {
13048       /*
13049         Display former image.
13050       */
13051       XClientMessage(display,windows->image.id,windows->im_protocols,
13052         windows->im_former_image,CurrentTime);
13053       break;
13054     }
13055     case TileDeleteCommand:
13056     {
13057       /*
13058         Delete tile image.
13059       */
13060       if (IsPathAccessible(filename) == MagickFalse)
13061         {
13062           XNoticeWidget(display,windows,"Image file does not exist:",filename);
13063           break;
13064         }
13065       status=XConfirmWidget(display,windows,"Really delete tile",filename);
13066       if (status <= 0)
13067         break;
13068       status=remove(filename) != 0 ? MagickTrue : MagickFalse;
13069       if (status != MagickFalse)
13070         {
13071           XNoticeWidget(display,windows,"Unable to delete image file:",
13072             filename);
13073           break;
13074         }
13075     }
13076     case TileUpdateCommand:
13077     {
13078       ExceptionInfo
13079         *exception;
13080
13081       int
13082         x_offset,
13083         y_offset;
13084
13085       PixelPacket
13086         pixel;
13087
13088       register int
13089         j;
13090
13091       register PixelPacket
13092         *s;
13093
13094       /*
13095         Ensure all the images exist.
13096       */
13097       tile=0;
13098       for (p=image->directory; *p != '\0'; p++)
13099       {
13100         CacheView
13101           *image_view;
13102
13103         q=p;
13104         while ((*q != '\n') && (*q != '\0'))
13105           q++;
13106         (void) CopyMagickString(filename,p,(size_t) (q-p+1));
13107         p=q;
13108         if (IsPathAccessible(filename) != MagickFalse)
13109           {
13110             tile++;
13111             continue;
13112           }
13113         /*
13114           Overwrite tile with background color.
13115         */
13116         x_offset=(int) (width*(tile % (((int) image->columns-x)/width))+x);
13117         y_offset=(int) (height*(tile/(((int) image->columns-x)/width))+y);
13118         exception=(&image->exception);
13119         image_view=AcquireCacheView(image);
13120         (void) GetOneCacheViewVirtualPixel(image_view,0,0,&pixel,exception);
13121         for (i=0; i < (int) height; i++)
13122         {
13123           s=GetCacheViewAuthenticPixels(image_view,(ssize_t) x_offset,(ssize_t)
13124             y_offset+i,width,1,exception);
13125           if (s == (PixelPacket *) NULL)
13126             break;
13127           for (j=0; j < (int) width; j++)
13128             *s++=pixel;
13129           if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
13130             break;
13131         }
13132         image_view=DestroyCacheView(image_view);
13133         tile++;
13134       }
13135       windows->image.window_changes.width=(int) image->columns;
13136       windows->image.window_changes.height=(int) image->rows;
13137       XConfigureImageColormap(display,resource_info,windows,image);
13138       (void) XConfigureImage(display,resource_info,windows,image);
13139       break;
13140     }
13141     default:
13142       break;
13143   }
13144   XSetCursorState(display,windows,MagickFalse);
13145   return(tile_image);
13146 }
13147 \f
13148 /*
13149 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13150 %                                                                             %
13151 %                                                                             %
13152 %                                                                             %
13153 +   X T r a n s l a t e I m a g e                                             %
13154 %                                                                             %
13155 %                                                                             %
13156 %                                                                             %
13157 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13158 %
13159 %  XTranslateImage() translates the image within an Image window by one pixel
13160 %  as specified by the key symbol.  If the image has a `montage string the
13161 %  translation is respect to the width and height contained within the string.
13162 %
13163 %  The format of the XTranslateImage method is:
13164 %
13165 %      void XTranslateImage(Display *display,XWindows *windows,
13166 %        Image *image,const KeySym key_symbol)
13167 %
13168 %  A description of each parameter follows:
13169 %
13170 %    o display: Specifies a connection to an X server; returned from
13171 %      XOpenDisplay.
13172 %
13173 %    o windows: Specifies a pointer to a XWindows structure.
13174 %
13175 %    o image: the image.
13176 %
13177 %    o key_symbol: Specifies a KeySym which indicates which side of the image
13178 %      to trim.
13179 %
13180 */
13181 static void XTranslateImage(Display *display,XWindows *windows,
13182   Image *image,const KeySym key_symbol)
13183 {
13184   char
13185     text[MaxTextExtent];
13186
13187   int
13188     x,
13189     y;
13190
13191   unsigned int
13192     x_offset,
13193     y_offset;
13194
13195   /*
13196     User specified a pan position offset.
13197   */
13198   x_offset=windows->image.width;
13199   y_offset=windows->image.height;
13200   if (image->montage != (char *) NULL)
13201     (void) XParseGeometry(image->montage,&x,&y,&x_offset,&y_offset);
13202   switch ((int) key_symbol)
13203   {
13204     case XK_Home:
13205     case XK_KP_Home:
13206     {
13207       windows->image.x=(int) windows->image.width/2;
13208       windows->image.y=(int) windows->image.height/2;
13209       break;
13210     }
13211     case XK_Left:
13212     case XK_KP_Left:
13213     {
13214       windows->image.x-=x_offset;
13215       break;
13216     }
13217     case XK_Next:
13218     case XK_Up:
13219     case XK_KP_Up:
13220     {
13221       windows->image.y-=y_offset;
13222       break;
13223     }
13224     case XK_Right:
13225     case XK_KP_Right:
13226     {
13227       windows->image.x+=x_offset;
13228       break;
13229     }
13230     case XK_Prior:
13231     case XK_Down:
13232     case XK_KP_Down:
13233     {
13234       windows->image.y+=y_offset;
13235       break;
13236     }
13237     default:
13238       return;
13239   }
13240   /*
13241     Check boundary conditions.
13242   */
13243   if (windows->image.x < 0)
13244     windows->image.x=0;
13245   else
13246     if ((int) (windows->image.x+windows->image.width) >
13247         windows->image.ximage->width)
13248       windows->image.x=(int) windows->image.ximage->width-windows->image.width;
13249   if (windows->image.y < 0)
13250     windows->image.y=0;
13251   else
13252     if ((int) (windows->image.y+windows->image.height) >
13253         windows->image.ximage->height)
13254       windows->image.y=(int) windows->image.ximage->height-windows->image.height;
13255   /*
13256     Refresh Image window.
13257   */
13258   (void) FormatLocaleString(text,MaxTextExtent," %ux%u%+d%+d ",
13259     windows->image.width,windows->image.height,windows->image.x,
13260     windows->image.y);
13261   XInfoWidget(display,windows,text);
13262   XCheckRefreshWindows(display,windows);
13263   XDrawPanRectangle(display,windows);
13264   XRefreshWindow(display,&windows->image,(XEvent *) NULL);
13265   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
13266 }
13267 \f
13268 /*
13269 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13270 %                                                                             %
13271 %                                                                             %
13272 %                                                                             %
13273 +   X T r i m I m a g e                                                       %
13274 %                                                                             %
13275 %                                                                             %
13276 %                                                                             %
13277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13278 %
13279 %  XTrimImage() trims the edges from the Image window.
13280 %
13281 %  The format of the XTrimImage method is:
13282 %
13283 %      MagickBooleanType XTrimImage(Display *display,
13284 %        XResourceInfo *resource_info,XWindows *windows,Image *image)
13285 %
13286 %  A description of each parameter follows:
13287 %
13288 %    o display: Specifies a connection to an X server; returned from
13289 %      XOpenDisplay.
13290 %
13291 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13292 %
13293 %    o windows: Specifies a pointer to a XWindows structure.
13294 %
13295 %    o image: the image.
13296 %
13297 */
13298 static MagickBooleanType XTrimImage(Display *display,
13299   XResourceInfo *resource_info,XWindows *windows,Image *image)
13300 {
13301   RectangleInfo
13302     trim_info;
13303
13304   register int
13305     x,
13306     y;
13307
13308   size_t
13309     background,
13310     pixel;
13311
13312   /*
13313     Trim edges from image.
13314   */
13315   XSetCursorState(display,windows,MagickTrue);
13316   XCheckRefreshWindows(display,windows);
13317   /*
13318     Crop the left edge.
13319   */
13320   background=XGetPixel(windows->image.ximage,0,0);
13321   trim_info.width=(size_t) windows->image.ximage->width;
13322   for (x=0; x < windows->image.ximage->width; x++)
13323   {
13324     for (y=0; y < windows->image.ximage->height; y++)
13325     {
13326       pixel=XGetPixel(windows->image.ximage,x,y);
13327       if (pixel != background)
13328         break;
13329     }
13330     if (y < windows->image.ximage->height)
13331       break;
13332   }
13333   trim_info.x=(ssize_t) x;
13334   if (trim_info.x == (ssize_t) windows->image.ximage->width)
13335     {
13336       XSetCursorState(display,windows,MagickFalse);
13337       return(MagickFalse);
13338     }
13339   /*
13340     Crop the right edge.
13341   */
13342   background=XGetPixel(windows->image.ximage,windows->image.ximage->width-1,0);
13343   for (x=windows->image.ximage->width-1; x != 0; x--)
13344   {
13345     for (y=0; y < windows->image.ximage->height; y++)
13346     {
13347       pixel=XGetPixel(windows->image.ximage,x,y);
13348       if (pixel != background)
13349         break;
13350     }
13351     if (y < windows->image.ximage->height)
13352       break;
13353   }
13354   trim_info.width=(size_t) (x-trim_info.x+1);
13355   /*
13356     Crop the top edge.
13357   */
13358   background=XGetPixel(windows->image.ximage,0,0);
13359   trim_info.height=(size_t) windows->image.ximage->height;
13360   for (y=0; y < windows->image.ximage->height; y++)
13361   {
13362     for (x=0; x < windows->image.ximage->width; x++)
13363     {
13364       pixel=XGetPixel(windows->image.ximage,x,y);
13365       if (pixel != background)
13366         break;
13367     }
13368     if (x < windows->image.ximage->width)
13369       break;
13370   }
13371   trim_info.y=(ssize_t) y;
13372   /*
13373     Crop the bottom edge.
13374   */
13375   background=XGetPixel(windows->image.ximage,0,windows->image.ximage->height-1);
13376   for (y=windows->image.ximage->height-1; y != 0; y--)
13377   {
13378     for (x=0; x < windows->image.ximage->width; x++)
13379     {
13380       pixel=XGetPixel(windows->image.ximage,x,y);
13381       if (pixel != background)
13382         break;
13383     }
13384     if (x < windows->image.ximage->width)
13385       break;
13386   }
13387   trim_info.height=(size_t) y-trim_info.y+1;
13388   if (((unsigned int) trim_info.width != windows->image.width) ||
13389       ((unsigned int) trim_info.height != windows->image.height))
13390     {
13391       /*
13392         Reconfigure Image window as defined by the trimming rectangle.
13393       */
13394       XSetCropGeometry(display,windows,&trim_info,image);
13395       windows->image.window_changes.width=(int) trim_info.width;
13396       windows->image.window_changes.height=(int) trim_info.height;
13397       (void) XConfigureImage(display,resource_info,windows,image);
13398     }
13399   XSetCursorState(display,windows,MagickFalse);
13400   return(MagickTrue);
13401 }
13402 \f
13403 /*
13404 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13405 %                                                                             %
13406 %                                                                             %
13407 %                                                                             %
13408 +   X V i s u a l D i r e c t o r y I m a g e                                 %
13409 %                                                                             %
13410 %                                                                             %
13411 %                                                                             %
13412 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13413 %
13414 %  XVisualDirectoryImage() creates a Visual Image Directory.
13415 %
13416 %  The format of the XVisualDirectoryImage method is:
13417 %
13418 %      Image *XVisualDirectoryImage(Display *display,
13419 %        XResourceInfo *resource_info,XWindows *windows)
13420 %
13421 %  A description of each parameter follows:
13422 %
13423 %    o nexus: Method XVisualDirectoryImage returns a visual image
13424 %      directory if it can be created successfully.  Otherwise a null image
13425 %      is returned.
13426 %
13427 %    o display: Specifies a connection to an X server; returned from
13428 %      XOpenDisplay.
13429 %
13430 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13431 %
13432 %    o windows: Specifies a pointer to a XWindows structure.
13433 %
13434 */
13435 static Image *XVisualDirectoryImage(Display *display,
13436   XResourceInfo *resource_info,XWindows *windows)
13437 {
13438 #define TileImageTag  "Scale/Image"
13439 #define XClientName  "montage"
13440
13441   char
13442     **filelist;
13443
13444   ExceptionInfo
13445     *exception;
13446
13447   Image
13448     *images,
13449     *montage_image,
13450     *next_image,
13451     *thumbnail_image;
13452
13453   ImageInfo
13454     *read_info;
13455
13456   int
13457     number_files;
13458
13459   MagickBooleanType
13460     backdrop;
13461
13462   MagickStatusType
13463     status;
13464
13465   MontageInfo
13466     *montage_info;
13467
13468   RectangleInfo
13469     geometry;
13470
13471   register int
13472     i;
13473
13474   static char
13475     filename[MaxTextExtent] = "\0",
13476     filenames[MaxTextExtent] = "*";
13477
13478   XResourceInfo
13479     background_resources;
13480
13481   /*
13482     Request file name from user.
13483   */
13484   XFileBrowserWidget(display,windows,"Directory",filenames);
13485   if (*filenames == '\0')
13486     return((Image *) NULL);
13487   /*
13488     Expand the filenames.
13489   */
13490   filelist=(char **) AcquireMagickMemory(sizeof(*filelist));
13491   if (filelist == (char **) NULL)
13492     {
13493       ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13494         filenames);
13495       return((Image *) NULL);
13496     }
13497   number_files=1;
13498   filelist[0]=filenames;
13499   status=ExpandFilenames(&number_files,&filelist);
13500   if ((status == MagickFalse) || (number_files == 0))
13501     {
13502       if (number_files == 0)
13503         ThrowXWindowFatalException(ImageError,"NoImagesWereFound",filenames)
13504       else
13505         ThrowXWindowFatalException(ResourceLimitError,"MemoryAllocationFailed",
13506           filenames);
13507       return((Image *) NULL);
13508     }
13509   /*
13510     Set image background resources.
13511   */
13512   background_resources=(*resource_info);
13513   background_resources.window_id=AcquireString("");
13514   (void) FormatLocaleString(background_resources.window_id,MaxTextExtent,
13515     "0x%lx",windows->image.id);
13516   background_resources.backdrop=MagickTrue;
13517   /*
13518     Read each image and convert them to a tile.
13519   */
13520   backdrop=(windows->visual_info->klass == TrueColor) ||
13521     (windows->visual_info->klass == DirectColor) ? MagickTrue : MagickFalse;
13522   read_info=CloneImageInfo(resource_info->image_info);
13523   (void) SetImageOption(read_info,"jpeg:size","120x120");
13524   (void) CloneString(&read_info->size,DefaultTileGeometry);
13525   (void) SetImageInfoProgressMonitor(read_info,(MagickProgressMonitor) NULL,
13526     (void *) NULL);
13527   images=NewImageList();
13528   exception=AcquireExceptionInfo();
13529   XSetCursorState(display,windows,MagickTrue);
13530   XCheckRefreshWindows(display,windows);
13531   for (i=0; i < (int) number_files; i++)
13532   {
13533     (void) CopyMagickString(read_info->filename,filelist[i],MaxTextExtent);
13534     filelist[i]=DestroyString(filelist[i]);
13535     *read_info->magick='\0';
13536     next_image=ReadImage(read_info,exception);
13537     CatchException(exception);
13538     if (next_image != (Image *) NULL)
13539       {
13540         (void) DeleteImageProperty(next_image,"label");
13541         (void) SetImageProperty(next_image,"label",InterpretImageProperties(
13542           read_info,next_image,DefaultTileLabel));
13543         (void) ParseRegionGeometry(next_image,read_info->size,&geometry,
13544           exception);
13545         thumbnail_image=ThumbnailImage(next_image,geometry.width,
13546           geometry.height,exception);
13547         if (thumbnail_image != (Image *) NULL)
13548           {
13549             next_image=DestroyImage(next_image);
13550             next_image=thumbnail_image;
13551           }
13552         if (backdrop)
13553           {
13554             (void) XDisplayBackgroundImage(display,&background_resources,
13555               next_image);
13556             XSetCursorState(display,windows,MagickTrue);
13557           }
13558         AppendImageToList(&images,next_image);
13559         if (images->progress_monitor != (MagickProgressMonitor) NULL)
13560           {
13561             MagickBooleanType
13562               proceed;
13563
13564             proceed=SetImageProgress(images,LoadImageTag,(MagickOffsetType) i,
13565               (MagickSizeType) number_files);
13566             if (proceed == MagickFalse)
13567               break;
13568           }
13569       }
13570   }
13571   exception=DestroyExceptionInfo(exception);
13572   filelist=(char **) RelinquishMagickMemory(filelist);
13573   if (images == (Image *) NULL)
13574     {
13575       read_info=DestroyImageInfo(read_info);
13576       XSetCursorState(display,windows,MagickFalse);
13577       ThrowXWindowFatalException(ImageError,"NoImagesWereLoaded",filenames);
13578       return((Image *) NULL);
13579     }
13580   /*
13581     Create the Visual Image Directory.
13582   */
13583   montage_info=CloneMontageInfo(read_info,(MontageInfo *) NULL);
13584   montage_info->pointsize=10;
13585   if (resource_info->font != (char *) NULL)
13586     (void) CloneString(&montage_info->font,resource_info->font);
13587   (void) CopyMagickString(montage_info->filename,filename,MaxTextExtent);
13588   montage_image=MontageImageList(read_info,montage_info,GetFirstImageInList(
13589     images),&images->exception);
13590   images=DestroyImageList(images);
13591   montage_info=DestroyMontageInfo(montage_info);
13592   read_info=DestroyImageInfo(read_info);
13593   XSetCursorState(display,windows,MagickFalse);
13594   if (montage_image == (Image *) NULL)
13595     return(montage_image);
13596   XClientMessage(display,windows->image.id,windows->im_protocols,
13597     windows->im_next_image,CurrentTime);
13598   return(montage_image);
13599 }
13600 \f
13601 /*
13602 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13603 %                                                                             %
13604 %                                                                             %
13605 %                                                                             %
13606 %   X D i s p l a y B a c k g r o u n d I m a g e                             %
13607 %                                                                             %
13608 %                                                                             %
13609 %                                                                             %
13610 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13611 %
13612 %  XDisplayBackgroundImage() displays an image in the background of a window.
13613 %
13614 %  The format of the XDisplayBackgroundImage method is:
13615 %
13616 %      MagickBooleanType XDisplayBackgroundImage(Display *display,
13617 %        XResourceInfo *resource_info,Image *image)
13618 %
13619 %  A description of each parameter follows:
13620 %
13621 %    o display: Specifies a connection to an X server;  returned from
13622 %      XOpenDisplay.
13623 %
13624 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13625 %
13626 %    o image: the image.
13627 %
13628 */
13629 MagickExport MagickBooleanType XDisplayBackgroundImage(Display *display,
13630   XResourceInfo *resource_info,Image *image)
13631 {
13632   char
13633     geometry[MaxTextExtent],
13634     visual_type[MaxTextExtent];
13635
13636   int
13637     height,
13638     status,
13639     width;
13640
13641   RectangleInfo
13642     geometry_info;
13643
13644   static XPixelInfo
13645     pixel;
13646
13647   static XStandardColormap
13648     *map_info;
13649
13650   static XVisualInfo
13651     *visual_info = (XVisualInfo *) NULL;
13652
13653   static XWindowInfo
13654     window_info;
13655
13656   size_t
13657     delay;
13658
13659   Window
13660     root_window;
13661
13662   XGCValues
13663     context_values;
13664
13665   XResourceInfo
13666     resources;
13667
13668   XWindowAttributes
13669     window_attributes;
13670
13671   /*
13672     Determine target window.
13673   */
13674   assert(image != (Image *) NULL);
13675   assert(image->signature == MagickSignature);
13676   if (image->debug != MagickFalse)
13677     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
13678   resources=(*resource_info);
13679   window_info.id=(Window) NULL;
13680   root_window=XRootWindow(display,XDefaultScreen(display));
13681   if (LocaleCompare(resources.window_id,"root") == 0)
13682     window_info.id=root_window;
13683   else
13684     {
13685       if (isdigit((unsigned char) *resources.window_id) != 0)
13686         window_info.id=XWindowByID(display,root_window,
13687           (Window) strtol((char *) resources.window_id,(char **) NULL,0));
13688       if (window_info.id == (Window) NULL)
13689         window_info.id=XWindowByName(display,root_window,resources.window_id);
13690     }
13691   if (window_info.id == (Window) NULL)
13692     {
13693       ThrowXWindowFatalException(XServerError,"NoWindowWithSpecifiedIDExists",
13694         resources.window_id);
13695       return(MagickFalse);
13696     }
13697   /*
13698     Determine window visual id.
13699   */
13700   window_attributes.width=XDisplayWidth(display,XDefaultScreen(display));
13701   window_attributes.height=XDisplayHeight(display,XDefaultScreen(display));
13702   (void) CopyMagickString(visual_type,"default",MaxTextExtent);
13703   status=XGetWindowAttributes(display,window_info.id,&window_attributes);
13704   if (status != 0)
13705     (void) FormatLocaleString(visual_type,MaxTextExtent,"0x%lx",
13706       XVisualIDFromVisual(window_attributes.visual));
13707   if (visual_info == (XVisualInfo *) NULL)
13708     {
13709       /*
13710         Allocate standard colormap.
13711       */
13712       map_info=XAllocStandardColormap();
13713       if (map_info == (XStandardColormap *) NULL)
13714         ThrowXWindowFatalException(XServerFatalError,"MemoryAllocationFailed",
13715           image->filename);
13716       map_info->colormap=(Colormap) NULL;
13717       pixel.pixels=(unsigned long *) NULL;
13718       /*
13719         Initialize visual info.
13720       */
13721       resources.map_type=(char *) NULL;
13722       resources.visual_type=visual_type;
13723       visual_info=XBestVisualInfo(display,map_info,&resources);
13724       if (visual_info == (XVisualInfo *) NULL)
13725         ThrowXWindowFatalException(XServerFatalError,"UnableToGetVisual",
13726           resources.visual_type);
13727       /*
13728         Initialize window info.
13729       */
13730       window_info.ximage=(XImage *) NULL;
13731       window_info.matte_image=(XImage *) NULL;
13732       window_info.pixmap=(Pixmap) NULL;
13733       window_info.matte_pixmap=(Pixmap) NULL;
13734     }
13735   /*
13736     Free previous root colors.
13737   */
13738   if (window_info.id == root_window)
13739     (void) XDestroyWindowColors(display,root_window);
13740   /*
13741     Initialize Standard Colormap.
13742   */
13743   resources.colormap=SharedColormap;
13744   XMakeStandardColormap(display,visual_info,&resources,image,map_info,&pixel);
13745   /*
13746     Graphic context superclass.
13747   */
13748   context_values.background=pixel.background_color.pixel;
13749   context_values.foreground=pixel.foreground_color.pixel;
13750   pixel.annotate_context=XCreateGC(display,window_info.id,
13751     (size_t) (GCBackground | GCForeground),&context_values);
13752   if (pixel.annotate_context == (GC) NULL)
13753     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
13754       image->filename);
13755   /*
13756     Initialize Image window attributes.
13757   */
13758   window_info.name=AcquireString("\0");
13759   window_info.icon_name=AcquireString("\0");
13760   XGetWindowInfo(display,visual_info,map_info,&pixel,(XFontStruct *) NULL,
13761     &resources,&window_info);
13762   /*
13763     Create the X image.
13764   */
13765   window_info.width=(unsigned int) image->columns;
13766   window_info.height=(unsigned int) image->rows;
13767   if ((image->columns != window_info.width) ||
13768       (image->rows != window_info.height))
13769     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13770       image->filename);
13771   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>",
13772     window_attributes.width,window_attributes.height);
13773   geometry_info.width=window_info.width;
13774   geometry_info.height=window_info.height;
13775   geometry_info.x=(ssize_t) window_info.x;
13776   geometry_info.y=(ssize_t) window_info.y;
13777   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
13778     &geometry_info.width,&geometry_info.height);
13779   window_info.width=(unsigned int) geometry_info.width;
13780   window_info.height=(unsigned int) geometry_info.height;
13781   window_info.x=(int) geometry_info.x;
13782   window_info.y=(int) geometry_info.y;
13783   status=XMakeImage(display,&resources,&window_info,image,window_info.width,
13784     window_info.height);
13785   if (status == MagickFalse)
13786     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
13787       image->filename);
13788   window_info.x=0;
13789   window_info.y=0;
13790   if (image->debug != MagickFalse)
13791     {
13792       (void) LogMagickEvent(X11Event,GetMagickModule(),
13793         "Image: %s[%.20g] %.20gx%.20g ",image->filename,(double) image->scene,
13794         (double) image->columns,(double) image->rows);
13795       if (image->colors != 0)
13796         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
13797           image->colors);
13798       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",image->magick);
13799     }
13800   /*
13801     Adjust image dimensions as specified by backdrop or geometry options.
13802   */
13803   width=(int) window_info.width;
13804   height=(int) window_info.height;
13805   if (resources.backdrop != MagickFalse)
13806     {
13807       /*
13808         Center image on window.
13809       */
13810       window_info.x=(window_attributes.width/2)-
13811         (window_info.ximage->width/2);
13812       window_info.y=(window_attributes.height/2)-
13813         (window_info.ximage->height/2);
13814       width=window_attributes.width;
13815       height=window_attributes.height;
13816     }
13817   if ((resources.image_geometry != (char *) NULL) &&
13818       (*resources.image_geometry != '\0'))
13819     {
13820       char
13821         default_geometry[MaxTextExtent];
13822
13823       int
13824         flags,
13825         gravity;
13826
13827       XSizeHints
13828         *size_hints;
13829
13830       /*
13831         User specified geometry.
13832       */
13833       size_hints=XAllocSizeHints();
13834       if (size_hints == (XSizeHints *) NULL)
13835         ThrowXWindowFatalException(ResourceLimitFatalError,
13836           "MemoryAllocationFailed",image->filename);
13837       size_hints->flags=0L;
13838       (void) FormatLocaleString(default_geometry,MaxTextExtent,"%dx%d",
13839         width,height);
13840       flags=XWMGeometry(display,visual_info->screen,resources.image_geometry,
13841         default_geometry,window_info.border_width,size_hints,&window_info.x,
13842         &window_info.y,&width,&height,&gravity);
13843       if (flags & (XValue | YValue))
13844         {
13845           width=window_attributes.width;
13846           height=window_attributes.height;
13847         }
13848       (void) XFree((void *) size_hints);
13849     }
13850   /*
13851     Create the X pixmap.
13852   */
13853   window_info.pixmap=XCreatePixmap(display,window_info.id,(unsigned int) width,
13854     (unsigned int) height,window_info.depth);
13855   if (window_info.pixmap == (Pixmap) NULL)
13856     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXPixmap",
13857       image->filename);
13858   /*
13859     Display pixmap on the window.
13860   */
13861   if (((unsigned int) width > window_info.width) ||
13862       ((unsigned int) height > window_info.height))
13863     (void) XFillRectangle(display,window_info.pixmap,
13864       window_info.annotate_context,0,0,(unsigned int) width,
13865       (unsigned int) height);
13866   (void) XPutImage(display,window_info.pixmap,window_info.annotate_context,
13867     window_info.ximage,0,0,window_info.x,window_info.y,(unsigned int)
13868     window_info.width,(unsigned int) window_info.height);
13869   (void) XSetWindowBackgroundPixmap(display,window_info.id,window_info.pixmap);
13870   (void) XClearWindow(display,window_info.id);
13871   delay=1000*image->delay/MagickMax(image->ticks_per_second,1L);
13872   XDelay(display,delay == 0UL ? 10UL : delay);
13873   (void) XSync(display,MagickFalse);
13874   return(window_info.id == root_window ? MagickTrue : MagickFalse);
13875 }
13876 \f
13877 /*
13878 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13879 %                                                                             %
13880 %                                                                             %
13881 %                                                                             %
13882 +   X D i s p l a y I m a g e                                                 %
13883 %                                                                             %
13884 %                                                                             %
13885 %                                                                             %
13886 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13887 %
13888 %  XDisplayImage() displays an image via X11.  A new image is created and
13889 %  returned if the user interactively transforms the displayed image.
13890 %
13891 %  The format of the XDisplayImage method is:
13892 %
13893 %      Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13894 %        char **argv,int argc,Image **image,size_t *state)
13895 %
13896 %  A description of each parameter follows:
13897 %
13898 %    o nexus:  Method XDisplayImage returns an image when the
13899 %      user chooses 'Open Image' from the command menu or picks a tile
13900 %      from the image directory.  Otherwise a null image is returned.
13901 %
13902 %    o display: Specifies a connection to an X server;  returned from
13903 %      XOpenDisplay.
13904 %
13905 %    o resource_info: Specifies a pointer to a X11 XResourceInfo structure.
13906 %
13907 %    o argv: Specifies the application's argument list.
13908 %
13909 %    o argc: Specifies the number of arguments.
13910 %
13911 %    o image: Specifies an address to an address of an Image structure;
13912 %
13913 */
13914 MagickExport Image *XDisplayImage(Display *display,XResourceInfo *resource_info,
13915   char **argv,int argc,Image **image,size_t *state)
13916 {
13917 #define MagnifySize  256  /* must be a power of 2 */
13918 #define MagickMenus  10
13919 #define MagickTitle  "Commands"
13920
13921   static const char
13922     *CommandMenu[] =
13923     {
13924       "File",
13925       "Edit",
13926       "View",
13927       "Transform",
13928       "Enhance",
13929       "Effects",
13930       "F/X",
13931       "Image Edit",
13932       "Miscellany",
13933       "Help",
13934       (char *) NULL
13935     },
13936     *FileMenu[] =
13937     {
13938       "Open...",
13939       "Next",
13940       "Former",
13941       "Select...",
13942       "Save...",
13943       "Print...",
13944       "Delete...",
13945       "New...",
13946       "Visual Directory...",
13947       "Quit",
13948       (char *) NULL
13949     },
13950     *EditMenu[] =
13951     {
13952       "Undo",
13953       "Redo",
13954       "Cut",
13955       "Copy",
13956       "Paste",
13957       (char *) NULL
13958     },
13959     *ViewMenu[] =
13960     {
13961       "Half Size",
13962       "Original Size",
13963       "Double Size",
13964       "Resize...",
13965       "Apply",
13966       "Refresh",
13967       "Restore",
13968       (char *) NULL
13969     },
13970     *TransformMenu[] =
13971     {
13972       "Crop",
13973       "Chop",
13974       "Flop",
13975       "Flip",
13976       "Rotate Right",
13977       "Rotate Left",
13978       "Rotate...",
13979       "Shear...",
13980       "Roll...",
13981       "Trim Edges",
13982       (char *) NULL
13983     },
13984     *EnhanceMenu[] =
13985     {
13986       "Hue...",
13987       "Saturation...",
13988       "Brightness...",
13989       "Gamma...",
13990       "Spiff",
13991       "Dull",
13992       "Contrast Stretch...",
13993       "Sigmoidal Contrast...",
13994       "Normalize",
13995       "Equalize",
13996       "Negate",
13997       "Grayscale",
13998       "Map...",
13999       "Quantize...",
14000       (char *) NULL
14001     },
14002     *EffectsMenu[] =
14003     {
14004       "Despeckle",
14005       "Emboss",
14006       "Reduce Noise",
14007       "Add Noise...",
14008       "Sharpen...",
14009       "Blur...",
14010       "Threshold...",
14011       "Edge Detect...",
14012       "Spread...",
14013       "Shade...",
14014       "Raise...",
14015       "Segment...",
14016       (char *) NULL
14017     },
14018     *FXMenu[] =
14019     {
14020       "Solarize...",
14021       "Sepia Tone...",
14022       "Swirl...",
14023       "Implode...",
14024       "Vignette...",
14025       "Wave...",
14026       "Oil Paint...",
14027       "Charcoal Draw...",
14028       (char *) NULL
14029     },
14030     *ImageEditMenu[] =
14031     {
14032       "Annotate...",
14033       "Draw...",
14034       "Color...",
14035       "Matte...",
14036       "Composite...",
14037       "Add Border...",
14038       "Add Frame...",
14039       "Comment...",
14040       "Launch...",
14041       "Region of Interest...",
14042       (char *) NULL
14043     },
14044     *MiscellanyMenu[] =
14045     {
14046       "Image Info",
14047       "Zoom Image",
14048       "Show Preview...",
14049       "Show Histogram",
14050       "Show Matte",
14051       "Background...",
14052       "Slide Show...",
14053       "Preferences...",
14054       (char *) NULL
14055     },
14056     *HelpMenu[] =
14057     {
14058       "Overview",
14059       "Browse Documentation",
14060       "About Display",
14061       (char *) NULL
14062     },
14063     *ShortCutsMenu[] =
14064     {
14065       "Next",
14066       "Former",
14067       "Open...",
14068       "Save...",
14069       "Print...",
14070       "Undo",
14071       "Restore",
14072       "Image Info",
14073       "Quit",
14074       (char *) NULL
14075     },
14076     *VirtualMenu[] =
14077     {
14078       "Image Info",
14079       "Print",
14080       "Next",
14081       "Quit",
14082       (char *) NULL
14083     };
14084
14085   static const char
14086     **Menus[MagickMenus] =
14087     {
14088       FileMenu,
14089       EditMenu,
14090       ViewMenu,
14091       TransformMenu,
14092       EnhanceMenu,
14093       EffectsMenu,
14094       FXMenu,
14095       ImageEditMenu,
14096       MiscellanyMenu,
14097       HelpMenu
14098     };
14099
14100   static CommandType
14101     CommandMenus[] =
14102     {
14103       NullCommand,
14104       NullCommand,
14105       NullCommand,
14106       NullCommand,
14107       NullCommand,
14108       NullCommand,
14109       NullCommand,
14110       NullCommand,
14111       NullCommand,
14112       NullCommand,
14113     },
14114     FileCommands[] =
14115     {
14116       OpenCommand,
14117       NextCommand,
14118       FormerCommand,
14119       SelectCommand,
14120       SaveCommand,
14121       PrintCommand,
14122       DeleteCommand,
14123       NewCommand,
14124       VisualDirectoryCommand,
14125       QuitCommand
14126     },
14127     EditCommands[] =
14128     {
14129       UndoCommand,
14130       RedoCommand,
14131       CutCommand,
14132       CopyCommand,
14133       PasteCommand
14134     },
14135     ViewCommands[] =
14136     {
14137       HalfSizeCommand,
14138       OriginalSizeCommand,
14139       DoubleSizeCommand,
14140       ResizeCommand,
14141       ApplyCommand,
14142       RefreshCommand,
14143       RestoreCommand
14144     },
14145     TransformCommands[] =
14146     {
14147       CropCommand,
14148       ChopCommand,
14149       FlopCommand,
14150       FlipCommand,
14151       RotateRightCommand,
14152       RotateLeftCommand,
14153       RotateCommand,
14154       ShearCommand,
14155       RollCommand,
14156       TrimCommand
14157     },
14158     EnhanceCommands[] =
14159     {
14160       HueCommand,
14161       SaturationCommand,
14162       BrightnessCommand,
14163       GammaCommand,
14164       SpiffCommand,
14165       DullCommand,
14166       ContrastStretchCommand,
14167       SigmoidalContrastCommand,
14168       NormalizeCommand,
14169       EqualizeCommand,
14170       NegateCommand,
14171       GrayscaleCommand,
14172       MapCommand,
14173       QuantizeCommand
14174     },
14175     EffectsCommands[] =
14176     {
14177       DespeckleCommand,
14178       EmbossCommand,
14179       ReduceNoiseCommand,
14180       AddNoiseCommand,
14181       SharpenCommand,
14182       BlurCommand,
14183       ThresholdCommand,
14184       EdgeDetectCommand,
14185       SpreadCommand,
14186       ShadeCommand,
14187       RaiseCommand,
14188       SegmentCommand
14189     },
14190     FXCommands[] =
14191     {
14192       SolarizeCommand,
14193       SepiaToneCommand,
14194       SwirlCommand,
14195       ImplodeCommand,
14196       VignetteCommand,
14197       WaveCommand,
14198       OilPaintCommand,
14199       CharcoalDrawCommand
14200     },
14201     ImageEditCommands[] =
14202     {
14203       AnnotateCommand,
14204       DrawCommand,
14205       ColorCommand,
14206       MatteCommand,
14207       CompositeCommand,
14208       AddBorderCommand,
14209       AddFrameCommand,
14210       CommentCommand,
14211       LaunchCommand,
14212       RegionofInterestCommand
14213     },
14214     MiscellanyCommands[] =
14215     {
14216       InfoCommand,
14217       ZoomCommand,
14218       ShowPreviewCommand,
14219       ShowHistogramCommand,
14220       ShowMatteCommand,
14221       BackgroundCommand,
14222       SlideShowCommand,
14223       PreferencesCommand
14224     },
14225     HelpCommands[] =
14226     {
14227       HelpCommand,
14228       BrowseDocumentationCommand,
14229       VersionCommand
14230     },
14231     ShortCutsCommands[] =
14232     {
14233       NextCommand,
14234       FormerCommand,
14235       OpenCommand,
14236       SaveCommand,
14237       PrintCommand,
14238       UndoCommand,
14239       RestoreCommand,
14240       InfoCommand,
14241       QuitCommand
14242     },
14243     VirtualCommands[] =
14244     {
14245       InfoCommand,
14246       PrintCommand,
14247       NextCommand,
14248       QuitCommand
14249     };
14250
14251   static CommandType
14252     *Commands[MagickMenus] =
14253     {
14254       FileCommands,
14255       EditCommands,
14256       ViewCommands,
14257       TransformCommands,
14258       EnhanceCommands,
14259       EffectsCommands,
14260       FXCommands,
14261       ImageEditCommands,
14262       MiscellanyCommands,
14263       HelpCommands
14264     };
14265
14266   char
14267     command[MaxTextExtent],
14268     *directory,
14269     geometry[MaxTextExtent],
14270     resource_name[MaxTextExtent];
14271
14272   CommandType
14273     command_type;
14274
14275   Image
14276     *display_image,
14277     *nexus;
14278
14279   int
14280     entry,
14281     id;
14282
14283   KeySym
14284     key_symbol;
14285
14286   MagickStatusType
14287     context_mask,
14288     status;
14289
14290   RectangleInfo
14291     geometry_info;
14292
14293   register int
14294     i;
14295
14296   static char
14297     working_directory[MaxTextExtent];
14298
14299   static XPoint
14300     vid_info;
14301
14302   static XWindowInfo
14303     *magick_windows[MaxXWindows];
14304
14305   static unsigned int
14306     number_windows;
14307
14308   struct stat
14309     attributes;
14310
14311   time_t
14312     timer,
14313     timestamp,
14314     update_time;
14315
14316   unsigned int
14317     height,
14318     width;
14319
14320   size_t
14321     delay;
14322
14323   WarningHandler
14324     warning_handler;
14325
14326   Window
14327     root_window;
14328
14329   XClassHint
14330     *class_hints;
14331
14332   XEvent
14333     event;
14334
14335   XFontStruct
14336     *font_info;
14337
14338   XGCValues
14339     context_values;
14340
14341   XPixelInfo
14342     *icon_pixel,
14343     *pixel;
14344
14345   XResourceInfo
14346     *icon_resources;
14347
14348   XStandardColormap
14349     *icon_map,
14350     *map_info;
14351
14352   XVisualInfo
14353     *icon_visual,
14354     *visual_info;
14355
14356   XWindowChanges
14357     window_changes;
14358
14359   XWindows
14360     *windows;
14361
14362   XWMHints
14363     *manager_hints;
14364
14365   assert(image != (Image **) NULL);
14366   assert((*image)->signature == MagickSignature);
14367   if ((*image)->debug != MagickFalse)
14368     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
14369   display_image=(*image);
14370   warning_handler=(WarningHandler) NULL;
14371   windows=XSetWindows((XWindows *) ~0);
14372   if (windows != (XWindows *) NULL)
14373     {
14374       int
14375         status;
14376
14377       status=chdir(working_directory);
14378       if (status == -1)
14379         (void) ThrowMagickException(&(*image)->exception,GetMagickModule(),
14380           FileOpenError,"UnableToOpenFile","%s",working_directory);
14381       warning_handler=resource_info->display_warnings ?
14382         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14383       warning_handler=resource_info->display_warnings ?
14384         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14385     }
14386   else
14387     {
14388       /*
14389         Allocate windows structure.
14390       */
14391       resource_info->colors=display_image->colors;
14392       windows=XSetWindows(XInitializeWindows(display,resource_info));
14393       if (windows == (XWindows *) NULL)
14394         ThrowXWindowFatalException(XServerFatalError,"UnableToCreateWindow",
14395           (*image)->filename);
14396       /*
14397         Initialize window id's.
14398       */
14399       number_windows=0;
14400       magick_windows[number_windows++]=(&windows->icon);
14401       magick_windows[number_windows++]=(&windows->backdrop);
14402       magick_windows[number_windows++]=(&windows->image);
14403       magick_windows[number_windows++]=(&windows->info);
14404       magick_windows[number_windows++]=(&windows->command);
14405       magick_windows[number_windows++]=(&windows->widget);
14406       magick_windows[number_windows++]=(&windows->popup);
14407       magick_windows[number_windows++]=(&windows->magnify);
14408       magick_windows[number_windows++]=(&windows->pan);
14409       for (i=0; i < (int) number_windows; i++)
14410         magick_windows[i]->id=(Window) NULL;
14411       vid_info.x=0;
14412       vid_info.y=0;
14413     }
14414   /*
14415     Initialize font info.
14416   */
14417   if (windows->font_info != (XFontStruct *) NULL)
14418     (void) XFreeFont(display,windows->font_info);
14419   windows->font_info=XBestFont(display,resource_info,MagickFalse);
14420   if (windows->font_info == (XFontStruct *) NULL)
14421     ThrowXWindowFatalException(XServerFatalError,"UnableToLoadFont",
14422       resource_info->font);
14423   /*
14424     Initialize Standard Colormap.
14425   */
14426   map_info=windows->map_info;
14427   icon_map=windows->icon_map;
14428   visual_info=windows->visual_info;
14429   icon_visual=windows->icon_visual;
14430   pixel=windows->pixel_info;
14431   icon_pixel=windows->icon_pixel;
14432   font_info=windows->font_info;
14433   icon_resources=windows->icon_resources;
14434   class_hints=windows->class_hints;
14435   manager_hints=windows->manager_hints;
14436   root_window=XRootWindow(display,visual_info->screen);
14437   nexus=NewImageList();
14438   if (display_image->debug != MagickFalse)
14439     {
14440       (void) LogMagickEvent(X11Event,GetMagickModule(),
14441         "Image: %s[%.20g] %.20gx%.20g ",display_image->filename,
14442         (double) display_image->scene,(double) display_image->columns,
14443         (double) display_image->rows);
14444       if (display_image->colors != 0)
14445         (void) LogMagickEvent(X11Event,GetMagickModule(),"%.20gc ",(double)
14446           display_image->colors);
14447       (void) LogMagickEvent(X11Event,GetMagickModule(),"%s",
14448         display_image->magick);
14449     }
14450   XMakeStandardColormap(display,visual_info,resource_info,display_image,
14451     map_info,pixel);
14452   display_image->taint=MagickFalse;
14453   /*
14454     Initialize graphic context.
14455   */
14456   windows->context.id=(Window) NULL;
14457   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14458     resource_info,&windows->context);
14459   (void) CloneString(&class_hints->res_name,resource_info->client_name);
14460   (void) CloneString(&class_hints->res_class,resource_info->client_name);
14461   class_hints->res_class[0]=(char) toupper((int) class_hints->res_class[0]);
14462   manager_hints->flags=InputHint | StateHint;
14463   manager_hints->input=MagickFalse;
14464   manager_hints->initial_state=WithdrawnState;
14465   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14466     &windows->context);
14467   if (display_image->debug != MagickFalse)
14468     (void) LogMagickEvent(X11Event,GetMagickModule(),
14469       "Window id: 0x%lx (context)",windows->context.id);
14470   context_values.background=pixel->background_color.pixel;
14471   context_values.font=font_info->fid;
14472   context_values.foreground=pixel->foreground_color.pixel;
14473   context_values.graphics_exposures=MagickFalse;
14474   context_mask=(MagickStatusType)
14475     (GCBackground | GCFont | GCForeground | GCGraphicsExposures);
14476   if (pixel->annotate_context != (GC) NULL)
14477     (void) XFreeGC(display,pixel->annotate_context);
14478   pixel->annotate_context=XCreateGC(display,windows->context.id,
14479     context_mask,&context_values);
14480   if (pixel->annotate_context == (GC) NULL)
14481     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14482       display_image->filename);
14483   context_values.background=pixel->depth_color.pixel;
14484   if (pixel->widget_context != (GC) NULL)
14485     (void) XFreeGC(display,pixel->widget_context);
14486   pixel->widget_context=XCreateGC(display,windows->context.id,context_mask,
14487     &context_values);
14488   if (pixel->widget_context == (GC) NULL)
14489     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14490       display_image->filename);
14491   context_values.background=pixel->foreground_color.pixel;
14492   context_values.foreground=pixel->background_color.pixel;
14493   context_values.plane_mask=context_values.background ^
14494     context_values.foreground;
14495   if (pixel->highlight_context != (GC) NULL)
14496     (void) XFreeGC(display,pixel->highlight_context);
14497   pixel->highlight_context=XCreateGC(display,windows->context.id,
14498     (size_t) (context_mask | GCPlaneMask),&context_values);
14499   if (pixel->highlight_context == (GC) NULL)
14500     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14501       display_image->filename);
14502   (void) XDestroyWindow(display,windows->context.id);
14503   /*
14504     Initialize icon window.
14505   */
14506   XGetWindowInfo(display,icon_visual,icon_map,icon_pixel,(XFontStruct *) NULL,
14507     icon_resources,&windows->icon);
14508   windows->icon.geometry=resource_info->icon_geometry;
14509   XBestIconSize(display,&windows->icon,display_image);
14510   windows->icon.attributes.colormap=XDefaultColormap(display,
14511     icon_visual->screen);
14512   windows->icon.attributes.event_mask=ExposureMask | StructureNotifyMask;
14513   manager_hints->flags=InputHint | StateHint;
14514   manager_hints->input=MagickFalse;
14515   manager_hints->initial_state=IconicState;
14516   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14517     &windows->icon);
14518   if (display_image->debug != MagickFalse)
14519     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (icon)",
14520       windows->icon.id);
14521   /*
14522     Initialize graphic context for icon window.
14523   */
14524   if (icon_pixel->annotate_context != (GC) NULL)
14525     (void) XFreeGC(display,icon_pixel->annotate_context);
14526   context_values.background=icon_pixel->background_color.pixel;
14527   context_values.foreground=icon_pixel->foreground_color.pixel;
14528   icon_pixel->annotate_context=XCreateGC(display,windows->icon.id,
14529     (size_t) (GCBackground | GCForeground),&context_values);
14530   if (icon_pixel->annotate_context == (GC) NULL)
14531     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateGraphicContext",
14532       display_image->filename);
14533   windows->icon.annotate_context=icon_pixel->annotate_context;
14534   /*
14535     Initialize Image window.
14536   */
14537   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14538     &windows->image);
14539   windows->image.shape=MagickTrue;  /* non-rectangular shape hint */
14540   if (resource_info->use_shared_memory == MagickFalse)
14541     windows->image.shared_memory=MagickFalse;
14542   if ((resource_info->title != (char *) NULL) && !(*state & MontageImageState))
14543     {
14544       char
14545         *title;
14546
14547       title=InterpretImageProperties(resource_info->image_info,display_image,
14548         resource_info->title);
14549       (void) CopyMagickString(windows->image.name,title,MaxTextExtent);
14550       (void) CopyMagickString(windows->image.icon_name,title,MaxTextExtent);
14551       title=DestroyString(title);
14552     }
14553   else
14554     {
14555       char
14556         filename[MaxTextExtent];
14557
14558       /*
14559         Window name is the base of the filename.
14560       */
14561       GetPathComponent(display_image->magick_filename,TailPath,filename);
14562       if (GetImageListLength(display_image) == 1)
14563         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14564           "%s: %s",MagickPackageName,filename);
14565       else
14566         (void) FormatLocaleString(windows->image.name,MaxTextExtent,
14567           "%s: %s[scene: %.20g frames: %.20g]",MagickPackageName,filename,
14568           (double) display_image->scene,(double) GetImageListLength(
14569           display_image));
14570       (void) CopyMagickString(windows->image.icon_name,filename,MaxTextExtent);
14571     }
14572   if (resource_info->immutable)
14573     windows->image.immutable=MagickTrue;
14574   windows->image.use_pixmap=resource_info->use_pixmap;
14575   windows->image.geometry=resource_info->image_geometry;
14576   (void) FormatLocaleString(geometry,MaxTextExtent,"%ux%u+0+0>!",
14577     XDisplayWidth(display,visual_info->screen),
14578     XDisplayHeight(display,visual_info->screen));
14579   geometry_info.width=display_image->columns;
14580   geometry_info.height=display_image->rows;
14581   geometry_info.x=0;
14582   geometry_info.y=0;
14583   (void) ParseMetaGeometry(geometry,&geometry_info.x,&geometry_info.y,
14584     &geometry_info.width,&geometry_info.height);
14585   windows->image.width=(unsigned int) geometry_info.width;
14586   windows->image.height=(unsigned int) geometry_info.height;
14587   windows->image.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14588     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14589     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14590     PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask;
14591   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14592     resource_info,&windows->backdrop);
14593   if ((resource_info->backdrop) || (windows->backdrop.id != (Window) NULL))
14594     {
14595       /*
14596         Initialize backdrop window.
14597       */
14598       windows->backdrop.x=0;
14599       windows->backdrop.y=0;
14600       (void) CloneString(&windows->backdrop.name,"Backdrop");
14601       windows->backdrop.flags=(size_t) (USSize | USPosition);
14602       windows->backdrop.width=(unsigned int)
14603         XDisplayWidth(display,visual_info->screen);
14604       windows->backdrop.height=(unsigned int)
14605         XDisplayHeight(display,visual_info->screen);
14606       windows->backdrop.border_width=0;
14607       windows->backdrop.immutable=MagickTrue;
14608       windows->backdrop.attributes.do_not_propagate_mask=ButtonPressMask |
14609         ButtonReleaseMask;
14610       windows->backdrop.attributes.event_mask=ButtonPressMask | KeyPressMask |
14611         StructureNotifyMask;
14612       manager_hints->flags=IconWindowHint | InputHint | StateHint;
14613       manager_hints->icon_window=windows->icon.id;
14614       manager_hints->input=MagickTrue;
14615       manager_hints->initial_state=resource_info->iconic ? IconicState :
14616         NormalState;
14617       XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14618         &windows->backdrop);
14619       if (display_image->debug != MagickFalse)
14620         (void) LogMagickEvent(X11Event,GetMagickModule(),
14621           "Window id: 0x%lx (backdrop)",windows->backdrop.id);
14622       (void) XMapWindow(display,windows->backdrop.id);
14623       (void) XClearWindow(display,windows->backdrop.id);
14624       if (windows->image.id != (Window) NULL)
14625         {
14626           (void) XDestroyWindow(display,windows->image.id);
14627           windows->image.id=(Window) NULL;
14628         }
14629       /*
14630         Position image in the center the backdrop.
14631       */
14632       windows->image.flags|=USPosition;
14633       windows->image.x=(XDisplayWidth(display,visual_info->screen)/2)-
14634         (windows->image.width/2);
14635       windows->image.y=(XDisplayHeight(display,visual_info->screen)/2)-
14636         (windows->image.height/2);
14637     }
14638   manager_hints->flags=IconWindowHint | InputHint | StateHint;
14639   manager_hints->icon_window=windows->icon.id;
14640   manager_hints->input=MagickTrue;
14641   manager_hints->initial_state=resource_info->iconic ? IconicState :
14642     NormalState;
14643   if (windows->group_leader.id != (Window) NULL)
14644     {
14645       /*
14646         Follow the leader.
14647       */
14648       manager_hints->flags|=WindowGroupHint;
14649       manager_hints->window_group=windows->group_leader.id;
14650       (void) XSelectInput(display,windows->group_leader.id,StructureNotifyMask);
14651       if (display_image->debug != MagickFalse)
14652         (void) LogMagickEvent(X11Event,GetMagickModule(),
14653           "Window id: 0x%lx (group leader)",windows->group_leader.id);
14654     }
14655   XMakeWindow(display,
14656     (Window) (resource_info->backdrop ? windows->backdrop.id : root_window),
14657     argv,argc,class_hints,manager_hints,&windows->image);
14658   (void) XChangeProperty(display,windows->image.id,windows->im_protocols,
14659     XA_STRING,8,PropModeReplace,(unsigned char *) NULL,0);
14660   if (windows->group_leader.id != (Window) NULL)
14661     (void) XSetTransientForHint(display,windows->image.id,
14662       windows->group_leader.id);
14663   if (display_image->debug != MagickFalse)
14664     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (image)",
14665       windows->image.id);
14666   /*
14667     Initialize Info widget.
14668   */
14669   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,resource_info,
14670     &windows->info);
14671   (void) CloneString(&windows->info.name,"Info");
14672   (void) CloneString(&windows->info.icon_name,"Info");
14673   windows->info.border_width=1;
14674   windows->info.x=2;
14675   windows->info.y=2;
14676   windows->info.flags|=PPosition;
14677   windows->info.attributes.win_gravity=UnmapGravity;
14678   windows->info.attributes.event_mask=ButtonPressMask | ExposureMask |
14679     StructureNotifyMask;
14680   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14681   manager_hints->input=MagickFalse;
14682   manager_hints->initial_state=NormalState;
14683   manager_hints->window_group=windows->image.id;
14684   XMakeWindow(display,windows->image.id,argv,argc,class_hints,manager_hints,
14685     &windows->info);
14686   windows->info.highlight_stipple=XCreateBitmapFromData(display,
14687     windows->info.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14688   windows->info.shadow_stipple=XCreateBitmapFromData(display,
14689     windows->info.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14690   (void) XSetTransientForHint(display,windows->info.id,windows->image.id);
14691   if (windows->image.mapped != MagickFalse)
14692     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14693   if (display_image->debug != MagickFalse)
14694     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (info)",
14695       windows->info.id);
14696   /*
14697     Initialize Command widget.
14698   */
14699   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14700     resource_info,&windows->command);
14701   windows->command.data=MagickMenus;
14702   (void) XCommandWidget(display,windows,CommandMenu,(XEvent *) NULL);
14703   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.command",
14704     resource_info->client_name);
14705   windows->command.geometry=XGetResourceClass(resource_info->resource_database,
14706     resource_name,"geometry",(char *) NULL);
14707   (void) CloneString(&windows->command.name,MagickTitle);
14708   windows->command.border_width=0;
14709   windows->command.flags|=PPosition;
14710   windows->command.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14711     ButtonReleaseMask | EnterWindowMask | ExposureMask | LeaveWindowMask |
14712     OwnerGrabButtonMask | StructureNotifyMask;
14713   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14714   manager_hints->input=MagickTrue;
14715   manager_hints->initial_state=NormalState;
14716   manager_hints->window_group=windows->image.id;
14717   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14718     &windows->command);
14719   windows->command.highlight_stipple=XCreateBitmapFromData(display,
14720     windows->command.id,(char *) HighlightBitmap,HighlightWidth,
14721     HighlightHeight);
14722   windows->command.shadow_stipple=XCreateBitmapFromData(display,
14723     windows->command.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14724   (void) XSetTransientForHint(display,windows->command.id,windows->image.id);
14725   if (windows->command.mapped != MagickFalse)
14726     (void) XMapRaised(display,windows->command.id);
14727   if (display_image->debug != MagickFalse)
14728     (void) LogMagickEvent(X11Event,GetMagickModule(),
14729       "Window id: 0x%lx (command)",windows->command.id);
14730   /*
14731     Initialize Widget window.
14732   */
14733   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14734     resource_info,&windows->widget);
14735   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.widget",
14736     resource_info->client_name);
14737   windows->widget.geometry=XGetResourceClass(resource_info->resource_database,
14738     resource_name,"geometry",(char *) NULL);
14739   windows->widget.border_width=0;
14740   windows->widget.flags|=PPosition;
14741   windows->widget.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14742     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14743     KeyReleaseMask | LeaveWindowMask | OwnerGrabButtonMask |
14744     StructureNotifyMask;
14745   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14746   manager_hints->input=MagickTrue;
14747   manager_hints->initial_state=NormalState;
14748   manager_hints->window_group=windows->image.id;
14749   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14750     &windows->widget);
14751   windows->widget.highlight_stipple=XCreateBitmapFromData(display,
14752     windows->widget.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14753   windows->widget.shadow_stipple=XCreateBitmapFromData(display,
14754     windows->widget.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14755   (void) XSetTransientForHint(display,windows->widget.id,windows->image.id);
14756   if (display_image->debug != MagickFalse)
14757     (void) LogMagickEvent(X11Event,GetMagickModule(),
14758       "Window id: 0x%lx (widget)",windows->widget.id);
14759   /*
14760     Initialize popup window.
14761   */
14762   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14763     resource_info,&windows->popup);
14764   windows->popup.border_width=0;
14765   windows->popup.flags|=PPosition;
14766   windows->popup.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14767     ButtonReleaseMask | EnterWindowMask | ExposureMask | KeyPressMask |
14768     KeyReleaseMask | LeaveWindowMask | StructureNotifyMask;
14769   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14770   manager_hints->input=MagickTrue;
14771   manager_hints->initial_state=NormalState;
14772   manager_hints->window_group=windows->image.id;
14773   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14774     &windows->popup);
14775   windows->popup.highlight_stipple=XCreateBitmapFromData(display,
14776     windows->popup.id,(char *) HighlightBitmap,HighlightWidth,HighlightHeight);
14777   windows->popup.shadow_stipple=XCreateBitmapFromData(display,
14778     windows->popup.id,(char *) ShadowBitmap,ShadowWidth,ShadowHeight);
14779   (void) XSetTransientForHint(display,windows->popup.id,windows->image.id);
14780   if (display_image->debug != MagickFalse)
14781     (void) LogMagickEvent(X11Event,GetMagickModule(),
14782       "Window id: 0x%lx (pop up)",windows->popup.id);
14783   /*
14784     Initialize Magnify window and cursor.
14785   */
14786   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14787     resource_info,&windows->magnify);
14788   if (resource_info->use_shared_memory == MagickFalse)
14789     windows->magnify.shared_memory=MagickFalse;
14790   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.magnify",
14791     resource_info->client_name);
14792   windows->magnify.geometry=XGetResourceClass(resource_info->resource_database,
14793     resource_name,"geometry",(char *) NULL);
14794   (void) FormatLocaleString(windows->magnify.name,MaxTextExtent,"Magnify %uX",
14795     resource_info->magnify);
14796   if (windows->magnify.cursor != (Cursor) NULL)
14797     (void) XFreeCursor(display,windows->magnify.cursor);
14798   windows->magnify.cursor=XMakeCursor(display,windows->image.id,
14799     map_info->colormap,resource_info->background_color,
14800     resource_info->foreground_color);
14801   if (windows->magnify.cursor == (Cursor) NULL)
14802     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateCursor",
14803       display_image->filename);
14804   windows->magnify.width=MagnifySize;
14805   windows->magnify.height=MagnifySize;
14806   windows->magnify.flags|=PPosition;
14807   windows->magnify.min_width=MagnifySize;
14808   windows->magnify.min_height=MagnifySize;
14809   windows->magnify.width_inc=MagnifySize;
14810   windows->magnify.height_inc=MagnifySize;
14811   windows->magnify.data=resource_info->magnify;
14812   windows->magnify.attributes.cursor=windows->magnify.cursor;
14813   windows->magnify.attributes.event_mask=ButtonPressMask | ButtonReleaseMask |
14814     ExposureMask | KeyPressMask | KeyReleaseMask | OwnerGrabButtonMask |
14815     StructureNotifyMask;
14816   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14817   manager_hints->input=MagickTrue;
14818   manager_hints->initial_state=NormalState;
14819   manager_hints->window_group=windows->image.id;
14820   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14821     &windows->magnify);
14822   if (display_image->debug != MagickFalse)
14823     (void) LogMagickEvent(X11Event,GetMagickModule(),
14824       "Window id: 0x%lx (magnify)",windows->magnify.id);
14825   (void) XSetTransientForHint(display,windows->magnify.id,windows->image.id);
14826   /*
14827     Initialize panning window.
14828   */
14829   XGetWindowInfo(display,visual_info,map_info,pixel,font_info,
14830     resource_info,&windows->pan);
14831   (void) CloneString(&windows->pan.name,"Pan Icon");
14832   windows->pan.width=windows->icon.width;
14833   windows->pan.height=windows->icon.height;
14834   (void) FormatLocaleString(resource_name,MaxTextExtent,"%s.pan",
14835     resource_info->client_name);
14836   windows->pan.geometry=XGetResourceClass(resource_info->resource_database,
14837     resource_name,"geometry",(char *) NULL);
14838   (void) XParseGeometry(windows->pan.geometry,&windows->pan.x,&windows->pan.y,
14839     &windows->pan.width,&windows->pan.height);
14840   windows->pan.flags|=PPosition;
14841   windows->pan.immutable=MagickTrue;
14842   windows->pan.attributes.event_mask=ButtonMotionMask | ButtonPressMask |
14843     ButtonReleaseMask | ExposureMask | KeyPressMask | KeyReleaseMask |
14844     StructureNotifyMask;
14845   manager_hints->flags=InputHint | StateHint | WindowGroupHint;
14846   manager_hints->input=MagickFalse;
14847   manager_hints->initial_state=NormalState;
14848   manager_hints->window_group=windows->image.id;
14849   XMakeWindow(display,root_window,argv,argc,class_hints,manager_hints,
14850     &windows->pan);
14851   if (display_image->debug != MagickFalse)
14852     (void) LogMagickEvent(X11Event,GetMagickModule(),"Window id: 0x%lx (pan)",
14853       windows->pan.id);
14854   (void) XSetTransientForHint(display,windows->pan.id,windows->image.id);
14855   if (windows->info.mapped != MagickFalse)
14856     (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14857   if ((windows->image.mapped == MagickFalse) ||
14858       (windows->backdrop.id != (Window) NULL))
14859     (void) XMapWindow(display,windows->image.id);
14860   /*
14861     Set our progress monitor and warning handlers.
14862   */
14863   if (warning_handler == (WarningHandler) NULL)
14864     {
14865       warning_handler=resource_info->display_warnings ?
14866         SetErrorHandler(XWarning) : SetErrorHandler((ErrorHandler) NULL);
14867       warning_handler=resource_info->display_warnings ?
14868         SetWarningHandler(XWarning) : SetWarningHandler((WarningHandler) NULL);
14869     }
14870   /*
14871     Initialize Image and Magnify X images.
14872   */
14873   windows->image.x=0;
14874   windows->image.y=0;
14875   windows->magnify.shape=MagickFalse;
14876   width=(unsigned int) display_image->columns;
14877   height=(unsigned int) display_image->rows;
14878   if ((display_image->columns != width) || (display_image->rows != height))
14879     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14880       display_image->filename);
14881   status=XMakeImage(display,resource_info,&windows->image,display_image,
14882     width,height);
14883   if (status == MagickFalse)
14884     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14885       display_image->filename);
14886   status=XMakeImage(display,resource_info,&windows->magnify,(Image *) NULL,
14887     windows->magnify.width,windows->magnify.height);
14888   if (status == MagickFalse)
14889     ThrowXWindowFatalException(XServerFatalError,"UnableToCreateXImage",
14890       display_image->filename);
14891   if (windows->magnify.mapped != MagickFalse)
14892     (void) XMapRaised(display,windows->magnify.id);
14893   if (windows->pan.mapped != MagickFalse)
14894     (void) XMapRaised(display,windows->pan.id);
14895   windows->image.window_changes.width=(int) display_image->columns;
14896   windows->image.window_changes.height=(int) display_image->rows;
14897   (void) XConfigureImage(display,resource_info,windows,display_image);
14898   (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
14899   (void) XSync(display,MagickFalse);
14900   /*
14901     Respond to events.
14902   */
14903   delay=display_image->delay/MagickMax(display_image->ticks_per_second,1L);
14904   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14905   update_time=0;
14906   if (resource_info->update != MagickFalse)
14907     {
14908       MagickBooleanType
14909         status;
14910
14911       /*
14912         Determine when file data was last modified.
14913       */
14914       status=GetPathAttributes(display_image->filename,&attributes);
14915       if (status != MagickFalse)
14916         update_time=attributes.st_mtime;
14917     }
14918   *state&=(~FormerImageState);
14919   *state&=(~MontageImageState);
14920   *state&=(~NextImageState);
14921   do
14922   {
14923     /*
14924       Handle a window event.
14925     */
14926     if (windows->image.mapped != MagickFalse)
14927       if ((display_image->delay != 0) || (resource_info->update != 0))
14928         {
14929           if (timer < time((time_t *) NULL))
14930             {
14931               if (resource_info->update == MagickFalse)
14932                 *state|=NextImageState | ExitState;
14933               else
14934                 {
14935                   MagickBooleanType
14936                     status;
14937
14938                   /*
14939                     Determine if image file was modified.
14940                   */
14941                   status=GetPathAttributes(display_image->filename,&attributes);
14942                   if (status != MagickFalse)
14943                     if (update_time != attributes.st_mtime)
14944                       {
14945                         /*
14946                           Redisplay image.
14947                         */
14948                         (void) FormatLocaleString(
14949                           resource_info->image_info->filename,MaxTextExtent,
14950                           "%s:%s",display_image->magick,
14951                           display_image->filename);
14952                         nexus=ReadImage(resource_info->image_info,
14953                           &display_image->exception);
14954                         if (nexus != (Image *) NULL)
14955                           {
14956                             nexus=DestroyImage(nexus);
14957                             *state|=NextImageState | ExitState;
14958                           }
14959                       }
14960                   delay=display_image->delay/MagickMax(
14961                     display_image->ticks_per_second,1L);
14962                   timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
14963                 }
14964             }
14965           if (XEventsQueued(display,QueuedAfterFlush) == 0)
14966             {
14967               /*
14968                 Do not block if delay > 0.
14969               */
14970               XDelay(display,SuspendTime << 2);
14971               continue;
14972             }
14973         }
14974     timestamp=time((time_t *) NULL);
14975     (void) XNextEvent(display,&event);
14976     if (windows->image.stasis == MagickFalse)
14977       windows->image.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14978         MagickTrue : MagickFalse;
14979     if (windows->magnify.stasis == MagickFalse)
14980       windows->magnify.stasis=(time((time_t *) NULL)-timestamp) > 0 ?
14981         MagickTrue : MagickFalse;
14982     if (event.xany.window == windows->command.id)
14983       {
14984         /*
14985           Select a command from the Command widget.
14986         */
14987         id=XCommandWidget(display,windows,CommandMenu,&event);
14988         if (id < 0)
14989           continue;
14990         (void) CopyMagickString(command,CommandMenu[id],MaxTextExtent);
14991         command_type=CommandMenus[id];
14992         if (id < MagickMenus)
14993           {
14994             /*
14995               Select a command from a pop-up menu.
14996             */
14997             entry=XMenuWidget(display,windows,CommandMenu[id],Menus[id],
14998               command);
14999             if (entry < 0)
15000               continue;
15001             (void) CopyMagickString(command,Menus[id][entry],MaxTextExtent);
15002             command_type=Commands[id][entry];
15003           }
15004         if (command_type != NullCommand)
15005           nexus=XMagickCommand(display,resource_info,windows,command_type,
15006             &display_image);
15007         continue;
15008       }
15009     switch (event.type)
15010     {
15011       case ButtonPress:
15012       {
15013         if (display_image->debug != MagickFalse)
15014           (void) LogMagickEvent(X11Event,GetMagickModule(),
15015             "Button Press: 0x%lx %u +%d+%d",event.xbutton.window,
15016             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15017         if ((event.xbutton.button == Button3) &&
15018             (event.xbutton.state & Mod1Mask))
15019           {
15020             /*
15021               Convert Alt-Button3 to Button2.
15022             */
15023             event.xbutton.button=Button2;
15024             event.xbutton.state&=(~Mod1Mask);
15025           }
15026         if (event.xbutton.window == windows->backdrop.id)
15027           {
15028             (void) XSetInputFocus(display,event.xbutton.window,RevertToParent,
15029               event.xbutton.time);
15030             break;
15031           }
15032         if (event.xbutton.window == windows->image.id)
15033           {
15034             switch (event.xbutton.button)
15035             {
15036               case Button1:
15037               {
15038                 if (resource_info->immutable)
15039                   {
15040                     /*
15041                       Select a command from the Virtual menu.
15042                     */
15043                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15044                       command);
15045                     if (entry >= 0)
15046                       nexus=XMagickCommand(display,resource_info,windows,
15047                         VirtualCommands[entry],&display_image);
15048                     break;
15049                   }
15050                 /*
15051                   Map/unmap Command widget.
15052                 */
15053                 if (windows->command.mapped != MagickFalse)
15054                   (void) XWithdrawWindow(display,windows->command.id,
15055                     windows->command.screen);
15056                 else
15057                   {
15058                     (void) XCommandWidget(display,windows,CommandMenu,
15059                       (XEvent *) NULL);
15060                     (void) XMapRaised(display,windows->command.id);
15061                   }
15062                 break;
15063               }
15064               case Button2:
15065               {
15066                 /*
15067                   User pressed the image magnify button.
15068                 */
15069                 (void) XMagickCommand(display,resource_info,windows,ZoomCommand,
15070                   &display_image);
15071                 XMagnifyImage(display,windows,&event);
15072                 break;
15073               }
15074               case Button3:
15075               {
15076                 if (resource_info->immutable)
15077                   {
15078                     /*
15079                       Select a command from the Virtual menu.
15080                     */
15081                     entry=XMenuWidget(display,windows,"Commands",VirtualMenu,
15082                       command);
15083                     if (entry >= 0)
15084                       nexus=XMagickCommand(display,resource_info,windows,
15085                         VirtualCommands[entry],&display_image);
15086                     break;
15087                   }
15088                 if (display_image->montage != (char *) NULL)
15089                   {
15090                     /*
15091                       Open or delete a tile from a visual image directory.
15092                     */
15093                     nexus=XTileImage(display,resource_info,windows,
15094                       display_image,&event);
15095                     if (nexus != (Image *) NULL)
15096                       *state|=MontageImageState | NextImageState | ExitState;
15097                     vid_info.x=(short int) windows->image.x;
15098                     vid_info.y=(short int) windows->image.y;
15099                     break;
15100                   }
15101                 /*
15102                   Select a command from the Short Cuts menu.
15103                 */
15104                 entry=XMenuWidget(display,windows,"Short Cuts",ShortCutsMenu,
15105                   command);
15106                 if (entry >= 0)
15107                   nexus=XMagickCommand(display,resource_info,windows,
15108                     ShortCutsCommands[entry],&display_image);
15109                 break;
15110               }
15111               case Button4:
15112               {
15113                 /*
15114                   Wheel up.
15115                 */
15116                 XTranslateImage(display,windows,*image,XK_Up);
15117                 break;
15118               }
15119               case Button5:
15120               {
15121                 /*
15122                   Wheel down.
15123                 */
15124                 XTranslateImage(display,windows,*image,XK_Down);
15125                 break;
15126               }
15127               default:
15128                 break;
15129             }
15130             break;
15131           }
15132         if (event.xbutton.window == windows->magnify.id)
15133           {
15134             int
15135               factor;
15136
15137             static const char
15138               *MagnifyMenu[] =
15139               {
15140                 "2",
15141                 "4",
15142                 "5",
15143                 "6",
15144                 "7",
15145                 "8",
15146                 "9",
15147                 "3",
15148                 (char *) NULL,
15149               };
15150
15151             static KeySym
15152               MagnifyCommands[] =
15153               {
15154                 XK_2,
15155                 XK_4,
15156                 XK_5,
15157                 XK_6,
15158                 XK_7,
15159                 XK_8,
15160                 XK_9,
15161                 XK_3
15162               };
15163
15164             /*
15165               Select a magnify factor from the pop-up menu.
15166             */
15167             factor=XMenuWidget(display,windows,"Magnify",MagnifyMenu,command);
15168             if (factor >= 0)
15169               XMagnifyWindowCommand(display,windows,0,MagnifyCommands[factor]);
15170             break;
15171           }
15172         if (event.xbutton.window == windows->pan.id)
15173           {
15174             switch (event.xbutton.button)
15175             {
15176               case Button4:
15177               {
15178                 /*
15179                   Wheel up.
15180                 */
15181                 XTranslateImage(display,windows,*image,XK_Up);
15182                 break;
15183               }
15184               case Button5:
15185               {
15186                 /*
15187                   Wheel down.
15188                 */
15189                 XTranslateImage(display,windows,*image,XK_Down);
15190                 break;
15191               }
15192               default:
15193               {
15194                 XPanImage(display,windows,&event);
15195                 break;
15196               }
15197             }
15198             break;
15199           }
15200         delay=display_image->delay/MagickMax(display_image->ticks_per_second,
15201           1L);
15202         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15203         break;
15204       }
15205       case ButtonRelease:
15206       {
15207         if (display_image->debug != MagickFalse)
15208           (void) LogMagickEvent(X11Event,GetMagickModule(),
15209             "Button Release: 0x%lx %u +%d+%d",event.xbutton.window,
15210             event.xbutton.button,event.xbutton.x,event.xbutton.y);
15211         break;
15212       }
15213       case ClientMessage:
15214       {
15215         if (display_image->debug != MagickFalse)
15216           (void) LogMagickEvent(X11Event,GetMagickModule(),
15217             "Client Message: 0x%lx 0x%lx %d 0x%lx",event.xclient.window,
15218             event.xclient.message_type,event.xclient.format,(unsigned long)
15219             event.xclient.data.l[0]);
15220         if (event.xclient.message_type == windows->im_protocols)
15221           {
15222             if (*event.xclient.data.l == (long) windows->im_update_widget)
15223               {
15224                 (void) CloneString(&windows->command.name,MagickTitle);
15225                 windows->command.data=MagickMenus;
15226                 (void) XCommandWidget(display,windows,CommandMenu,
15227                   (XEvent *) NULL);
15228                 break;
15229               }
15230             if (*event.xclient.data.l == (long) windows->im_update_colormap)
15231               {
15232                 /*
15233                   Update graphic context and window colormap.
15234                 */
15235                 for (i=0; i < (int) number_windows; i++)
15236                 {
15237                   if (magick_windows[i]->id == windows->icon.id)
15238                     continue;
15239                   context_values.background=pixel->background_color.pixel;
15240                   context_values.foreground=pixel->foreground_color.pixel;
15241                   (void) XChangeGC(display,magick_windows[i]->annotate_context,
15242                     context_mask,&context_values);
15243                   (void) XChangeGC(display,magick_windows[i]->widget_context,
15244                     context_mask,&context_values);
15245                   context_values.background=pixel->foreground_color.pixel;
15246                   context_values.foreground=pixel->background_color.pixel;
15247                   context_values.plane_mask=context_values.background ^
15248                     context_values.foreground;
15249                   (void) XChangeGC(display,magick_windows[i]->highlight_context,
15250                     (size_t) (context_mask | GCPlaneMask),
15251                     &context_values);
15252                   magick_windows[i]->attributes.background_pixel=
15253                     pixel->background_color.pixel;
15254                   magick_windows[i]->attributes.border_pixel=
15255                     pixel->border_color.pixel;
15256                   magick_windows[i]->attributes.colormap=map_info->colormap;
15257                   (void) XChangeWindowAttributes(display,magick_windows[i]->id,
15258                     (unsigned long) magick_windows[i]->mask,
15259                     &magick_windows[i]->attributes);
15260                 }
15261                 if (windows->pan.mapped != MagickFalse)
15262                   {
15263                     (void) XSetWindowBackgroundPixmap(display,windows->pan.id,
15264                       windows->pan.pixmap);
15265                     (void) XClearWindow(display,windows->pan.id);
15266                     XDrawPanRectangle(display,windows);
15267                   }
15268                 if (windows->backdrop.id != (Window) NULL)
15269                   (void) XInstallColormap(display,map_info->colormap);
15270                 break;
15271               }
15272             if (*event.xclient.data.l == (long) windows->im_former_image)
15273               {
15274                 *state|=FormerImageState | ExitState;
15275                 break;
15276               }
15277             if (*event.xclient.data.l == (long) windows->im_next_image)
15278               {
15279                 *state|=NextImageState | ExitState;
15280                 break;
15281               }
15282             if (*event.xclient.data.l == (long) windows->im_retain_colors)
15283               {
15284                 *state|=RetainColorsState;
15285                 break;
15286               }
15287             if (*event.xclient.data.l == (long) windows->im_exit)
15288               {
15289                 *state|=ExitState;
15290                 break;
15291               }
15292             break;
15293           }
15294         if (event.xclient.message_type == windows->dnd_protocols)
15295           {
15296             Atom
15297               selection,
15298               type;
15299
15300             int
15301               format,
15302               status;
15303
15304             unsigned char
15305               *data;
15306
15307             unsigned long
15308               after,
15309               length;
15310
15311             /*
15312               Display image named by the Drag-and-Drop selection.
15313             */
15314             if ((*event.xclient.data.l != 2) && (*event.xclient.data.l != 128))
15315               break;
15316             selection=XInternAtom(display,"DndSelection",MagickFalse);
15317             status=XGetWindowProperty(display,root_window,selection,0L,(long)
15318               MaxTextExtent,MagickFalse,(Atom) AnyPropertyType,&type,&format,
15319               &length,&after,&data);
15320             if ((status != Success) || (length == 0))
15321               break;
15322             if (*event.xclient.data.l == 2)
15323               {
15324                 /*
15325                   Offix DND.
15326                 */
15327                 (void) CopyMagickString(resource_info->image_info->filename,
15328                   (char *) data,MaxTextExtent);
15329               }
15330             else
15331               {
15332                 /*
15333                   XDND.
15334                 */
15335                 if (strncmp((char *) data, "file:", 5) != 0)
15336                   {
15337                     (void) XFree((void *) data);
15338                     break;
15339                   }
15340                 (void) CopyMagickString(resource_info->image_info->filename,
15341                   ((char *) data)+5,MaxTextExtent);
15342               }
15343             nexus=ReadImage(resource_info->image_info,
15344               &display_image->exception);
15345             CatchException(&display_image->exception);
15346             if (nexus != (Image *) NULL)
15347               *state|=NextImageState | ExitState;
15348             (void) XFree((void *) data);
15349             break;
15350           }
15351         /*
15352           If client window delete message, exit.
15353         */
15354         if (event.xclient.message_type != windows->wm_protocols)
15355           break;
15356         if (*event.xclient.data.l != (long) windows->wm_delete_window)
15357           break;
15358         (void) XWithdrawWindow(display,event.xclient.window,
15359           visual_info->screen);
15360         if (event.xclient.window == windows->image.id)
15361           {
15362             *state|=ExitState;
15363             break;
15364           }
15365         if (event.xclient.window == windows->pan.id)
15366           {
15367             /*
15368               Restore original image size when pan window is deleted.
15369             */
15370             windows->image.window_changes.width=windows->image.ximage->width;
15371             windows->image.window_changes.height=windows->image.ximage->height;
15372             (void) XConfigureImage(display,resource_info,windows,
15373               display_image);
15374           }
15375         break;
15376       }
15377       case ConfigureNotify:
15378       {
15379         if (display_image->debug != MagickFalse)
15380           (void) LogMagickEvent(X11Event,GetMagickModule(),
15381             "Configure Notify: 0x%lx %dx%d+%d+%d %d",event.xconfigure.window,
15382             event.xconfigure.width,event.xconfigure.height,event.xconfigure.x,
15383             event.xconfigure.y,event.xconfigure.send_event);
15384         if (event.xconfigure.window == windows->image.id)
15385           {
15386             /*
15387               Image window has a new configuration.
15388             */
15389             if (event.xconfigure.send_event != 0)
15390               {
15391                 XWindowChanges
15392                   window_changes;
15393
15394                 /*
15395                   Position the transient windows relative of the Image window.
15396                 */
15397                 if (windows->command.geometry == (char *) NULL)
15398                   if (windows->command.mapped == MagickFalse)
15399                     {
15400                       windows->command.x=event.xconfigure.x-
15401                         windows->command.width-25;
15402                       windows->command.y=event.xconfigure.y;
15403                       XConstrainWindowPosition(display,&windows->command);
15404                       window_changes.x=windows->command.x;
15405                       window_changes.y=windows->command.y;
15406                       (void) XReconfigureWMWindow(display,windows->command.id,
15407                         windows->command.screen,(unsigned int) (CWX | CWY),
15408                         &window_changes);
15409                     }
15410                 if (windows->widget.geometry == (char *) NULL)
15411                   if (windows->widget.mapped == MagickFalse)
15412                     {
15413                       windows->widget.x=event.xconfigure.x+
15414                         event.xconfigure.width/10;
15415                       windows->widget.y=event.xconfigure.y+
15416                         event.xconfigure.height/10;
15417                       XConstrainWindowPosition(display,&windows->widget);
15418                       window_changes.x=windows->widget.x;
15419                       window_changes.y=windows->widget.y;
15420                       (void) XReconfigureWMWindow(display,windows->widget.id,
15421                         windows->widget.screen,(unsigned int) (CWX | CWY),
15422                         &window_changes);
15423                     }
15424                 if (windows->magnify.geometry == (char *) NULL)
15425                   if (windows->magnify.mapped == MagickFalse)
15426                     {
15427                       windows->magnify.x=event.xconfigure.x+
15428                         event.xconfigure.width+25;
15429                       windows->magnify.y=event.xconfigure.y;
15430                       XConstrainWindowPosition(display,&windows->magnify);
15431                       window_changes.x=windows->magnify.x;
15432                       window_changes.y=windows->magnify.y;
15433                       (void) XReconfigureWMWindow(display,windows->magnify.id,
15434                         windows->magnify.screen,(unsigned int) (CWX | CWY),
15435                         &window_changes);
15436                     }
15437                 if (windows->pan.geometry == (char *) NULL)
15438                   if (windows->pan.mapped == MagickFalse)
15439                     {
15440                       windows->pan.x=event.xconfigure.x+
15441                         event.xconfigure.width+25;
15442                       windows->pan.y=event.xconfigure.y+
15443                         windows->magnify.height+50;
15444                       XConstrainWindowPosition(display,&windows->pan);
15445                       window_changes.x=windows->pan.x;
15446                       window_changes.y=windows->pan.y;
15447                       (void) XReconfigureWMWindow(display,windows->pan.id,
15448                         windows->pan.screen,(unsigned int) (CWX | CWY),
15449                         &window_changes);
15450                     }
15451               }
15452             if ((event.xconfigure.width == (int) windows->image.width) &&
15453                 (event.xconfigure.height == (int) windows->image.height))
15454               break;
15455             windows->image.width=(unsigned int) event.xconfigure.width;
15456             windows->image.height=(unsigned int) event.xconfigure.height;
15457             windows->image.x=0;
15458             windows->image.y=0;
15459             if (display_image->montage != (char *) NULL)
15460               {
15461                 windows->image.x=vid_info.x;
15462                 windows->image.y=vid_info.y;
15463               }
15464             if ((windows->image.mapped != MagickFalse) &&
15465                 (windows->image.stasis != MagickFalse))
15466               {
15467                 /*
15468                   Update image window configuration.
15469                 */
15470                 windows->image.window_changes.width=event.xconfigure.width;
15471                 windows->image.window_changes.height=event.xconfigure.height;
15472                 (void) XConfigureImage(display,resource_info,windows,
15473                   display_image);
15474               }
15475             /*
15476               Update pan window configuration.
15477             */
15478             if ((event.xconfigure.width < windows->image.ximage->width) ||
15479                 (event.xconfigure.height < windows->image.ximage->height))
15480               {
15481                 (void) XMapRaised(display,windows->pan.id);
15482                 XDrawPanRectangle(display,windows);
15483               }
15484             else
15485               if (windows->pan.mapped != MagickFalse)
15486                 (void) XWithdrawWindow(display,windows->pan.id,
15487                   windows->pan.screen);
15488             break;
15489           }
15490         if (event.xconfigure.window == windows->magnify.id)
15491           {
15492             unsigned int
15493               magnify;
15494
15495             /*
15496               Magnify window has a new configuration.
15497             */
15498             windows->magnify.width=(unsigned int) event.xconfigure.width;
15499             windows->magnify.height=(unsigned int) event.xconfigure.height;
15500             if (windows->magnify.mapped == MagickFalse)
15501               break;
15502             magnify=1;
15503             while ((int) magnify <= event.xconfigure.width)
15504               magnify<<=1;
15505             while ((int) magnify <= event.xconfigure.height)
15506               magnify<<=1;
15507             magnify>>=1;
15508             if (((int) magnify != event.xconfigure.width) ||
15509                 ((int) magnify != event.xconfigure.height))
15510               {
15511                 window_changes.width=(int) magnify;
15512                 window_changes.height=(int) magnify;
15513                 (void) XReconfigureWMWindow(display,windows->magnify.id,
15514                   windows->magnify.screen,(unsigned int) (CWWidth | CWHeight),
15515                   &window_changes);
15516                 break;
15517               }
15518             if ((windows->magnify.mapped != MagickFalse) &&
15519                 (windows->magnify.stasis != MagickFalse))
15520               {
15521                 status=XMakeImage(display,resource_info,&windows->magnify,
15522                   display_image,windows->magnify.width,windows->magnify.height);
15523                 XMakeMagnifyImage(display,windows);
15524               }
15525             break;
15526           }
15527         if ((windows->magnify.mapped != MagickFalse) &&
15528             (event.xconfigure.window == windows->pan.id))
15529           {
15530             /*
15531               Pan icon window has a new configuration.
15532             */
15533             if (event.xconfigure.send_event != 0)
15534               {
15535                 windows->pan.x=event.xconfigure.x;
15536                 windows->pan.y=event.xconfigure.y;
15537               }
15538             windows->pan.width=(unsigned int) event.xconfigure.width;
15539             windows->pan.height=(unsigned int) event.xconfigure.height;
15540             break;
15541           }
15542         if (event.xconfigure.window == windows->icon.id)
15543           {
15544             /*
15545               Icon window has a new configuration.
15546             */
15547             windows->icon.width=(unsigned int) event.xconfigure.width;
15548             windows->icon.height=(unsigned int) event.xconfigure.height;
15549             break;
15550           }
15551         break;
15552       }
15553       case DestroyNotify:
15554       {
15555         /*
15556           Group leader has exited.
15557         */
15558         if (display_image->debug != MagickFalse)
15559           (void) LogMagickEvent(X11Event,GetMagickModule(),
15560             "Destroy Notify: 0x%lx",event.xdestroywindow.window);
15561         if (event.xdestroywindow.window == windows->group_leader.id)
15562           {
15563             *state|=ExitState;
15564             break;
15565           }
15566         break;
15567       }
15568       case EnterNotify:
15569       {
15570         /*
15571           Selectively install colormap.
15572         */
15573         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15574           if (event.xcrossing.mode != NotifyUngrab)
15575             XInstallColormap(display,map_info->colormap);
15576         break;
15577       }
15578       case Expose:
15579       {
15580         if (display_image->debug != MagickFalse)
15581           (void) LogMagickEvent(X11Event,GetMagickModule(),
15582             "Expose: 0x%lx %dx%d+%d+%d",event.xexpose.window,
15583             event.xexpose.width,event.xexpose.height,event.xexpose.x,
15584             event.xexpose.y);
15585         /*
15586           Refresh windows that are now exposed.
15587         */
15588         if ((event.xexpose.window == windows->image.id) &&
15589             (windows->image.mapped != MagickFalse))
15590           {
15591             XRefreshWindow(display,&windows->image,&event);
15592             delay=display_image->delay/MagickMax(
15593               display_image->ticks_per_second,1L);
15594             timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15595             break;
15596           }
15597         if ((event.xexpose.window == windows->magnify.id) &&
15598             (windows->magnify.mapped != MagickFalse))
15599           {
15600             XMakeMagnifyImage(display,windows);
15601             break;
15602           }
15603         if (event.xexpose.window == windows->pan.id)
15604           {
15605             XDrawPanRectangle(display,windows);
15606             break;
15607           }
15608         if (event.xexpose.window == windows->icon.id)
15609           {
15610             XRefreshWindow(display,&windows->icon,&event);
15611             break;
15612           }
15613         break;
15614       }
15615       case KeyPress:
15616       {
15617         int
15618           length;
15619
15620         /*
15621           Respond to a user key press.
15622         */
15623         length=XLookupString((XKeyEvent *) &event.xkey,command,(int)
15624           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15625         *(command+length)='\0';
15626         if (display_image->debug != MagickFalse)
15627           (void) LogMagickEvent(X11Event,GetMagickModule(),
15628             "Key press: %d 0x%lx (%s)",event.xkey.state,(unsigned long)
15629             key_symbol,command);
15630         if (event.xkey.window == windows->image.id)
15631           {
15632             command_type=XImageWindowCommand(display,resource_info,windows,
15633               event.xkey.state,key_symbol,&display_image);
15634             if (command_type != NullCommand)
15635               nexus=XMagickCommand(display,resource_info,windows,command_type,
15636                 &display_image);
15637           }
15638         if (event.xkey.window == windows->magnify.id)
15639           XMagnifyWindowCommand(display,windows,event.xkey.state,key_symbol);
15640         if (event.xkey.window == windows->pan.id)
15641           {
15642             if ((key_symbol == XK_q) || (key_symbol == XK_Escape))
15643               (void) XWithdrawWindow(display,windows->pan.id,
15644                 windows->pan.screen);
15645             else
15646               if ((key_symbol == XK_F1) || (key_symbol == XK_Help))
15647                 XTextViewWidget(display,resource_info,windows,MagickFalse,
15648                   "Help Viewer - Image Pan",ImagePanHelp);
15649               else
15650                 XTranslateImage(display,windows,*image,key_symbol);
15651           }
15652         delay=display_image->delay/MagickMax(
15653           display_image->ticks_per_second,1L);
15654         timer=time((time_t *) NULL)+(delay == 0 ? 1 : delay)+1;
15655         break;
15656       }
15657       case KeyRelease:
15658       {
15659         /*
15660           Respond to a user key release.
15661         */
15662         (void) XLookupString((XKeyEvent *) &event.xkey,command,(int)
15663           sizeof(command),&key_symbol,(XComposeStatus *) NULL);
15664         if (display_image->debug != MagickFalse)
15665           (void) LogMagickEvent(X11Event,GetMagickModule(),
15666             "Key release: 0x%lx (%c)",(unsigned long) key_symbol,*command);
15667         break;
15668       }
15669       case LeaveNotify:
15670       {
15671         /*
15672           Selectively uninstall colormap.
15673         */
15674         if (map_info->colormap != XDefaultColormap(display,visual_info->screen))
15675           if (event.xcrossing.mode != NotifyUngrab)
15676             XUninstallColormap(display,map_info->colormap);
15677         break;
15678       }
15679       case MapNotify:
15680       {
15681         if (display_image->debug != MagickFalse)
15682           (void) LogMagickEvent(X11Event,GetMagickModule(),"Map Notify: 0x%lx",
15683             event.xmap.window);
15684         if (event.xmap.window == windows->backdrop.id)
15685           {
15686             (void) XSetInputFocus(display,event.xmap.window,RevertToParent,
15687               CurrentTime);
15688             windows->backdrop.mapped=MagickTrue;
15689             break;
15690           }
15691         if (event.xmap.window == windows->image.id)
15692           {
15693             if (windows->backdrop.id != (Window) NULL)
15694               (void) XInstallColormap(display,map_info->colormap);
15695             if (LocaleCompare(display_image->magick,"LOGO") == 0)
15696               {
15697                 if (LocaleCompare(display_image->filename,"LOGO") == 0)
15698                   nexus=XOpenImage(display,resource_info,windows,MagickFalse);
15699               }
15700             if (((int) windows->image.width < windows->image.ximage->width) ||
15701                 ((int) windows->image.height < windows->image.ximage->height))
15702               (void) XMapRaised(display,windows->pan.id);
15703             windows->image.mapped=MagickTrue;
15704             break;
15705           }
15706         if (event.xmap.window == windows->magnify.id)
15707           {
15708             XMakeMagnifyImage(display,windows);
15709             windows->magnify.mapped=MagickTrue;
15710             (void) XWithdrawWindow(display,windows->info.id,
15711               windows->info.screen);
15712             break;
15713           }
15714         if (event.xmap.window == windows->pan.id)
15715           {
15716             XMakePanImage(display,resource_info,windows,display_image);
15717             windows->pan.mapped=MagickTrue;
15718             break;
15719           }
15720         if (event.xmap.window == windows->info.id)
15721           {
15722             windows->info.mapped=MagickTrue;
15723             break;
15724           }
15725         if (event.xmap.window == windows->icon.id)
15726           {
15727             MagickBooleanType
15728               taint;
15729
15730             /*
15731               Create an icon image.
15732             */
15733             taint=display_image->taint;
15734             XMakeStandardColormap(display,icon_visual,icon_resources,
15735               display_image,icon_map,icon_pixel);
15736             (void) XMakeImage(display,icon_resources,&windows->icon,
15737               display_image,windows->icon.width,windows->icon.height);
15738             display_image->taint=taint;
15739             (void) XSetWindowBackgroundPixmap(display,windows->icon.id,
15740               windows->icon.pixmap);
15741             (void) XClearWindow(display,windows->icon.id);
15742             (void) XWithdrawWindow(display,windows->info.id,
15743               windows->info.screen);
15744             windows->icon.mapped=MagickTrue;
15745             break;
15746           }
15747         if (event.xmap.window == windows->command.id)
15748           {
15749             windows->command.mapped=MagickTrue;
15750             break;
15751           }
15752         if (event.xmap.window == windows->popup.id)
15753           {
15754             windows->popup.mapped=MagickTrue;
15755             break;
15756           }
15757         if (event.xmap.window == windows->widget.id)
15758           {
15759             windows->widget.mapped=MagickTrue;
15760             break;
15761           }
15762         break;
15763       }
15764       case MappingNotify:
15765       {
15766         (void) XRefreshKeyboardMapping(&event.xmapping);
15767         break;
15768       }
15769       case NoExpose:
15770         break;
15771       case PropertyNotify:
15772       {
15773         Atom
15774           type;
15775
15776         int
15777           format,
15778           status;
15779
15780         unsigned char
15781           *data;
15782
15783         unsigned long
15784           after,
15785           length;
15786
15787         if (display_image->debug != MagickFalse)
15788           (void) LogMagickEvent(X11Event,GetMagickModule(),
15789             "Property Notify: 0x%lx 0x%lx %d",event.xproperty.window,
15790             event.xproperty.atom,event.xproperty.state);
15791         if (event.xproperty.atom != windows->im_remote_command)
15792           break;
15793         /*
15794           Display image named by the remote command protocol.
15795         */
15796         status=XGetWindowProperty(display,event.xproperty.window,
15797           event.xproperty.atom,0L,(long) MaxTextExtent,MagickFalse,(Atom)
15798           AnyPropertyType,&type,&format,&length,&after,&data);
15799         if ((status != Success) || (length == 0))
15800           break;
15801         if (LocaleCompare((char *) data,"-quit") == 0)
15802           {
15803             XClientMessage(display,windows->image.id,windows->im_protocols,
15804               windows->im_exit,CurrentTime);
15805             (void) XFree((void *) data);
15806             break;
15807           }
15808         (void) CopyMagickString(resource_info->image_info->filename,
15809           (char *) data,MaxTextExtent);
15810         (void) XFree((void *) data);
15811         nexus=ReadImage(resource_info->image_info,&display_image->exception);
15812         CatchException(&display_image->exception);
15813         if (nexus != (Image *) NULL)
15814           *state|=NextImageState | ExitState;
15815         break;
15816       }
15817       case ReparentNotify:
15818       {
15819         if (display_image->debug != MagickFalse)
15820           (void) LogMagickEvent(X11Event,GetMagickModule(),
15821             "Reparent Notify: 0x%lx=>0x%lx",event.xreparent.parent,
15822             event.xreparent.window);
15823         break;
15824       }
15825       case UnmapNotify:
15826       {
15827         if (display_image->debug != MagickFalse)
15828           (void) LogMagickEvent(X11Event,GetMagickModule(),
15829             "Unmap Notify: 0x%lx",event.xunmap.window);
15830         if (event.xunmap.window == windows->backdrop.id)
15831           {
15832             windows->backdrop.mapped=MagickFalse;
15833             break;
15834           }
15835         if (event.xunmap.window == windows->image.id)
15836           {
15837             windows->image.mapped=MagickFalse;
15838             break;
15839           }
15840         if (event.xunmap.window == windows->magnify.id)
15841           {
15842             windows->magnify.mapped=MagickFalse;
15843             break;
15844           }
15845         if (event.xunmap.window == windows->pan.id)
15846           {
15847             windows->pan.mapped=MagickFalse;
15848             break;
15849           }
15850         if (event.xunmap.window == windows->info.id)
15851           {
15852             windows->info.mapped=MagickFalse;
15853             break;
15854           }
15855         if (event.xunmap.window == windows->icon.id)
15856           {
15857             if (map_info->colormap == icon_map->colormap)
15858               XConfigureImageColormap(display,resource_info,windows,
15859                 display_image);
15860             (void) XFreeStandardColormap(display,icon_visual,icon_map,
15861               icon_pixel);
15862             windows->icon.mapped=MagickFalse;
15863             break;
15864           }
15865         if (event.xunmap.window == windows->command.id)
15866           {
15867             windows->command.mapped=MagickFalse;
15868             break;
15869           }
15870         if (event.xunmap.window == windows->popup.id)
15871           {
15872             if (windows->backdrop.id != (Window) NULL)
15873               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15874                 CurrentTime);
15875             windows->popup.mapped=MagickFalse;
15876             break;
15877           }
15878         if (event.xunmap.window == windows->widget.id)
15879           {
15880             if (windows->backdrop.id != (Window) NULL)
15881               (void) XSetInputFocus(display,windows->image.id,RevertToParent,
15882                 CurrentTime);
15883             windows->widget.mapped=MagickFalse;
15884             break;
15885           }
15886         break;
15887       }
15888       default:
15889       {
15890         if (display_image->debug != MagickFalse)
15891           (void) LogMagickEvent(X11Event,GetMagickModule(),"Event type: %d",
15892             event.type);
15893         break;
15894       }
15895     }
15896   } while (!(*state & ExitState));
15897   if ((*state & ExitState) == 0)
15898     (void) XMagickCommand(display,resource_info,windows,FreeBuffersCommand,
15899       &display_image);
15900   else
15901     if (resource_info->confirm_edit != MagickFalse)
15902       {
15903         /*
15904           Query user if image has changed.
15905         */
15906         if ((resource_info->immutable == MagickFalse) &&
15907             (display_image->taint != MagickFalse))
15908           {
15909             int
15910               status;
15911
15912             status=XConfirmWidget(display,windows,"Your image changed.",
15913               "Do you want to save it");
15914             if (status == 0)
15915               *state&=(~ExitState);
15916             else
15917               if (status > 0)
15918                 (void) XMagickCommand(display,resource_info,windows,SaveCommand,
15919                   &display_image);
15920           }
15921       }
15922   if ((windows->visual_info->klass == GrayScale) ||
15923       (windows->visual_info->klass == PseudoColor) ||
15924       (windows->visual_info->klass == DirectColor))
15925     {
15926       /*
15927         Withdraw pan and Magnify window.
15928       */
15929       if (windows->info.mapped != MagickFalse)
15930         (void) XWithdrawWindow(display,windows->info.id,windows->info.screen);
15931       if (windows->magnify.mapped != MagickFalse)
15932         (void) XWithdrawWindow(display,windows->magnify.id,
15933           windows->magnify.screen);
15934       if (windows->command.mapped != MagickFalse)
15935         (void) XWithdrawWindow(display,windows->command.id,
15936           windows->command.screen);
15937     }
15938   if (windows->pan.mapped != MagickFalse)
15939     (void) XWithdrawWindow(display,windows->pan.id,windows->pan.screen);
15940   if (resource_info->backdrop == MagickFalse)
15941     if (windows->backdrop.mapped)
15942       {
15943         (void) XWithdrawWindow(display,windows->backdrop.id,
15944           windows->backdrop.screen);
15945         (void) XDestroyWindow(display,windows->backdrop.id);
15946         windows->backdrop.id=(Window) NULL;
15947         (void) XWithdrawWindow(display,windows->image.id,
15948           windows->image.screen);
15949         (void) XDestroyWindow(display,windows->image.id);
15950         windows->image.id=(Window) NULL;
15951       }
15952   XSetCursorState(display,windows,MagickTrue);
15953   XCheckRefreshWindows(display,windows);
15954   if (((*state & FormerImageState) != 0) || ((*state & NextImageState) != 0))
15955     *state&=(~ExitState);
15956   if (*state & ExitState)
15957     {
15958       /*
15959         Free Standard Colormap.
15960       */
15961       (void) XFreeStandardColormap(display,icon_visual,icon_map,icon_pixel);
15962       if (resource_info->map_type == (char *) NULL)
15963         (void) XFreeStandardColormap(display,visual_info,map_info,pixel);
15964       /*
15965         Free X resources.
15966       */
15967       if (resource_info->copy_image != (Image *) NULL)
15968         {
15969           resource_info->copy_image=DestroyImage(resource_info->copy_image);
15970           resource_info->copy_image=NewImageList();
15971         }
15972       DestroyXResources();
15973     }
15974   (void) XSync(display,MagickFalse);
15975   /*
15976     Restore our progress monitor and warning handlers.
15977   */
15978   (void) SetErrorHandler(warning_handler);
15979   (void) SetWarningHandler(warning_handler);
15980   /*
15981     Change to home directory.
15982   */
15983   directory=getcwd(working_directory,MaxTextExtent);
15984   (void) directory;
15985   {
15986     int
15987       status;
15988
15989     status=chdir(resource_info->home_directory);
15990     if (status == -1)
15991       (void) ThrowMagickException(&display_image->exception,GetMagickModule(),
15992         FileOpenError,"UnableToOpenFile","%s",resource_info->home_directory);
15993   }
15994   *image=display_image;
15995   return(nexus);
15996 }
15997 #else
15998 \f
15999 /*
16000 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16001 %                                                                             %
16002 %                                                                             %
16003 %                                                                             %
16004 +   D i s p l a y I m a g e s                                                 %
16005 %                                                                             %
16006 %                                                                             %
16007 %                                                                             %
16008 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16009 %
16010 %  DisplayImages() displays an image sequence to any X window screen.  It
16011 %  returns a value other than 0 if successful.  Check the exception member
16012 %  of image to determine the reason for any failure.
16013 %
16014 %  The format of the DisplayImages method is:
16015 %
16016 %      MagickBooleanType DisplayImages(const ImageInfo *image_info,
16017 %        Image *images)
16018 %
16019 %  A description of each parameter follows:
16020 %
16021 %    o image_info: the image info.
16022 %
16023 %    o image: the image.
16024 %
16025 */
16026 MagickExport MagickBooleanType DisplayImages(const ImageInfo *image_info,
16027   Image *image)
16028 {
16029   assert(image_info != (const ImageInfo *) NULL);
16030   assert(image_info->signature == MagickSignature);
16031   assert(image != (Image *) NULL);
16032   assert(image->signature == MagickSignature);
16033   if (image->debug != MagickFalse)
16034     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
16035   (void) ThrowMagickException(&image->exception,GetMagickModule(),
16036     MissingDelegateError,"DelegateLibrarySupportNotBuiltIn","`%s' (X11)",
16037     image->filename);
16038   return(MagickFalse);
16039 }
16040 \f
16041 /*
16042 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16043 %                                                                             %
16044 %                                                                             %
16045 %                                                                             %
16046 +   R e m o t e D i s p l a y C o m m a n d                                   %
16047 %                                                                             %
16048 %                                                                             %
16049 %                                                                             %
16050 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16051 %
16052 %  RemoteDisplayCommand() encourages a remote display program to display the
16053 %  specified image filename.
16054 %
16055 %  The format of the RemoteDisplayCommand method is:
16056 %
16057 %      MagickBooleanType RemoteDisplayCommand(const ImageInfo *image,
16058 %        const char *window,const char *filename,ExceptionInfo *exception)
16059 %
16060 %  A description of each parameter follows:
16061 %
16062 %    o image_info: the image info.
16063 %
16064 %    o window: Specifies the name or id of an X window.
16065 %
16066 %    o filename: the name of the image filename to display.
16067 %
16068 %    o exception: return any errors or warnings in this structure.
16069 %
16070 */
16071 MagickExport MagickBooleanType RemoteDisplayCommand(const ImageInfo *image_info,
16072   const char *window,const char *filename,ExceptionInfo *exception)
16073 {
16074   assert(image_info != (const ImageInfo *) NULL);
16075   assert(image_info->signature == MagickSignature);
16076   assert(filename != (char *) NULL);
16077   (void) window;
16078   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",filename);
16079   (void) ThrowMagickException(exception,GetMagickModule(),MissingDelegateError,
16080     "DelegateLibrarySupportNotBuiltIn","`%s' (X11)",image_info->filename);
16081   return(MagickFalse);
16082 }
16083 #endif